Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/strands_evals/simulation/tool_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def __init__(
state_registry: StateRegistry | None = None,
model: Model | str | None = None,
max_tool_call_cache_size: int = 20,
tools: list | None = None,
):
"""
Initialize a ToolSimulator instance.
Expand All @@ -178,8 +179,12 @@ def __init__(
Only used when creating a new StateRegistry (ignored if state_registry
is provided). Older calls are automatically evicted when limit is exceeded.
Default is 20.
tools: Optional list of tools to provide to the internal simulation agent.
When provided, the agent can use these tools (e.g. calculator) to improve
accuracy of generated responses. Defaults to an empty list.
"""
self.model = model
self.tools = tools or []
self.state_registry = state_registry or StateRegistry(max_tool_call_cache_size=max_tool_call_cache_size)
self._registered_tools: dict[str, RegisteredTool] = {}

Expand Down Expand Up @@ -229,7 +234,7 @@ def wrapper(*args, **kwargs):
def _simulate_tool_call(self, prompt: str, structured_output_model=None) -> Any:
"""Tool simulation agent creation and response generation."""
agent = Agent(
tools=[],
tools=self.tools,
model=self.model,
callback_handler=None,
)
Expand Down
72 changes: 72 additions & 0 deletions tests/strands_evals/simulation/test_tool_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,75 @@ def test_tool_no_params() -> dict:
properties = schema.get("properties", {})
# Empty model should mean no properties
assert len(properties) == 0, "Tool with empty schema should have no properties"


def test_tool_simulator_init_default_tools():
"""Test ToolSimulator initializes with empty tools list by default."""
simulator = ToolSimulator()
assert simulator.tools == []


def test_tool_simulator_init_with_tools():
"""Test ToolSimulator stores provided tools."""
mock_tool = MagicMock()
simulator = ToolSimulator(tools=[mock_tool])
assert simulator.tools == [mock_tool]


def test_tool_simulator_init_tools_none_becomes_empty_list():
"""Test ToolSimulator converts None tools to empty list."""
simulator = ToolSimulator(tools=None)
assert simulator.tools == []


def test_simulate_tool_call_passes_tools_to_agent(mock_model):
"""Test that tools are passed through to the internal Agent."""
mock_tool = MagicMock()
simulator = ToolSimulator(model=mock_model, tools=[mock_tool])

@simulator.tool(output_schema=GenericOutput)
def test_func(message: str) -> dict:
"""Test function."""
pass

captured_kwargs = {}

def mock_agent_constructor(**kwargs):
captured_kwargs.update(kwargs)
mock_agent = MagicMock()
mock_result = MagicMock()
mock_result.__str__ = MagicMock(return_value='{"result": "response"}')
mock_agent.return_value = mock_result
return mock_agent

with pytest.MonkeyPatch().context() as m:
m.setattr("strands_evals.simulation.tool_simulator.Agent", mock_agent_constructor)
simulator.test_func("hello")

assert captured_kwargs["tools"] == [mock_tool]


def test_simulate_tool_call_default_empty_tools(mock_model):
"""Test that default simulator passes empty tools list to Agent."""
simulator = ToolSimulator(model=mock_model)

@simulator.tool(output_schema=GenericOutput)
def test_func(message: str) -> dict:
"""Test function."""
pass

captured_kwargs = {}

def mock_agent_constructor(**kwargs):
captured_kwargs.update(kwargs)
mock_agent = MagicMock()
mock_result = MagicMock()
mock_result.__str__ = MagicMock(return_value='{"result": "response"}')
mock_agent.return_value = mock_result
return mock_agent

with pytest.MonkeyPatch().context() as m:
m.setattr("strands_evals.simulation.tool_simulator.Agent", mock_agent_constructor)
simulator.test_func("hello")

assert captured_kwargs["tools"] == []
Loading