Skip to content

Commit 28577a3

Browse files
committed
feat: Use ToolRegistry in AgentConfig
1 parent f350261 commit 28577a3

File tree

7 files changed

+222
-495
lines changed

7 files changed

+222
-495
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ dependencies = [
134134
"pytest-asyncio>=1.0.0,<1.2.0",
135135
"pytest-xdist>=3.0.0,<4.0.0",
136136
"moto>=5.1.0,<6.0.0",
137+
"strands-agents-tools>=0.2.0,<1.0.0",
137138
]
138139

139140
[[tool.hatch.envs.hatch-test.matrix]]

src/strands/experimental/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
"""
55

66
from .agent_config import AgentConfig
7-
from .tool_box import ToolBox
87

9-
__all__ = ["AgentConfig", "ToolBox"]
8+
__all__ = ["AgentConfig"]
Lines changed: 91 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
"""Experimental agent configuration with enhanced instantiation patterns."""
22

3-
import json
43
import importlib
5-
from typing import TYPE_CHECKING
4+
import json
5+
from typing import TYPE_CHECKING, Any
6+
7+
from ..tools.registry import ToolRegistry
68

79
if TYPE_CHECKING:
810
# Import here to avoid circular imports:
9-
# experimental/agent_config.py -> agent.agent -> event_loop.event_loop ->
11+
# experimental/agent_config.py -> agent.agent -> event_loop.event_loop ->
1012
# experimental.hooks -> experimental.__init__.py -> AgentConfig
1113
from ..agent.agent import Agent
1214

13-
from .tool_box import ToolBox
14-
1515
# File prefix for configuration file paths
1616
FILE_PREFIX = "file://"
1717

@@ -21,170 +21,173 @@
2121

2222

2323
class AgentConfig:
24-
"""Agent configuration with to_agent() method and ToolBox integration.
25-
24+
"""Agent configuration with to_agent() method and ToolRegistry integration.
25+
2626
Example config.json:
2727
{
2828
"model": "anthropic.claude-3-5-sonnet-20241022-v2:0",
2929
"prompt": "You are a helpful assistant",
3030
"tools": ["file_read", "editor"]
3131
}
3232
"""
33-
34-
def __init__(self, config_source: str | dict[str, any], tool_box: ToolBox | None = None, raise_exception_on_missing_tool: bool = True):
33+
34+
def __init__(
35+
self,
36+
config_source: str | dict[str, Any],
37+
tool_registry: ToolRegistry | None = None,
38+
raise_exception_on_missing_tool: bool = True,
39+
):
3540
"""Initialize AgentConfig from file path or dictionary.
36-
41+
3742
Args:
3843
config_source: Path to JSON config file (must start with 'file://') or config dictionary
39-
tool_box: Optional ToolBox to select tools from when 'tools' is specified in config
44+
tool_registry: Optional ToolRegistry to select tools from when 'tools' is specified in config
4045
raise_exception_on_missing_tool: If False, skip missing tools instead of raising ImportError
41-
46+
4247
Example:
4348
# Dictionary config
4449
config = AgentConfig({
4550
"model": "anthropic.claude-3-5-sonnet-20241022-v2:0",
4651
"prompt": "You are a helpful assistant",
4752
"tools": ["file_read", "editor"]
4853
})
49-
54+
5055
# File config
5156
config = AgentConfig("file://config.json")
5257
"""
5358
if isinstance(config_source, str):
5459
# Require file:// prefix for file paths
5560
if not config_source.startswith(FILE_PREFIX):
5661
raise ValueError(f"File paths must be prefixed with '{FILE_PREFIX}'")
57-
62+
5863
# Remove file:// prefix and load from file
5964
file_path = config_source.removeprefix(FILE_PREFIX)
60-
with open(file_path, 'r') as f:
65+
with open(file_path, "r") as f:
6166
config_data = json.load(f)
6267
else:
6368
# Use dictionary directly
6469
config_data = config_source
65-
66-
self.model = config_data.get('model')
67-
self.system_prompt = config_data.get('prompt') # Only accept 'prompt' key
70+
71+
self.model = config_data.get("model")
72+
self.system_prompt = config_data.get("prompt") # Only accept 'prompt' key
6873
self._raise_exception_on_missing_tool = raise_exception_on_missing_tool
69-
70-
# Handle tool selection from ToolBox
71-
if tool_box is not None:
72-
self._toolbox = tool_box
74+
75+
# Handle tool selection from ToolRegistry
76+
if tool_registry is not None:
77+
self._tool_registry = tool_registry
7378
else:
74-
# Create default ToolBox with strands_tools
75-
self._toolbox = self._create_default_toolbox()
76-
79+
# Create default ToolRegistry with strands_tools
80+
self._tool_registry = self._create_default_tool_registry()
81+
7782
# Process tools configuration if provided
78-
config_tools = config_data.get('tools')
79-
83+
config_tools = config_data.get("tools")
84+
8085
# Track configured tools separately from full tool pool
8186
self._configured_tools = []
82-
87+
8388
# Apply tool selection if specified
8489
if config_tools is not None:
85-
# Validate all tool names exist in the ToolBox
86-
available_tools = self._toolbox.list_tool_names()
87-
88-
missing_tools = set(config_tools).difference(set(available_tools))
89-
if missing_tools and self._raise_exception_on_missing_tool:
90-
raise ValueError(f"Tool(s) '{missing_tools}' not found in ToolBox. Available tools: {available_tools}")
91-
92-
# Store selected tools from the ToolBox (only ones that exist)
93-
all_tools = self._toolbox.list_tools()
94-
for tool in all_tools:
95-
if tool.tool_name in config_tools:
90+
# Validate all tool names exist in the ToolRegistry
91+
available_tools = self._tool_registry.registry.keys()
92+
93+
missing_tools = set(config_tools).difference(available_tools)
94+
if missing_tools and self._raise_exception_on_missing_tool:
95+
raise ValueError(
96+
f"Tool(s) '{missing_tools}' not found in ToolRegistry. Available tools: {available_tools}"
97+
)
98+
99+
for tool_name in config_tools:
100+
if tool_name in self._tool_registry.registry:
101+
tool = self._tool_registry.registry[tool_name]
96102
self._configured_tools.append(tool)
97103
# If no tools specified in config, use no tools (empty list)
98-
99-
def _create_default_toolbox(self) -> ToolBox:
100-
"""Create default ToolBox with strands_tools."""
101-
pool = ToolBox()
102-
103-
for tool in DEFAULT_TOOLS:
104-
try:
105-
module_name = f"strands_tools.{tool}"
106-
tool_module = importlib.import_module(module_name)
107-
pool.add_tools_from_module(tool_module)
108-
except ImportError:
109-
if self._raise_exception_on_missing_tool:
110-
raise ImportError(
111-
f"strands_tools is not available and no ToolBox was specified. "
112-
f"Either install strands_tools with 'pip install strands-agents-tools' "
113-
f"or provide your own ToolBox with your own tools."
114-
)
115-
# Skip missing tools when flag is False
116-
continue
117-
118-
return pool
119-
104+
105+
def _create_default_tool_registry(self) -> ToolRegistry:
106+
"""Create default ToolRegistry with strands_tools."""
107+
tool_registry = ToolRegistry()
108+
109+
try:
110+
tool_modules = [importlib.import_module(f"strands_tools.{tool}") for tool in DEFAULT_TOOLS]
111+
tool_registry.process_tools(tool_modules)
112+
except ImportError as e:
113+
if self._raise_exception_on_missing_tool:
114+
raise ImportError(
115+
"strands_tools is not available and no ToolRegistry was specified. "
116+
"Either install strands_tools with 'pip install strands-agents-tools' "
117+
"or provide your own ToolRegistry with your own tools."
118+
) from e
119+
120+
return tool_registry
121+
120122
@property
121-
def toolbox(self) -> ToolBox:
122-
"""Get the full ToolBox (superset of all available tools).
123-
123+
def tool_registry(self) -> ToolRegistry:
124+
"""Get the full ToolRegistry (superset of all available tools).
125+
124126
Returns:
125-
ToolBox instance containing all available tools
127+
ToolRegistry instance containing all available tools
126128
"""
127-
return self._toolbox
128-
129+
return self._tool_registry
130+
129131
@property
130132
def configured_tools(self) -> list:
131133
"""Get the configured tools (subset selected for this agent).
132-
134+
133135
Returns:
134136
List of tools configured for this agent
135137
"""
136138
return self._configured_tools
137-
138-
def to_agent(self, **kwargs: any) -> "Agent":
139+
140+
def to_agent(self, **kwargs: Any) -> "Agent":
139141
"""Create an Agent instance from this configuration.
140-
142+
141143
Args:
142144
**kwargs: Additional parameters to override config values.
143145
Supports all Agent constructor parameters.
144-
146+
145147
Returns:
146148
Configured Agent instance
147-
149+
148150
Example:
149151
# Using default tools from strands_tools
150152
config = AgentConfig({
151-
"model": "anthropic.claude-3-5-sonnet-20241022-v2:0",
153+
"model": "anthropic.claude-3-5-sonnet-20241022-v2:0",
152154
"prompt": "You are a helpful assistant",
153155
"tools": ["file_read"]
154156
})
155157
agent = config.to_agent()
156158
response = agent("Read the contents of README.md")
157-
158-
# Using custom ToolBox
159+
160+
# Using custom ToolRegistry
159161
from strands import tool
160-
162+
161163
@tool
162164
def custom_tool(input: str) -> str:
163165
return f"Custom: {input}"
164-
165-
custom_toolbox = ToolBox([custom_tool])
166+
167+
custom_tool_registry = ToolRegistry()
168+
custom_tool_registry.process_tools([custom_tool])
166169
config = AgentConfig({
167170
"model": "anthropic.claude-3-5-sonnet-20241022-v2:0",
168171
"prompt": "You are a custom assistant",
169172
"tools": ["custom_tool"]
170-
}, tool_box=custom_toolbox)
173+
}, tool_registry=custom_tool_registry)
171174
agent = config.to_agent()
172175
"""
173176
# Import at runtime since TYPE_CHECKING import is not available during execution
174177
from ..agent.agent import Agent
175-
178+
176179
# Start with config values
177180
agent_params = {}
178-
181+
179182
if self.model is not None:
180-
agent_params['model'] = self.model
183+
agent_params["model"] = self.model
181184
if self.system_prompt is not None:
182-
agent_params['system_prompt'] = self.system_prompt
183-
185+
agent_params["system_prompt"] = self.system_prompt
186+
184187
# Use configured tools (subset of tool pool)
185-
agent_params['tools'] = self._configured_tools
186-
188+
agent_params["tools"] = self._configured_tools
189+
187190
# Override with any other provided kwargs
188191
agent_params.update(kwargs)
189-
192+
190193
return Agent(**agent_params)

src/strands/experimental/tool_box.py

Lines changed: 0 additions & 90 deletions
This file was deleted.

0 commit comments

Comments
 (0)