8000 refactor: Disallow similar tool names in the tool registry (#193) · StrandForge/sdk-python@264f511 · GitHub
[go: up one dir, main page]

Skip to content

Commit 264f511

Browse files
authored
refactor: Disallow similar tool names in the tool registry (strands-agents#193)
Per follow-up to strands-agents#178, where we discussed preventing similar_tool and similar-tool from both being added to the tool registry, to avoid ambiguity in direct-method invocations
1 parent 7c7f91e commit 264f511

File tree

5 files changed

+38
-31
lines changed

5 files changed

+38
-31
lines changed

src/strands/agent/agent.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,10 @@ def find_normalized_tool_name() -> Optional[str]:
108108
# all tools that can be represented with the normalized name
109109
if "_" in name:
110110
filtered_tools = [
111-
tool_name
112-
for (tool_name, tool) in tool_registry.items()
113-
if tool_name.replace("-", "_") == name
111+
tool_name for (tool_name, tool) in tool_registry.items() if tool_name.replace("-", "_") == name
114112
]
115113

116-
if len(filtered_tools) > 1:
117-
raise AttributeError(f"Multiple tools matching '{name}' found: {', '.join(filtered_tools)}")
118-
114+
# The registry itself defends against similar names, so we can just take the first match
119115
if filtered_tools:
120116
return filtered_tools[0]
121117

src/strands/telemetry/tracer.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313

1414
from opentelemetry import trace
1515
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
16-
17-
# See https://github.com/open-telemetry/opentelemetry-python/issues/4615 for the type ignore
18-
from opentelemetry.sdk.resources import Resource # type: ignore[attr-defined]
16+
from opentelemetry.sdk.resources import Resource
1917
from opentelemetry.sdk.trace import TracerProvider
2018
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter, SimpleSpanProcessor
2119
from opentelemetry.trace import StatusCode

src/strands/tools/registry.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,21 @@ def register_tool(self, tool: AgentTool) -> None:
189189
tool.is_dynamic,
190190
)
191191

192+
if self.registry.get(tool.tool_name) is None:
193+
normalized_name = tool.tool_name.replace("-", "_")
194+
195+
matching_tools = [
196+
tool_name
197+
for (tool_name, tool) in self.registry.items()
198+
if tool_name.replace("-", "_") == normalized_name
199+
]
200+
201+
if matching_tools:
202+
raise ValueError(
203+
f"Tool name '{tool.tool_name}' already exists as '{matching_tools[0]}'."
204+
" Cannot add a duplicate tool which differs by a '-' or '_'"
205+
)
206+
192207
# Register in main registry
193208
self.registry[tool.tool_name] = tool
194209

tests/strands/agent/test_agent.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -739,28 +739,6 @@ def function(system_prompt: str) -> str:
739739
}
740740

741741

742-
def test_agent_tool_with_multiple_normalized_matches(agent, tool_registry, mock_randint):
743-
agent.tool_handler = unittest.mock.Mock()
744-
745-
@strands.tools.tool(name="system-prompter_1")
746-
def function1(system_prompt: str) -> str:
747-
return system_prompt
748-
749-
@strands.tools.tool(name="system-prompter-1")
750-
def function2(system_prompt: str) -> str:
751-
return system_prompt
752-
753-
agent.tool_registry.register_tool(strands.tools.tools.FunctionTool(function1))
754-
agent.tool_registry.register_tool(strands.tools.tools.FunctionTool(function2))
755-
756-
mock_randint.return_value = 1
757-
758-
with pytest.raises(AttributeError) as err:
759-
agent.tool.system_prompter_1(system_prompt="tool prompt")
760-
761-
assert str(err.value) == "Multiple tools matching 'system_prompter_1' found: system-prompter_1, system-prompter-1"
762-
763-
764742
def test_agent_tool_with_no_normalized_match(agent, tool_registry, mock_randint):
765743
agent.tool_handler = unittest.mock.Mock()
766744

tests/strands/tools/test_registry.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
Tests for the SDK tool registry module.
33
"""
44

5+
from unittest.mock import MagicMock
6+
57
import pytest
68

9+
from strands.tools import PythonAgentTool
710
from strands.tools.registry import ToolRegistry
811

912

@@ -23,3 +26,20 @@ def test_process_tools_with_invalid_path():
2326

2427
with pytest.raises(ValueError, match=f"Failed to load tool {invalid_path.split('.')[0]}: Tool file not found:.*"):
2528
tool_registry.process_tools([invalid_path])
29+
30+
31+
def test_register_tool_with_similar_name_raises():
32+
tool_1 = PythonAgentTool(tool_name="tool-like-this", tool_spec=MagicMock(), callback=lambda: None)
33+
tool_2 = PythonAgentTool(tool_name="tool_like_this", tool_spec=MagicMock(), callback=lambda: None)
34+
35+
tool_registry = ToolRegistry()
36+
37+
tool_registry.register_tool(tool_1)
38+
39+
with pytest.raises(ValueError) as err:
40+
tool_registry.register_tool(tool_2)
41+
42+
assert (
43+
str(err.value) == "Tool name 'tool_like_this' already exists as 'tool-like-this'. "
44+
"Cannot add a duplicate tool which differs by a '-' or '_'"
45+
)

0 commit comments

Comments
 (0)
0