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
- 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
- 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
- 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
- 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
- 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
- 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
- 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 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
- 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
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants