UserSyncUI / tests /unit /test_tiny_tool.py
harvesthealth's picture
Upload folder using huggingface_hub
f6686e1 verified
import pytest
import logging
logger = logging.getLogger("tinytroupe")
import sys
sys.path.insert(0, '../../tinytroupe/')
sys.path.insert(0, '../../')
sys.path.insert(0, '..')
from tinytroupe.tools.tiny_tool import TinyTool
from tinytroupe.examples import create_oscar_the_architect, create_lisa_the_data_scientist
from tinytroupe.extraction import ArtifactExporter
from tinytroupe.enrichment import TinyEnricher
from testing_utils import *
class MockTool(TinyTool):
"""A mock tool implementation for testing."""
def __init__(self, name="MockTool", description="A test tool", **kwargs):
super().__init__(name, description, **kwargs)
self.actions_processed = []
def _process_action(self, agent, action: dict) -> bool:
"""Mock implementation that records processed actions."""
self.actions_processed.append(action)
return True
def actions_definitions_prompt(self) -> str:
"""Mock implementation of action definitions."""
return "MOCK_ACTION: Use this tool for testing purposes."
def actions_constraints_prompt(self) -> str:
"""Mock implementation of action constraints."""
return "Only use this tool during testing."
class DangerousMockTool(TinyTool):
"""A mock tool with real-world side effects for testing."""
def __init__(self):
super().__init__(
name="DangerousTool",
description="A tool with real-world side effects",
real_world_side_effects=True
)
def _process_action(self, agent, action: dict) -> bool:
return True
def actions_definitions_prompt(self) -> str:
return "DANGEROUS_ACTION: This action has real-world effects."
def actions_constraints_prompt(self) -> str:
return "Use with extreme caution."
def test_tiny_tool_initialization(setup):
"""Test TinyTool initialization with various parameters."""
# Test basic initialization
tool = MockTool()
assert tool.name == "MockTool"
assert tool.description == "A test tool"
assert tool.owner is None
assert tool.real_world_side_effects == False
assert tool.exporter is None
assert tool.enricher is None
# Test initialization with all parameters
agent = create_oscar_the_architect()
exporter = ArtifactExporter(base_output_folder="test")
enricher = TinyEnricher()
tool = MockTool(
name="CustomTool",
description="Custom description",
owner=agent,
real_world_side_effects=True,
exporter=exporter,
enricher=enricher
)
assert tool.name == "CustomTool"
assert tool.description == "Custom description"
assert tool.owner == agent
assert tool.real_world_side_effects == True
assert tool.exporter == exporter
assert tool.enricher == enricher
def test_tiny_tool_ownership(setup):
"""Test TinyTool ownership mechanisms."""
oscar = create_oscar_the_architect()
lisa = create_lisa_the_data_scientist()
# Create tool owned by Oscar
tool = MockTool(owner=oscar)
# Test that Oscar can use the tool
mock_action = {"type": "MOCK_ACTION", "content": "test"}
result = tool.process_action(oscar, mock_action)
assert result == True, "Owner should be able to use tool"
# Test that Lisa cannot use the tool
with pytest.raises(ValueError, match="does not own tool"):
tool.process_action(lisa, mock_action)
# Test setting owner
tool.set_owner(lisa)
assert tool.owner == lisa
# Now Lisa should be able to use it
result = tool.process_action(lisa, mock_action)
assert result == True, "New owner should be able to use tool"
def test_tiny_tool_real_world_side_effects(setup):
"""Test TinyTool real-world side effects warning."""
# Test tool without side effects
safe_tool = MockTool(real_world_side_effects=False)
agent = create_oscar_the_architect()
# Should not raise warnings for safe tools
mock_action = {"type": "MOCK_ACTION", "content": "test"}
result = safe_tool.process_action(agent, mock_action)
assert result == True
# Test tool with side effects
dangerous_tool = DangerousMockTool()
# Should log warning (we can't easily test logging in unit tests,
# but we can verify the tool still works)
result = dangerous_tool.process_action(agent, mock_action)
assert result == True
def test_tiny_tool_action_processing(setup):
"""Test TinyTool action processing functionality."""
tool = MockTool()
agent = create_oscar_the_architect()
# Test processing different types of actions
actions = [
{"type": "MOCK_ACTION", "content": "action 1"},
{"type": "MOCK_ACTION", "content": "action 2"},
{"type": "OTHER_ACTION", "content": "action 3"}
]
for action in actions:
result = tool.process_action(agent, action)
assert result == True, "Should process actions successfully"
# Verify actions were recorded
assert len(tool.actions_processed) == 3, "Should have processed all actions"
assert tool.actions_processed[0]["content"] == "action 1"
assert tool.actions_processed[1]["content"] == "action 2"
assert tool.actions_processed[2]["content"] == "action 3"
def test_tiny_tool_prompts():
"""Test TinyTool prompt generation methods."""
tool = MockTool()
# Test action definitions prompt
definitions = tool.actions_definitions_prompt()
assert isinstance(definitions, str), "Should return string"
assert len(definitions) > 0, "Should not be empty"
assert "MOCK_ACTION" in definitions, "Should contain action definition"
# Test action constraints prompt
constraints = tool.actions_constraints_prompt()
assert isinstance(constraints, str), "Should return string"
assert len(constraints) > 0, "Should not be empty"
def test_tiny_tool_serialization():
"""Test TinyTool serialization/deserialization."""
tool = MockTool(
name="SerializationTest",
description="Test serialization",
real_world_side_effects=True
)
# Test serialization
serialized = tool.to_json()
assert isinstance(serialized, dict), "Should serialize to dictionary"
assert "name" in serialized, "Should include name"
assert "description" in serialized, "Should include description"
assert "real_world_side_effects" in serialized, "Should include side effects flag"
# Test deserialization
new_tool = MockTool.from_json(serialized)
assert new_tool.name == tool.name
assert new_tool.description == tool.description
assert new_tool.real_world_side_effects == tool.real_world_side_effects
def test_tiny_tool_with_exporter_and_enricher(setup):
"""Test TinyTool with exporter and enricher components."""
exporter = ArtifactExporter(base_output_folder="test")
enricher = TinyEnricher()
tool = MockTool(
exporter=exporter,
enricher=enricher
)
assert tool.exporter == exporter, "Should store exporter"
assert tool.enricher == enricher, "Should store enricher"
# Test that tool can still process actions with these components
agent = create_oscar_the_architect()
mock_action = {"type": "MOCK_ACTION", "content": "test with components"}
result = tool.process_action(agent, mock_action)
assert result == True, "Should work with exporter and enricher"
def test_tiny_tool_abstract_methods(setup):
"""Test that TinyTool properly enforces abstract methods."""
# Create a tool without implementing required methods
class IncompleteTool(TinyTool):
pass
incomplete_tool = IncompleteTool("incomplete", "missing methods")
agent = create_oscar_the_architect()
# Should raise NotImplementedError for unimplemented methods
with pytest.raises(NotImplementedError):
incomplete_tool._process_action(agent, {})
with pytest.raises(NotImplementedError):
incomplete_tool.actions_definitions_prompt()
with pytest.raises(NotImplementedError):
incomplete_tool.actions_constraints_prompt()
def test_tiny_tool_edge_cases(setup):
"""Test TinyTool edge cases and error handling."""
tool = MockTool()
agent = create_oscar_the_architect()
# Test with None action
result = tool.process_action(agent, None)
assert result == True, "Should handle None action gracefully"
# Test with empty action
result = tool.process_action(agent, {})
assert result == True, "Should handle empty action gracefully"
# Test with malformed action
malformed_action = {"invalid": "structure"}
result = tool.process_action(agent, malformed_action)
assert result == True, "Should handle malformed action gracefully"
def test_tiny_tool_multiple_agents(setup):
"""Test TinyTool with multiple agents."""
tool = MockTool() # No owner, so any agent can use it
oscar = create_oscar_the_architect()
lisa = create_lisa_the_data_scientist()
# Both agents should be able to use the tool
oscar_action = {"type": "MOCK_ACTION", "content": "Oscar's action"}
lisa_action = {"type": "MOCK_ACTION", "content": "Lisa's action"}
result1 = tool.process_action(oscar, oscar_action)
result2 = tool.process_action(lisa, lisa_action)
assert result1 == True, "Oscar should be able to use tool"
assert result2 == True, "Lisa should be able to use tool"
# Verify both actions were processed
assert len(tool.actions_processed) == 2
assert tool.actions_processed[0]["content"] == "Oscar's action"
assert tool.actions_processed[1]["content"] == "Lisa's action"