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,264 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoAgent - Automated execution profile.
|
|
3
|
+
|
|
4
|
+
Framework generates and executes code from natural language prompts.
|
|
5
|
+
User writes just 3 attributes, framework handles everything.
|
|
6
|
+
"""
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from jarviscore.core.profile import Profile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AutoAgent(Profile):
|
|
12
|
+
"""
|
|
13
|
+
Automated execution profile.
|
|
14
|
+
|
|
15
|
+
User defines:
|
|
16
|
+
- role: str
|
|
17
|
+
- capabilities: List[str]
|
|
18
|
+
- system_prompt: str
|
|
19
|
+
|
|
20
|
+
Framework provides:
|
|
21
|
+
- LLM code generation from task descriptions
|
|
22
|
+
- Sandboxed code execution with resource limits
|
|
23
|
+
- Autonomous repair when execution fails
|
|
24
|
+
- Meta-cognition (detect spinning, paralysis)
|
|
25
|
+
- Token budget tracking
|
|
26
|
+
- Cost tracking per task
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
class ScraperAgent(AutoAgent):
|
|
30
|
+
role = "scraper"
|
|
31
|
+
capabilities = ["web_scraping", "data_extraction"]
|
|
32
|
+
system_prompt = '''
|
|
33
|
+
You are an expert web scraper. Use BeautifulSoup or Selenium
|
|
34
|
+
to extract structured data from websites. Return JSON results.
|
|
35
|
+
'''
|
|
36
|
+
|
|
37
|
+
# That's it! Framework handles execution automatically.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Additional user-defined attribute (beyond Agent base class)
|
|
41
|
+
system_prompt: str = None
|
|
42
|
+
|
|
43
|
+
def __init__(self, agent_id=None):
|
|
44
|
+
super().__init__(agent_id)
|
|
45
|
+
|
|
46
|
+
if not self.system_prompt:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"{self.__class__.__name__} must define 'system_prompt' class attribute\n"
|
|
49
|
+
f"Example: system_prompt = 'You are an expert...'"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Execution components (initialized in setup() on Day 4)
|
|
53
|
+
self.llm = None
|
|
54
|
+
self.codegen = None
|
|
55
|
+
self.sandbox = None
|
|
56
|
+
self.repair = None
|
|
57
|
+
|
|
58
|
+
async def setup(self):
|
|
59
|
+
"""
|
|
60
|
+
Initialize LLM and execution components with ZERO CONFIG.
|
|
61
|
+
|
|
62
|
+
Framework auto-detects available LLM providers and sets up:
|
|
63
|
+
- LLM client (tries vLLM → Azure → Gemini → Claude)
|
|
64
|
+
- Internet search (DuckDuckGo, no API key needed)
|
|
65
|
+
- Code generator with search injection
|
|
66
|
+
- Sandbox executor with timeout
|
|
67
|
+
- Autonomous repair system
|
|
68
|
+
"""
|
|
69
|
+
await super().setup()
|
|
70
|
+
|
|
71
|
+
self._logger.info(f"AutoAgent setup: {self.agent_id}")
|
|
72
|
+
self._logger.info(f" Role: {self.role}")
|
|
73
|
+
self._logger.info(f" Capabilities: {self.capabilities}")
|
|
74
|
+
self._logger.info(f" System Prompt: {self.system_prompt[:50]}...")
|
|
75
|
+
|
|
76
|
+
# Get config from mesh (or use empty dict)
|
|
77
|
+
config = self._mesh.config if self._mesh else {}
|
|
78
|
+
|
|
79
|
+
# Import execution components
|
|
80
|
+
from jarviscore.execution import (
|
|
81
|
+
create_llm_client,
|
|
82
|
+
create_search_client,
|
|
83
|
+
create_code_generator,
|
|
84
|
+
create_sandbox_executor,
|
|
85
|
+
create_autonomous_repair,
|
|
86
|
+
create_result_handler,
|
|
87
|
+
create_code_registry
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# 1. Initialize LLM (auto-detects providers)
|
|
91
|
+
self._logger.info("Initializing LLM client...")
|
|
92
|
+
self.llm = create_llm_client(config)
|
|
93
|
+
|
|
94
|
+
# 2. Initialize search (zero-config)
|
|
95
|
+
self._logger.info("Initializing internet search...")
|
|
96
|
+
self.search = create_search_client()
|
|
97
|
+
|
|
98
|
+
# 3. Initialize code generator (with search injection)
|
|
99
|
+
self._logger.info("Initializing code generator...")
|
|
100
|
+
self.codegen = create_code_generator(self.llm, self.search)
|
|
101
|
+
|
|
102
|
+
# 4. Initialize sandbox executor (with search access)
|
|
103
|
+
timeout = config.get('execution_timeout', 300)
|
|
104
|
+
self._logger.info(f"Initializing sandbox executor ({timeout}s timeout)...")
|
|
105
|
+
self.sandbox = create_sandbox_executor(timeout, self.search, config)
|
|
106
|
+
|
|
107
|
+
# 5. Initialize autonomous repair
|
|
108
|
+
max_repairs = config.get('max_repair_attempts', 3)
|
|
109
|
+
self._logger.info(f"Initializing autonomous repair ({max_repairs} attempts)...")
|
|
110
|
+
self.repair = create_autonomous_repair(self.codegen, max_repairs)
|
|
111
|
+
|
|
112
|
+
# 6. Initialize result handler (file + in-memory storage)
|
|
113
|
+
log_dir = config.get('log_directory', './logs')
|
|
114
|
+
self._logger.info(f"Initializing result handler (dir: {log_dir})...")
|
|
115
|
+
self.result_handler = create_result_handler(log_dir)
|
|
116
|
+
|
|
117
|
+
# 7. Initialize code registry (reusable generated functions)
|
|
118
|
+
registry_dir = f"{log_dir}/code_registry"
|
|
119
|
+
self._logger.info(f"Initializing code registry (dir: {registry_dir})...")
|
|
120
|
+
self.code_registry = create_code_registry(registry_dir)
|
|
121
|
+
|
|
122
|
+
self._logger.info(f"✓ AutoAgent ready: {self.agent_id}")
|
|
123
|
+
|
|
124
|
+
async def execute_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
Execute task via LLM code generation with automatic repair.
|
|
127
|
+
|
|
128
|
+
Pipeline:
|
|
129
|
+
1. Generate Python code from natural language task
|
|
130
|
+
2. Execute code in sandbox
|
|
131
|
+
3. If fails → autonomous repair (up to 3 attempts)
|
|
132
|
+
4. Return result with tokens/cost
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
task: Task specification with 'task' key (natural language)
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
{
|
|
139
|
+
"status": "success" | "failure",
|
|
140
|
+
"output": Any, # Task result
|
|
141
|
+
"error": str, # Error if failed
|
|
142
|
+
"tokens": {...}, # Token usage
|
|
143
|
+
"cost_usd": float,
|
|
144
|
+
"code": str, # Generated code
|
|
145
|
+
"repairs": int # Number of repair attempts
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
result = await agent.execute_task({
|
|
150
|
+
"task": "Calculate factorial of 10"
|
|
151
|
+
})
|
|
152
|
+
"""
|
|
153
|
+
task_desc = task.get('task', '')
|
|
154
|
+
self._logger.info(f"[AutoAgent] Executing: {task_desc[:80]}...")
|
|
155
|
+
|
|
156
|
+
total_tokens = {"input": 0, "output": 0, "total": 0}
|
|
157
|
+
total_cost = 0.0
|
|
158
|
+
repairs_attempted = 0
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
# Step 1: Generate code from natural language
|
|
162
|
+
self._logger.info("Step 1: Generating code...")
|
|
163
|
+
code = await self.codegen.generate(
|
|
164
|
+
task=task,
|
|
165
|
+
system_prompt=self.system_prompt,
|
|
166
|
+
context=task.get('context'), # Dependencies from previous steps
|
|
167
|
+
enable_search=True
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Track generation cost (from LLM response)
|
|
171
|
+
# Note: codegen now returns just code, cost tracked in llm
|
|
172
|
+
self._logger.debug(f"Generated {len(code)} characters of code")
|
|
173
|
+
|
|
174
|
+
# Step 2: Execute in sandbox
|
|
175
|
+
self._logger.info("Step 2: Executing code in sandbox...")
|
|
176
|
+
result = await self.sandbox.execute(code)
|
|
177
|
+
|
|
178
|
+
# Step 3: Handle execution failure with autonomous repair
|
|
179
|
+
if result['status'] == 'failure':
|
|
180
|
+
self._logger.warning(f"Execution failed: {result.get('error')}")
|
|
181
|
+
self._logger.info("Step 3: Attempting autonomous repair...")
|
|
182
|
+
|
|
183
|
+
# Use repair system with automatic retries
|
|
184
|
+
repair_result = await self.repair.repair_with_retries(
|
|
185
|
+
code=code,
|
|
186
|
+
error=Exception(result.get('error', 'Unknown error')),
|
|
187
|
+
task=task,
|
|
188
|
+
system_prompt=self.system_prompt,
|
|
189
|
+
executor=self.sandbox
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Update result and track repairs
|
|
193
|
+
result = repair_result
|
|
194
|
+
repairs_attempted = len(repair_result.get('attempts', []))
|
|
195
|
+
self._logger.info(f"Repair attempts: {repairs_attempted}")
|
|
196
|
+
|
|
197
|
+
# Enrich result with metadata
|
|
198
|
+
result['code'] = code
|
|
199
|
+
result['repairs'] = repairs_attempted
|
|
200
|
+
result['agent_id'] = self.agent_id
|
|
201
|
+
result['role'] = self.role
|
|
202
|
+
|
|
203
|
+
# Add token/cost info if not already present
|
|
204
|
+
if 'tokens' not in result:
|
|
205
|
+
result['tokens'] = total_tokens
|
|
206
|
+
if 'cost_usd' not in result:
|
|
207
|
+
result['cost_usd'] = total_cost
|
|
208
|
+
|
|
209
|
+
# Store result to file system + in-memory cache
|
|
210
|
+
stored_result = self.result_handler.process_result(
|
|
211
|
+
agent_id=self.agent_id,
|
|
212
|
+
task=task_desc,
|
|
213
|
+
code=code,
|
|
214
|
+
output=result.get('output'),
|
|
215
|
+
status=result['status'],
|
|
216
|
+
error=result.get('error'),
|
|
217
|
+
execution_time=result.get('execution_time'),
|
|
218
|
+
tokens=result.get('tokens'),
|
|
219
|
+
cost_usd=result.get('cost_usd'),
|
|
220
|
+
repairs=repairs_attempted,
|
|
221
|
+
metadata={
|
|
222
|
+
'role': self.role,
|
|
223
|
+
'capabilities': self.capabilities,
|
|
224
|
+
'system_prompt': self.system_prompt[:100] # First 100 chars
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Add result_id to response
|
|
229
|
+
result['result_id'] = stored_result['result_id']
|
|
230
|
+
|
|
231
|
+
# Register successful code in registry for reuse
|
|
232
|
+
if result['status'] == 'success':
|
|
233
|
+
function_id = self.code_registry.register(
|
|
234
|
+
code=code,
|
|
235
|
+
agent_id=self.agent_id,
|
|
236
|
+
task=task_desc,
|
|
237
|
+
capabilities=self.capabilities,
|
|
238
|
+
output=result.get('output'),
|
|
239
|
+
result_id=result['result_id'],
|
|
240
|
+
metadata={
|
|
241
|
+
'role': self.role,
|
|
242
|
+
'execution_time': result.get('execution_time'),
|
|
243
|
+
'repairs': repairs_attempted
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
result['function_id'] = function_id
|
|
247
|
+
self._logger.info(f"✓ Task completed successfully (result_id: {result['result_id']}, function_id: {function_id})")
|
|
248
|
+
else:
|
|
249
|
+
self._logger.error(f"✗ Task failed: {result.get('error')}")
|
|
250
|
+
|
|
251
|
+
return result
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
self._logger.error(f"Fatal error in execute_task: {e}", exc_info=True)
|
|
255
|
+
return {
|
|
256
|
+
"status": "failure",
|
|
257
|
+
"error": f"Fatal error: {str(e)}",
|
|
258
|
+
"error_type": type(e).__name__,
|
|
259
|
+
"agent_id": self.agent_id,
|
|
260
|
+
"role": self.role,
|
|
261
|
+
"repairs": repairs_attempted,
|
|
262
|
+
"tokens": total_tokens,
|
|
263
|
+
"cost_usd": total_cost
|
|
264
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CustomAgent - User-controlled execution profile.
|
|
3
|
+
|
|
4
|
+
User provides their own implementation using any framework:
|
|
5
|
+
- LangChain
|
|
6
|
+
- MCP (Model Context Protocol)
|
|
7
|
+
- CrewAI
|
|
8
|
+
- Raw Python
|
|
9
|
+
- Any other tool/framework
|
|
10
|
+
"""
|
|
11
|
+
from typing import Dict, Any
|
|
12
|
+
from jarviscore.core.profile import Profile
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CustomAgent(Profile):
|
|
16
|
+
"""
|
|
17
|
+
Custom execution profile with full user control.
|
|
18
|
+
|
|
19
|
+
User defines:
|
|
20
|
+
- role: str
|
|
21
|
+
- capabilities: List[str]
|
|
22
|
+
- setup(): Initialize custom framework/tools
|
|
23
|
+
- execute_task(): Custom execution logic
|
|
24
|
+
|
|
25
|
+
Framework provides:
|
|
26
|
+
- Orchestration (task claiming, dependencies, nudging)
|
|
27
|
+
- P2P coordination (agent discovery, task routing)
|
|
28
|
+
- State management (crash recovery, HITL)
|
|
29
|
+
- Cost tracking (if user provides token counts)
|
|
30
|
+
|
|
31
|
+
Example with LangChain:
|
|
32
|
+
class APIAgent(CustomAgent):
|
|
33
|
+
role = "api_client"
|
|
34
|
+
capabilities = ["api_calls"]
|
|
35
|
+
|
|
36
|
+
async def setup(self):
|
|
37
|
+
await super().setup()
|
|
38
|
+
from langchain.agents import Agent
|
|
39
|
+
self.lc_agent = Agent(...)
|
|
40
|
+
|
|
41
|
+
async def execute_task(self, task):
|
|
42
|
+
result = await self.lc_agent.run(task["task"])
|
|
43
|
+
return {"status": "success", "output": result}
|
|
44
|
+
|
|
45
|
+
Example with MCP:
|
|
46
|
+
class MCPAgent(CustomAgent):
|
|
47
|
+
role = "tool_user"
|
|
48
|
+
capabilities = ["mcp_tools"]
|
|
49
|
+
mcp_server_url = "stdio://./server.py"
|
|
50
|
+
|
|
51
|
+
async def setup(self):
|
|
52
|
+
await super().setup()
|
|
53
|
+
from mcp import Client
|
|
54
|
+
self.mcp = Client(self.mcp_server_url)
|
|
55
|
+
await self.mcp.connect()
|
|
56
|
+
|
|
57
|
+
async def execute_task(self, task):
|
|
58
|
+
result = await self.mcp.call_tool("my_tool", task["params"])
|
|
59
|
+
return {"status": "success", "data": result}
|
|
60
|
+
|
|
61
|
+
Example with Raw Python:
|
|
62
|
+
class DataProcessor(CustomAgent):
|
|
63
|
+
role = "processor"
|
|
64
|
+
capabilities = ["data_processing"]
|
|
65
|
+
|
|
66
|
+
async def execute_task(self, task):
|
|
67
|
+
# Pure Python logic
|
|
68
|
+
data = task["params"]["data"]
|
|
69
|
+
processed = [x * 2 for x in data]
|
|
70
|
+
return {"status": "success", "output": processed}
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, agent_id=None):
|
|
74
|
+
super().__init__(agent_id)
|
|
75
|
+
|
|
76
|
+
# User can add any custom attributes
|
|
77
|
+
# e.g., mcp_server_url, langchain_config, etc.
|
|
78
|
+
|
|
79
|
+
async def setup(self):
|
|
80
|
+
"""
|
|
81
|
+
User implements this to initialize custom framework/tools.
|
|
82
|
+
|
|
83
|
+
DAY 1: Base implementation (user overrides)
|
|
84
|
+
DAY 5+: Full examples with LangChain, MCP, etc.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
async def setup(self):
|
|
88
|
+
await super().setup()
|
|
89
|
+
# Initialize your framework
|
|
90
|
+
from langchain.agents import Agent
|
|
91
|
+
self.agent = Agent(...)
|
|
92
|
+
"""
|
|
93
|
+
await super().setup()
|
|
94
|
+
|
|
95
|
+
self._logger.info(f"CustomAgent setup: {self.agent_id}")
|
|
96
|
+
self._logger.info(
|
|
97
|
+
f" Note: Override setup() to initialize your custom framework"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def execute_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
|
101
|
+
"""
|
|
102
|
+
User implements this with custom execution logic.
|
|
103
|
+
|
|
104
|
+
DAY 1: Raises NotImplementedError (user must override)
|
|
105
|
+
DAY 5+: Full examples provided
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
task: Task specification
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Result dictionary with at least:
|
|
112
|
+
- status: "success" or "failure"
|
|
113
|
+
- output: Task result
|
|
114
|
+
- error (optional): Error message if failed
|
|
115
|
+
- tokens_used (optional): For cost tracking
|
|
116
|
+
- cost_usd (optional): For cost tracking
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
NotImplementedError: User must override this method
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
async def execute_task(self, task):
|
|
123
|
+
result = await self.my_framework.run(task)
|
|
124
|
+
return {
|
|
125
|
+
"status": "success",
|
|
126
|
+
"output": result,
|
|
127
|
+
"tokens_used": 1000, # Optional
|
|
128
|
+
"cost_usd": 0.002 # Optional
|
|
129
|
+
}
|
|
130
|
+
"""
|
|
131
|
+
raise NotImplementedError(
|
|
132
|
+
f"{self.__class__.__name__} must implement execute_task()\n\n"
|
|
133
|
+
f"Example:\n"
|
|
134
|
+
f" async def execute_task(self, task):\n"
|
|
135
|
+
f" result = await self.my_framework.run(task['task'])\n"
|
|
136
|
+
f" return {{'status': 'success', 'output': result}}\n"
|
|
137
|
+
)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jarviscore-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: P2P distributed agent framework with LLM code generation and event-sourced state management
|
|
5
|
+
Author-email: Ruth Mutua <mutuandinda82@gmail.com>, Muyukani Kizito <muyukani@prescottdata.io>
|
|
6
|
+
Maintainer-email: Prescott Data <info@prescottdata.io>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/yourusername/jarviscore
|
|
9
|
+
Project-URL: Documentation, https://jarviscore.readthedocs.io
|
|
10
|
+
Project-URL: Repository, https://github.com/yourusername/jarviscore
|
|
11
|
+
Project-URL: Issues, https://github.com/yourusername/jarviscore/issues
|
|
12
|
+
Keywords: agents,p2p,llm,distributed,workflow,orchestration
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
25
|
+
Requires-Dist: swim-p2p
|
|
26
|
+
Requires-Dist: pyzmq
|
|
27
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
28
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
29
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
30
|
+
Requires-Dist: anthropic>=0.18.0
|
|
31
|
+
Requires-Dist: openai>=1.0.0
|
|
32
|
+
Requires-Dist: google-generativeai>=0.3.0
|
|
33
|
+
Requires-Dist: httpx>=0.25.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
38
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
40
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
41
|
+
Provides-Extra: all
|
|
42
|
+
Requires-Dist: jarviscore[dev]; extra == "all"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# JarvisCore Framework
|
|
46
|
+
|
|
47
|
+
**P2P distributed agent framework with LLM code generation and production-grade state management**
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- ✅ **Simple Agent Definition** - Write just 3 attributes, framework handles everything
|
|
52
|
+
- ✅ **P2P Mesh Architecture** - Automatic agent discovery and task routing via SWIM protocol
|
|
53
|
+
- ✅ **Event-Sourced State** - Complete audit trail with crash recovery
|
|
54
|
+
- ✅ **Autonomous Execution** - LLM code generation with automatic repair
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install jarviscore
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Setup & Validation
|
|
63
|
+
|
|
64
|
+
### 1. Configure LLM Provider
|
|
65
|
+
|
|
66
|
+
Copy the example config and add your API key:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cp .env.example .env
|
|
70
|
+
# Edit .env and add one of: CLAUDE_API_KEY, AZURE_API_KEY, GEMINI_API_KEY, or LLM_ENDPOINT
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Validate Installation
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Check setup
|
|
77
|
+
python -m jarviscore.cli.check
|
|
78
|
+
|
|
79
|
+
# Test LLM connectivity
|
|
80
|
+
python -m jarviscore.cli.check --validate-llm
|
|
81
|
+
|
|
82
|
+
# Run smoke test (end-to-end validation)
|
|
83
|
+
python -m jarviscore.cli.smoketest
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
✅ **All checks pass?** You're ready to build agents!
|
|
87
|
+
|
|
88
|
+
## Quick Start
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from jarviscore import Mesh
|
|
92
|
+
from jarviscore.profiles import PromptDevAgent
|
|
93
|
+
|
|
94
|
+
# Define agent (3 lines)
|
|
95
|
+
class ScraperAgent(PromptDevAgent):
|
|
96
|
+
role = "scraper"
|
|
97
|
+
capabilities = ["web_scraping"]
|
|
98
|
+
system_prompt = "You are an expert web scraper..."
|
|
99
|
+
|
|
100
|
+
# Create mesh and run workflow
|
|
101
|
+
mesh = Mesh(mode="autonomous")
|
|
102
|
+
mesh.add(ScraperAgent)
|
|
103
|
+
await mesh.start()
|
|
104
|
+
|
|
105
|
+
results = await mesh.workflow(
|
|
106
|
+
workflow_id="wf-123",
|
|
107
|
+
steps=[
|
|
108
|
+
{"id": "scrape", "task": "Scrape example.com", "role": "scraper"}
|
|
109
|
+
]
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Architecture
|
|
114
|
+
|
|
115
|
+
JarvisCore is built on three layers:
|
|
116
|
+
|
|
117
|
+
1. **Execution Layer (20%)** - Profile-specific execution (Prompt-Dev, MCP)
|
|
118
|
+
2. **Orchestration Layer (60%)** - Workflow engine, dependencies, state management
|
|
119
|
+
3. **P2P Layer (20%)** - Agent discovery, task routing, mesh coordination
|
|
120
|
+
|
|
121
|
+
## Documentation
|
|
122
|
+
|
|
123
|
+
- [User Guide](jarviscore/docs/USER_GUIDE.md) - Complete guide for AutoAgent users
|
|
124
|
+
- [API Reference](jarviscore/docs/API_REFERENCE.md) - Detailed API documentation
|
|
125
|
+
- [Configuration Guide](jarviscore/docs/CONFIGURATION.md) - Settings and environment variables
|
|
126
|
+
- [Troubleshooting](jarviscore/docs/TROUBLESHOOTING.md) - Common issues and solutions
|
|
127
|
+
- [Examples](examples/) - Working code examples
|
|
128
|
+
|
|
129
|
+
## Development Status
|
|
130
|
+
|
|
131
|
+
**Version:** 0.1.0 (Alpha)
|
|
132
|
+
**Day 1:** Core framework foundation ✅
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT License - see LICENSE file for details
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
examples/calculator_agent_example.py,sha256=x7TrzE45WT_1DqwEnw8U3Fw56WpR9jBe3SLZz5vsKWc,2276
|
|
2
|
+
examples/multi_agent_workflow.py,sha256=Sygx3iEBM9WzorVMXqtiwn4rLYrW9BsxsiQSKceuzsE,4303
|
|
3
|
+
examples/research_agent_example.py,sha256=phJ5AHNnZ_pxCfiKvHoTp_IFwOAW7VD1fRNHlXvfgj4,2287
|
|
4
|
+
jarviscore/__init__.py,sha256=DKyYPM5AAcNcm6VnOJihC3sQRvkONgnYDupmQrmd3YQ,1380
|
|
5
|
+
jarviscore/cli/__init__.py,sha256=OnpJ37xDcbh3jFhALLY1jimgp1mxlB1-VhsKhGS6TDY,123
|
|
6
|
+
jarviscore/cli/__main__.py,sha256=GuIqW9NKJ3n70ei54ItzrBYEVaWG5dAWGxdu87w7YgI,829
|
|
7
|
+
jarviscore/cli/check.py,sha256=1A7X_CjHd8rDQ4QNAzXWJgaYaxOMU5BcRlCuFuFyop8,13698
|
|
8
|
+
jarviscore/cli/smoketest.py,sha256=1qek9Vb7DlX9YAtQwLg-L7Gr90q61oMy4elaF7rQA4I,13363
|
|
9
|
+
jarviscore/config/__init__.py,sha256=ZLjbRHSi5azaDyoSOFr9cQ65J5Fvi56xI-WHdczc204,178
|
|
10
|
+
jarviscore/config/settings.py,sha256=ueYpJAZxT1zoEPymzrn0OHAXZxQXBqSMs87VwolPdhg,3516
|
|
11
|
+
jarviscore/core/__init__.py,sha256=30K2aqZckYTRZupn6X-mGV2QDSqWCgJ1cpN6Zk1gqlQ,177
|
|
12
|
+
jarviscore/core/agent.py,sha256=qcqdhRVoYNxIhO_FYmttiDFA354EYtSaM2xp-TNDWGM,5402
|
|
13
|
+
jarviscore/core/mesh.py,sha256=880H25tyteX7ppoI7KkTOnPQoCjoQ3_YdMPzWTb3Zzc,16380
|
|
14
|
+
jarviscore/core/profile.py,sha256=sTrGTxV9mAqbt5l3z0-BSNOeWzq8YDLR3mlaPFSgt1c,2190
|
|
15
|
+
jarviscore/docs/API_REFERENCE.md,sha256=26OhRSfX6n3eSSgvtQdoeITmjzDjl9qUOU4PuH1NqKM,20066
|
|
16
|
+
jarviscore/docs/CONFIGURATION.md,sha256=xX7jSfIhCZ9mL-PK6QzRd9YIK7-WW4Hnkyw09NYn49A,14073
|
|
17
|
+
jarviscore/docs/GETTING_STARTED.md,sha256=T8sCwjscFYlMkz2YBDEse8LEtyLlTcijKY5FOu7E7kk,13630
|
|
18
|
+
jarviscore/docs/TROUBLESHOOTING.md,sha256=5sozZeJwTanFZ8-GctzgNN0R7ENM3CoS-Wu2Xt43oWk,8277
|
|
19
|
+
jarviscore/docs/USER_GUIDE.md,sha256=f6zFDSIXejRanKp3WGOhm2GZ18tEG5NuVl8Bopi2St0,21179
|
|
20
|
+
jarviscore/execution/__init__.py,sha256=yDAMehMO2dVvdKjxVx7zQV2AaxySmvymA24QF3O9tlY,1754
|
|
21
|
+
jarviscore/execution/code_registry.py,sha256=C3_hAVXIeCG31qwSBUrmBBicmd2vnUrXJhJgj8MKlJw,9213
|
|
22
|
+
jarviscore/execution/generator.py,sha256=zY7IxxDu4xoifeuCGZZN8_l8zQCsB5eUO9HGIiLIttw,8696
|
|
23
|
+
jarviscore/execution/llm.py,sha256=3anWSfRlBPKf76b3SolyG78tT37461pT1uak1oqdscw,16476
|
|
24
|
+
jarviscore/execution/repair.py,sha256=yy6GTX6nFoA38S9V1ZGvqOeH3iRThRkMI3GZ6F_2WrU,9092
|
|
25
|
+
jarviscore/execution/result_handler.py,sha256=7SKr-teFksqNgejhnZNrjAzKbtDXbOSV3Tv7gfYsdig,10590
|
|
26
|
+
jarviscore/execution/sandbox.py,sha256=IVkccce_WHDxXO6l8BCcuxAB5iueJfYtbryydoE972c,19981
|
|
27
|
+
jarviscore/execution/search.py,sha256=JSoT8vb_yT6_EKaAgUQDS8ONgFeKf6s8YlEeTn6FaWQ,9923
|
|
28
|
+
jarviscore/orchestration/__init__.py,sha256=Ia9GfEMWif0tN0Ju89q6M_x_BRw9FcQl5Rf99p8CIKU,386
|
|
29
|
+
jarviscore/orchestration/claimer.py,sha256=ekhHqhtxpi_USnPsIioFK6bA2nhH6jalulBkptYubVU,3106
|
|
30
|
+
jarviscore/orchestration/dependency.py,sha256=UtSSwSC2Ak5V5dYeZWJy3wZZuTE-Y-fcglkoIgNT_70,4377
|
|
31
|
+
jarviscore/orchestration/engine.py,sha256=vviPOIYvhRqK5TbyS2epiRayhGe8fBg1j8N0VB6Vzyc,10455
|
|
32
|
+
jarviscore/orchestration/status.py,sha256=XeKASMNQDwpJ6HpDw3m3eAAMNWsWCj4k9jrvMnLHPbo,2540
|
|
33
|
+
jarviscore/p2p/__init__.py,sha256=EMggUUf7E0MGJlayy0GNGiFGnd0vr-PJ2T2suR_mwAI,632
|
|
34
|
+
jarviscore/p2p/broadcaster.py,sha256=PQRBRfVQD1FNntnxi7H8VsOyYGk_63N-QtnRB4Dx_-I,14127
|
|
35
|
+
jarviscore/p2p/coordinator.py,sha256=dqSyg2xUAlxuRIn121fY1246KqT1O8-bImjpH80WIC0,13397
|
|
36
|
+
jarviscore/p2p/keepalive.py,sha256=Ml4TyXv2pzJ7UZl3uYVgMlHmpjxYV6fAebWLSEEXo5U,14079
|
|
37
|
+
jarviscore/p2p/swim_manager.py,sha256=Mdr6D0uxJR0h1JMO_faAda2Ojv6gfjoY-ZOzD9q_DWo,10816
|
|
38
|
+
jarviscore/profiles/__init__.py,sha256=vBV6W5vszx3px4UOZwCh2wsH-TYzIoPp4Zo_STT8pNo,154
|
|
39
|
+
jarviscore/profiles/autoagent.py,sha256=1nJAVf1oU9lLO47BP1xFGBDZtypXXkwKy6kZjtpdlX0,10424
|
|
40
|
+
jarviscore/profiles/customagent.py,sha256=GRauTYlWyYSgZrWyYZlAPNkJoVgjDHjfY_c0rdeoOgM,4618
|
|
41
|
+
jarviscore_framework-0.1.0.dist-info/licenses/LICENSE,sha256=SjsXanvmQJFYz_SVFa17O85-bKIa_aG99wrkPpWtypo,1101
|
|
42
|
+
tests/conftest.py,sha256=vK5f8DVxCkOGTz3K1835ru5vRgHnaDL_V9M5AUaJ2Zw,974
|
|
43
|
+
tests/test_agent.py,sha256=qx9SFDTP4DlcQi6hV8y6LZyEYX6IB8D3VnM7fODnW9s,5182
|
|
44
|
+
tests/test_autoagent.py,sha256=_mzinLdQwskOn6a-yGqdfoOsqw2f52XSyTCmj8hLqlg,4628
|
|
45
|
+
tests/test_autoagent_day4.py,sha256=TTb0kSImF9stMsq4cMlkGahf9UBpYjoNXAkgnkKuuQA,4719
|
|
46
|
+
tests/test_customagent.py,sha256=auxU6j3GVVT1r2kwoxarFUSCHtwSTjCrxBTqZzezqzw,8310
|
|
47
|
+
tests/test_integration.py,sha256=X9TYRW2WKF1doLFARTEyCCYLFAnYsazsshDwBzQZcZE,9191
|
|
48
|
+
tests/test_llm_fallback.py,sha256=CNajpKkQ6MO503dRbgaP2cz9kXHwUGKo5381tHKTe4c,5742
|
|
49
|
+
tests/test_mesh.py,sha256=QD0qbVRms7__ox2Ye7Ps4tfuH63m3_EoJzikjHIHjbc,10902
|
|
50
|
+
tests/test_p2p_integration.py,sha256=F9B21eWlwRzSRphm2Kacs9nM1FgSbSzi6RSLPDvvt2U,10995
|
|
51
|
+
tests/test_remote_sandbox.py,sha256=80ebc0pWInauWnywsQ0VSzlk8OexSCgGL7BcJUCPkR8,3268
|
|
52
|
+
jarviscore_framework-0.1.0.dist-info/METADATA,sha256=g-qil-HPVU4PZJub2x0eRsy8G0LPghls6Vbr_OvLtks,4250
|
|
53
|
+
jarviscore_framework-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
54
|
+
jarviscore_framework-0.1.0.dist-info/top_level.txt,sha256=P1SVqN5qA97MpLqnuowxDioZ49zccGrx0tjKz-7wz5o,26
|
|
55
|
+
jarviscore_framework-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JarvisCore Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tests/conftest.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pytest configuration and fixtures for JarvisCore tests.
|
|
3
|
+
"""
|
|
4
|
+
import pytest
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture(autouse=True)
|
|
9
|
+
def setup_logging():
|
|
10
|
+
"""Setup logging for tests."""
|
|
11
|
+
logging.basicConfig(
|
|
12
|
+
level=logging.INFO,
|
|
13
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def sample_task():
|
|
19
|
+
"""Sample task for testing."""
|
|
20
|
+
return {
|
|
21
|
+
"task": "Test task description",
|
|
22
|
+
"params": {"key": "value"}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def sample_workflow():
|
|
28
|
+
"""Sample workflow for testing."""
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
"agent": "agent1",
|
|
32
|
+
"task": "Step 1: Process data"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"agent": "agent2",
|
|
36
|
+
"task": "Step 2: Transform results",
|
|
37
|
+
"depends_on": [0]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"agent": "agent3",
|
|
41
|
+
"task": "Step 3: Save to storage",
|
|
42
|
+
"depends_on": [1]
|
|
43
|
+
}
|
|
44
|
+
]
|