fix: prune dead web_search tool — ddgs never installed (#87)
Remove DuckDuckGoTools import, all web_search registrations across 4 toolkit factories, catalog entry, safety classification, prompt references, and session regex. Total: -41 lines of dead code. consult_grok is functional (grok_enabled=True, API key set) and opt-in, so it stays — but Timmy never calls it autonomously, which is correct sovereign behavior (no cloud calls unless user permits). Closes #87
This commit is contained in:
@@ -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..."
|
||||
|
||||
@@ -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*\([^)]*\)",
|
||||
)
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user