pygeai-orchestration 0.1.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygeai_orchestration/__init__.py +99 -0
- pygeai_orchestration/cli/__init__.py +7 -0
- pygeai_orchestration/cli/__main__.py +11 -0
- pygeai_orchestration/cli/commands/__init__.py +13 -0
- pygeai_orchestration/cli/commands/base.py +192 -0
- pygeai_orchestration/cli/error_handler.py +123 -0
- pygeai_orchestration/cli/formatters.py +419 -0
- pygeai_orchestration/cli/geai_orch.py +270 -0
- pygeai_orchestration/cli/interactive.py +265 -0
- pygeai_orchestration/cli/texts/help.py +169 -0
- pygeai_orchestration/core/__init__.py +130 -0
- pygeai_orchestration/core/base/__init__.py +23 -0
- pygeai_orchestration/core/base/agent.py +121 -0
- pygeai_orchestration/core/base/geai_agent.py +144 -0
- pygeai_orchestration/core/base/geai_orchestrator.py +77 -0
- pygeai_orchestration/core/base/orchestrator.py +142 -0
- pygeai_orchestration/core/base/pattern.py +161 -0
- pygeai_orchestration/core/base/tool.py +149 -0
- pygeai_orchestration/core/common/__init__.py +18 -0
- pygeai_orchestration/core/common/context.py +140 -0
- pygeai_orchestration/core/common/memory.py +176 -0
- pygeai_orchestration/core/common/message.py +50 -0
- pygeai_orchestration/core/common/state.py +181 -0
- pygeai_orchestration/core/composition.py +190 -0
- pygeai_orchestration/core/config.py +356 -0
- pygeai_orchestration/core/exceptions.py +400 -0
- pygeai_orchestration/core/handlers.py +380 -0
- pygeai_orchestration/core/utils/__init__.py +37 -0
- pygeai_orchestration/core/utils/cache.py +138 -0
- pygeai_orchestration/core/utils/config.py +94 -0
- pygeai_orchestration/core/utils/logging.py +57 -0
- pygeai_orchestration/core/utils/metrics.py +184 -0
- pygeai_orchestration/core/utils/validators.py +140 -0
- pygeai_orchestration/dev/__init__.py +15 -0
- pygeai_orchestration/dev/debug.py +288 -0
- pygeai_orchestration/dev/templates.py +321 -0
- pygeai_orchestration/dev/testing.py +301 -0
- pygeai_orchestration/patterns/__init__.py +15 -0
- pygeai_orchestration/patterns/multi_agent.py +237 -0
- pygeai_orchestration/patterns/planning.py +219 -0
- pygeai_orchestration/patterns/react.py +221 -0
- pygeai_orchestration/patterns/reflection.py +134 -0
- pygeai_orchestration/patterns/tool_use.py +170 -0
- pygeai_orchestration/tests/__init__.py +1 -0
- pygeai_orchestration/tests/test_base_classes.py +187 -0
- pygeai_orchestration/tests/test_cache.py +184 -0
- pygeai_orchestration/tests/test_cli_formatters.py +232 -0
- pygeai_orchestration/tests/test_common.py +214 -0
- pygeai_orchestration/tests/test_composition.py +265 -0
- pygeai_orchestration/tests/test_config.py +301 -0
- pygeai_orchestration/tests/test_dev_utils.py +337 -0
- pygeai_orchestration/tests/test_exceptions.py +327 -0
- pygeai_orchestration/tests/test_handlers.py +307 -0
- pygeai_orchestration/tests/test_metrics.py +171 -0
- pygeai_orchestration/tests/test_patterns.py +165 -0
- pygeai_orchestration-0.1.0b2.dist-info/METADATA +290 -0
- pygeai_orchestration-0.1.0b2.dist-info/RECORD +61 -0
- pygeai_orchestration-0.1.0b2.dist-info/WHEEL +5 -0
- pygeai_orchestration-0.1.0b2.dist-info/entry_points.txt +2 -0
- pygeai_orchestration-0.1.0b2.dist-info/licenses/LICENSE +8 -0
- pygeai_orchestration-0.1.0b2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"""Pattern templates and code generators."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PatternTemplate(str, Enum):
|
|
9
|
+
"""Available pattern templates."""
|
|
10
|
+
|
|
11
|
+
REFLECTION = "reflection"
|
|
12
|
+
REACT = "react"
|
|
13
|
+
PLANNING = "planning"
|
|
14
|
+
MULTI_AGENT = "multi_agent"
|
|
15
|
+
CUSTOM = "custom"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TemplateGenerator:
|
|
19
|
+
"""Generate pattern boilerplate code."""
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def generate_reflection_pattern(
|
|
23
|
+
name: str,
|
|
24
|
+
description: str = "Custom reflection pattern"
|
|
25
|
+
) -> str:
|
|
26
|
+
"""Generate reflection pattern template.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
name: Pattern class name
|
|
30
|
+
description: Pattern description
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Generated code
|
|
34
|
+
"""
|
|
35
|
+
return f'''"""
|
|
36
|
+
{description}
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from pygeai_orchestration.patterns.reflection import ReflectionPattern
|
|
40
|
+
from pygeai_orchestration.core.base import PatternConfig, PatternResult
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class {name}(ReflectionPattern):
|
|
44
|
+
"""{description}."""
|
|
45
|
+
|
|
46
|
+
async def reflect(self, task: str, result: str, context=None) -> str:
|
|
47
|
+
"""Reflect on task and result.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
task: Original task
|
|
51
|
+
result: Initial result
|
|
52
|
+
context: Optional context
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Reflection feedback
|
|
56
|
+
"""
|
|
57
|
+
reflection_prompt = f"""
|
|
58
|
+
Task: {{task}}
|
|
59
|
+
Result: {{result}}
|
|
60
|
+
|
|
61
|
+
Please analyze this result and provide feedback for improvement.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
reflection = await self.agent.generate(reflection_prompt)
|
|
65
|
+
return reflection
|
|
66
|
+
|
|
67
|
+
async def improve(self, task: str, result: str, reflection: str, context=None) -> str:
|
|
68
|
+
"""Improve result based on reflection.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
task: Original task
|
|
72
|
+
result: Initial result
|
|
73
|
+
reflection: Reflection feedback
|
|
74
|
+
context: Optional context
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Improved result
|
|
78
|
+
"""
|
|
79
|
+
improvement_prompt = f"""
|
|
80
|
+
Task: {{task}}
|
|
81
|
+
Previous Result: {{result}}
|
|
82
|
+
Reflection: {{reflection}}
|
|
83
|
+
|
|
84
|
+
Please provide an improved result based on the reflection.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
improved = await self.agent.generate(improvement_prompt)
|
|
88
|
+
return improved
|
|
89
|
+
'''
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def generate_react_pattern(
|
|
93
|
+
name: str,
|
|
94
|
+
description: str = "Custom ReAct pattern"
|
|
95
|
+
) -> str:
|
|
96
|
+
"""Generate ReAct pattern template.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
name: Pattern class name
|
|
100
|
+
description: Pattern description
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Generated code
|
|
104
|
+
"""
|
|
105
|
+
return f'''"""
|
|
106
|
+
{description}
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
from pygeai_orchestration.patterns.react import ReActPattern
|
|
110
|
+
from pygeai_orchestration.core.base import PatternConfig, PatternResult
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class {name}(ReActPattern):
|
|
114
|
+
"""{description}."""
|
|
115
|
+
|
|
116
|
+
async def think(self, task: str, context=None) -> str:
|
|
117
|
+
"""Reason about the task.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
task: Task to think about
|
|
121
|
+
context: Optional context
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Reasoning result
|
|
125
|
+
"""
|
|
126
|
+
thought_prompt = f"""
|
|
127
|
+
Task: {{task}}
|
|
128
|
+
|
|
129
|
+
Think step by step about how to solve this task.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
thought = await self.agent.generate(thought_prompt)
|
|
133
|
+
return thought
|
|
134
|
+
|
|
135
|
+
async def act(self, task: str, thought: str, context=None) -> str:
|
|
136
|
+
"""Take action based on reasoning.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
task: Original task
|
|
140
|
+
thought: Reasoning result
|
|
141
|
+
context: Optional context
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Action result
|
|
145
|
+
"""
|
|
146
|
+
action_prompt = f"""
|
|
147
|
+
Task: {{task}}
|
|
148
|
+
Thought: {{thought}}
|
|
149
|
+
|
|
150
|
+
Based on this reasoning, take the appropriate action.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
action = await self.agent.generate(action_prompt)
|
|
154
|
+
return action
|
|
155
|
+
'''
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def generate_custom_pattern(
|
|
159
|
+
name: str,
|
|
160
|
+
description: str = "Custom orchestration pattern"
|
|
161
|
+
) -> str:
|
|
162
|
+
"""Generate custom pattern template.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
name: Pattern class name
|
|
166
|
+
description: Pattern description
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Generated code
|
|
170
|
+
"""
|
|
171
|
+
return f'''"""
|
|
172
|
+
{description}
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
from pygeai_orchestration.core.base import OrchestrationPattern, PatternConfig, PatternResult
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class {name}(OrchestrationPattern):
|
|
179
|
+
"""{description}."""
|
|
180
|
+
|
|
181
|
+
async def _execute_impl(self, task: str, context=None) -> PatternResult:
|
|
182
|
+
"""Execute pattern implementation.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
task: Task to execute
|
|
186
|
+
context: Optional execution context
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Pattern execution result
|
|
190
|
+
"""
|
|
191
|
+
# TODO: Implement your pattern logic here
|
|
192
|
+
|
|
193
|
+
result = await self.agent.generate(task)
|
|
194
|
+
|
|
195
|
+
return PatternResult(
|
|
196
|
+
success=True,
|
|
197
|
+
result=result,
|
|
198
|
+
metadata={{"pattern": "{name}"}}
|
|
199
|
+
)
|
|
200
|
+
'''
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def generate_test_file(
|
|
204
|
+
pattern_name: str,
|
|
205
|
+
template: PatternTemplate
|
|
206
|
+
) -> str:
|
|
207
|
+
"""Generate test file for pattern.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
pattern_name: Pattern class name
|
|
211
|
+
template: Pattern template type
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Generated test code
|
|
215
|
+
"""
|
|
216
|
+
return f'''"""
|
|
217
|
+
Tests for {pattern_name}.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
import unittest
|
|
221
|
+
from pygeai_orchestration.dev.testing import MockAgent, PatternTestCase
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Test{pattern_name}(PatternTestCase):
|
|
225
|
+
"""Test cases for {pattern_name}."""
|
|
226
|
+
|
|
227
|
+
def setUp(self):
|
|
228
|
+
"""Set up test fixtures."""
|
|
229
|
+
self.agent = MockAgent()
|
|
230
|
+
# TODO: Initialize your pattern
|
|
231
|
+
# self.pattern = {pattern_name}(self.agent)
|
|
232
|
+
|
|
233
|
+
async def test_basic_execution(self):
|
|
234
|
+
"""Test basic pattern execution."""
|
|
235
|
+
# TODO: Implement test
|
|
236
|
+
pass
|
|
237
|
+
|
|
238
|
+
async def test_error_handling(self):
|
|
239
|
+
"""Test error handling."""
|
|
240
|
+
# TODO: Implement test
|
|
241
|
+
pass
|
|
242
|
+
|
|
243
|
+
async def test_result_format(self):
|
|
244
|
+
"""Test result format."""
|
|
245
|
+
# TODO: Implement test
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
if __name__ == "__main__":
|
|
250
|
+
unittest.main()
|
|
251
|
+
'''
|
|
252
|
+
|
|
253
|
+
@staticmethod
|
|
254
|
+
def save_template(
|
|
255
|
+
code: str,
|
|
256
|
+
output_path: Path,
|
|
257
|
+
overwrite: bool = False
|
|
258
|
+
) -> None:
|
|
259
|
+
"""Save generated template to file.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
code: Generated code
|
|
263
|
+
output_path: Output file path
|
|
264
|
+
overwrite: Whether to overwrite existing file
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
FileExistsError: If file exists and overwrite is False
|
|
268
|
+
"""
|
|
269
|
+
if output_path.exists() and not overwrite:
|
|
270
|
+
raise FileExistsError(f"File already exists: {output_path}")
|
|
271
|
+
|
|
272
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
273
|
+
|
|
274
|
+
with open(output_path, "w") as f:
|
|
275
|
+
f.write(code)
|
|
276
|
+
|
|
277
|
+
@classmethod
|
|
278
|
+
def create_pattern(
|
|
279
|
+
cls,
|
|
280
|
+
template: PatternTemplate,
|
|
281
|
+
name: str,
|
|
282
|
+
description: str,
|
|
283
|
+
output_dir: Optional[Path] = None,
|
|
284
|
+
include_tests: bool = True
|
|
285
|
+
) -> Dict[str, str]:
|
|
286
|
+
"""Create complete pattern with optional tests.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
template: Pattern template type
|
|
290
|
+
name: Pattern class name
|
|
291
|
+
description: Pattern description
|
|
292
|
+
output_dir: Output directory (optional)
|
|
293
|
+
include_tests: Generate test file
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Dictionary of filename -> code
|
|
297
|
+
"""
|
|
298
|
+
files = {}
|
|
299
|
+
|
|
300
|
+
if template == PatternTemplate.REFLECTION:
|
|
301
|
+
code = cls.generate_reflection_pattern(name, description)
|
|
302
|
+
elif template == PatternTemplate.REACT:
|
|
303
|
+
code = cls.generate_react_pattern(name, description)
|
|
304
|
+
else:
|
|
305
|
+
code = cls.generate_custom_pattern(name, description)
|
|
306
|
+
|
|
307
|
+
pattern_filename = f"{name.lower()}.py"
|
|
308
|
+
files[pattern_filename] = code
|
|
309
|
+
|
|
310
|
+
if include_tests:
|
|
311
|
+
test_code = cls.generate_test_file(name, template)
|
|
312
|
+
test_filename = f"test_{name.lower()}.py"
|
|
313
|
+
files[test_filename] = test_code
|
|
314
|
+
|
|
315
|
+
if output_dir:
|
|
316
|
+
output_path = Path(output_dir)
|
|
317
|
+
for filename, content in files.items():
|
|
318
|
+
file_path = output_path / filename
|
|
319
|
+
cls.save_template(content, file_path)
|
|
320
|
+
|
|
321
|
+
return files
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""Testing utilities and helpers."""
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
6
|
+
|
|
7
|
+
from pygeai_orchestration.core.base import BasePattern, PatternConfig, PatternResult, PatternType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MockAgent:
|
|
11
|
+
"""Mock agent for testing."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, responses: Optional[List[str]] = None):
|
|
14
|
+
"""Initialize mock agent.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
responses: Pre-defined responses
|
|
18
|
+
"""
|
|
19
|
+
self.responses = responses or ["Mock response"]
|
|
20
|
+
self.call_count = 0
|
|
21
|
+
self.calls: List[Dict[str, Any]] = []
|
|
22
|
+
|
|
23
|
+
async def generate(self, prompt: str, **kwargs) -> str:
|
|
24
|
+
"""Generate mock response.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
prompt: Input prompt
|
|
28
|
+
**kwargs: Additional arguments
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Mock response
|
|
32
|
+
"""
|
|
33
|
+
self.calls.append({
|
|
34
|
+
"prompt": prompt,
|
|
35
|
+
"kwargs": kwargs,
|
|
36
|
+
"call_number": self.call_count
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
response_index = min(self.call_count, len(self.responses) - 1)
|
|
40
|
+
response = self.responses[response_index]
|
|
41
|
+
self.call_count += 1
|
|
42
|
+
|
|
43
|
+
return response
|
|
44
|
+
|
|
45
|
+
async def chat(self, messages: List[Dict[str, str]], **kwargs) -> str:
|
|
46
|
+
"""Mock chat method.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
messages: Chat messages
|
|
50
|
+
**kwargs: Additional arguments
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Mock response
|
|
54
|
+
"""
|
|
55
|
+
last_message = messages[-1]["content"] if messages else ""
|
|
56
|
+
return await self.generate(last_message, **kwargs)
|
|
57
|
+
|
|
58
|
+
def reset(self) -> None:
|
|
59
|
+
"""Reset mock agent state."""
|
|
60
|
+
self.call_count = 0
|
|
61
|
+
self.calls.clear()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class MockPattern(BasePattern):
|
|
65
|
+
"""Mock pattern for testing."""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
name: str = "MockPattern",
|
|
70
|
+
result: str = "Mock result",
|
|
71
|
+
success: bool = True,
|
|
72
|
+
iterations: int = 1
|
|
73
|
+
):
|
|
74
|
+
"""Initialize mock pattern.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
name: Pattern name
|
|
78
|
+
result: Result to return
|
|
79
|
+
success: Success status
|
|
80
|
+
iterations: Number of iterations
|
|
81
|
+
"""
|
|
82
|
+
config = PatternConfig(
|
|
83
|
+
name=name,
|
|
84
|
+
pattern_type=PatternType.REFLECTION
|
|
85
|
+
)
|
|
86
|
+
super().__init__(config)
|
|
87
|
+
|
|
88
|
+
self.agent = MockAgent([result])
|
|
89
|
+
self.mock_result = result
|
|
90
|
+
self.mock_success = success
|
|
91
|
+
self.mock_iterations = iterations
|
|
92
|
+
self.execution_count = 0
|
|
93
|
+
|
|
94
|
+
async def execute(self, task: str, **kwargs) -> PatternResult:
|
|
95
|
+
"""Execute mock pattern.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
task: Task to execute
|
|
99
|
+
**kwargs: Additional arguments
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Mock result
|
|
103
|
+
"""
|
|
104
|
+
self.execution_count += 1
|
|
105
|
+
|
|
106
|
+
return PatternResult(
|
|
107
|
+
success=self.mock_success,
|
|
108
|
+
result=self.mock_result,
|
|
109
|
+
iterations=self.mock_iterations,
|
|
110
|
+
metadata={"execution_count": self.execution_count}
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
async def step(self, task: str, context=None) -> PatternResult:
|
|
114
|
+
"""Execute one step.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
task: Task to execute
|
|
118
|
+
context: Optional context
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Mock result
|
|
122
|
+
"""
|
|
123
|
+
return await self.execute(task)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class PatternTestCase(unittest.IsolatedAsyncioTestCase):
|
|
127
|
+
"""Base test case for pattern testing."""
|
|
128
|
+
|
|
129
|
+
def create_mock_agent(self, responses: Optional[List[str]] = None) -> MockAgent:
|
|
130
|
+
"""Create mock agent for testing.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
responses: Pre-defined responses
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Mock agent instance
|
|
137
|
+
"""
|
|
138
|
+
return MockAgent(responses)
|
|
139
|
+
|
|
140
|
+
def assert_pattern_result(
|
|
141
|
+
self,
|
|
142
|
+
result: PatternResult,
|
|
143
|
+
success: Optional[bool] = None,
|
|
144
|
+
has_result: bool = True,
|
|
145
|
+
min_iterations: int = 1
|
|
146
|
+
) -> None:
|
|
147
|
+
"""Assert pattern result properties.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
result: Pattern result to check
|
|
151
|
+
success: Expected success status
|
|
152
|
+
has_result: Whether result should have output
|
|
153
|
+
min_iterations: Minimum iterations
|
|
154
|
+
"""
|
|
155
|
+
self.assertIsInstance(result, PatternResult)
|
|
156
|
+
|
|
157
|
+
if success is not None:
|
|
158
|
+
self.assertEqual(result.success, success)
|
|
159
|
+
|
|
160
|
+
if has_result:
|
|
161
|
+
self.assertIsNotNone(result.result)
|
|
162
|
+
self.assertNotEqual(result.result, "")
|
|
163
|
+
|
|
164
|
+
self.assertGreaterEqual(result.iterations, min_iterations)
|
|
165
|
+
|
|
166
|
+
def assert_agent_called(
|
|
167
|
+
self,
|
|
168
|
+
agent: MockAgent,
|
|
169
|
+
min_calls: int = 1,
|
|
170
|
+
contains: Optional[str] = None
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Assert agent was called correctly.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
agent: Mock agent
|
|
176
|
+
min_calls: Minimum number of calls
|
|
177
|
+
contains: Text that should be in prompts
|
|
178
|
+
"""
|
|
179
|
+
self.assertGreaterEqual(agent.call_count, min_calls)
|
|
180
|
+
|
|
181
|
+
if contains:
|
|
182
|
+
prompts = [call["prompt"] for call in agent.calls]
|
|
183
|
+
self.assertTrue(
|
|
184
|
+
any(contains in prompt for prompt in prompts),
|
|
185
|
+
f"No prompt contains '{contains}'"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def create_test_pattern(
|
|
190
|
+
pattern_class: type,
|
|
191
|
+
agent_responses: Optional[List[str]] = None,
|
|
192
|
+
config_overrides: Optional[Dict[str, Any]] = None
|
|
193
|
+
) -> BasePattern:
|
|
194
|
+
"""Create pattern instance for testing.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
pattern_class: Pattern class to instantiate
|
|
198
|
+
agent_responses: Mock agent responses
|
|
199
|
+
config_overrides: Configuration overrides
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Pattern instance
|
|
203
|
+
"""
|
|
204
|
+
config_dict = {
|
|
205
|
+
"name": pattern_class.__name__,
|
|
206
|
+
"pattern_type": PatternType.REFLECTION
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if config_overrides:
|
|
210
|
+
config_dict.update(config_overrides)
|
|
211
|
+
|
|
212
|
+
config = PatternConfig(**config_dict)
|
|
213
|
+
pattern = pattern_class(config)
|
|
214
|
+
|
|
215
|
+
if agent_responses:
|
|
216
|
+
pattern.agent = MockAgent(agent_responses)
|
|
217
|
+
|
|
218
|
+
return pattern
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class AsyncMockHelper:
|
|
222
|
+
"""Helper for creating async mocks."""
|
|
223
|
+
|
|
224
|
+
@staticmethod
|
|
225
|
+
def create_async_mock(return_value: Any = None) -> AsyncMock:
|
|
226
|
+
"""Create async mock with return value.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
return_value: Value to return
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Async mock
|
|
233
|
+
"""
|
|
234
|
+
mock = AsyncMock()
|
|
235
|
+
mock.return_value = return_value
|
|
236
|
+
return mock
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def create_agent_mock(responses: List[str]) -> MagicMock:
|
|
240
|
+
"""Create mock agent with multiple responses.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
responses: List of responses
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Mock agent
|
|
247
|
+
"""
|
|
248
|
+
agent = MagicMock()
|
|
249
|
+
agent.generate = AsyncMock(side_effect=responses)
|
|
250
|
+
return agent
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class TestDataBuilder:
|
|
254
|
+
"""Builder for test data."""
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def build_pattern_result(
|
|
258
|
+
success: bool = True,
|
|
259
|
+
result: str = "Test result",
|
|
260
|
+
iterations: int = 1,
|
|
261
|
+
**metadata
|
|
262
|
+
) -> PatternResult:
|
|
263
|
+
"""Build pattern result for testing.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
success: Success status
|
|
267
|
+
result: Result string
|
|
268
|
+
iterations: Number of iterations
|
|
269
|
+
**metadata: Additional metadata
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Pattern result
|
|
273
|
+
"""
|
|
274
|
+
return PatternResult(
|
|
275
|
+
success=success,
|
|
276
|
+
result=result,
|
|
277
|
+
iterations=iterations,
|
|
278
|
+
metadata=metadata
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def build_pattern_config(
|
|
283
|
+
name: str = "TestPattern",
|
|
284
|
+
pattern_type: PatternType = PatternType.REFLECTION,
|
|
285
|
+
**kwargs
|
|
286
|
+
) -> PatternConfig:
|
|
287
|
+
"""Build pattern config for testing.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
name: Pattern name
|
|
291
|
+
pattern_type: Pattern type
|
|
292
|
+
**kwargs: Additional config
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Pattern config
|
|
296
|
+
"""
|
|
297
|
+
return PatternConfig(
|
|
298
|
+
name=name,
|
|
299
|
+
pattern_type=pattern_type,
|
|
300
|
+
**kwargs
|
|
301
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .reflection import ReflectionPattern
|
|
2
|
+
from .tool_use import ToolUsePattern
|
|
3
|
+
from .react import ReActPattern
|
|
4
|
+
from .planning import PlanningPattern, PlanStep
|
|
5
|
+
from .multi_agent import MultiAgentPattern, AgentRole
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"ReflectionPattern",
|
|
9
|
+
"ToolUsePattern",
|
|
10
|
+
"ReActPattern",
|
|
11
|
+
"PlanningPattern",
|
|
12
|
+
"PlanStep",
|
|
13
|
+
"MultiAgentPattern",
|
|
14
|
+
"AgentRole",
|
|
15
|
+
]
|