Skip to content

Commit 03221ce

Browse files
committed
Revert Agent(), Graph(), and Swarm() constructor modifications
- Remove config parameter integration from main constructors - Revert Agent() constructor to original state without config parameter - Revert Swarm() constructor to original state without config parameter - Remove Graph() constructor config modifications - Maintain ConfigLoader classes for direct programmatic use - Preserve experimental config loader functionality - Remove related unit tests for constructor config parameter - Clean up constructor parameter handling logic
1 parent 1978662 commit 03221ce

File tree

3 files changed

+22
-373
lines changed

3 files changed

+22
-373
lines changed

src/strands/agent/agent.py

Lines changed: 8 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414
import logging
1515
import random
1616
from concurrent.futures import ThreadPoolExecutor
17-
from pathlib import Path
18-
from typing import Any, AsyncGenerator, AsyncIterator, Callable, Dict, Mapping, Optional, Type, TypeVar, Union, cast
17+
from typing import Any, AsyncGenerator, AsyncIterator, Callable, Mapping, Optional, Type, TypeVar, Union, cast
1918

20-
import yaml
2119
from opentelemetry import trace as trace_api
2220
from pydantic import BaseModel
2321

@@ -200,8 +198,8 @@ def __init__(
200198
Union[Callable[..., Any], _DefaultCallbackHandlerSentinel]
201199
] = _DEFAULT_CALLBACK_HANDLER,
202200
conversation_manager: Optional[ConversationManager] = None,
203-
record_direct_tool_call: Optional[bool] = None,
204-
load_tools_from_directory: Optional[bool] = None,
201+
record_direct_tool_call: bool = True,
202+
load_tools_from_directory: bool = False,
205203
trace_attributes: Optional[Mapping[str, AttributeValue]] = None,
206204
*,
207205
agent_id: Optional[str] = None,
@@ -210,7 +208,6 @@ def __init__(
210208
state: Optional[Union[AgentState, dict]] = None,
211209
hooks: Optional[list[HookProvider]] = None,
212210
session_manager: Optional[SessionManager] = None,
213-
config: Optional[Union[str, Path, Dict[str, Any]]] = None,
214211
):
215212
"""Initialize the Agent with the specified configuration.
216213
@@ -237,11 +234,9 @@ def __init__(
237234
conversation_manager: Manager for conversation history and context window.
238235
Defaults to strands.agent.conversation_manager.SlidingWindowConversationManager if None.
239236
record_direct_tool_call: Whether to record direct tool calls in message history.
240-
Defaults to True. When using config, this parameter only overrides the config value
241-
if explicitly provided.
237+
Defaults to True.
242238
load_tools_from_directory: Whether to load and automatically reload tools in the `./tools/` directory.
243-
Defaults to False. When using config, this parameter only overrides the config value
244-
if explicitly provided.
239+
Defaults to False.
245240
trace_attributes: Custom trace attributes to apply to the agent's trace span.
246241
agent_id: Optional ID for the agent, useful for session management and multi-agent scenarios.
247242
Defaults to "default".
@@ -255,108 +250,10 @@ def __init__(
255250
Defaults to None.
256251
session_manager: Manager for handling agent sessions including conversation history and state.
257252
If provided, enables session-based persistence and state management.
258-
config: Configuration for initializing the agent from a file or dictionary.
259-
Can be specified as:
260-
261-
- String: Path to a YAML (.yaml, .yml) or JSON (.json) configuration file
262-
- Path: Path object to a YAML (.yaml, .yml) or JSON (.json) configuration file
263-
- Dict: Configuration dictionary with agent parameters
264-
265-
When provided, the agent is initialized using the AgentConfigLoader with the specified
266-
configuration. The config takes precedence over individual parameters, but individual
267-
parameters can override config values when both are provided.
268253
269254
Raises:
270255
ValueError: If agent id contains path separators.
271-
FileNotFoundError: If config is a string/Path that doesn't exist.
272-
ValueError: If config file format is not supported (must be .yaml, .yml, or .json).
273256
"""
274-
# Handle config-based initialization
275-
if config is not None:
276-
# Load configuration from file or use provided dict
277-
config_dict = self._load_config(config)
278-
279-
# Handle both direct agent config and config with 'agent' key
280-
if "agent" in config_dict:
281-
agent_config = config_dict["agent"]
282-
else:
283-
agent_config = config_dict
284-
285-
# Import AgentConfigLoader here to avoid circular imports
286-
from ..experimental.config_loader.agent import AgentConfigLoader
287-
288-
# Create agent from config and return early
289-
config_loader = AgentConfigLoader()
290-
configured_agent = config_loader.load_agent(agent_config)
291-
292-
# There is an odd mypy type discrepancy in this file for some reason
293-
# There is a mismatch between src.strands.* and strands.*
294-
# type/ignore annotations allow type checking to pass
295-
296-
# Override config values with any explicitly provided parameters
297-
if model is not None:
298-
configured_agent.model = (
299-
BedrockModel() if not model else BedrockModel(model_id=model) if isinstance(model, str) else model # type: ignore
300-
)
301-
if messages is not None:
302-
configured_agent.messages = messages
303-
if tools is not None:
304-
# Need to reinitialize tool registry with new tools
305-
configured_agent.tool_registry = ToolRegistry() # type: ignore
306-
configured_agent.tool_registry.process_tools(tools)
307-
configured_agent.tool_registry.initialize_tools(load_tools_from_directory) # type: ignore
308-
if system_prompt is not None:
309-
configured_agent.system_prompt = system_prompt
310-
if not isinstance(callback_handler, _DefaultCallbackHandlerSentinel):
311-
if callback_handler is None:
312-
configured_agent.callback_handler = null_callback_handler
313-
else:
314-
configured_agent.callback_handler = callback_handler
315-
if conversation_manager is not None:
316-
configured_agent.conversation_manager = conversation_manager # type: ignore
317-
if trace_attributes is not None:
318-
configured_agent.trace_attributes = {}
319-
for k, v in trace_attributes.items():
320-
if isinstance(v, (str, int, float, bool)) or (
321-
isinstance(v, list) and all(isinstance(x, (str, int, float, bool)) for x in v)
322-
):
323-
configured_agent.trace_attributes[k] = v
324-
if agent_id is not None:
325-
configured_agent.agent_id = _identifier.validate(agent_id, _identifier.Identifier.AGENT)
326-
if name is not None:
327-
configured_agent.name = name
328-
if description is not None:
329-
configured_agent.description = description
330-
if state is not None:
331-
if isinstance(state, dict):
332-
configured_agent.state = AgentState(state) # type: ignore
333-
elif isinstance(state, AgentState):
334-
configured_agent.state = state # type: ignore
335-
else:
336-
raise ValueError("state must be an AgentState object or a dict")
337-
if hooks is not None:
338-
for hook in hooks:
339-
configured_agent.hooks.add_hook(hook) # type: ignore
340-
if session_manager is not None:
341-
configured_agent._session_manager = session_manager # type: ignore
342-
configured_agent.hooks.add_hook(session_manager) # type: ignore
343-
344-
# Override record_direct_tool_call and load_tools_from_directory only if explicitly provided
345-
if record_direct_tool_call is not None:
346-
configured_agent.record_direct_tool_call = record_direct_tool_call
347-
if load_tools_from_directory is not None:
348-
configured_agent.load_tools_from_directory = load_tools_from_directory
349-
if load_tools_from_directory:
350-
if hasattr(configured_agent, "tool_watcher"):
351-
configured_agent.tool_watcher = ToolWatcher(tool_registry=configured_agent.tool_registry) # type: ignore
352-
else:
353-
configured_agent.tool_watcher = ToolWatcher(tool_registry=configured_agent.tool_registry) # type: ignore
354-
355-
# Copy all attributes from configured agent to self
356-
self.__dict__.update(configured_agent.__dict__)
357-
return
358-
359-
# Standard initialization when no config is provided
360257
self.model = BedrockModel() if not model else BedrockModel(model_id=model) if isinstance(model, str) else model
361258
self.messages = messages if messages is not None else []
362259

@@ -387,8 +284,8 @@ def __init__(
387284
):
388285
self.trace_attributes[k] = v
389286

390-
self.record_direct_tool_call = record_direct_tool_call if record_direct_tool_call is not None else True
391-
self.load_tools_from_directory = load_tools_from_directory if load_tools_from_directory is not None else False
287+
self.record_direct_tool_call = record_direct_tool_call
288+
self.load_tools_from_directory = load_tools_from_directory
392289

393290
self.tool_registry = ToolRegistry()
394291

@@ -432,42 +329,6 @@ def __init__(
432329
self.hooks.add_hook(hook)
433330
self.hooks.invoke_callbacks(AgentInitializedEvent(agent=self))
434331

435-
def _load_config(self, config: Union[str, Path, Dict[str, Any]]) -> Dict[str, Any]:
436-
"""Load configuration from file or return provided dictionary.
437-
438-
Args:
439-
config: Either a file path (string or Path), or configuration dictionary
440-
441-
Returns:
442-
Configuration dictionary
443-
444-
Raises:
445-
FileNotFoundError: If config is a string/Path that doesn't exist
446-
ValueError: If config file format is not supported
447-
"""
448-
if isinstance(config, dict):
449-
return config
450-
451-
# Handle both str and Path objects
452-
if isinstance(config, (str, Path)):
453-
config_path = Path(config)
454-
455-
if not config_path.exists():
456-
raise FileNotFoundError(f"Configuration file not found: {config}")
457-
458-
suffix = config_path.suffix.lower()
459-
460-
if suffix in [".yaml", ".yml"]:
461-
with open(config_path, "r", encoding="utf-8") as f:
462-
return dict[str, Any](yaml.safe_load(f))
463-
elif suffix == ".json":
464-
with open(config_path, "r", encoding="utf-8") as f:
465-
return dict[str, Any](json.load(f))
466-
else:
467-
raise ValueError(f"Unsupported config file format: {suffix}. Supported formats: .yaml, .yml, .json")
468-
469-
raise ValueError(f"Config must be a string (file path), Path object, or dictionary, got: {type(config)}")
470-
471332
@property
472333
def tool(self) -> ToolCaller:
473334
"""Call tool as a function.
@@ -859,4 +720,4 @@ def _end_agent_trace_span(
859720
def _append_message(self, message: Message) -> None:
860721
"""Appends a message to the agent's list of messages and invokes the callbacks for the MessageCreatedEvent."""
861722
self.messages.append(message)
862-
self.hooks.invoke_callbacks(MessageAddedEvent(agent=self, message=message))
723+
self.hooks.invoke_callbacks(MessageAddedEvent(agent=self, message=message))

src/strands/multiagent/graph.py

Lines changed: 2 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@
1616

1717
import asyncio
1818
import copy
19-
import json
2019
import logging
2120
import time
2221
from concurrent.futures import ThreadPoolExecutor
2322
from dataclasses import dataclass, field
24-
from pathlib import Path
25-
from typing import Any, Callable, Dict, Optional, Tuple, Union
23+
from typing import Any, Callable, Optional, Tuple
2624

2725
from opentelemetry import trace as trace_api
2826

@@ -223,100 +221,6 @@ def __init__(self) -> None:
223221
self._node_timeout: Optional[float] = None
224222
self._reset_on_revisit: bool = False
225223

226-
@classmethod
227-
def from_config(cls, config: Union[str, Path, Dict[str, Any]]) -> "GraphBuilder":
228-
"""Create a GraphBuilder from YAML/JSON configuration.
229-
230-
Args:
231-
config: Configuration source. Can be:
232-
- String: Path to YAML (.yaml, .yml) or JSON (.json) file
233-
- Path: Path object to configuration file
234-
- Dict: Configuration dictionary
235-
236-
Returns:
237-
GraphBuilder instance pre-configured with nodes, edges, and settings
238-
239-
Example:
240-
# From file
241-
builder = GraphBuilder.from_config("workflow.yaml")
242-
graph = builder.build()
243-
244-
# From dictionary
245-
config = {...}
246-
builder = GraphBuilder.from_config(config)
247-
248-
# Can still modify after loading
249-
builder.add_node(additional_agent, "extra_node")
250-
builder.set_max_node_executions(100)
251-
graph = builder.build()
252-
"""
253-
# Load and validate configuration
254-
if isinstance(config, (str, Path)):
255-
config_dict = cls._load_config_file(config)
256-
else:
257-
config_dict = config
258-
259-
# Handle both direct graph config and config with 'graph' key
260-
if "graph" in config_dict:
261-
graph_config = config_dict["graph"]
262-
else:
263-
graph_config = config_dict
264-
265-
# Use GraphConfigLoader to populate builder
266-
from ..experimental.config_loader.graph import GraphConfigLoader
267-
268-
loader = GraphConfigLoader()
269-
builder = cls()
270-
271-
# Validate configuration structure
272-
loader._validate_config(graph_config)
273-
274-
# Load nodes using existing loaders
275-
if "nodes" in graph_config:
276-
nodes = loader._load_nodes(graph_config["nodes"])
277-
for node_id, node in nodes.items():
278-
builder.nodes[node_id] = node # type: ignore
279-
280-
# Load edges with conditions
281-
if "edges" in graph_config:
282-
edges = loader._load_edges(graph_config["edges"], builder.nodes) # type: ignore
283-
builder.edges = edges # type: ignore
284-
285-
# Load entry points
286-
if "entry_points" in graph_config:
287-
for entry_point_id in graph_config["entry_points"]:
288-
if entry_point_id in builder.nodes:
289-
builder.entry_points.add(builder.nodes[entry_point_id])
290-
291-
# Load execution configuration
292-
if "max_node_executions" in graph_config:
293-
builder._max_node_executions = graph_config["max_node_executions"]
294-
if "execution_timeout" in graph_config:
295-
builder._execution_timeout = graph_config["execution_timeout"]
296-
if "node_timeout" in graph_config:
297-
builder._node_timeout = graph_config["node_timeout"]
298-
if "reset_on_revisit" in graph_config:
299-
builder._reset_on_revisit = graph_config["reset_on_revisit"]
300-
301-
return builder
302-
303-
@staticmethod
304-
def _load_config_file(config_path: Union[str, Path]) -> Dict[str, Any]:
305-
"""Load configuration from YAML or JSON file."""
306-
import yaml
307-
308-
path = Path(config_path)
309-
if not path.exists():
310-
raise FileNotFoundError(f"Configuration file not found: {config_path}")
311-
312-
with open(path, "r") as f:
313-
if path.suffix.lower() in [".yaml", ".yml"]:
314-
return dict[str, Any](yaml.safe_load(f))
315-
elif path.suffix.lower() == ".json":
316-
return dict[str, Any](json.load(f))
317-
else:
318-
raise ValueError(f"Unsupported config file format: {path.suffix}")
319-
320224
def add_node(self, executor: Agent | MultiAgentBase, node_id: str | None = None) -> GraphNode:
321225
"""Add an Agent or MultiAgentBase instance as a node to the graph."""
322226
_validate_node_executor(executor, self.nodes)
@@ -812,4 +716,4 @@ def _build_result(self) -> GraphResult:
812716
execution_order=self.state.execution_order,
813717
edges=self.state.edges,
814718
entry_points=self.state.entry_points,
815-
)
719+
)

0 commit comments

Comments
 (0)