""" agent.py – central coordinator for smolagents-powered agent. This file exposes a single helper function `my_agent()` that returns an object which is **callable** (i.e. implements `__call__(question:str) -> str`) so that `app.py` can stay unchanged apart from a single import. * Adding new tools ------------------ 1. Drop the tool file inside the ``/tools`` package. 2. Import the tool class in `my_agent` and append it to the ``tools`` list. The rest of the application will automatically pick it up. """ from typing import List, Sequence try: from smolagents import Agent, Tool # type: ignore except ImportError as exc: # pragma: no cover raise ImportError( "smolagents must be in requirements.txt. " "Add `smolagents` to your dependencies." ) from exc # Available tools from tools.web_search import DuckDuckGoSearchTool # noqa: E402 class SmolAgentWrapper: """ Thin wrapper that makes a smolagents.Agent *callable*. The evaluation harness in app.py expects an object that can be called directly with a single question and that returns a string. The underlying smolagents agent is session-aware and can handle multi-turn conversations but we keep the public interface single-turn for now. """ def __init__(self, tools: Sequence["Tool"] | None = None) -> None: # type: ignore[name-defined] if tools is None: tools = [DuckDuckGoSearchTool()] self._agent = Agent(tools=list(tools)) # Allow the object itself to be called like a function def __call__(self, question: str) -> str: # noqa: D401 (simple summary ok) """ Ask the underlying smolagents Agent a **single** question and return the answer. Any exception is caught and surfaced as a readable string in order not to crash the evaluation loop. """ try: response = self._agent.run(question) # smolagents may return dicts or ToolOutput objects; normalise to str if isinstance(response, str): return response return str(response) except Exception as err: # pragma: no cover return f"ERROR: {type(err).__name__}: {err}" # --------------------------------------------------------------------------- # # Helper – this is what app.py will import # --------------------------------------------------------------------------- # def my_agent(extra_tools: Sequence["Tool"] | None = None) -> SmolAgentWrapper: # type: ignore[name-defined] """ Factory that returns a ready-to-go agent. Parameters ---------- extra_tools: Optional sequence of additional smolagents Tool objects to extend the agent's capabilities. They are appended **after** the default search tool so they can override it if they expose the same name. Returns ------- SmolAgentWrapper A callable object compatible with the original BasicAgent. """ tools: List["Tool"] = [DuckDuckGoSearchTool()] if extra_tools: tools.extend(extra_tools) return SmolAgentWrapper(tools=tools) __all__ = ["my_agent", "SmolAgentWrapper"]