Description
hot-reloading-issue.mp4
Checks
- I have updated to the lastest minor and patch version of Strands
- I have checked the documentation and this is not expected behavior
- I have searched ./issues and there are no duplicates of my issue
Strands Version
0.1.18
Python Version
3.13.3
Operating System
macOS (Darwin with Clang 17.0.0)
Installation Method
git clone
Steps to Reproduce
Hot-reloading of tools from the ./tools
directory fails when the directory is empty during Agent initialization. The first tool added to an empty ./tools
directory is not automatically loaded, breaking the expected hot-reload functionality.
-
Setup the environment:
git clone [email protected]:strands-agents/sdk-python.git /tmp/sdk-python && cd /tmp/sdk-python python3 -m venv .venv && source .venv/bin/activate && pip3 install -e ".[dev]"
-
Ensure ./tools directory exists but is empty:
mkdir -p tools # Ensure tools directory is empty rm -f tools/*.py
-
Create a simple agent in Python REPL:
from strands import Agent agent = Agent() print(agent.tool_config) # Shows: {'tools': [], 'toolChoice': {'auto': {}}}
-
Add a tool to ./tools directory while REPL is running:
# Create tools/basic_calculator.py with @tool decorator # (see full tool code in Additional Context section)
-
Check if tool is loaded:
print(agent.tool_config) # Still shows: {'tools': [], 'toolChoice': {'auto': {}}} print(agent.tool_names) # Shows: []
Expected Behavior
When a tool is added to the ./tools
directory, it should be automatically hot-reloaded and available in:
agent.tool_config
- should include the new tool specificationagent.tool_names
- should include the tool name in the listagent.tool.tool_name()
- should be callable
This should work regardless of whether the ./tools
directory was empty when the Agent was initialized.
Actual Behavior
- First tool addition: Tool is NOT automatically loaded when added to an initially empty
./tools
directory - Subsequent tool additions: Once at least one tool exists, additional tools ARE automatically hot-reloaded
- Workaround: Restarting the Python process loads the tool correctly
Additional Context
Code Analysis
Based on examination of the codebase, the issue appears to be in the ToolWatcher
initialization logic:
- In
Agent.__init__()
: TheToolWatcher
is only initialized ifload_tools_from_directory=True
- In
ToolRegistry.initialize_tools()
: Tool discovery happens, but if no tools exist, the watcher may not be properly set up - In
ToolWatcher.start()
: The file system watcher is configured, but may not monitor empty directories effectively
Test Case Evidence
# Case 1: Empty tools directory - FAILS
agent = Agent()
# Add tool to empty ./tools/
# Result: tool_config remains empty
# Case 2: Pre-existing tool - WORKS
agent = Agent() # with existing tool in ./tools/
# Add another tool
# Result: new tool appears in tool_config
# Case 3: Restart after adding to empty directory - WORKS
# Exit Python, add tool, restart Python
agent = Agent()
# Result: tool_config includes the tool
Proposed Solution Areas
The issue likely resides in one or more of these areas:
ToolWatcher
initialization: May need to properly handle empty directories- File system monitoring: The watchdog observer might not be monitoring empty directories
- Tool discovery timing: Initial discovery vs. runtime discovery synchronization
Additional Context
Sample tool used for testing:
# tools/basic_calculator.py
from strands import tool
@tool
def basic_calculator(expression: str) -> dict:
"""
A very simple calculator for basic arithmetic only.
Supports: +, -, *, /, and parentheses
Args:
expression: Simple math expression (e.g., "2 + 3", "10 / 2", "(5 + 3) * 2")
Returns:
Dictionary with the calculation result
"""
try:
# Only allow basic math characters and numbers
allowed = set('0123456789+-*/().')
if not all(c in allowed or c.isspace() for c in expression):
raise ValueError("Only basic math operations allowed: +, -, *, /, ()")
result = eval(expression)
return {
"status": "success",
"content": [{"text": f"{expression} = {result}"}]
}
except ZeroDivisionError:
return {
"status": "error",
"content": [{"text": "Cannot divide by zero"}]
}
except:
return {
"status": "error",
"content": [{"text": "Invalid math expression"}]
}
Impact
This bug affects the developer experience for:
- New users setting up their first Strands agent
- Development workflows where tools are added incrementally
- Any scenario where the
./tools
directory starts empty
Related Files
src/strands/agent/agent.py
- Agent initializationsrc/strands/tools/registry.py
- Tool discovery and registrationsrc/strands/tools/watcher.py
- File system monitoring for hot-reload
Verification
This issue can be consistently reproduced with the steps above. The hot-reload functionality works correctly in all cases except when the initial ./tools
directory is empty during Agent initialization.
Possible Solution
No response
Related Issues
No response