mangubee Claude Sonnet 4.5 commited on
Commit
59f3cce
·
1 Parent(s): 4eed151

Fix: Tool name consistency across TOOLS registry and TOOL_FUNCTIONS

Browse files

Resolved mismatch between:
- TOOLS registry (src/tools/__init__.py): "web_search", "calculator", "vision"
- TOOL_FUNCTIONS (src/agent/graph.py): Was using "search", "safe_eval", "analyze_image"

Changes:
- Updated TOOL_FUNCTIONS to match TOOLS registry names
- Updated fallback_tool_selection() to use correct names
- Fixed test mock to use "web_search" instead of "search"

Result: HuggingFace LLM function calling now works correctly
Tests: 99/99 passing ✅

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Files changed (2) hide show
  1. src/agent/graph.py +13 -11
  2. test/test_llm_integration.py +1 -1
src/agent/graph.py CHANGED
@@ -108,10 +108,10 @@ def fallback_tool_selection(question: str, plan: str) -> List[dict]:
108
  # Extract search query - use first sentence or full question
109
  query = question.split('.')[0] if '.' in question else question
110
  tool_calls.append({
111
- "tool": "search",
112
  "params": {"query": query}
113
  })
114
- logger.info(f"[fallback_tool_selection] Added search tool with query: {query}")
115
 
116
  # Math tool: keywords like "calculate", "compute", "+", "-", "*", "/", "="
117
  math_keywords = ["calculate", "compute", "math", "sum", "multiply", "divide", "+", "-", "*", "/", "="]
@@ -123,10 +123,10 @@ def fallback_tool_selection(question: str, plan: str) -> List[dict]:
123
  if expr_match:
124
  expression = expr_match.group().strip()
125
  tool_calls.append({
126
- "tool": "safe_eval",
127
  "params": {"expression": expression}
128
  })
129
- logger.info(f"[fallback_tool_selection] Added safe_eval tool with expression: {expression}")
130
 
131
  # File tool: keywords like "file", "parse", "read", "csv", "json", "txt"
132
  file_keywords = ["file", "parse", "read", "csv", "json", "txt", "document"]
@@ -144,7 +144,7 @@ def fallback_tool_selection(question: str, plan: str) -> List[dict]:
144
  logger.warning("[fallback_tool_selection] No tools selected by fallback - adding default search")
145
  # Default: just search the question
146
  tool_calls.append({
147
- "tool": "search",
148
  "params": {"query": question}
149
  })
150
 
@@ -220,11 +220,12 @@ def execute_node(state: AgentState) -> AgentState:
220
  logger.info(f"[execute_node] Question: {state['question']}")
221
 
222
  # Map tool names to actual functions
 
223
  TOOL_FUNCTIONS = {
224
- "search": search,
225
  "parse_file": parse_file,
226
- "safe_eval": safe_eval,
227
- "analyze_image": analyze_image,
228
  }
229
 
230
  # Initialize results lists
@@ -315,11 +316,12 @@ def execute_node(state: AgentState) -> AgentState:
315
  logger.info(f"[execute_node] Fallback after exception returned {len(tool_calls)} tool(s)")
316
 
317
  # Try to execute fallback tools
 
318
  TOOL_FUNCTIONS = {
319
- "search": search,
320
  "parse_file": parse_file,
321
- "safe_eval": safe_eval,
322
- "analyze_image": analyze_image,
323
  }
324
 
325
  for tool_call in tool_calls:
 
108
  # Extract search query - use first sentence or full question
109
  query = question.split('.')[0] if '.' in question else question
110
  tool_calls.append({
111
+ "tool": "web_search",
112
  "params": {"query": query}
113
  })
114
+ logger.info(f"[fallback_tool_selection] Added web_search tool with query: {query}")
115
 
116
  # Math tool: keywords like "calculate", "compute", "+", "-", "*", "/", "="
117
  math_keywords = ["calculate", "compute", "math", "sum", "multiply", "divide", "+", "-", "*", "/", "="]
 
123
  if expr_match:
124
  expression = expr_match.group().strip()
125
  tool_calls.append({
126
+ "tool": "calculator",
127
  "params": {"expression": expression}
128
  })
129
+ logger.info(f"[fallback_tool_selection] Added calculator tool with expression: {expression}")
130
 
131
  # File tool: keywords like "file", "parse", "read", "csv", "json", "txt"
132
  file_keywords = ["file", "parse", "read", "csv", "json", "txt", "document"]
 
144
  logger.warning("[fallback_tool_selection] No tools selected by fallback - adding default search")
145
  # Default: just search the question
146
  tool_calls.append({
147
+ "tool": "web_search",
148
  "params": {"query": question}
149
  })
150
 
 
220
  logger.info(f"[execute_node] Question: {state['question']}")
221
 
222
  # Map tool names to actual functions
223
+ # NOTE: Keys must match TOOLS registry in src/tools/__init__.py
224
  TOOL_FUNCTIONS = {
225
+ "web_search": search,
226
  "parse_file": parse_file,
227
+ "calculator": safe_eval,
228
+ "vision": analyze_image,
229
  }
230
 
231
  # Initialize results lists
 
316
  logger.info(f"[execute_node] Fallback after exception returned {len(tool_calls)} tool(s)")
317
 
318
  # Try to execute fallback tools
319
+ # NOTE: Keys must match TOOLS registry in src/tools/__init__.py
320
  TOOL_FUNCTIONS = {
321
+ "web_search": search,
322
  "parse_file": parse_file,
323
+ "calculator": safe_eval,
324
+ "vision": analyze_image,
325
  }
326
 
327
  for tool_call in tool_calls:
test/test_llm_integration.py CHANGED
@@ -234,7 +234,7 @@ class TestEndToEndWorkflow:
234
  mock_tool_response = MagicMock()
235
  mock_tool_use = MagicMock()
236
  mock_tool_use.type = "tool_use"
237
- mock_tool_use.name = "search"
238
  mock_tool_use.input = {"query": "capital of France"}
239
  mock_tool_use.id = "call_001"
240
  mock_tool_response.content = [mock_tool_use]
 
234
  mock_tool_response = MagicMock()
235
  mock_tool_use = MagicMock()
236
  mock_tool_use.type = "tool_use"
237
+ mock_tool_use.name = "web_search"
238
  mock_tool_use.input = {"query": "capital of France"}
239
  mock_tool_use.id = "call_001"
240
  mock_tool_response.content = [mock_tool_use]