Spaces:
Paused
Paused
File size: 9,813 Bytes
f6686e1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | 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"
|