- run_librarian / run_librarian_stream raise AgentError instead of
returning/yielding error text as normal output; detail stays in logs
- delegate_to_* wrappers now put a curated butler-toned sentence in
DelegationResult.output on failure and never expose str(e), so
streaming's error branch is reachable and honest
- _execute_single_delegation propagates success; direct delegation only
records delegate_to_* as called when the expert actually succeeded
- librarian tools return user-safe messages instead of
'Error searching: {e}' strings that leaked internal URLs into
synthesis; coordination stream errors are curated as well
- ruff cleanups (TYPE_CHECKING forward refs, B904, unused locals) in
the touched files to keep them lint-clean
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
"""
|
|
Tests for structured failure behavior of The Librarian entry points.
|
|
|
|
run_librarian and run_librarian_stream must raise AgentError on failure
|
|
instead of returning/yielding error text as if it were research output,
|
|
and the raised error must not leak exception detail (internal URLs etc.).
|
|
"""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from src.agents.librarian.agent import run_librarian, run_librarian_stream
|
|
from src.agents.protocol import AgentError
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRunLibrarianFailures:
|
|
"""run_librarian raises structured errors instead of returning text."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_run_librarian_raises_agent_error(self):
|
|
"""Failures raise AgentError rather than returning error prose."""
|
|
mock_agent = MagicMock()
|
|
mock_agent.run = AsyncMock(
|
|
side_effect=RuntimeError("Connection refused to http://internal:8089")
|
|
)
|
|
|
|
with patch(
|
|
"src.agents.librarian.agent.get_librarian_agent",
|
|
return_value=mock_agent,
|
|
):
|
|
with pytest.raises(AgentError) as exc_info:
|
|
await run_librarian(task="Find Docker docs")
|
|
|
|
assert exc_info.value.agent_name == "librarian"
|
|
# Exception detail stays in logs only
|
|
assert "internal" not in str(exc_info.value)
|
|
assert "Connection refused" not in str(exc_info.value)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_run_librarian_stream_raises_agent_error(self):
|
|
"""Streaming failures raise instead of yielding error text."""
|
|
mock_agent = MagicMock()
|
|
mock_agent.run_stream = MagicMock(
|
|
side_effect=RuntimeError("Connection refused to http://internal:8089")
|
|
)
|
|
|
|
with patch(
|
|
"src.agents.librarian.agent.get_librarian_agent",
|
|
return_value=mock_agent,
|
|
):
|
|
collected: list[str] = []
|
|
with pytest.raises(AgentError) as exc_info:
|
|
async for delta in run_librarian_stream(task="Find Docker docs"):
|
|
collected.append(delta)
|
|
|
|
assert exc_info.value.agent_name == "librarian"
|
|
assert collected == [], "no error text may be yielded as output"
|
|
assert "internal" not in str(exc_info.value)
|