fix: use None sentinel instead of 'unknown' in _extract_tool_name

Addresses Cursor Bugbot feedback: the string 'unknown' could collide
with a tool legitimately named 'unknown'. Now _extract_tool_name
returns None for unrecognised formats, and _should_parallelize checks
'is None' instead of '== "unknown"'.

Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
Devin AI
2026-04-02 23:12:11 +00:00
parent 901475e78c
commit e40b29b5f5
2 changed files with 16 additions and 11 deletions

View File

@@ -1690,7 +1690,7 @@ class AgentExecutor(Flow[AgentExecutorState], CrewAgentExecutorMixin):
for tool_call in tool_calls:
func_name = self._extract_tool_name(tool_call)
if func_name == "unknown":
if func_name is None:
continue
original_tool = self._resolve_original_tool(func_name)
@@ -1908,8 +1908,14 @@ class AgentExecutor(Flow[AgentExecutorState], CrewAgentExecutorMixin):
"original_tool": original_tool,
}
def _extract_tool_name(self, tool_call: Any) -> str:
"""Extract tool name from various tool call formats."""
def _extract_tool_name(self, tool_call: Any) -> str | None:
"""Extract tool name from various tool call formats.
Returns:
The sanitized tool name, or ``None`` when the format is
unrecognised (avoids collisions with a tool legitimately
named "unknown").
"""
if hasattr(tool_call, "function"):
return sanitize_tool_name(tool_call.function.name)
if hasattr(tool_call, "function_call") and tool_call.function_call:
@@ -1918,10 +1924,9 @@ class AgentExecutor(Flow[AgentExecutorState], CrewAgentExecutorMixin):
return sanitize_tool_name(tool_call.name)
if isinstance(tool_call, dict):
func_info = tool_call.get("function", {})
return sanitize_tool_name(
func_info.get("name", "") or tool_call.get("name", "unknown")
)
return "unknown"
name = func_info.get("name", "") or tool_call.get("name", "")
return sanitize_tool_name(name) if name else None
return None
@router(execute_native_tool)
def check_native_todo_completion(

View File

@@ -2141,12 +2141,12 @@ class TestExtractToolName:
assert executor._extract_tool_name(tc) == "bedrock_tool"
def test_unknown_format_returns_unknown(self, mock_dependencies):
"""Completely unrecognized object returns 'unknown'."""
def test_unknown_format_returns_none(self, mock_dependencies):
"""Completely unrecognized object returns None (not 'unknown')."""
executor = _build_executor(**mock_dependencies)
assert executor._extract_tool_name(42) == "unknown"
assert executor._extract_tool_name("not a tool call") == "unknown"
assert executor._extract_tool_name(42) is None
assert executor._extract_tool_name("not a tool call") is None
def test_sanitizes_names(self, mock_dependencies):
"""Tool names with special characters are sanitized."""