diff --git a/src/timmy/prompts.py b/src/timmy/prompts.py index 20935069..9b2fae4f 100644 --- a/src/timmy/prompts.py +++ b/src/timmy/prompts.py @@ -66,7 +66,6 @@ MEMORY (three tiers): TOOL USAGE: - Arithmetic: always use calculator. Never compute in your head. - Past context: memory_search -- Current events: web_search - File ops, code, shell: only on explicit request - General knowledge / greetings: no tools needed @@ -114,10 +113,9 @@ DECISION ORDER: 1. Is this arithmetic or math? → calculator (ALWAYS — never compute in your head) 2. Can I answer from training data? → Answer directly (NO TOOL) 3. Is this about past conversations? → memory_search -4. Is this current/real-time info? → web_search -5. Did user request file operations? → file tools -6. Requires code execution? → python -7. System command requested? → shell +4. Did user request file operations? → file tools +5. Requires code execution? → python +6. System command requested? → shell MEMORY SEARCH TRIGGERS: - "Have we discussed..." diff --git a/src/timmy/session.py b/src/timmy/session.py index c31402d5..f0ae99d2 100644 --- a/src/timmy/session.py +++ b/src/timmy/session.py @@ -31,7 +31,7 @@ _TOOL_CALL_JSON = re.compile( # Matches function-call-style text: memory_search(query="...") etc. _FUNC_CALL_TEXT = re.compile( - r"\b(?:memory_search|web_search|shell|python|read_file|write_file|list_files|calculator)" + r"\b(?:memory_search|shell|python|read_file|write_file|list_files|calculator)" r"\s*\([^)]*\)", ) diff --git a/src/timmy/tool_safety.py b/src/timmy/tool_safety.py index 95fee7d4..87977962 100644 --- a/src/timmy/tool_safety.py +++ b/src/timmy/tool_safety.py @@ -37,7 +37,6 @@ DANGEROUS_TOOLS = frozenset( # Tools that are safe to execute without confirmation. SAFE_TOOLS = frozenset( { - "web_search", "calculator", "memory_search", "memory_read", diff --git a/src/timmy/tools.py b/src/timmy/tools.py index a7513997..08a8d24d 100644 --- a/src/timmy/tools.py +++ b/src/timmy/tools.py @@ -1,7 +1,6 @@ """Tool integration for the agent swarm. Provides agents with capabilities for: -- Web search (DuckDuckGo) - File read/write (local filesystem) - Shell command execution (sandboxed) - Python code execution @@ -38,15 +37,6 @@ except ImportError as e: _AGNO_TOOLS_AVAILABLE = False _ImportError = e -# DuckDuckGo is optional — don't let it kill all tools -try: - from agno.tools.duckduckgo import DuckDuckGoTools - - _DUCKDUCKGO_AVAILABLE = True -except ImportError: - _DUCKDUCKGO_AVAILABLE = False - DuckDuckGoTools = None # type: ignore[assignment, misc] - # Track tool usage stats _TOOL_USAGE: dict[str, list[dict]] = {} @@ -228,17 +218,12 @@ def _make_smart_read_file(file_tools: FileTools) -> Callable: def create_research_tools(base_dir: str | Path | None = None): """Create tools for the research agent (Echo). - Includes: web search, file reading + Includes: file reading """ if not _AGNO_TOOLS_AVAILABLE: raise ImportError(f"Agno tools not available: {_ImportError}") toolkit = Toolkit(name="research") - # Web search via DuckDuckGo - if _DUCKDUCKGO_AVAILABLE: - search_tools = DuckDuckGoTools() - toolkit.register(search_tools.web_search, name="web_search") - # File reading from config import settings @@ -355,11 +340,6 @@ def create_data_tools(base_dir: str | Path | None = None): toolkit.register(_make_smart_read_file(file_tools), name="read_file") toolkit.register(file_tools.list_files, name="list_files") - # Web search for finding datasets - if _DUCKDUCKGO_AVAILABLE: - search_tools = DuckDuckGoTools() - toolkit.register(search_tools.web_search, name="web_search") - return toolkit @@ -385,7 +365,7 @@ def create_writing_tools(base_dir: str | Path | None = None): def create_security_tools(base_dir: str | Path | None = None): """Create tools for the security agent (Mace). - Includes: shell commands (for scanning), web search (for threat intel), file read + Includes: shell commands (for scanning), file read """ if not _AGNO_TOOLS_AVAILABLE: raise ImportError(f"Agno tools not available: {_ImportError}") @@ -395,11 +375,6 @@ def create_security_tools(base_dir: str | Path | None = None): shell_tools = ShellTools() toolkit.register(shell_tools.run_shell_command, name="shell") - # Web search for threat intelligence - if _DUCKDUCKGO_AVAILABLE: - search_tools = DuckDuckGoTools() - toolkit.register(search_tools.web_search, name="web_search") - # File reading for logs/configs base_path = Path(base_dir) if base_dir else Path(settings.repo_root) file_tools = FileTools(base_dir=base_path) @@ -510,13 +485,6 @@ def create_full_toolkit(base_dir: str | Path | None = None): # Function gets requires_confirmation=True). Fixes #79. toolkit.requires_confirmation_tools = list(DANGEROUS_TOOLS) - # Web search (optional — degrades gracefully if ddgs not installed) - if _DUCKDUCKGO_AVAILABLE: - search_tools = DuckDuckGoTools() - toolkit.register(search_tools.web_search, name="web_search") - else: - logger.debug("DuckDuckGo tools unavailable (ddgs not installed) — skipping web_search") - # Python execution python_tools = PythonTools() toolkit.register(python_tools.run_python_code, name="python") @@ -739,11 +707,6 @@ def get_all_available_tools() -> dict[str, dict]: Dict mapping tool categories to their tools and descriptions. """ catalog = { - "web_search": { - "name": "Web Search", - "description": "Search the web using DuckDuckGo", - "available_in": ["echo", "seer", "mace", "orchestrator"], - }, "shell": { "name": "Shell Commands", "description": "Execute shell commands (sandboxed)", diff --git a/tests/integrations/test_discord_confirmation.py b/tests/integrations/test_discord_confirmation.py index 9aa0f9df..358920b7 100644 --- a/tests/integrations/test_discord_confirmation.py +++ b/tests/integrations/test_discord_confirmation.py @@ -69,7 +69,7 @@ class TestGetImpactLevel: def test_low_impact(self): from integrations.chat_bridge.vendors.discord import _get_impact_level - assert _get_impact_level("web_search") == "low" + assert _get_impact_level("calculator") == "low" assert _get_impact_level("unknown") == "low" @@ -104,10 +104,10 @@ class TestToolSafety: assert requires_confirmation("calculator") is False - def test_web_search_is_safe(self): + def test_memory_search_is_safe(self): from timmy.tool_safety import requires_confirmation - assert requires_confirmation("web_search") is False + assert requires_confirmation("memory_search") is False def test_unknown_tool_requires_confirmation(self): from timmy.tool_safety import requires_confirmation diff --git a/tests/timmy/test_timmy_tools.py b/tests/timmy/test_timmy_tools.py index f3ccaa4f..94e1b443 100644 --- a/tests/timmy/test_timmy_tools.py +++ b/tests/timmy/test_timmy_tools.py @@ -29,7 +29,7 @@ def clear_usage(): class TestToolTracking: def test_track_creates_agent_entry(self): - _track_tool_usage("agent-1", "web_search", success=True) + _track_tool_usage("agent-1", "calculator", success=True) assert "agent-1" in _TOOL_USAGE assert len(_TOOL_USAGE["agent-1"]) == 1 @@ -132,7 +132,6 @@ class TestToolCatalog: def test_catalog_contains_base_tools(self): catalog = get_all_available_tools() base_tools = { - "web_search", "shell", "python", "read_file", @@ -141,6 +140,8 @@ class TestToolCatalog: } for tool_id in base_tools: assert tool_id in catalog, f"Missing base tool: {tool_id}" + # web_search removed — dead code, ddgs never installed (#87) + assert "web_search" not in catalog def test_catalog_tool_structure(self): catalog = get_all_available_tools() @@ -153,7 +154,6 @@ class TestToolCatalog: def test_catalog_orchestrator_has_all_base_tools(self): catalog = get_all_available_tools() base_tools = { - "web_search", "shell", "python", "read_file", @@ -167,7 +167,6 @@ class TestToolCatalog: def test_catalog_echo_research_tools(self): catalog = get_all_available_tools() - assert "echo" in catalog["web_search"]["available_in"] assert "echo" in catalog["read_file"]["available_in"] # Echo should NOT have shell assert "echo" not in catalog["shell"]["available_in"] diff --git a/tests/timmy/test_tool_safety.py b/tests/timmy/test_tool_safety.py index 49b8fc9a..c4703cd6 100644 --- a/tests/timmy/test_tool_safety.py +++ b/tests/timmy/test_tool_safety.py @@ -27,7 +27,7 @@ class TestRequiresConfirmation: assert requires_confirmation(tool) is True def test_safe_tools(self): - for tool in ("web_search", "calculator", "read_file", "list_files"): + for tool in ("calculator", "read_file", "list_files"): assert requires_confirmation(tool) is False def test_unknown_defaults_to_dangerous(self): @@ -118,7 +118,7 @@ class TestGetImpactLevel: assert get_impact_level("aider") == "medium" def test_low(self): - assert get_impact_level("web_search") == "low" + assert get_impact_level("calculator") == "low" assert get_impact_level("unknown") == "low"