Skip to content

Conversation

@mr-lee
Copy link
Member

@mr-lee mr-lee commented Sep 26, 2025

Resuming #865

Description

  • Add AgentConfig class for loading configuration from JSON files or dicts
  • Support model, prompt, and tools configuration options

Goal of this feature:

  • Start the centralization of serializing the agent config from a file. This currently exists in the tools repo, for spawning sub-agents: https://github.com/strands-agents/tools/blob/main/src/strands_tools/utils/models/model.py
    • More strands features will be represented in this feature in follow up pull requests
    • We have at least one more upcoming feature that will take advantage of this feature
  • Make sharing of agents easier: This isnt the most readable format, but we can convert a front matter markdown to a json dict, and then validate the schema using this definition in the future

Usage example

For basic usage with the default set of tools available to choose from the config: file_read, editor, http_request, use_agent, and shell

from strands.experimental import config_to_agent

agent = config_to_agent({"model": "test-model", "prompt": "Test prompt", "tools": ["strands_tools.file_read", "strands_tools.shell"]}, description="My custom description") # everything after config arg is passed to the agent constructor as kwargs

You can also load a config from a file:

from strands.experimental import AgentConfig

config = AgentConfig("file://path/to/local/config) # Config gets loaded into dict
agent = config.to_agent() # kwargs are passed to the agent init

Documentation PR

strands-agents/docs#252

Type of Change

New feature

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Copy link
Member

@awsarron awsarron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proposed interface during experimental:

from strands.experimental import config_to_agent

agent = config_to_agent("/path/to/amazing-agent.json")

agent("do the thing")

potential interface for non-experimental in future releases:

from strands import config_to_agent

agent = config_to_agent("/path/to/amazing-agent.json")

agent("do the thing")

example amazing-agent.json definition of tools:

{
  // ...
  tools: ["strands_tools.file_read", "my_app.tools.cake_tool", "/path/to/another_tool.py"]
}

strands supports loading tools from python modules and filepaths:

def add_tool(tool: Any) -> None:
# Case 1: String file path
if isinstance(tool, str):
# Extract tool name from path
tool_name = os.path.basename(tool).split(".")[0]
self.load_tool_from_filepath(tool_name=tool_name, tool_path=tool)
tool_names.append(tool_name)
# Case 2: Dictionary with name and path
elif isinstance(tool, dict) and "name" in tool and "path" in tool:
self.load_tool_from_filepath(tool_name=tool["name"], tool_path=tool["path"])
tool_names.append(tool["name"])
# Case 3: Dictionary with path only
elif isinstance(tool, dict) and "path" in tool:
tool_name = os.path.basename(tool["path"]).split(".")[0]
self.load_tool_from_filepath(tool_name=tool_name, tool_path=tool["path"])
tool_names.append(tool_name)
# Case 4: Imported Python module
elif hasattr(tool, "__file__") and inspect.ismodule(tool):
# Get the module file path
module_path = tool.__file__
# Extract the tool name from the module name
tool_name = tool.__name__.split(".")[-1]
# Check for TOOL_SPEC in module to validate it's a Strands tool
if hasattr(tool, "TOOL_SPEC") and hasattr(tool, tool_name) and module_path:
self.load_tool_from_filepath(tool_name=tool_name, tool_path=module_path)
tool_names.append(tool_name)
else:
function_tools = self._scan_module_for_tools(tool)
for function_tool in function_tools:
self.register_tool(function_tool)
tool_names.append(function_tool.tool_name)
if not function_tools:
logger.warning("tool_name=<%s>, module_path=<%s> | invalid agent tool", tool_name, module_path)
# Case 5: AgentTools (which also covers @tool)
elif isinstance(tool, AgentTool):
self.register_tool(tool)
tool_names.append(tool.tool_name)
# Case 6: Nested iterable (list, tuple, etc.) - add each sub-tool
elif isinstance(tool, Iterable) and not isinstance(tool, (str, bytes, bytearray)):
for t in tool:
add_tool(t)
else:
logger.warning("tool=<%s> | unrecognized tool specification", tool)

for the initial implementation in this PR we could do something like:

def config_to_agent(filepath: str) -> Agent:
    # pseudo code
    parsed_config = JSON.loads(filepath)
    tools_list = []
    for tool in parsed_config["tools"]:
        tool_arg = tool
        if not is_filepath(tool):
            try:
                tool_arg = importlib.import_module(tool)
            except ImportError as e:
                # handle tool not a file and not a module
        tools_list.append(tool_arg)
    return Agent(tools=tools_list)
@mr-lee mr-lee force-pushed the feature/agent-config-squashed branch from 832b290 to f372666 Compare September 26, 2025 22:34
@mr-lee mr-lee force-pushed the feature/agent-config-squashed branch from 27bb82f to c2d1baa Compare September 26, 2025 22:40
@Unshure Unshure self-assigned this Sep 29, 2025
- Add AgentConfig class for declarative agent configuration via JSON/dict
- Support file:// prefix for loading configurations from JSON files
- Implement ToolRegistry integration with automatic default tool loading
- Add raise_exception_on_missing_tool parameter for flexible error handling
- Support tool selection from registry via tool names in config
- Add comprehensive test coverage for all configuration scenarios
- Move hook events from experimental to production with updated names
- Add OpenAI model provider enhancements and Gemini model improvements
- Update event loop and tool executors to use production hook events

🤖 Assisted by Amazon Q Developer
- Reset experimental/__init__.py to not import AgentConfig by default
- This may resolve import issues in CI environments
- AgentConfig can still be imported directly from strands.experimental.agent_config

🤖 Assisted by Amazon Q Developer
- Change error message from 'Tool X not found' to 'Module X not found'
- More accurate since we're trying to import it as a module at this point
- Maintains existing test compatibility and error handling logic

🤖 Assisted by Amazon Q Developer
- Revert previous change from 'Module X not found' back to 'Tool X not found'
- Keep original error message format as requested

🤖 Assisted by Amazon Q Developer
@Unshure Unshure force-pushed the feature/agent-config-squashed branch from da9ff2e to cb507dd Compare October 8, 2025 19:55
@codecov
Copy link

codecov bot commented Oct 8, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Unshure Unshure force-pushed the feature/agent-config-squashed branch from 5f3c0a9 to 64a31ab Compare October 8, 2025 20:49
@Unshure Unshure force-pushed the feature/agent-config-squashed branch from 64a31ab to 27cef45 Compare October 8, 2025 20:58
@Unshure Unshure force-pushed the feature/agent-config-squashed branch from 27cef45 to 9f33f62 Compare October 8, 2025 21:04
Copy link
Member

@awsarron awsarron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some follow-ups:

  • Document in code (docstrings) and docs page that it only supports bedrock model ID parameters
  • Document using other model providers (change agent.model after transformation)
  • Track a task for supporting non-bedrock model providers
  • Track a task for moving from experimental to production-ready
@Unshure Unshure dismissed stale reviews from zastrowm and themself October 21, 2025 19:18

Feedback addressed

@Unshure Unshure merged commit b69478b into strands-agents:main Oct 21, 2025
16 of 19 checks passed
@Unshure
Copy link
Member

Unshure commented Oct 21, 2025

Some follow-ups:

* Document in code (docstrings) and docs page that it only supports bedrock model ID parameters

* Document using other model providers (change `agent.model` after transformation)

* Track a task for supporting non-bedrock model providers

* Track a task for moving from experimental to production-ready

Updated the docs to reflect the model provider specification. I also created a follow up task for supporting more model providers: #1064

Here is the task for moving it out of experimental: #1065

dbschmigelski pushed a commit to dbschmigelski/sdk-python that referenced this pull request Dec 9, 2025
…strands-agents#935)

* feat: add experimental AgentConfig with comprehensive tool management

- Add AgentConfig class for declarative agent configuration via JSON/dict
- Support file:// prefix for loading configurations from JSON files
- Implement ToolRegistry integration with automatic default tool loading
- Add raise_exception_on_missing_tool parameter for flexible error handling
- Support tool selection from registry via tool names in config
- Add comprehensive test coverage for all configuration scenarios
- Move hook events from experimental to production with updated names
- Add OpenAI model provider enhancements and Gemini model improvements
- Update event loop and tool executors to use production hook events

🤖 Assisted by Amazon Q Developer

* fix: remove AgentConfig import from experimental/__init__.py

- Reset experimental/__init__.py to not import AgentConfig by default
- This may resolve import issues in CI environments
- AgentConfig can still be imported directly from strands.experimental.agent_config

🤖 Assisted by Amazon Q Developer

* fix: remove strands-agents-tools test dependency

- Reset pyproject.toml to not include strands-agents-tools as test dependency
- Tests handle missing strands_tools gracefully with mocking
- This should resolve CI dependency issues

🤖 Assisted by Amazon Q Developer

* test: remove test that depends on strands_tools availability

- Remove test_agent_config_loads_from_default_tools_without_tool_registry
- This test assumes strands_tools is available which causes CI failures
- Other tests adequately cover AgentConfig functionality

🤖 Assisted by Amazon Q Developer

* test: add back tests with proper mocking for strands_tools

- Add back test_agent_config_tools_without_tool_registry_error with mocking
- Add back test_agent_config_loads_from_default_tools_without_tool_registry with mocking
- Mock _create_default_tool_registry to avoid dependency on strands_tools
- Add tool import for creating mock tools in tests
- All 15 tests now pass without external dependencies

🤖 Assisted by Amazon Q Developer

* test: fix Windows compatibility for file prefix test

- Use platform-specific tempfile handling in test_agent_config_file_prefix_valid
- Use mkstemp() with explicit cleanup on Windows for better permission handling
- Keep NamedTemporaryFile on non-Windows platforms for simplicity
- Should resolve permission errors on Windows GitHub runners

🤖 Assisted by Amazon Q Developer

* refactor: replace AgentConfig class with config_to_agent function

BREAKING CHANGE: Replace class-based AgentConfig with function-based config_to_agent

- Replace AgentConfig class with config_to_agent function for simpler interface
- Remove ToolRegistry dependency - let Agent handle tool loading internally
- Remove DEFAULT_TOOLS concept and raise_exception_on_missing_tool parameter
- Support both file paths and dictionary inputs with file:// prefix handling
- Only pass non-None config values to Agent constructor (use Agent defaults)
- Update experimental module exports to expose config_to_agent function
- Rewrite all tests to use new function-based interface
- Simplify tool handling by delegating to Agent class

New interface:
  from strands.experimental import config_to_agent
  agent = config_to_agent('/path/to/config.json')

Previous interface (removed):
  from strands.experimental.agent_config import AgentConfig
  config = AgentConfig('/path/to/config.json')
  agent = config.to_agent()

🤖 Assisted by Amazon Q Developer

* feat: limit config_to_agent to core configuration keys

- Remove support for advanced Agent parameters in config_to_agent
- Only support: model, prompt, tools, name in configuration
- Advanced parameters can still be passed via kwargs
- Remove agent_id test and update function mapping
- Keep interface simple and focused on basic agent configuration

🤖 Assisted by Amazon Q Developer

* fix: use native Python typing instead of typing module

- Replace Union[str, Dict[str, Any]] with str | dict[str, any]
- Remove typing module imports
- Use modern Python 3.10+ native typing syntax

🤖 Assisted by Amazon Q Developer

* test: simplify file prefix test with proper context manager

- Use NamedTemporaryFile with delete=True for automatic cleanup
- Remove manual os.unlink call and try/finally block
- Keep file operation within single context manager scope
- Add f.flush() to ensure data is written before reading

🤖 Assisted by Amazon Q Developer

* feat: add JSON schema validation to config_to_agent

- Add jsonschema dependency for configuration validation
- Implement JSON schema based on supported configuration keys
- Provide detailed validation error messages with field paths
- Add validation tests for invalid fields, types, and tool items
- Support null values for optional fields (model, prompt, name)
- Reject additional properties not in the schema
- All 14 tests passing including new validation tests

🤖 Assisted by Amazon Q Developer

* refactor: move JSON schema to separate file

- Extract agent configuration schema to schemas/agent-config-v1.json
- Add _load_schema() function to load schema from file at runtime
- Improve code readability by separating schema from Python logic
- Enable schema reuse by other tools and documentation
- Maintain all existing validation functionality and tests

🤖 Assisted by Amazon Q Developer

* perf: use pre-compiled JSON schema validator

- Create Draft7Validator instance at module level for better performance
- Avoid loading and compiling schema on every validation call
- Schema is loaded once at import time and validator is reused
- Maintains all existing validation functionality and error messages
- Standard best practice for jsonschema validation performance

🤖 Assisted by Amazon Q Developer

* feat: add tool validation and clarify limitations

- Move JSON schema back to inline variable for simplicity
- Add comprehensive tool validation with helpful error messages
- Validate tools can be loaded as files, modules, or @tool functions
- Add clear documentation about code-based instantiation limitations
- Update module docstring and function comments with usage patterns
- Add test for tool validation error messages
- Remove schemas directory (no longer needed)

🤖 Assisted by Amazon Q Developer

* fix: improve tool validation error messages and add comprehensive tests

- Fix error message for missing modules to be more descriptive
- Remove redundant 'to properly import this tool' text from error messages
- Add specific error messages for missing modules vs missing functions
- Add unit tests for each error case:
  - Invalid tool (not file/module/@tool)
  - Missing module (module doesn't exist)
  - Missing function (function not found in existing module)
- All 17 tests passing with better error coverage

🤖 Assisted by Amazon Q Developer

* fix: reference module instead of tool in error message

- Change error message from 'Tool X not found' to 'Module X not found'
- More accurate since we're trying to import it as a module at this point
- Maintains existing test compatibility and error handling logic

🤖 Assisted by Amazon Q Developer

* revert: change error message back to reference tool

- Revert previous change from 'Module X not found' back to 'Tool X not found'
- Keep original error message format as requested

🤖 Assisted by Amazon Q Developer

* feat: use agent tool loading logic

* fix: address pr comments

---------

Co-authored-by: Matt Lee <mrlee@amazon.com>
Co-authored-by: Nicholas Clegg <ncclegg@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

7 participants