jarviscore-framework 0.1.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/calculator_agent_example.py +77 -0
- examples/multi_agent_workflow.py +132 -0
- examples/research_agent_example.py +76 -0
- jarviscore/__init__.py +54 -0
- jarviscore/cli/__init__.py +7 -0
- jarviscore/cli/__main__.py +33 -0
- jarviscore/cli/check.py +404 -0
- jarviscore/cli/smoketest.py +371 -0
- jarviscore/config/__init__.py +7 -0
- jarviscore/config/settings.py +128 -0
- jarviscore/core/__init__.py +7 -0
- jarviscore/core/agent.py +163 -0
- jarviscore/core/mesh.py +463 -0
- jarviscore/core/profile.py +64 -0
- jarviscore/docs/API_REFERENCE.md +932 -0
- jarviscore/docs/CONFIGURATION.md +753 -0
- jarviscore/docs/GETTING_STARTED.md +600 -0
- jarviscore/docs/TROUBLESHOOTING.md +424 -0
- jarviscore/docs/USER_GUIDE.md +983 -0
- jarviscore/execution/__init__.py +94 -0
- jarviscore/execution/code_registry.py +298 -0
- jarviscore/execution/generator.py +268 -0
- jarviscore/execution/llm.py +430 -0
- jarviscore/execution/repair.py +283 -0
- jarviscore/execution/result_handler.py +332 -0
- jarviscore/execution/sandbox.py +555 -0
- jarviscore/execution/search.py +281 -0
- jarviscore/orchestration/__init__.py +18 -0
- jarviscore/orchestration/claimer.py +101 -0
- jarviscore/orchestration/dependency.py +143 -0
- jarviscore/orchestration/engine.py +292 -0
- jarviscore/orchestration/status.py +96 -0
- jarviscore/p2p/__init__.py +23 -0
- jarviscore/p2p/broadcaster.py +353 -0
- jarviscore/p2p/coordinator.py +364 -0
- jarviscore/p2p/keepalive.py +361 -0
- jarviscore/p2p/swim_manager.py +290 -0
- jarviscore/profiles/__init__.py +6 -0
- jarviscore/profiles/autoagent.py +264 -0
- jarviscore/profiles/customagent.py +137 -0
- jarviscore_framework-0.1.0.dist-info/METADATA +136 -0
- jarviscore_framework-0.1.0.dist-info/RECORD +55 -0
- jarviscore_framework-0.1.0.dist-info/WHEEL +5 -0
- jarviscore_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
- jarviscore_framework-0.1.0.dist-info/top_level.txt +3 -0
- tests/conftest.py +44 -0
- tests/test_agent.py +165 -0
- tests/test_autoagent.py +140 -0
- tests/test_autoagent_day4.py +186 -0
- tests/test_customagent.py +248 -0
- tests/test_integration.py +293 -0
- tests/test_llm_fallback.py +185 -0
- tests/test_mesh.py +356 -0
- tests/test_p2p_integration.py +375 -0
- tests/test_remote_sandbox.py +116 -0
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
# JarvisCore User Guide
|
|
2
|
+
|
|
3
|
+
Practical guide to building agent systems with JarvisCore.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Quick Start](#quick-start)
|
|
10
|
+
2. [Basic Concepts](#basic-concepts)
|
|
11
|
+
3. [AutoAgent Tutorial](#autoagent-tutorial)
|
|
12
|
+
4. [CustomAgent Tutorial](#customagent-tutorial)
|
|
13
|
+
5. [Multi-Agent Workflows](#multi-agent-workflows)
|
|
14
|
+
6. [Internet Search](#internet-search)
|
|
15
|
+
7. [Remote Sandbox](#remote-sandbox)
|
|
16
|
+
8. [Result Storage](#result-storage)
|
|
17
|
+
9. [Code Registry](#code-registry)
|
|
18
|
+
10. [Best Practices](#best-practices)
|
|
19
|
+
11. [Common Patterns](#common-patterns)
|
|
20
|
+
12. [Troubleshooting](#troubleshooting)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Step 1: Installation (1 minute)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install jarviscore
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Step 2: Configuration (2 minutes)
|
|
33
|
+
|
|
34
|
+
JarvisCore needs an LLM provider to generate code for AutoAgent. Copy the example config:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cp .env.example .env
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Edit `.env` and add **one** of these API keys:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Option 1: Claude (Recommended)
|
|
44
|
+
CLAUDE_API_KEY=sk-ant-your-key-here
|
|
45
|
+
|
|
46
|
+
# Option 2: Azure OpenAI
|
|
47
|
+
AZURE_API_KEY=your-key-here
|
|
48
|
+
AZURE_ENDPOINT=https://your-resource.openai.azure.com
|
|
49
|
+
AZURE_DEPLOYMENT=gpt-4o
|
|
50
|
+
|
|
51
|
+
# Option 3: Google Gemini
|
|
52
|
+
GEMINI_API_KEY=your-key-here
|
|
53
|
+
|
|
54
|
+
# Option 4: Local vLLM
|
|
55
|
+
LLM_ENDPOINT=http://localhost:8000
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Step 3: Validate Setup (30 seconds)
|
|
59
|
+
|
|
60
|
+
Run the health check to ensure everything works:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Basic check
|
|
64
|
+
python -m jarviscore.cli.check
|
|
65
|
+
|
|
66
|
+
# Test LLM connectivity
|
|
67
|
+
python -m jarviscore.cli.check --validate-llm
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Run the smoke test to validate end-to-end:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python -m jarviscore.cli.smoketest
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Step 4: Your First Agent (30 seconds)
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import asyncio
|
|
80
|
+
from jarviscore import Mesh
|
|
81
|
+
from jarviscore.profiles import AutoAgent
|
|
82
|
+
|
|
83
|
+
async def main():
|
|
84
|
+
# Create mesh
|
|
85
|
+
mesh = Mesh(mode="autonomous")
|
|
86
|
+
|
|
87
|
+
# Add calculator agent
|
|
88
|
+
mesh.add_agent(
|
|
89
|
+
AutoAgent,
|
|
90
|
+
role="calculator",
|
|
91
|
+
capabilities=["math", "calculation"],
|
|
92
|
+
system_prompt="You are a math expert"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Start mesh
|
|
96
|
+
await mesh.start()
|
|
97
|
+
|
|
98
|
+
# Execute task
|
|
99
|
+
results = await mesh.run_workflow([
|
|
100
|
+
{"agent": "calculator", "task": "Calculate the factorial of 10"}
|
|
101
|
+
])
|
|
102
|
+
|
|
103
|
+
print(results[0]['output']) # 3628800
|
|
104
|
+
|
|
105
|
+
# Cleanup
|
|
106
|
+
await mesh.stop()
|
|
107
|
+
|
|
108
|
+
if __name__ == '__main__':
|
|
109
|
+
asyncio.run(main())
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**That's it!** No configuration files, no setup, just three steps:
|
|
113
|
+
1. Create mesh
|
|
114
|
+
2. Add agent
|
|
115
|
+
3. Run task
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Basic Concepts
|
|
120
|
+
|
|
121
|
+
### The Mesh
|
|
122
|
+
|
|
123
|
+
The **Mesh** is your control center. It manages agents and orchestrates workflows.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# Autonomous mode (single machine)
|
|
127
|
+
mesh = Mesh(mode="autonomous")
|
|
128
|
+
|
|
129
|
+
# Distributed mode (P2P mesh across network)
|
|
130
|
+
mesh = Mesh(mode="distributed")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Agents
|
|
134
|
+
|
|
135
|
+
**Agents** are workers that execute tasks. JarvisCore has two agent types:
|
|
136
|
+
|
|
137
|
+
1. **AutoAgent**: Zero-config, LLM-powered (for rapid prototyping)
|
|
138
|
+
2. **CustomAgent**: Full control (for production systems)
|
|
139
|
+
|
|
140
|
+
### Workflows
|
|
141
|
+
|
|
142
|
+
**Workflows** are sequences of tasks with dependencies:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
await mesh.run_workflow([
|
|
146
|
+
{"agent": "scraper", "task": "Scrape data"},
|
|
147
|
+
{"agent": "processor", "task": "Clean data", "depends_on": [0]},
|
|
148
|
+
{"agent": "storage", "task": "Save data", "depends_on": [1]}
|
|
149
|
+
])
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## AutoAgent Tutorial
|
|
155
|
+
|
|
156
|
+
### Example 1: Simple Calculator
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
import asyncio
|
|
160
|
+
from jarviscore import Mesh
|
|
161
|
+
from jarviscore.profiles import AutoAgent
|
|
162
|
+
|
|
163
|
+
async def calculator_demo():
|
|
164
|
+
mesh = Mesh()
|
|
165
|
+
|
|
166
|
+
mesh.add_agent(
|
|
167
|
+
AutoAgent,
|
|
168
|
+
role="calculator",
|
|
169
|
+
capabilities=["math", "calculation"],
|
|
170
|
+
system_prompt="You are a mathematical calculation expert"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
await mesh.start()
|
|
174
|
+
|
|
175
|
+
# Single calculation
|
|
176
|
+
result = await mesh.run_workflow([
|
|
177
|
+
{"agent": "calculator", "task": "Calculate 15!"}
|
|
178
|
+
])
|
|
179
|
+
|
|
180
|
+
print(f"15! = {result[0]['output']}")
|
|
181
|
+
|
|
182
|
+
await mesh.stop()
|
|
183
|
+
|
|
184
|
+
asyncio.run(calculator_demo())
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Example 2: Data Analyst
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
async def data_analyst_demo():
|
|
191
|
+
mesh = Mesh()
|
|
192
|
+
|
|
193
|
+
mesh.add_agent(
|
|
194
|
+
AutoAgent,
|
|
195
|
+
role="analyst",
|
|
196
|
+
capabilities=["data_analysis", "statistics"],
|
|
197
|
+
system_prompt="You are a data analyst expert"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
await mesh.start()
|
|
201
|
+
|
|
202
|
+
result = await mesh.run_workflow([{
|
|
203
|
+
"agent": "analyst",
|
|
204
|
+
"task": """
|
|
205
|
+
Given this data: [23, 45, 12, 67, 89, 34, 56, 78, 90, 11]
|
|
206
|
+
Calculate: mean, median, mode, standard deviation, and min/max
|
|
207
|
+
"""
|
|
208
|
+
}])
|
|
209
|
+
|
|
210
|
+
print(result[0]['output'])
|
|
211
|
+
# {'mean': 50.5, 'median': 50.5, 'std': 28.7, ...}
|
|
212
|
+
|
|
213
|
+
await mesh.stop()
|
|
214
|
+
|
|
215
|
+
asyncio.run(data_analyst_demo())
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Example 3: Text Processor
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
async def text_processor_demo():
|
|
222
|
+
mesh = Mesh()
|
|
223
|
+
|
|
224
|
+
mesh.add_agent(
|
|
225
|
+
AutoAgent,
|
|
226
|
+
role="processor",
|
|
227
|
+
capabilities=["text_processing", "nlp"],
|
|
228
|
+
system_prompt="You are a text processing expert"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
await mesh.start()
|
|
232
|
+
|
|
233
|
+
text = """
|
|
234
|
+
The quick brown fox jumps over the lazy dog.
|
|
235
|
+
Python is a popular programming language.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
result = await mesh.run_workflow([{
|
|
239
|
+
"agent": "processor",
|
|
240
|
+
"task": f"""
|
|
241
|
+
Analyze this text and return:
|
|
242
|
+
- Word count
|
|
243
|
+
- Sentence count
|
|
244
|
+
- Most common word
|
|
245
|
+
- Text: {text}
|
|
246
|
+
"""
|
|
247
|
+
}])
|
|
248
|
+
|
|
249
|
+
print(result[0]['output'])
|
|
250
|
+
|
|
251
|
+
await mesh.stop()
|
|
252
|
+
|
|
253
|
+
asyncio.run(text_processor_demo())
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## CustomAgent Tutorial
|
|
259
|
+
|
|
260
|
+
### Example 1: API Integration
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
from jarviscore.profiles import CustomAgent
|
|
264
|
+
import aiohttp
|
|
265
|
+
|
|
266
|
+
class WeatherAgent(CustomAgent):
|
|
267
|
+
"""Agent that fetches weather data from external API."""
|
|
268
|
+
|
|
269
|
+
async def setup(self):
|
|
270
|
+
"""Initialize API client."""
|
|
271
|
+
self.api_key = "your-api-key"
|
|
272
|
+
self.base_url = "https://api.weather.com"
|
|
273
|
+
|
|
274
|
+
async def execute_task(self, task):
|
|
275
|
+
"""Execute weather query."""
|
|
276
|
+
task_desc = task.get('task', '')
|
|
277
|
+
|
|
278
|
+
# Extract city from task description
|
|
279
|
+
city = self._extract_city(task_desc)
|
|
280
|
+
|
|
281
|
+
# Fetch weather data
|
|
282
|
+
async with aiohttp.ClientSession() as session:
|
|
283
|
+
async with session.get(
|
|
284
|
+
f"{self.base_url}/weather?city={city}&key={self.api_key}"
|
|
285
|
+
) as response:
|
|
286
|
+
data = await response.json()
|
|
287
|
+
|
|
288
|
+
# Track cost (optional)
|
|
289
|
+
self.track_cost(cost_usd=0.001)
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
"status": "success",
|
|
293
|
+
"output": {
|
|
294
|
+
"city": city,
|
|
295
|
+
"temperature": data['temp'],
|
|
296
|
+
"conditions": data['conditions']
|
|
297
|
+
},
|
|
298
|
+
"agent": self.agent_id
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
def _extract_city(self, text):
|
|
302
|
+
"""Simple city extraction logic."""
|
|
303
|
+
# Your parsing logic here
|
|
304
|
+
return "New York"
|
|
305
|
+
|
|
306
|
+
# Usage
|
|
307
|
+
async def weather_demo():
|
|
308
|
+
mesh = Mesh()
|
|
309
|
+
|
|
310
|
+
mesh.add_agent(
|
|
311
|
+
WeatherAgent,
|
|
312
|
+
role="weather",
|
|
313
|
+
capabilities=["weather", "api"]
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
await mesh.start()
|
|
317
|
+
|
|
318
|
+
result = await mesh.run_workflow([
|
|
319
|
+
{"agent": "weather", "task": "Get weather for New York"}
|
|
320
|
+
])
|
|
321
|
+
|
|
322
|
+
print(result[0]['output'])
|
|
323
|
+
|
|
324
|
+
await mesh.stop()
|
|
325
|
+
|
|
326
|
+
asyncio.run(weather_demo())
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Example 2: Database Agent
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
from jarviscore.profiles import CustomAgent
|
|
333
|
+
import asyncpg
|
|
334
|
+
|
|
335
|
+
class DatabaseAgent(CustomAgent):
|
|
336
|
+
"""Agent that queries PostgreSQL database."""
|
|
337
|
+
|
|
338
|
+
async def setup(self):
|
|
339
|
+
"""Connect to database."""
|
|
340
|
+
self.pool = await asyncpg.create_pool(
|
|
341
|
+
host='localhost',
|
|
342
|
+
database='mydb',
|
|
343
|
+
user='user',
|
|
344
|
+
password='password'
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
async def teardown(self):
|
|
348
|
+
"""Close database connection."""
|
|
349
|
+
await self.pool.close()
|
|
350
|
+
|
|
351
|
+
async def execute_task(self, task):
|
|
352
|
+
"""Execute database query."""
|
|
353
|
+
query = task.get('task', '')
|
|
354
|
+
|
|
355
|
+
async with self.pool.acquire() as conn:
|
|
356
|
+
# Execute query
|
|
357
|
+
rows = await conn.fetch(query)
|
|
358
|
+
|
|
359
|
+
# Convert to list of dicts
|
|
360
|
+
results = [dict(row) for row in rows]
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
"status": "success",
|
|
364
|
+
"output": results,
|
|
365
|
+
"agent": self.agent_id
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# Usage
|
|
369
|
+
async def database_demo():
|
|
370
|
+
mesh = Mesh()
|
|
371
|
+
|
|
372
|
+
mesh.add_agent(
|
|
373
|
+
DatabaseAgent,
|
|
374
|
+
role="database",
|
|
375
|
+
capabilities=["database", "query"]
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
await mesh.start()
|
|
379
|
+
|
|
380
|
+
result = await mesh.run_workflow([
|
|
381
|
+
{"agent": "database", "task": "SELECT * FROM users LIMIT 10"}
|
|
382
|
+
])
|
|
383
|
+
|
|
384
|
+
print(f"Found {len(result[0]['output'])} users")
|
|
385
|
+
|
|
386
|
+
await mesh.stop()
|
|
387
|
+
|
|
388
|
+
asyncio.run(database_demo())
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Example 3: LangChain Integration
|
|
392
|
+
|
|
393
|
+
```python
|
|
394
|
+
from jarviscore.profiles import CustomAgent
|
|
395
|
+
from langchain_openai import ChatOpenAI
|
|
396
|
+
from langchain.prompts import ChatPromptTemplate
|
|
397
|
+
from langchain.schema.runnable import RunnableSequence
|
|
398
|
+
|
|
399
|
+
class LangChainAgent(CustomAgent):
|
|
400
|
+
"""Agent using LangChain for LLM interactions."""
|
|
401
|
+
|
|
402
|
+
async def setup(self):
|
|
403
|
+
"""Initialize LangChain components."""
|
|
404
|
+
self.llm = ChatOpenAI(model="gpt-4")
|
|
405
|
+
|
|
406
|
+
self.prompt = ChatPromptTemplate.from_messages([
|
|
407
|
+
("system", "You are a helpful assistant"),
|
|
408
|
+
("user", "{input}")
|
|
409
|
+
])
|
|
410
|
+
|
|
411
|
+
self.chain = self.prompt | self.llm
|
|
412
|
+
|
|
413
|
+
async def execute_task(self, task):
|
|
414
|
+
"""Execute task using LangChain."""
|
|
415
|
+
task_desc = task.get('task', '')
|
|
416
|
+
|
|
417
|
+
# Run LangChain
|
|
418
|
+
response = await self.chain.ainvoke({"input": task_desc})
|
|
419
|
+
|
|
420
|
+
# Track tokens and cost
|
|
421
|
+
self.track_cost(
|
|
422
|
+
input_tokens=100,
|
|
423
|
+
output_tokens=50,
|
|
424
|
+
cost_usd=0.002
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
"status": "success",
|
|
429
|
+
"output": response.content,
|
|
430
|
+
"agent": self.agent_id
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
# Usage
|
|
434
|
+
async def langchain_demo():
|
|
435
|
+
mesh = Mesh()
|
|
436
|
+
|
|
437
|
+
mesh.add_agent(
|
|
438
|
+
LangChainAgent,
|
|
439
|
+
role="assistant",
|
|
440
|
+
capabilities=["chat", "qa"]
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
await mesh.start()
|
|
444
|
+
|
|
445
|
+
result = await mesh.run_workflow([
|
|
446
|
+
{"agent": "assistant", "task": "Explain quantum computing"}
|
|
447
|
+
])
|
|
448
|
+
|
|
449
|
+
print(result[0]['output'])
|
|
450
|
+
|
|
451
|
+
await mesh.stop()
|
|
452
|
+
|
|
453
|
+
asyncio.run(langchain_demo())
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Multi-Agent Workflows
|
|
459
|
+
|
|
460
|
+
### Example 1: Data Pipeline
|
|
461
|
+
|
|
462
|
+
```python
|
|
463
|
+
async def data_pipeline():
|
|
464
|
+
mesh = Mesh()
|
|
465
|
+
|
|
466
|
+
# Add three agents
|
|
467
|
+
mesh.add_agent(
|
|
468
|
+
AutoAgent,
|
|
469
|
+
role="scraper",
|
|
470
|
+
capabilities=["web_scraping", "data_collection"],
|
|
471
|
+
system_prompt="You are a web scraping expert"
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
mesh.add_agent(
|
|
475
|
+
AutoAgent,
|
|
476
|
+
role="processor",
|
|
477
|
+
capabilities=["data_processing", "cleaning"],
|
|
478
|
+
system_prompt="You are a data cleaning expert"
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
mesh.add_agent(
|
|
482
|
+
AutoAgent,
|
|
483
|
+
role="analyzer",
|
|
484
|
+
capabilities=["analysis", "statistics"],
|
|
485
|
+
system_prompt="You are a data analysis expert"
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
await mesh.start()
|
|
489
|
+
|
|
490
|
+
# Run workflow with dependencies
|
|
491
|
+
results = await mesh.run_workflow([
|
|
492
|
+
{
|
|
493
|
+
"id": "scrape",
|
|
494
|
+
"agent": "scraper",
|
|
495
|
+
"task": "Generate sample e-commerce data (10 products with prices)"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"id": "clean",
|
|
499
|
+
"agent": "processor",
|
|
500
|
+
"task": "Clean and normalize the product data",
|
|
501
|
+
"depends_on": ["scrape"]
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"id": "analyze",
|
|
505
|
+
"agent": "analyzer",
|
|
506
|
+
"task": "Calculate price statistics (mean, median, range)",
|
|
507
|
+
"depends_on": ["clean"]
|
|
508
|
+
}
|
|
509
|
+
])
|
|
510
|
+
|
|
511
|
+
# Each step gets context from previous steps
|
|
512
|
+
print("Scrape result:", results[0]['output'])
|
|
513
|
+
print("Clean result:", results[1]['output'])
|
|
514
|
+
print("Analysis:", results[2]['output'])
|
|
515
|
+
|
|
516
|
+
await mesh.stop()
|
|
517
|
+
|
|
518
|
+
asyncio.run(data_pipeline())
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Example 2: Report Generator
|
|
522
|
+
|
|
523
|
+
```python
|
|
524
|
+
async def report_generator():
|
|
525
|
+
mesh = Mesh()
|
|
526
|
+
|
|
527
|
+
mesh.add_agent(
|
|
528
|
+
AutoAgent,
|
|
529
|
+
role="researcher",
|
|
530
|
+
capabilities=["research", "data_gathering"],
|
|
531
|
+
system_prompt="You are a researcher",
|
|
532
|
+
enable_search=True # Enable internet search
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
mesh.add_agent(
|
|
536
|
+
AutoAgent,
|
|
537
|
+
role="writer",
|
|
538
|
+
capabilities=["writing", "formatting"],
|
|
539
|
+
system_prompt="You are a technical writer"
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
await mesh.start()
|
|
543
|
+
|
|
544
|
+
results = await mesh.run_workflow([
|
|
545
|
+
{
|
|
546
|
+
"id": "research",
|
|
547
|
+
"agent": "researcher",
|
|
548
|
+
"task": "Research latest Python 3.12 features"
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
"id": "write",
|
|
552
|
+
"agent": "writer",
|
|
553
|
+
"task": "Write a 2-paragraph summary of the research findings",
|
|
554
|
+
"depends_on": ["research"]
|
|
555
|
+
}
|
|
556
|
+
])
|
|
557
|
+
|
|
558
|
+
print("Research:", results[0]['output'])
|
|
559
|
+
print("\nReport:", results[1]['output'])
|
|
560
|
+
|
|
561
|
+
await mesh.stop()
|
|
562
|
+
|
|
563
|
+
asyncio.run(report_generator())
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Internet Search
|
|
569
|
+
|
|
570
|
+
Enable web search for research tasks:
|
|
571
|
+
|
|
572
|
+
```python
|
|
573
|
+
from jarviscore import Mesh
|
|
574
|
+
from jarviscore.profiles import AutoAgent
|
|
575
|
+
|
|
576
|
+
async def search_demo():
|
|
577
|
+
mesh = Mesh()
|
|
578
|
+
|
|
579
|
+
mesh.add_agent(
|
|
580
|
+
AutoAgent,
|
|
581
|
+
role="researcher",
|
|
582
|
+
capabilities=["research", "web_search"],
|
|
583
|
+
system_prompt="You are an expert researcher",
|
|
584
|
+
enable_search=True # ← Enable internet search
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
await mesh.start()
|
|
588
|
+
|
|
589
|
+
result = await mesh.run_workflow([{
|
|
590
|
+
"agent": "researcher",
|
|
591
|
+
"task": "Search for 'Python asyncio best practices' and summarize the top 3 results"
|
|
592
|
+
}])
|
|
593
|
+
|
|
594
|
+
print(result[0]['output'])
|
|
595
|
+
# Returns: Summary of top 3 search results
|
|
596
|
+
|
|
597
|
+
await mesh.stop()
|
|
598
|
+
|
|
599
|
+
asyncio.run(search_demo())
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**Search Capabilities:**
|
|
603
|
+
- DuckDuckGo web search
|
|
604
|
+
- Content extraction from URLs
|
|
605
|
+
- Automatic summarization
|
|
606
|
+
- No API keys required
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## Remote Sandbox
|
|
611
|
+
|
|
612
|
+
Use remote code execution for better security:
|
|
613
|
+
|
|
614
|
+
### Enable Remote Sandbox
|
|
615
|
+
|
|
616
|
+
```bash
|
|
617
|
+
# .env file
|
|
618
|
+
SANDBOX_MODE=remote
|
|
619
|
+
SANDBOX_SERVICE_URL=https://browser-task-executor.bravesea-3f5f7e75.eastus.azurecontainerapps.io
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Test Remote Execution
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
from jarviscore.execution import create_sandbox_executor
|
|
626
|
+
|
|
627
|
+
async def test_remote():
|
|
628
|
+
executor = create_sandbox_executor(
|
|
629
|
+
timeout=30,
|
|
630
|
+
config={
|
|
631
|
+
'sandbox_mode': 'remote',
|
|
632
|
+
'sandbox_service_url': 'https://...'
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
code = "result = 2 + 2"
|
|
637
|
+
result = await executor.execute(code)
|
|
638
|
+
|
|
639
|
+
print(f"Mode: {result['mode']}") # "remote"
|
|
640
|
+
print(f"Output: {result['output']}") # 4
|
|
641
|
+
print(f"Time: {result['execution_time']}s")
|
|
642
|
+
|
|
643
|
+
asyncio.run(test_remote())
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
**Benefits:**
|
|
647
|
+
- Full process isolation
|
|
648
|
+
- Better security
|
|
649
|
+
- Azure Container Apps hosting
|
|
650
|
+
- Automatic fallback to local
|
|
651
|
+
|
|
652
|
+
**When to use:**
|
|
653
|
+
- Production deployments
|
|
654
|
+
- Untrusted code execution
|
|
655
|
+
- Multi-tenant systems
|
|
656
|
+
- High security requirements
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## Result Storage
|
|
661
|
+
|
|
662
|
+
All execution results are automatically stored:
|
|
663
|
+
|
|
664
|
+
### Access Result Storage
|
|
665
|
+
|
|
666
|
+
```python
|
|
667
|
+
from jarviscore.execution import create_result_handler
|
|
668
|
+
|
|
669
|
+
handler = create_result_handler()
|
|
670
|
+
|
|
671
|
+
# Get specific result
|
|
672
|
+
result = handler.get_result("calculator-abc123_2026-01-12T12-00-00_123456")
|
|
673
|
+
|
|
674
|
+
# Get agent's recent results
|
|
675
|
+
recent = handler.get_agent_results("calculator-abc123", limit=10)
|
|
676
|
+
|
|
677
|
+
for r in recent:
|
|
678
|
+
print(f"{r['task']}: {r['output']}")
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Storage Location
|
|
682
|
+
|
|
683
|
+
```
|
|
684
|
+
logs/
|
|
685
|
+
├── calculator-abc123/
|
|
686
|
+
│ ├── calculator-abc123_2026-01-12T12-00-00_123456.json
|
|
687
|
+
│ └── calculator-abc123_2026-01-12T12-05-30_789012.json
|
|
688
|
+
├── analyzer-def456/
|
|
689
|
+
│ └── analyzer-def456_2026-01-12T12-10-00_345678.json
|
|
690
|
+
└── code_registry/
|
|
691
|
+
├── index.json
|
|
692
|
+
└── functions/
|
|
693
|
+
├── calculator-abc123_3a5b2f76.py
|
|
694
|
+
└── analyzer-def456_8b2c4d91.py
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**What's Stored:**
|
|
698
|
+
- Task description
|
|
699
|
+
- Generated code
|
|
700
|
+
- Execution output
|
|
701
|
+
- Status (success/failure)
|
|
702
|
+
- Execution time
|
|
703
|
+
- Token usage
|
|
704
|
+
- Cost (if tracked)
|
|
705
|
+
- Repair attempts
|
|
706
|
+
- Timestamp
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## Code Registry
|
|
711
|
+
|
|
712
|
+
Reuse successful code across agents:
|
|
713
|
+
|
|
714
|
+
### Search Registry
|
|
715
|
+
|
|
716
|
+
```python
|
|
717
|
+
from jarviscore.execution import create_code_registry
|
|
718
|
+
|
|
719
|
+
registry = create_code_registry()
|
|
720
|
+
|
|
721
|
+
# Search for math functions
|
|
722
|
+
matches = registry.search(
|
|
723
|
+
query="factorial calculation",
|
|
724
|
+
capabilities=["math"],
|
|
725
|
+
limit=3
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
for match in matches:
|
|
729
|
+
print(f"Function: {match['function_id']}")
|
|
730
|
+
print(f"Task: {match['task']}")
|
|
731
|
+
print(f"Output sample: {match['output_sample']}")
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### Get Function Code
|
|
735
|
+
|
|
736
|
+
```python
|
|
737
|
+
# Get specific function
|
|
738
|
+
func = registry.get("calculator-abc123_3a5b2f76")
|
|
739
|
+
|
|
740
|
+
print("Code:")
|
|
741
|
+
print(func['code'])
|
|
742
|
+
|
|
743
|
+
print("\nMetadata:")
|
|
744
|
+
print(f"Agent: {func['agent_id']}")
|
|
745
|
+
print(f"Capabilities: {func['capabilities']}")
|
|
746
|
+
print(f"Registered: {func['registered_at']}")
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
**Use Cases:**
|
|
750
|
+
- Share functions between agents
|
|
751
|
+
- Build function library
|
|
752
|
+
- Audit generated code
|
|
753
|
+
- Performance analysis
|
|
754
|
+
|
|
755
|
+
---
|
|
756
|
+
|
|
757
|
+
## Best Practices
|
|
758
|
+
|
|
759
|
+
### 1. Always Use Context Managers
|
|
760
|
+
|
|
761
|
+
```python
|
|
762
|
+
# Good
|
|
763
|
+
async with Mesh() as mesh:
|
|
764
|
+
mesh.add_agent(AutoAgent, ...)
|
|
765
|
+
await mesh.start()
|
|
766
|
+
results = await mesh.run_workflow([...])
|
|
767
|
+
# Automatic cleanup
|
|
768
|
+
|
|
769
|
+
# Manual (also works)
|
|
770
|
+
mesh = Mesh()
|
|
771
|
+
try:
|
|
772
|
+
await mesh.start()
|
|
773
|
+
results = await mesh.run_workflow([...])
|
|
774
|
+
finally:
|
|
775
|
+
await mesh.stop()
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### 2. Handle Errors Gracefully
|
|
779
|
+
|
|
780
|
+
```python
|
|
781
|
+
try:
|
|
782
|
+
results = await mesh.run_workflow([...])
|
|
783
|
+
|
|
784
|
+
for i, result in enumerate(results):
|
|
785
|
+
if result['status'] == 'failure':
|
|
786
|
+
print(f"Step {i} failed: {result['error']}")
|
|
787
|
+
else:
|
|
788
|
+
print(f"Step {i} succeeded: {result['output']}")
|
|
789
|
+
|
|
790
|
+
except TimeoutError:
|
|
791
|
+
print("Workflow timed out")
|
|
792
|
+
except RuntimeError as e:
|
|
793
|
+
print(f"Runtime error: {e}")
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### 3. Use Clear System Prompts
|
|
797
|
+
|
|
798
|
+
```python
|
|
799
|
+
# Good
|
|
800
|
+
system_prompt = """
|
|
801
|
+
You are a financial data analyst expert.
|
|
802
|
+
Your task is to analyze stock data and provide insights.
|
|
803
|
+
Always return results as structured JSON.
|
|
804
|
+
"""
|
|
805
|
+
|
|
806
|
+
# Bad
|
|
807
|
+
system_prompt = "You are helpful"
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
### 4. Set Appropriate Timeouts
|
|
811
|
+
|
|
812
|
+
```python
|
|
813
|
+
# Short tasks
|
|
814
|
+
mesh.add_agent(AutoAgent, ..., max_repair_attempts=1)
|
|
815
|
+
|
|
816
|
+
# Long-running tasks
|
|
817
|
+
config = {'execution_timeout': 600} # 10 minutes
|
|
818
|
+
mesh = Mesh(config=config)
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
### 5. Monitor Costs
|
|
822
|
+
|
|
823
|
+
```python
|
|
824
|
+
class MyAgent(CustomAgent):
|
|
825
|
+
async def execute_task(self, task):
|
|
826
|
+
# ... do work ...
|
|
827
|
+
|
|
828
|
+
# Track costs
|
|
829
|
+
self.track_cost(
|
|
830
|
+
input_tokens=500,
|
|
831
|
+
output_tokens=200,
|
|
832
|
+
cost_usd=0.015
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
return result
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
---
|
|
839
|
+
|
|
840
|
+
## Common Patterns
|
|
841
|
+
|
|
842
|
+
### Pattern 1: Fan-Out, Fan-In
|
|
843
|
+
|
|
844
|
+
```python
|
|
845
|
+
# Process multiple items in parallel, then aggregate
|
|
846
|
+
results = await mesh.run_workflow([
|
|
847
|
+
# Fan-out: Process items
|
|
848
|
+
{"id": "item1", "agent": "processor", "task": "Process item 1"},
|
|
849
|
+
{"id": "item2", "agent": "processor", "task": "Process item 2"},
|
|
850
|
+
{"id": "item3", "agent": "processor", "task": "Process item 3"},
|
|
851
|
+
|
|
852
|
+
# Fan-in: Aggregate results
|
|
853
|
+
{
|
|
854
|
+
"id": "aggregate",
|
|
855
|
+
"agent": "aggregator",
|
|
856
|
+
"task": "Combine all processed items",
|
|
857
|
+
"depends_on": ["item1", "item2", "item3"]
|
|
858
|
+
}
|
|
859
|
+
])
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
### Pattern 2: Conditional Execution
|
|
863
|
+
|
|
864
|
+
```python
|
|
865
|
+
# Execute step 1
|
|
866
|
+
results = await mesh.run_workflow([
|
|
867
|
+
{"id": "check", "agent": "validator", "task": "Validate input data"}
|
|
868
|
+
])
|
|
869
|
+
|
|
870
|
+
# Decide next step based on result
|
|
871
|
+
if results[0]['output']['valid']:
|
|
872
|
+
results = await mesh.run_workflow([
|
|
873
|
+
{"agent": "processor", "task": "Process valid data"}
|
|
874
|
+
])
|
|
875
|
+
else:
|
|
876
|
+
print("Validation failed, skipping processing")
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
### Pattern 3: Retry with Different Agent
|
|
880
|
+
|
|
881
|
+
```python
|
|
882
|
+
try:
|
|
883
|
+
# Try primary agent
|
|
884
|
+
result = await mesh.run_workflow([
|
|
885
|
+
{"agent": "primary_scraper", "task": "Scrape website"}
|
|
886
|
+
])
|
|
887
|
+
except Exception:
|
|
888
|
+
# Fallback to backup agent
|
|
889
|
+
result = await mesh.run_workflow([
|
|
890
|
+
{"agent": "backup_scraper", "task": "Scrape website"}
|
|
891
|
+
])
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
---
|
|
895
|
+
|
|
896
|
+
## Troubleshooting
|
|
897
|
+
|
|
898
|
+
### Issue: Agent not found
|
|
899
|
+
|
|
900
|
+
```python
|
|
901
|
+
# Error: No agent found for step
|
|
902
|
+
# Solution: Check role/capability spelling
|
|
903
|
+
mesh.add_agent(AutoAgent, role="calculator", capabilities=["math"])
|
|
904
|
+
|
|
905
|
+
# This will fail
|
|
906
|
+
await mesh.run_workflow([
|
|
907
|
+
{"agent": "calcul", "task": "..."} # Typo!
|
|
908
|
+
])
|
|
909
|
+
|
|
910
|
+
# This works
|
|
911
|
+
await mesh.run_workflow([
|
|
912
|
+
{"agent": "calculator", "task": "..."} # Correct role
|
|
913
|
+
])
|
|
914
|
+
|
|
915
|
+
# This also works
|
|
916
|
+
await mesh.run_workflow([
|
|
917
|
+
{"agent": "math", "task": "..."} # Uses capability
|
|
918
|
+
])
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
### Issue: Mesh not started
|
|
922
|
+
|
|
923
|
+
```python
|
|
924
|
+
# Error: RuntimeError: Workflow engine not started
|
|
925
|
+
# Solution: Call mesh.start() before run_workflow()
|
|
926
|
+
|
|
927
|
+
mesh = Mesh()
|
|
928
|
+
mesh.add_agent(...)
|
|
929
|
+
await mesh.start() # ← Don't forget this!
|
|
930
|
+
await mesh.run_workflow([...])
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### Issue: Timeout
|
|
934
|
+
|
|
935
|
+
```python
|
|
936
|
+
# Error: TimeoutError: Execution exceeded 300 seconds
|
|
937
|
+
# Solution: Increase timeout
|
|
938
|
+
|
|
939
|
+
config = {'execution_timeout': 600} # 10 minutes
|
|
940
|
+
mesh = Mesh(config=config)
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### Issue: No LLM provider configured
|
|
944
|
+
|
|
945
|
+
```python
|
|
946
|
+
# Error: RuntimeError: No LLM provider configured
|
|
947
|
+
# Solution: Set environment variables
|
|
948
|
+
|
|
949
|
+
# .env file
|
|
950
|
+
CLAUDE_API_KEY=your-key
|
|
951
|
+
# or
|
|
952
|
+
AZURE_API_KEY=your-key
|
|
953
|
+
AZURE_ENDPOINT=https://...
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
### Issue: Code execution fails
|
|
957
|
+
|
|
958
|
+
```python
|
|
959
|
+
# Check logs for details
|
|
960
|
+
import logging
|
|
961
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
962
|
+
|
|
963
|
+
# Enable verbose output
|
|
964
|
+
config = {'log_level': 'DEBUG'}
|
|
965
|
+
mesh = Mesh(config=config)
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
## Next Steps
|
|
971
|
+
|
|
972
|
+
1. **Read the [API Reference](API_REFERENCE.md)** for detailed component documentation
|
|
973
|
+
2. **Check the [Configuration Guide](CONFIGURATION.md)** for environment setup
|
|
974
|
+
3. **Explore examples/** directory for more code samples
|
|
975
|
+
4. **Join the community** on GitHub for support
|
|
976
|
+
|
|
977
|
+
---
|
|
978
|
+
|
|
979
|
+
## Version
|
|
980
|
+
|
|
981
|
+
User Guide for JarvisCore v0.1.0
|
|
982
|
+
|
|
983
|
+
Last Updated: 2026-01-12
|