| """Tool factory for the agent.""" |
|
|
| import os |
|
|
| from exa_py import Exa |
| from smolagents import Tool |
| from scripts.text_inspector_tool import TextInspectorTool |
| from scripts.text_web_browser import ( |
| ArchiveSearchTool, |
| FinderTool, |
| FindNextTool, |
| PageDownTool, |
| PageUpTool, |
| SimpleTextBrowser, |
| VisitTool, |
| ) |
| from scripts.visual_qa import visualizer |
|
|
| from config import BROWSER_CONFIG, TEXT_LIMIT |
|
|
|
|
| class ExaSearchTool(Tool): |
| name = "web_search" |
| description = ( |
| "Perform a web search and return relevant results with key excerpts. " |
| "Use a natural language query. Optionally filter by year." |
| ) |
| inputs = { |
| "query": {"type": "string", "description": "The natural language search query."}, |
| "filter_year": { |
| "type": "string", |
| "description": "[Optional]: Filter results to a specific year, e.g. '2024'.", |
| "nullable": True, |
| }, |
| } |
| output_type = "string" |
|
|
| def __init__(self, api_key: str): |
| super().__init__() |
| self.exa = Exa(api_key=api_key) |
|
|
| def forward(self, query: str, filter_year: str = None) -> str: |
| kwargs = { |
| "num_results": 10, |
| "contents": {"highlights": True}, |
| } |
| if filter_year: |
| kwargs["start_published_date"] = f"{filter_year}-01-01" |
| kwargs["end_published_date"] = f"{filter_year}-12-31" |
|
|
| results = self.exa.search(query, **kwargs) |
|
|
| if not results.results: |
| return f"No results found for '{query}'." |
|
|
| lines = [f"Exa search for '{query}' returned {len(results.results)} results:\n"] |
| for i, r in enumerate(results.results, 1): |
| date = f"\nDate published: {r.published_date}" if r.published_date else "" |
| highlights = "" |
| if r.highlights: |
| highlights = "\n" + "\n".join(f" • {h}" for h in r.highlights) |
| lines.append(f"{i}. [{r.title}]({r.url}){date}{highlights}") |
|
|
| return "\n\n".join(lines) |
|
|
|
|
| class UnavailableWebSearchTool(Tool): |
| name = "web_search" |
| description = ( |
| "Web search is currently unavailable because EXA_API_KEY is not set. " |
| "This tool returns a helpful message instead of raising an error." |
| ) |
| inputs = { |
| "query": {"type": "string", "description": "The natural language search query."}, |
| "filter_year": { |
| "type": "string", |
| "description": "[Optional]: Filter results to a specific year, e.g. '2024'.", |
| "nullable": True, |
| }, |
| } |
| output_type = "string" |
|
|
| def forward(self, query: str, filter_year: str = None) -> str: |
| year_note = f" for {filter_year}" if filter_year else "" |
| return ( |
| f"Web search{year_note} is unavailable because EXA_API_KEY is not set. " |
| "Set EXA_API_KEY in your environment or Space secrets to enable search." |
| ) |
|
|
|
|
| def _create_web_search_tool() -> Tool: |
| api_key = os.getenv("EXA_API_KEY") |
| if not api_key: |
| print( |
| "Warning: EXA_API_KEY not set. Web search will be disabled until it is configured." |
| ) |
| return UnavailableWebSearchTool() |
| return ExaSearchTool(api_key=api_key) |
|
|
|
|
| def build_tools(model): |
| web_search = _create_web_search_tool() |
| browser = SimpleTextBrowser(**BROWSER_CONFIG) |
| ti_tool = TextInspectorTool(model, TEXT_LIMIT) |
|
|
| web_tools = [ |
| web_search, |
| VisitTool(browser), |
| PageUpTool(browser), |
| PageDownTool(browser), |
| FinderTool(browser), |
| FindNextTool(browser), |
| ArchiveSearchTool(browser), |
| ti_tool, |
| ] |
|
|
| document_inspection_tool = TextInspectorTool(model, TEXT_LIMIT) |
|
|
| return web_tools, document_inspection_tool, visualizer |