jarviscore-framework 0.1.0__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.
Files changed (99) hide show
  1. examples/autoagent_distributed_example.py +211 -0
  2. examples/custom_profile_decorator.py +134 -0
  3. examples/custom_profile_wrap.py +168 -0
  4. examples/customagent_distributed_example.py +362 -0
  5. examples/customagent_p2p_example.py +347 -0
  6. jarviscore/__init__.py +60 -15
  7. jarviscore/adapter/__init__.py +40 -0
  8. jarviscore/adapter/decorator.py +336 -0
  9. jarviscore/adapter/wrapper.py +303 -0
  10. jarviscore/cli/check.py +18 -13
  11. jarviscore/cli/scaffold.py +178 -0
  12. jarviscore/cli/smoketest.py +3 -2
  13. jarviscore/context/__init__.py +40 -0
  14. jarviscore/context/dependency.py +160 -0
  15. jarviscore/context/jarvis_context.py +207 -0
  16. jarviscore/context/memory.py +155 -0
  17. jarviscore/core/agent.py +44 -1
  18. jarviscore/core/mesh.py +196 -35
  19. jarviscore/data/.env.example +146 -0
  20. jarviscore/data/__init__.py +7 -0
  21. jarviscore/data/examples/autoagent_distributed_example.py +211 -0
  22. jarviscore/data/examples/calculator_agent_example.py +77 -0
  23. jarviscore/data/examples/customagent_distributed_example.py +362 -0
  24. jarviscore/data/examples/customagent_p2p_example.py +347 -0
  25. jarviscore/data/examples/multi_agent_workflow.py +132 -0
  26. jarviscore/data/examples/research_agent_example.py +76 -0
  27. jarviscore/docs/API_REFERENCE.md +264 -51
  28. jarviscore/docs/AUTOAGENT_GUIDE.md +198 -0
  29. jarviscore/docs/CONFIGURATION.md +41 -23
  30. jarviscore/docs/CUSTOMAGENT_GUIDE.md +415 -0
  31. jarviscore/docs/GETTING_STARTED.md +113 -17
  32. jarviscore/docs/TROUBLESHOOTING.md +155 -13
  33. jarviscore/docs/USER_GUIDE.md +144 -363
  34. jarviscore/execution/llm.py +23 -16
  35. jarviscore/orchestration/engine.py +20 -8
  36. jarviscore/p2p/__init__.py +10 -0
  37. jarviscore/p2p/coordinator.py +129 -0
  38. jarviscore/p2p/messages.py +87 -0
  39. jarviscore/p2p/peer_client.py +576 -0
  40. jarviscore/p2p/peer_tool.py +268 -0
  41. jarviscore_framework-0.2.0.dist-info/METADATA +143 -0
  42. jarviscore_framework-0.2.0.dist-info/RECORD +132 -0
  43. {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/WHEEL +1 -1
  44. {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/top_level.txt +1 -0
  45. test_logs/code_registry/functions/data_generator-558779ed_560ebc37.py +7 -0
  46. test_logs/code_registry/functions/data_generator-5ed3609e_560ebc37.py +7 -0
  47. test_logs/code_registry/functions/data_generator-66da0356_43970bb9.py +25 -0
  48. test_logs/code_registry/functions/data_generator-7a2fac83_583709d9.py +36 -0
  49. test_logs/code_registry/functions/data_generator-888b670f_aa235863.py +9 -0
  50. test_logs/code_registry/functions/data_generator-9ca5f642_aa235863.py +9 -0
  51. test_logs/code_registry/functions/data_generator-bfd90775_560ebc37.py +7 -0
  52. test_logs/code_registry/functions/data_generator-e95d2f7d_aa235863.py +9 -0
  53. test_logs/code_registry/functions/data_generator-f60ca8a2_327eb8c2.py +29 -0
  54. test_logs/code_registry/functions/mathematician-02adf9ee_958658d9.py +19 -0
  55. test_logs/code_registry/functions/mathematician-0706fb57_5df13441.py +23 -0
  56. test_logs/code_registry/functions/mathematician-153c9c4a_ba59c918.py +83 -0
  57. test_logs/code_registry/functions/mathematician-287e61c0_41daa793.py +18 -0
  58. test_logs/code_registry/functions/mathematician-2967af5a_863c2cc6.py +17 -0
  59. test_logs/code_registry/functions/mathematician-303ca6d6_5df13441.py +23 -0
  60. test_logs/code_registry/functions/mathematician-308a4afd_cbf5064d.py +73 -0
  61. test_logs/code_registry/functions/mathematician-353f16e2_0968bcf5.py +18 -0
  62. test_logs/code_registry/functions/mathematician-3c22475a_41daa793.py +17 -0
  63. test_logs/code_registry/functions/mathematician-5bac1029_0968bcf5.py +18 -0
  64. test_logs/code_registry/functions/mathematician-640f76b2_9198780b.py +19 -0
  65. test_logs/code_registry/functions/mathematician-752fa7ea_863c2cc6.py +17 -0
  66. test_logs/code_registry/functions/mathematician-baf9ef39_0968bcf5.py +18 -0
  67. test_logs/code_registry/functions/mathematician-bc8b2a2f_5df13441.py +23 -0
  68. test_logs/code_registry/functions/mathematician-c31e4686_41daa793.py +18 -0
  69. test_logs/code_registry/functions/mathematician-cc84c84c_863c2cc6.py +17 -0
  70. test_logs/code_registry/functions/mathematician-dd7c7144_9198780b.py +19 -0
  71. test_logs/code_registry/functions/mathematician-e671c256_41ea4487.py +74 -0
  72. test_logs/code_registry/functions/report_generator-1a878fcc_18d44bdc.py +47 -0
  73. test_logs/code_registry/functions/report_generator-25c1c331_cea57d0d.py +35 -0
  74. test_logs/code_registry/functions/report_generator-37552117_e711c2b9.py +35 -0
  75. test_logs/code_registry/functions/report_generator-bc662768_e711c2b9.py +35 -0
  76. test_logs/code_registry/functions/report_generator-d6c0e76b_5e7722ec.py +44 -0
  77. test_logs/code_registry/functions/report_generator-f270fb02_680529c3.py +44 -0
  78. test_logs/code_registry/functions/text_processor-11393b14_4370d3ed.py +40 -0
  79. test_logs/code_registry/functions/text_processor-7d02dfc3_d3b569be.py +37 -0
  80. test_logs/code_registry/functions/text_processor-8adb5e32_9168c5fe.py +13 -0
  81. test_logs/code_registry/functions/text_processor-c58ffc19_78b4ceac.py +42 -0
  82. test_logs/code_registry/functions/text_processor-cd5977b1_9168c5fe.py +13 -0
  83. test_logs/code_registry/functions/text_processor-ec1c8773_9168c5fe.py +13 -0
  84. tests/test_01_analyst_standalone.py +124 -0
  85. tests/test_02_assistant_standalone.py +164 -0
  86. tests/test_03_analyst_with_framework.py +945 -0
  87. tests/test_04_assistant_with_framework.py +1002 -0
  88. tests/test_05_integration.py +1301 -0
  89. tests/test_06_real_llm_integration.py +760 -0
  90. tests/test_07_distributed_single_node.py +578 -0
  91. tests/test_08_distributed_multi_node.py +454 -0
  92. tests/test_09_distributed_autoagent.py +509 -0
  93. tests/test_10_distributed_customagent.py +787 -0
  94. tests/test_context.py +467 -0
  95. tests/test_decorator.py +622 -0
  96. tests/test_mesh.py +35 -4
  97. jarviscore_framework-0.1.0.dist-info/METADATA +0 -136
  98. jarviscore_framework-0.1.0.dist-info/RECORD +0 -55
  99. {jarviscore_framework-0.1.0.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())