jarviscore-framework 0.1.1__py3-none-any.whl → 0.2.0__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.
- examples/autoagent_distributed_example.py +211 -0
- examples/custom_profile_decorator.py +134 -0
- examples/custom_profile_wrap.py +168 -0
- examples/customagent_distributed_example.py +362 -0
- examples/customagent_p2p_example.py +347 -0
- jarviscore/__init__.py +49 -36
- jarviscore/adapter/__init__.py +15 -9
- jarviscore/adapter/decorator.py +23 -19
- jarviscore/adapter/wrapper.py +303 -0
- jarviscore/cli/scaffold.py +1 -1
- jarviscore/cli/smoketest.py +3 -2
- jarviscore/core/agent.py +44 -1
- jarviscore/core/mesh.py +196 -35
- jarviscore/data/examples/autoagent_distributed_example.py +211 -0
- jarviscore/data/examples/customagent_distributed_example.py +362 -0
- jarviscore/data/examples/customagent_p2p_example.py +347 -0
- jarviscore/docs/API_REFERENCE.md +264 -51
- jarviscore/docs/AUTOAGENT_GUIDE.md +198 -0
- jarviscore/docs/CONFIGURATION.md +35 -21
- jarviscore/docs/CUSTOMAGENT_GUIDE.md +415 -0
- jarviscore/docs/GETTING_STARTED.md +106 -13
- jarviscore/docs/TROUBLESHOOTING.md +144 -6
- jarviscore/docs/USER_GUIDE.md +138 -361
- jarviscore/orchestration/engine.py +20 -8
- jarviscore/p2p/__init__.py +10 -0
- jarviscore/p2p/coordinator.py +129 -0
- jarviscore/p2p/messages.py +87 -0
- jarviscore/p2p/peer_client.py +576 -0
- jarviscore/p2p/peer_tool.py +268 -0
- {jarviscore_framework-0.1.1.dist-info → jarviscore_framework-0.2.0.dist-info}/METADATA +60 -54
- jarviscore_framework-0.2.0.dist-info/RECORD +132 -0
- {jarviscore_framework-0.1.1.dist-info → jarviscore_framework-0.2.0.dist-info}/WHEEL +1 -1
- {jarviscore_framework-0.1.1.dist-info → jarviscore_framework-0.2.0.dist-info}/top_level.txt +1 -0
- test_logs/code_registry/functions/data_generator-558779ed_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-5ed3609e_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-66da0356_43970bb9.py +25 -0
- test_logs/code_registry/functions/data_generator-7a2fac83_583709d9.py +36 -0
- test_logs/code_registry/functions/data_generator-888b670f_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-9ca5f642_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-bfd90775_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-e95d2f7d_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-f60ca8a2_327eb8c2.py +29 -0
- test_logs/code_registry/functions/mathematician-02adf9ee_958658d9.py +19 -0
- test_logs/code_registry/functions/mathematician-0706fb57_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-153c9c4a_ba59c918.py +83 -0
- test_logs/code_registry/functions/mathematician-287e61c0_41daa793.py +18 -0
- test_logs/code_registry/functions/mathematician-2967af5a_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-303ca6d6_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-308a4afd_cbf5064d.py +73 -0
- test_logs/code_registry/functions/mathematician-353f16e2_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-3c22475a_41daa793.py +17 -0
- test_logs/code_registry/functions/mathematician-5bac1029_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-640f76b2_9198780b.py +19 -0
- test_logs/code_registry/functions/mathematician-752fa7ea_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-baf9ef39_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-bc8b2a2f_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-c31e4686_41daa793.py +18 -0
- test_logs/code_registry/functions/mathematician-cc84c84c_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-dd7c7144_9198780b.py +19 -0
- test_logs/code_registry/functions/mathematician-e671c256_41ea4487.py +74 -0
- test_logs/code_registry/functions/report_generator-1a878fcc_18d44bdc.py +47 -0
- test_logs/code_registry/functions/report_generator-25c1c331_cea57d0d.py +35 -0
- test_logs/code_registry/functions/report_generator-37552117_e711c2b9.py +35 -0
- test_logs/code_registry/functions/report_generator-bc662768_e711c2b9.py +35 -0
- test_logs/code_registry/functions/report_generator-d6c0e76b_5e7722ec.py +44 -0
- test_logs/code_registry/functions/report_generator-f270fb02_680529c3.py +44 -0
- test_logs/code_registry/functions/text_processor-11393b14_4370d3ed.py +40 -0
- test_logs/code_registry/functions/text_processor-7d02dfc3_d3b569be.py +37 -0
- test_logs/code_registry/functions/text_processor-8adb5e32_9168c5fe.py +13 -0
- test_logs/code_registry/functions/text_processor-c58ffc19_78b4ceac.py +42 -0
- test_logs/code_registry/functions/text_processor-cd5977b1_9168c5fe.py +13 -0
- test_logs/code_registry/functions/text_processor-ec1c8773_9168c5fe.py +13 -0
- tests/test_01_analyst_standalone.py +124 -0
- tests/test_02_assistant_standalone.py +164 -0
- tests/test_03_analyst_with_framework.py +945 -0
- tests/test_04_assistant_with_framework.py +1002 -0
- tests/test_05_integration.py +1301 -0
- tests/test_06_real_llm_integration.py +760 -0
- tests/test_07_distributed_single_node.py +578 -0
- tests/test_08_distributed_multi_node.py +454 -0
- tests/test_09_distributed_autoagent.py +509 -0
- tests/test_10_distributed_customagent.py +787 -0
- tests/test_mesh.py +35 -4
- jarviscore_framework-0.1.1.dist-info/RECORD +0 -69
- {jarviscore_framework-0.1.1.dist-info → jarviscore_framework-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test 9: Distributed Mode - AutoAgent Profile with Real LLM
|
|
3
|
+
|
|
4
|
+
Tests the AutoAgent profile in distributed execution mode:
|
|
5
|
+
- AutoAgent initialization and setup
|
|
6
|
+
- Real LLM code generation
|
|
7
|
+
- Workflow execution with AutoAgent
|
|
8
|
+
- Multi-step pipelines with dependencies
|
|
9
|
+
|
|
10
|
+
This file uses REAL LLM API calls (not mocks).
|
|
11
|
+
|
|
12
|
+
Run with: pytest tests/test_09_distributed_autoagent.py -v -s
|
|
13
|
+
"""
|
|
14
|
+
import asyncio
|
|
15
|
+
import sys
|
|
16
|
+
import pytest
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, '.')
|
|
20
|
+
|
|
21
|
+
from jarviscore import Mesh
|
|
22
|
+
from jarviscore.profiles.autoagent import AutoAgent
|
|
23
|
+
|
|
24
|
+
# Setup logging
|
|
25
|
+
logging.basicConfig(level=logging.INFO)
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# Skip all tests if no API key is configured
|
|
29
|
+
try:
|
|
30
|
+
from jarviscore.config import settings
|
|
31
|
+
HAS_API_KEY = bool(
|
|
32
|
+
settings.claude_api_key or
|
|
33
|
+
settings.azure_api_key or
|
|
34
|
+
settings.gemini_api_key
|
|
35
|
+
)
|
|
36
|
+
except Exception:
|
|
37
|
+
HAS_API_KEY = False
|
|
38
|
+
|
|
39
|
+
pytestmark = pytest.mark.skipif(
|
|
40
|
+
not HAS_API_KEY,
|
|
41
|
+
reason="No LLM API key configured in .env"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
46
|
+
# TEST AUTOAGENTS
|
|
47
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
48
|
+
|
|
49
|
+
class MathAgent(AutoAgent):
|
|
50
|
+
"""AutoAgent that performs mathematical calculations."""
|
|
51
|
+
role = "mathematician"
|
|
52
|
+
capabilities = ["math", "calculation", "algebra"]
|
|
53
|
+
system_prompt = """You are an expert mathematician. You write clean Python code
|
|
54
|
+
to solve mathematical problems. Always include proper error handling and return
|
|
55
|
+
results as a dictionary with 'result' key. Use only standard library (no numpy)."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DataGeneratorAgent(AutoAgent):
|
|
59
|
+
"""AutoAgent that generates sample data."""
|
|
60
|
+
role = "data_generator"
|
|
61
|
+
capabilities = ["data_generation", "sampling"]
|
|
62
|
+
system_prompt = """You are a data generation expert. You write Python code
|
|
63
|
+
to generate sample datasets. Return data as a dictionary with descriptive keys.
|
|
64
|
+
Use only standard library - no pandas or numpy. Generate simple lists/dicts."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TextProcessorAgent(AutoAgent):
|
|
68
|
+
"""AutoAgent that processes text."""
|
|
69
|
+
role = "text_processor"
|
|
70
|
+
capabilities = ["text_processing", "nlp", "summarization"]
|
|
71
|
+
system_prompt = """You are a text processing expert. You write Python code
|
|
72
|
+
to analyze and transform text. Return results as a dictionary with clear keys.
|
|
73
|
+
Use only standard library. Focus on string operations and basic analysis."""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ReportGeneratorAgent(AutoAgent):
|
|
77
|
+
"""AutoAgent that generates reports from data."""
|
|
78
|
+
role = "report_generator"
|
|
79
|
+
capabilities = ["reporting", "formatting", "visualization"]
|
|
80
|
+
system_prompt = """You are a report generation expert. You write Python code
|
|
81
|
+
to create formatted text reports from data. Return the report as a string in
|
|
82
|
+
a dictionary with 'report' key. Use only standard library for formatting."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
86
|
+
# FIXTURES
|
|
87
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
88
|
+
|
|
89
|
+
@pytest.fixture
|
|
90
|
+
async def distributed_mesh_math():
|
|
91
|
+
"""Create distributed mesh with MathAgent."""
|
|
92
|
+
mesh = Mesh(mode="distributed", config={
|
|
93
|
+
'bind_port': 7980,
|
|
94
|
+
'execution_timeout': 60,
|
|
95
|
+
'max_repair_attempts': 2,
|
|
96
|
+
'log_directory': './test_logs'
|
|
97
|
+
})
|
|
98
|
+
agent = mesh.add(MathAgent)
|
|
99
|
+
|
|
100
|
+
await mesh.start()
|
|
101
|
+
|
|
102
|
+
yield mesh, agent
|
|
103
|
+
|
|
104
|
+
await mesh.stop()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@pytest.fixture
|
|
108
|
+
async def distributed_mesh_pipeline():
|
|
109
|
+
"""Create distributed mesh with multiple AutoAgents for pipeline testing."""
|
|
110
|
+
mesh = Mesh(mode="distributed", config={
|
|
111
|
+
'bind_port': 7981,
|
|
112
|
+
'execution_timeout': 60,
|
|
113
|
+
'max_repair_attempts': 2,
|
|
114
|
+
'log_directory': './test_logs'
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
generator = mesh.add(DataGeneratorAgent)
|
|
118
|
+
processor = mesh.add(TextProcessorAgent)
|
|
119
|
+
reporter = mesh.add(ReportGeneratorAgent)
|
|
120
|
+
|
|
121
|
+
await mesh.start()
|
|
122
|
+
|
|
123
|
+
yield mesh, generator, processor, reporter
|
|
124
|
+
|
|
125
|
+
await mesh.stop()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
129
|
+
# TEST CLASS: AutoAgent Setup in Distributed Mode
|
|
130
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
131
|
+
|
|
132
|
+
class TestAutoAgentDistributedSetup:
|
|
133
|
+
"""Tests for AutoAgent initialization in distributed mode."""
|
|
134
|
+
|
|
135
|
+
@pytest.mark.asyncio
|
|
136
|
+
async def test_autoagent_inherits_from_profile(self, distributed_mesh_math):
|
|
137
|
+
"""AutoAgent should inherit from Profile (which inherits from Agent)."""
|
|
138
|
+
mesh, agent = distributed_mesh_math
|
|
139
|
+
|
|
140
|
+
from jarviscore.core.profile import Profile
|
|
141
|
+
from jarviscore.core.agent import Agent
|
|
142
|
+
|
|
143
|
+
assert isinstance(agent, Profile)
|
|
144
|
+
assert isinstance(agent, Agent)
|
|
145
|
+
assert isinstance(agent, AutoAgent)
|
|
146
|
+
|
|
147
|
+
@pytest.mark.asyncio
|
|
148
|
+
async def test_autoagent_has_required_attributes(self, distributed_mesh_math):
|
|
149
|
+
"""AutoAgent should have role, capabilities, and system_prompt."""
|
|
150
|
+
mesh, agent = distributed_mesh_math
|
|
151
|
+
|
|
152
|
+
assert agent.role == "mathematician"
|
|
153
|
+
assert "math" in agent.capabilities
|
|
154
|
+
assert "calculation" in agent.capabilities
|
|
155
|
+
assert agent.system_prompt is not None
|
|
156
|
+
assert len(agent.system_prompt) > 0
|
|
157
|
+
|
|
158
|
+
@pytest.mark.asyncio
|
|
159
|
+
async def test_autoagent_setup_initializes_components(self, distributed_mesh_math):
|
|
160
|
+
"""AutoAgent setup should initialize LLM and execution components."""
|
|
161
|
+
mesh, agent = distributed_mesh_math
|
|
162
|
+
|
|
163
|
+
# After mesh.start(), agent.setup() should have been called
|
|
164
|
+
assert agent.llm is not None, "LLM client should be initialized"
|
|
165
|
+
assert agent.codegen is not None, "Code generator should be initialized"
|
|
166
|
+
assert agent.sandbox is not None, "Sandbox executor should be initialized"
|
|
167
|
+
assert agent.repair is not None, "Repair system should be initialized"
|
|
168
|
+
assert agent.result_handler is not None, "Result handler should be initialized"
|
|
169
|
+
|
|
170
|
+
@pytest.mark.asyncio
|
|
171
|
+
async def test_autoagent_joins_distributed_mesh(self, distributed_mesh_math):
|
|
172
|
+
"""AutoAgent should be properly registered in distributed mesh."""
|
|
173
|
+
mesh, agent = distributed_mesh_math
|
|
174
|
+
|
|
175
|
+
# Agent should be registered
|
|
176
|
+
assert mesh.get_agent("mathematician") == agent
|
|
177
|
+
|
|
178
|
+
# Mesh should have P2P coordinator (distributed mode)
|
|
179
|
+
assert mesh._p2p_coordinator is not None
|
|
180
|
+
assert mesh._p2p_coordinator._started is True
|
|
181
|
+
|
|
182
|
+
# Mesh should have workflow engine (distributed mode)
|
|
183
|
+
assert mesh._workflow_engine is not None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
187
|
+
# TEST CLASS: AutoAgent Workflow Execution
|
|
188
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
189
|
+
|
|
190
|
+
class TestAutoAgentWorkflowExecution:
|
|
191
|
+
"""Tests for AutoAgent executing workflow steps with real LLM."""
|
|
192
|
+
|
|
193
|
+
@pytest.mark.asyncio
|
|
194
|
+
async def test_autoagent_executes_simple_task(self, distributed_mesh_math):
|
|
195
|
+
"""AutoAgent should execute a simple mathematical task."""
|
|
196
|
+
mesh, agent = distributed_mesh_math
|
|
197
|
+
|
|
198
|
+
print("\n" + "="*60)
|
|
199
|
+
print("TEST: AutoAgent executes simple math task")
|
|
200
|
+
print("="*60)
|
|
201
|
+
|
|
202
|
+
results = await mesh.workflow("simple-math", [
|
|
203
|
+
{"agent": "mathematician", "task": "Calculate the factorial of 5"}
|
|
204
|
+
])
|
|
205
|
+
|
|
206
|
+
print(f"\nResults: {results}")
|
|
207
|
+
|
|
208
|
+
assert len(results) == 1
|
|
209
|
+
result = results[0]
|
|
210
|
+
|
|
211
|
+
# Should succeed
|
|
212
|
+
assert result["status"] == "success", f"Task failed: {result.get('error')}"
|
|
213
|
+
|
|
214
|
+
# Should have generated code
|
|
215
|
+
assert "code" in result
|
|
216
|
+
assert len(result["code"]) > 0
|
|
217
|
+
|
|
218
|
+
print(f"\nGenerated code:\n{result['code'][:500]}...")
|
|
219
|
+
print(f"\nOutput: {result.get('output')}")
|
|
220
|
+
|
|
221
|
+
@pytest.mark.asyncio
|
|
222
|
+
async def test_autoagent_generates_executable_code(self, distributed_mesh_math):
|
|
223
|
+
"""AutoAgent should generate code that actually executes."""
|
|
224
|
+
mesh, agent = distributed_mesh_math
|
|
225
|
+
|
|
226
|
+
print("\n" + "="*60)
|
|
227
|
+
print("TEST: AutoAgent generates executable code")
|
|
228
|
+
print("="*60)
|
|
229
|
+
|
|
230
|
+
results = await mesh.workflow("exec-test", [
|
|
231
|
+
{"agent": "mathematician", "task": "Calculate the sum of numbers from 1 to 100"}
|
|
232
|
+
])
|
|
233
|
+
|
|
234
|
+
result = results[0]
|
|
235
|
+
assert result["status"] == "success", f"Task failed: {result.get('error')}"
|
|
236
|
+
|
|
237
|
+
# The output should contain the correct answer (5050)
|
|
238
|
+
output = str(result.get('output', ''))
|
|
239
|
+
print(f"\nOutput: {output}")
|
|
240
|
+
|
|
241
|
+
# Verify we got a meaningful result
|
|
242
|
+
assert result.get('output') is not None, "Should have output"
|
|
243
|
+
|
|
244
|
+
@pytest.mark.asyncio
|
|
245
|
+
async def test_autoagent_handles_complex_task(self, distributed_mesh_math):
|
|
246
|
+
"""AutoAgent should handle more complex mathematical tasks."""
|
|
247
|
+
mesh, agent = distributed_mesh_math
|
|
248
|
+
|
|
249
|
+
print("\n" + "="*60)
|
|
250
|
+
print("TEST: AutoAgent handles complex task")
|
|
251
|
+
print("="*60)
|
|
252
|
+
|
|
253
|
+
results = await mesh.workflow("complex-math", [
|
|
254
|
+
{
|
|
255
|
+
"agent": "mathematician",
|
|
256
|
+
"task": "Generate the first 10 Fibonacci numbers and return them as a list"
|
|
257
|
+
}
|
|
258
|
+
])
|
|
259
|
+
|
|
260
|
+
result = results[0]
|
|
261
|
+
print(f"\nResult status: {result['status']}")
|
|
262
|
+
print(f"Output: {result.get('output')}")
|
|
263
|
+
|
|
264
|
+
assert result["status"] == "success", f"Task failed: {result.get('error')}"
|
|
265
|
+
|
|
266
|
+
@pytest.mark.asyncio
|
|
267
|
+
async def test_autoagent_result_includes_metadata(self, distributed_mesh_math):
|
|
268
|
+
"""AutoAgent result should include agent metadata."""
|
|
269
|
+
mesh, agent = distributed_mesh_math
|
|
270
|
+
|
|
271
|
+
results = await mesh.workflow("metadata-test", [
|
|
272
|
+
{"agent": "mathematician", "task": "Calculate 2 + 2"}
|
|
273
|
+
])
|
|
274
|
+
|
|
275
|
+
result = results[0]
|
|
276
|
+
assert result["status"] == "success"
|
|
277
|
+
|
|
278
|
+
# Should include agent info
|
|
279
|
+
assert "agent_id" in result or "agent" in result
|
|
280
|
+
assert "role" in result or result.get("agent") == "mathematician"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
284
|
+
# TEST CLASS: Multi-Step Pipeline with AutoAgents
|
|
285
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
286
|
+
|
|
287
|
+
class TestAutoAgentMultiStepWorkflow:
|
|
288
|
+
"""Tests for multi-step workflows with multiple AutoAgents."""
|
|
289
|
+
|
|
290
|
+
@pytest.mark.asyncio
|
|
291
|
+
async def test_two_autoagents_sequential_workflow(self, distributed_mesh_pipeline):
|
|
292
|
+
"""Two AutoAgents should execute in sequence."""
|
|
293
|
+
mesh, generator, processor, reporter = distributed_mesh_pipeline
|
|
294
|
+
|
|
295
|
+
print("\n" + "="*60)
|
|
296
|
+
print("TEST: Two AutoAgents in sequential workflow")
|
|
297
|
+
print("="*60)
|
|
298
|
+
|
|
299
|
+
results = await mesh.workflow("sequential-test", [
|
|
300
|
+
{
|
|
301
|
+
"agent": "data_generator",
|
|
302
|
+
"task": "Generate a list of 5 random words"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"agent": "text_processor",
|
|
306
|
+
"task": "Count the total number of characters in these words: apple, banana, cherry, date, elderberry"
|
|
307
|
+
}
|
|
308
|
+
])
|
|
309
|
+
|
|
310
|
+
print(f"\nResults count: {len(results)}")
|
|
311
|
+
for i, r in enumerate(results):
|
|
312
|
+
print(f"Step {i+1}: {r['status']} - {str(r.get('output', ''))[:100]}")
|
|
313
|
+
|
|
314
|
+
assert len(results) == 2
|
|
315
|
+
assert results[0]["status"] == "success", f"Step 1 failed: {results[0].get('error')}"
|
|
316
|
+
assert results[1]["status"] == "success", f"Step 2 failed: {results[1].get('error')}"
|
|
317
|
+
|
|
318
|
+
@pytest.mark.asyncio
|
|
319
|
+
async def test_workflow_with_dependencies(self, distributed_mesh_pipeline):
|
|
320
|
+
"""Workflow should handle step dependencies."""
|
|
321
|
+
mesh, generator, processor, reporter = distributed_mesh_pipeline
|
|
322
|
+
|
|
323
|
+
print("\n" + "="*60)
|
|
324
|
+
print("TEST: Workflow with dependencies")
|
|
325
|
+
print("="*60)
|
|
326
|
+
|
|
327
|
+
results = await mesh.workflow("dependency-test", [
|
|
328
|
+
{
|
|
329
|
+
"agent": "data_generator",
|
|
330
|
+
"task": "Generate a dictionary with keys 'name', 'age', 'city' and sample values"
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"agent": "report_generator",
|
|
334
|
+
"task": "Create a formatted text report from this person data: name=John, age=30, city=NYC",
|
|
335
|
+
"depends_on": [0]
|
|
336
|
+
}
|
|
337
|
+
])
|
|
338
|
+
|
|
339
|
+
print(f"\nStep 1 (generator): {results[0]['status']}")
|
|
340
|
+
print(f"Step 2 (reporter): {results[1]['status']}")
|
|
341
|
+
|
|
342
|
+
assert len(results) == 2
|
|
343
|
+
# Both should succeed
|
|
344
|
+
assert results[0]["status"] == "success"
|
|
345
|
+
assert results[1]["status"] == "success"
|
|
346
|
+
|
|
347
|
+
@pytest.mark.asyncio
|
|
348
|
+
async def test_three_agent_pipeline(self, distributed_mesh_pipeline):
|
|
349
|
+
"""Three AutoAgents should work together in a pipeline."""
|
|
350
|
+
mesh, generator, processor, reporter = distributed_mesh_pipeline
|
|
351
|
+
|
|
352
|
+
print("\n" + "="*60)
|
|
353
|
+
print("TEST: Three agent pipeline")
|
|
354
|
+
print("="*60)
|
|
355
|
+
|
|
356
|
+
results = await mesh.workflow("three-agent-pipeline", [
|
|
357
|
+
{
|
|
358
|
+
"agent": "data_generator",
|
|
359
|
+
"task": "Generate a list of 3 product names with prices as a dictionary"
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"agent": "text_processor",
|
|
363
|
+
"task": "Format this product list into a bulleted text list: Widget=$10, Gadget=$25, Tool=$15"
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"agent": "report_generator",
|
|
367
|
+
"task": "Create a simple invoice header with company name 'Acme Corp' and date 'Jan 2024'"
|
|
368
|
+
}
|
|
369
|
+
])
|
|
370
|
+
|
|
371
|
+
print(f"\nPipeline results:")
|
|
372
|
+
for i, r in enumerate(results):
|
|
373
|
+
status = r['status']
|
|
374
|
+
output = str(r.get('output', ''))[:80]
|
|
375
|
+
print(f" Step {i+1}: [{status}] {output}...")
|
|
376
|
+
|
|
377
|
+
assert len(results) == 3
|
|
378
|
+
# Count successes
|
|
379
|
+
successes = sum(1 for r in results if r["status"] == "success")
|
|
380
|
+
print(f"\nSuccessful steps: {successes}/3")
|
|
381
|
+
|
|
382
|
+
# At least 2 should succeed for this test to be meaningful
|
|
383
|
+
assert successes >= 2, "At least 2 steps should succeed"
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
387
|
+
# TEST CLASS: AutoAgent Error Handling
|
|
388
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
389
|
+
|
|
390
|
+
class TestAutoAgentErrorHandling:
|
|
391
|
+
"""Tests for AutoAgent error handling and repair."""
|
|
392
|
+
|
|
393
|
+
@pytest.mark.asyncio
|
|
394
|
+
async def test_autoagent_handles_impossible_task_gracefully(self, distributed_mesh_math):
|
|
395
|
+
"""AutoAgent should handle impossible tasks gracefully."""
|
|
396
|
+
mesh, agent = distributed_mesh_math
|
|
397
|
+
|
|
398
|
+
print("\n" + "="*60)
|
|
399
|
+
print("TEST: AutoAgent handles difficult task")
|
|
400
|
+
print("="*60)
|
|
401
|
+
|
|
402
|
+
# This task is intentionally vague to test robustness
|
|
403
|
+
results = await mesh.workflow("difficult-task", [
|
|
404
|
+
{
|
|
405
|
+
"agent": "mathematician",
|
|
406
|
+
"task": "Calculate something mathematical and return a result"
|
|
407
|
+
}
|
|
408
|
+
])
|
|
409
|
+
|
|
410
|
+
result = results[0]
|
|
411
|
+
print(f"\nStatus: {result['status']}")
|
|
412
|
+
print(f"Output: {result.get('output')}")
|
|
413
|
+
|
|
414
|
+
# Should either succeed or fail gracefully with error info
|
|
415
|
+
assert result["status"] in ["success", "failure"]
|
|
416
|
+
if result["status"] == "failure":
|
|
417
|
+
assert "error" in result
|
|
418
|
+
|
|
419
|
+
@pytest.mark.asyncio
|
|
420
|
+
async def test_autoagent_repair_tracking(self, distributed_mesh_math):
|
|
421
|
+
"""AutoAgent should track repair attempts."""
|
|
422
|
+
mesh, agent = distributed_mesh_math
|
|
423
|
+
|
|
424
|
+
results = await mesh.workflow("repair-test", [
|
|
425
|
+
{
|
|
426
|
+
"agent": "mathematician",
|
|
427
|
+
"task": "Calculate the square root of 144"
|
|
428
|
+
}
|
|
429
|
+
])
|
|
430
|
+
|
|
431
|
+
result = results[0]
|
|
432
|
+
|
|
433
|
+
# Result should include repair count (even if 0)
|
|
434
|
+
assert "repairs" in result or result["status"] == "success"
|
|
435
|
+
print(f"\nRepairs attempted: {result.get('repairs', 0)}")
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
439
|
+
# MANUAL DEMONSTRATION
|
|
440
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
441
|
+
|
|
442
|
+
async def run_autoagent_demo():
|
|
443
|
+
"""Demonstrate AutoAgent in distributed mode with real LLM."""
|
|
444
|
+
print("\n" + "="*70)
|
|
445
|
+
print("AUTOAGENT DISTRIBUTED MODE DEMONSTRATION")
|
|
446
|
+
print("="*70)
|
|
447
|
+
|
|
448
|
+
# Create distributed mesh with AutoAgents
|
|
449
|
+
mesh = Mesh(mode="distributed", config={
|
|
450
|
+
'bind_port': 7985,
|
|
451
|
+
'execution_timeout': 60,
|
|
452
|
+
'max_repair_attempts': 2,
|
|
453
|
+
'log_directory': './demo_logs'
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
mesh.add(MathAgent)
|
|
457
|
+
mesh.add(DataGeneratorAgent)
|
|
458
|
+
mesh.add(TextProcessorAgent)
|
|
459
|
+
|
|
460
|
+
print("\n[SETUP] Created distributed mesh with:")
|
|
461
|
+
for agent in mesh.agents:
|
|
462
|
+
print(f" - {agent.role}: {agent.capabilities}")
|
|
463
|
+
|
|
464
|
+
await mesh.start()
|
|
465
|
+
print("\n[STARTED] Mesh is running in distributed mode")
|
|
466
|
+
|
|
467
|
+
try:
|
|
468
|
+
# Demo 1: Simple math task
|
|
469
|
+
print("\n" + "-"*60)
|
|
470
|
+
print("DEMO 1: Simple Math Task")
|
|
471
|
+
print("-"*60)
|
|
472
|
+
|
|
473
|
+
results = await mesh.workflow("demo-math", [
|
|
474
|
+
{"agent": "mathematician", "task": "Calculate 17 * 23 and return the result"}
|
|
475
|
+
])
|
|
476
|
+
|
|
477
|
+
print(f"Result: {results[0]['status']}")
|
|
478
|
+
print(f"Output: {results[0].get('output')}")
|
|
479
|
+
print(f"Code generated: {len(results[0].get('code', ''))} chars")
|
|
480
|
+
|
|
481
|
+
# Demo 2: Multi-agent workflow
|
|
482
|
+
print("\n" + "-"*60)
|
|
483
|
+
print("DEMO 2: Multi-Agent Workflow")
|
|
484
|
+
print("-"*60)
|
|
485
|
+
|
|
486
|
+
results = await mesh.workflow("demo-pipeline", [
|
|
487
|
+
{
|
|
488
|
+
"agent": "data_generator",
|
|
489
|
+
"task": "Generate a simple dictionary with 'x' and 'y' coordinates"
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
"agent": "mathematician",
|
|
493
|
+
"task": "Calculate the distance from origin for point x=3, y=4 using Pythagorean theorem"
|
|
494
|
+
}
|
|
495
|
+
])
|
|
496
|
+
|
|
497
|
+
for i, r in enumerate(results):
|
|
498
|
+
print(f"Step {i+1}: {r['status']} - {str(r.get('output', ''))[:100]}")
|
|
499
|
+
|
|
500
|
+
finally:
|
|
501
|
+
await mesh.stop()
|
|
502
|
+
|
|
503
|
+
print("\n" + "="*70)
|
|
504
|
+
print("DEMONSTRATION COMPLETE")
|
|
505
|
+
print("="*70)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
if __name__ == "__main__":
|
|
509
|
+
asyncio.run(run_autoagent_demo())
|