Skip to content

Fix(Tool): Class method with @tool decorator is also accepted as a valid Tool. #200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ __pycache__*
.ruff_cache
*.bak
.vscode
dist
dist
.idea
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def reduce_context(self, agent: "Agent", e: Optional[Exception] = None) -> None:
if results_truncated:
logger.debug("message_index=<%s> | tool results truncated", last_message_idx_with_tool_results)
return

# Try to trim index id when tool result cannot be truncated anymore
# If the number of messages is less than the window_size, then we default to 2, otherwise, trim to window size
trim_index = 2 if len(messages) <= self.window_size else len(messages) - self.window_size
Expand Down
4 changes: 2 additions & 2 deletions src/strands/tools/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ def process_tools(self, tools: List[Any]) -> List[str]:
if not function_tools:
logger.warning("tool_name=<%s>, module_path=<%s> | invalid agent tool", tool_name, module_path)

# Case 5: Function decorated with @tool
elif inspect.isfunction(tool) and hasattr(tool, "TOOL_SPEC"):
# Case 5: Function or Method decorated with @tool
elif (inspect.isfunction(tool) or inspect.ismethod(tool)) and hasattr(tool, "TOOL_SPEC"):
try:
function_tool = FunctionTool(tool)
logger.debug("tool_name=<%s> | registering function tool", function_tool.tool_name)
Expand Down
4 changes: 2 additions & 2 deletions tests-integ/test_mcp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ def test_can_reuse_mcp_client():


@pytest.mark.skipif(
condition=os.environ.get("GITHUB_ACTIONS") == 'true',
reason="streamable transport is failing in GitHub actions, debugging if linux compatibility issue"
condition=os.environ.get("GITHUB_ACTIONS") == "true",
reason="streamable transport is failing in GitHub actions, debugging if linux compatibility issue",
)
def test_streamable_http_mcp_client():
server_thread = threading.Thread(
Expand Down
Empty file.
23 changes: 23 additions & 0 deletions tests/strands/tools/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pytest

from strands import tool
from strands.tools import PythonAgentTool
from strands.tools.registry import ToolRegistry

Expand Down Expand Up @@ -43,3 +44,25 @@ def test_register_tool_with_similar_name_raises():
str(err.value) == "Tool name 'tool_like_this' already exists as 'tool-like-this'. "
"Cannot add a duplicate tool which differs by a '-' or '_'"
)


@pytest.fixture
def test_agent():
class TestAgent:
@tool
def word_counter(self, text: str) -> str:
"""Count words in text"""
return f"Word count: {len(text.split())}"

return TestAgent()


@pytest.fixture
def tool_registry():
return ToolRegistry()


def test_class_method_register_tool(test_agent, tool_registry):
registered_tool_names = tool_registry.process_tools([test_agent.word_counter])
assert len(registered_tool_names) == 1
assert registered_tool_names[0] == "word_counter"
Loading