Spaces:
Sleeping
Sleeping
File size: 3,267 Bytes
3f46b6e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
"""
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"]
|