fix(agents): make librarian failures structured and user-safe
- 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>
This commit is contained in:
@@ -7,7 +7,7 @@ the library-desk API, offering:
|
||||
- Wiki and document management
|
||||
- Semantic search and knowledge graph exploration
|
||||
"""
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
from pydantic_ai import Agent
|
||||
|
||||
@@ -27,7 +27,7 @@ from src.agents.librarian.tools import (
|
||||
smart_create_wiki_page,
|
||||
update_wiki_page,
|
||||
)
|
||||
from src.core.config import config
|
||||
from src.agents.protocol import AgentError
|
||||
from src.core.logging_config import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
@@ -138,7 +138,7 @@ If a tool fails or you cannot access a data source:
|
||||
"""
|
||||
|
||||
# Lazy initialization to avoid connection issues during imports
|
||||
_librarian_agent: Optional[Agent[None, str]] = None
|
||||
_librarian_agent: Agent[None, str] | None = None
|
||||
|
||||
|
||||
def _create_librarian_agent() -> Agent[None, str]:
|
||||
@@ -204,7 +204,7 @@ def get_librarian_agent() -> Agent[None, str]:
|
||||
async def run_librarian(
|
||||
task: str,
|
||||
context: str = "",
|
||||
message_history: Optional[list[Any]] = None,
|
||||
message_history: list[Any] | None = None,
|
||||
) -> str:
|
||||
"""
|
||||
Execute a research task with The Librarian.
|
||||
@@ -220,6 +220,10 @@ async def run_librarian(
|
||||
Returns:
|
||||
Research results and findings
|
||||
|
||||
Raises:
|
||||
AgentError: If the research task fails. Exception detail is
|
||||
logged here; callers map the failure to a user-safe message.
|
||||
|
||||
Example:
|
||||
result = await run_librarian(
|
||||
task="Find information about Docker networking",
|
||||
@@ -255,19 +259,23 @@ async def run_librarian(
|
||||
return result.output
|
||||
|
||||
except Exception as e:
|
||||
# Full detail stays in the logs; callers receive a structured
|
||||
# failure instead of error text masquerading as research output.
|
||||
logger.error(
|
||||
"librarian_task_error",
|
||||
task=task[:50],
|
||||
error=str(e),
|
||||
exc_info=True,
|
||||
)
|
||||
return f"The Librarian encountered an error: {str(e)}"
|
||||
raise AgentError(
|
||||
"Research task failed", agent_name="librarian"
|
||||
) from e
|
||||
|
||||
|
||||
async def run_librarian_stream(
|
||||
task: str,
|
||||
context: str = "",
|
||||
message_history: Optional[list[Any]] = None,
|
||||
message_history: list[Any] | None = None,
|
||||
):
|
||||
"""
|
||||
Execute a research task with streaming output.
|
||||
@@ -282,6 +290,10 @@ async def run_librarian_stream(
|
||||
Yields:
|
||||
str: Text deltas from the response
|
||||
|
||||
Raises:
|
||||
AgentError: If the research task fails. Exception detail is
|
||||
logged here; callers map the failure to a user-safe message.
|
||||
|
||||
Example:
|
||||
async for delta in run_librarian_stream("Find Docker docs"):
|
||||
print(delta, end="", flush=True)
|
||||
@@ -309,10 +321,14 @@ async def run_librarian_stream(
|
||||
logger.info("librarian_stream_completed", task=task[:50])
|
||||
|
||||
except Exception as e:
|
||||
# Full detail stays in the logs; raise instead of yielding error
|
||||
# text into the stream as if it were research output.
|
||||
logger.error(
|
||||
"librarian_stream_error",
|
||||
task=task[:50],
|
||||
error=str(e),
|
||||
exc_info=True,
|
||||
)
|
||||
yield f"\n\nThe Librarian encountered an error: {str(e)}"
|
||||
raise AgentError(
|
||||
"Research task failed", agent_name="librarian"
|
||||
) from e
|
||||
|
||||
Reference in New Issue
Block a user