Tesvia's picture
added agent, config, exception, web_search
49f4445
raw
history blame
3.18 kB
"""
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"]