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.
Files changed (55) hide show
  1. examples/calculator_agent_example.py +77 -0
  2. examples/multi_agent_workflow.py +132 -0
  3. examples/research_agent_example.py +76 -0
  4. jarviscore/__init__.py +54 -0
  5. jarviscore/cli/__init__.py +7 -0
  6. jarviscore/cli/__main__.py +33 -0
  7. jarviscore/cli/check.py +404 -0
  8. jarviscore/cli/smoketest.py +371 -0
  9. jarviscore/config/__init__.py +7 -0
  10. jarviscore/config/settings.py +128 -0
  11. jarviscore/core/__init__.py +7 -0
  12. jarviscore/core/agent.py +163 -0
  13. jarviscore/core/mesh.py +463 -0
  14. jarviscore/core/profile.py +64 -0
  15. jarviscore/docs/API_REFERENCE.md +932 -0
  16. jarviscore/docs/CONFIGURATION.md +753 -0
  17. jarviscore/docs/GETTING_STARTED.md +600 -0
  18. jarviscore/docs/TROUBLESHOOTING.md +424 -0
  19. jarviscore/docs/USER_GUIDE.md +983 -0
  20. jarviscore/execution/__init__.py +94 -0
  21. jarviscore/execution/code_registry.py +298 -0
  22. jarviscore/execution/generator.py +268 -0
  23. jarviscore/execution/llm.py +430 -0
  24. jarviscore/execution/repair.py +283 -0
  25. jarviscore/execution/result_handler.py +332 -0
  26. jarviscore/execution/sandbox.py +555 -0
  27. jarviscore/execution/search.py +281 -0
  28. jarviscore/orchestration/__init__.py +18 -0
  29. jarviscore/orchestration/claimer.py +101 -0
  30. jarviscore/orchestration/dependency.py +143 -0
  31. jarviscore/orchestration/engine.py +292 -0
  32. jarviscore/orchestration/status.py +96 -0
  33. jarviscore/p2p/__init__.py +23 -0
  34. jarviscore/p2p/broadcaster.py +353 -0
  35. jarviscore/p2p/coordinator.py +364 -0
  36. jarviscore/p2p/keepalive.py +361 -0
  37. jarviscore/p2p/swim_manager.py +290 -0
  38. jarviscore/profiles/__init__.py +6 -0
  39. jarviscore/profiles/autoagent.py +264 -0
  40. jarviscore/profiles/customagent.py +137 -0
  41. jarviscore_framework-0.1.0.dist-info/METADATA +136 -0
  42. jarviscore_framework-0.1.0.dist-info/RECORD +55 -0
  43. jarviscore_framework-0.1.0.dist-info/WHEEL +5 -0
  44. jarviscore_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
  45. jarviscore_framework-0.1.0.dist-info/top_level.txt +3 -0
  46. tests/conftest.py +44 -0
  47. tests/test_agent.py +165 -0
  48. tests/test_autoagent.py +140 -0
  49. tests/test_autoagent_day4.py +186 -0
  50. tests/test_customagent.py +248 -0
  51. tests/test_integration.py +293 -0
  52. tests/test_llm_fallback.py +185 -0
  53. tests/test_mesh.py +356 -0
  54. tests/test_p2p_integration.py +375 -0
  55. tests/test_remote_sandbox.py +116 -0
@@ -0,0 +1,283 @@
1
+ """
2
+ Autonomous Repair - LLM-based automatic code fixing
3
+ Analyzes errors and generates corrected code
4
+ """
5
+ import ast
6
+ import logging
7
+ from typing import Dict, Any, Optional
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class AutonomousRepair:
13
+ """
14
+ Automatic code repair using LLM.
15
+
16
+ Philosophy:
17
+ - Code execution fails → analyze error
18
+ - LLM generates fixed version
19
+ - Validate and retry (max 3 attempts)
20
+ - Learn from previous failures
21
+
22
+ Example:
23
+ repair = AutonomousRepair(code_generator)
24
+ fixed_code = await repair.repair(
25
+ original_code="result = 1/0",
26
+ error=ZeroDivisionError("division by zero"),
27
+ task={"task": "Calculate result"}
28
+ )
29
+ """
30
+
31
+ def __init__(self, code_generator, max_attempts: int = 3):
32
+ """
33
+ Initialize repair system.
34
+
35
+ Args:
36
+ code_generator: CodeGenerator instance
37
+ max_attempts: Maximum repair attempts (default 3)
38
+ """
39
+ self.codegen = code_generator
40
+ self.max_attempts = max_attempts
41
+
42
+ self.repair_template = self._load_repair_template()
43
+
44
+ def _load_repair_template(self) -> str:
45
+ """Load prompt template for code repair."""
46
+ return """You are an expert Python debugger. The following code FAILED during execution.
47
+
48
+ ORIGINAL CODE:
49
+ ```python
50
+ {original_code}
51
+ ```
52
+
53
+ ERROR DETAILS:
54
+ Type: {error_type}
55
+ Message: {error_message}
56
+
57
+ ORIGINAL TASK:
58
+ {task}
59
+
60
+ {previous_attempts}
61
+
62
+ INSTRUCTIONS:
63
+ 1. Analyze the error carefully
64
+ 2. Identify the root cause (syntax error, logic error, missing import, etc.)
65
+ 3. Generate FIXED Python code that:
66
+ - Solves the original task
67
+ - Handles the error properly
68
+ - Stores result in 'result' variable
69
+ - Is complete and executable
70
+
71
+ CRITICAL:
72
+ - Write ONLY executable Python code (no explanations)
73
+ - DO NOT repeat the same mistake
74
+ - Add proper error handling (try/except)
75
+ - Test edge cases in your mind
76
+
77
+ Generate the FIXED code now (code only):
78
+ """
79
+
80
+ async def repair(
81
+ self,
82
+ code: str,
83
+ error: Exception,
84
+ task: Dict[str, Any],
85
+ system_prompt: str,
86
+ attempt: int = 1,
87
+ previous_attempts: Optional[list] = None
88
+ ) -> str:
89
+ """
90
+ Repair failed code using LLM.
91
+
92
+ Args:
93
+ code: Original code that failed
94
+ error: Exception that occurred
95
+ task: Original task specification
96
+ system_prompt: Agent's system prompt
97
+ attempt: Current repair attempt number (1-3)
98
+ previous_attempts: List of previous failed attempts
99
+
100
+ Returns:
101
+ Fixed Python code
102
+
103
+ Raises:
104
+ RuntimeError: If max attempts exceeded
105
+ ValueError: If fixed code has syntax errors
106
+
107
+ Example:
108
+ try:
109
+ result = await executor.execute(code)
110
+ except Exception as e:
111
+ fixed_code = await repair.repair(code, e, task, system_prompt)
112
+ result = await executor.execute(fixed_code)
113
+ """
114
+ if attempt > self.max_attempts:
115
+ raise RuntimeError(
116
+ f"Repair failed after {self.max_attempts} attempts. "
117
+ f"Last error: {error}"
118
+ )
119
+
120
+ logger.info(f"Attempting code repair (attempt {attempt}/{self.max_attempts})")
121
+ logger.debug(f"Error: {type(error).__name__}: {error}")
122
+
123
+ # Format previous attempts info
124
+ attempts_info = ""
125
+ if previous_attempts:
126
+ attempts_info = "PREVIOUS FAILED ATTEMPTS:\n"
127
+ for i, prev in enumerate(previous_attempts, 1):
128
+ attempts_info += f"\nAttempt {i}:\n"
129
+ attempts_info += f"Error: {prev['error']}\n"
130
+ attempts_info += f"Code:\n```python\n{prev['code']}\n```\n"
131
+
132
+ # Build repair prompt
133
+ prompt = self.repair_template.format(
134
+ original_code=code,
135
+ error_type=type(error).__name__,
136
+ error_message=str(error),
137
+ task=task.get('task', ''),
138
+ previous_attempts=attempts_info
139
+ )
140
+
141
+ # Add system context
142
+ prompt = f"{system_prompt}\n\n{prompt}"
143
+
144
+ try:
145
+ # Generate fixed code via LLM
146
+ response = await self.codegen.llm.generate(
147
+ prompt=prompt,
148
+ temperature=0.4, # Slightly higher for creative problem solving
149
+ max_tokens=4000
150
+ )
151
+
152
+ fixed_code = response['content']
153
+
154
+ # Clean up code
155
+ fixed_code = self.codegen._clean_code(fixed_code)
156
+
157
+ # Validate syntax
158
+ self._validate_fix(fixed_code)
159
+
160
+ logger.info(f"Code repair attempt {attempt} successful")
161
+ return fixed_code
162
+
163
+ except Exception as e:
164
+ logger.error(f"Repair attempt {attempt} failed: {e}")
165
+ raise RuntimeError(f"Failed to repair code: {e}")
166
+
167
+ def _validate_fix(self, code: str):
168
+ """
169
+ Validate that fixed code has correct syntax.
170
+
171
+ Args:
172
+ code: Fixed Python code
173
+
174
+ Raises:
175
+ ValueError: If code has syntax errors
176
+ """
177
+ try:
178
+ ast.parse(code)
179
+ logger.debug("Fixed code syntax validation passed")
180
+ except SyntaxError as e:
181
+ logger.error(f"Fixed code still has syntax errors: {e}")
182
+ raise ValueError(f"Repaired code has syntax errors: {e}")
183
+
184
+ async def repair_with_retries(
185
+ self,
186
+ code: str,
187
+ error: Exception,
188
+ task: Dict[str, Any],
189
+ system_prompt: str,
190
+ executor
191
+ ) -> Dict[str, Any]:
192
+ """
193
+ Repair code with automatic retry on failure.
194
+
195
+ This is a high-level method that:
196
+ 1. Attempts repair
197
+ 2. Executes fixed code
198
+ 3. If still fails, tries again (up to max_attempts)
199
+
200
+ Args:
201
+ code: Original failed code
202
+ error: Exception from execution
203
+ task: Task specification
204
+ system_prompt: Agent system prompt
205
+ executor: SandboxExecutor instance for testing fixes
206
+
207
+ Returns:
208
+ Final execution result (success or failure)
209
+
210
+ Example:
211
+ result = await repair.repair_with_retries(
212
+ code, error, task, system_prompt, executor
213
+ )
214
+ """
215
+ previous_attempts = []
216
+ current_code = code
217
+ current_error = error
218
+
219
+ for attempt in range(1, self.max_attempts + 1):
220
+ try:
221
+ # Track this attempt
222
+ previous_attempts.append({
223
+ 'code': current_code,
224
+ 'error': str(current_error)
225
+ })
226
+
227
+ # Generate fix
228
+ fixed_code = await self.repair(
229
+ code=current_code,
230
+ error=current_error,
231
+ task=task,
232
+ system_prompt=system_prompt,
233
+ attempt=attempt,
234
+ previous_attempts=previous_attempts if attempt > 1 else None
235
+ )
236
+
237
+ # Test the fix
238
+ logger.info(f"Testing repair attempt {attempt}...")
239
+ result = await executor.execute(fixed_code)
240
+
241
+ if result['status'] == 'success':
242
+ logger.info(f"✓ Repair succeeded on attempt {attempt}")
243
+ return result
244
+ else:
245
+ # Fix didn't work, prepare for next attempt
246
+ current_code = fixed_code
247
+ current_error = Exception(result.get('error', 'Unknown error'))
248
+ logger.warning(
249
+ f"Repair attempt {attempt} executed but failed: "
250
+ f"{result.get('error')}"
251
+ )
252
+
253
+ except Exception as e:
254
+ logger.error(f"Repair attempt {attempt} failed with exception: {e}")
255
+ current_error = e
256
+ if attempt == self.max_attempts:
257
+ # Last attempt failed
258
+ return {
259
+ 'status': 'failure',
260
+ 'error': f'All {self.max_attempts} repair attempts exhausted',
261
+ 'last_error': str(e)
262
+ }
263
+
264
+ # Should not reach here, but just in case
265
+ return {
266
+ 'status': 'failure',
267
+ 'error': f'Repair failed after {self.max_attempts} attempts',
268
+ 'attempts': previous_attempts
269
+ }
270
+
271
+
272
+ def create_autonomous_repair(code_generator, max_attempts: int = 3) -> AutonomousRepair:
273
+ """
274
+ Factory function to create autonomous repair system.
275
+
276
+ Args:
277
+ code_generator: CodeGenerator instance
278
+ max_attempts: Max repair attempts (default 3)
279
+
280
+ Returns:
281
+ AutonomousRepair instance
282
+ """
283
+ return AutonomousRepair(code_generator, max_attempts)
@@ -0,0 +1,332 @@
1
+ """
2
+ Result Handler - Process and store function execution results
3
+ Storage: File system + In-memory (no external dependencies)
4
+ """
5
+ import json
6
+ import logging
7
+ import os
8
+ import time
9
+ import datetime
10
+ from typing import Dict, Any, Optional
11
+ from enum import Enum
12
+ from pathlib import Path
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class ResultStatus(Enum):
18
+ """Execution result status."""
19
+ SUCCESS = "success"
20
+ FAILURE = "failure"
21
+ SYNTAX_ERROR = "syntax_error"
22
+ RUNTIME_ERROR = "runtime_error"
23
+ TIMEOUT = "timeout"
24
+ PARTIAL = "partial"
25
+
26
+
27
+ class ErrorCategory(Enum):
28
+ """Error classification categories."""
29
+ SYNTAX = "syntax"
30
+ RUNTIME = "runtime"
31
+ NETWORK = "network"
32
+ TIMEOUT = "timeout"
33
+ RESOURCE = "resource"
34
+ UNKNOWN = "unknown"
35
+
36
+
37
+ class ResultHandler:
38
+ """
39
+ Handles processing, formatting, and storage of execution results.
40
+
41
+ Storage:
42
+ - File system: ./logs/{agent_id}/{result_id}.json
43
+ - In-memory: LRU cache for fast access
44
+
45
+ Features:
46
+ - Stores generated code with results
47
+ - Sanitizes sensitive parameters
48
+ - Classifies errors automatically
49
+ - Provides retrieval by result_id
50
+
51
+ Zero-config: No external dependencies (no Redis, no DB)
52
+ """
53
+
54
+ def __init__(self, log_directory: str = "./logs", max_cache_size: int = 100):
55
+ """
56
+ Initialize result handler.
57
+
58
+ Args:
59
+ log_directory: Base directory for storing results
60
+ max_cache_size: Maximum number of results to cache in memory
61
+ """
62
+ self.log_directory = Path(log_directory)
63
+ self.log_directory.mkdir(parents=True, exist_ok=True)
64
+
65
+ # In-memory cache (LRU)
66
+ self._cache: Dict[str, Dict[str, Any]] = {}
67
+ self._cache_order = [] # For LRU eviction
68
+ self.max_cache_size = max_cache_size
69
+
70
+ logger.info(f"ResultHandler initialized with directory: {self.log_directory}")
71
+
72
+ def process_result(
73
+ self,
74
+ agent_id: str,
75
+ task: str,
76
+ code: str,
77
+ output: Any,
78
+ status: str,
79
+ error: Optional[str] = None,
80
+ execution_time: Optional[float] = None,
81
+ tokens: Optional[Dict] = None,
82
+ cost_usd: Optional[float] = None,
83
+ repairs: int = 0,
84
+ metadata: Optional[Dict] = None
85
+ ) -> Dict[str, Any]:
86
+ """
87
+ Process and store execution result.
88
+
89
+ Args:
90
+ agent_id: ID of the agent that executed
91
+ task: Original task description
92
+ code: Generated code that was executed
93
+ output: Execution output (result variable)
94
+ status: "success" or "failure"
95
+ error: Error message if failed
96
+ execution_time: Time taken in seconds
97
+ tokens: Token usage {input, output, total}
98
+ cost_usd: Estimated cost
99
+ repairs: Number of repair attempts
100
+ metadata: Additional metadata
101
+
102
+ Returns:
103
+ Processed result dictionary with storage metadata
104
+ """
105
+ timestamp = datetime.datetime.now().isoformat()
106
+ result_id = self._generate_result_id(agent_id, timestamp)
107
+
108
+ # Classify error if present
109
+ error_category = None
110
+ if error:
111
+ error_category = self._classify_error(error)
112
+
113
+ # Determine detailed status
114
+ result_status = self._determine_status(status, error, error_category)
115
+
116
+ # Build result object
117
+ result_data = {
118
+ # Identity
119
+ "result_id": result_id,
120
+ "agent_id": agent_id,
121
+ "timestamp": timestamp,
122
+
123
+ # Execution details
124
+ "task": task,
125
+ "code": code,
126
+ "output": output,
127
+
128
+ # Status
129
+ "status": result_status.value,
130
+ "success": result_status == ResultStatus.SUCCESS,
131
+
132
+ # Error details
133
+ "error": error,
134
+ "error_category": error_category.value if error_category else None,
135
+
136
+ # Metrics
137
+ "execution_time": execution_time,
138
+ "tokens": tokens or {},
139
+ "cost_usd": cost_usd or 0.0,
140
+ "repairs": repairs,
141
+
142
+ # Metadata
143
+ "metadata": metadata or {}
144
+ }
145
+
146
+ # Store to file system
147
+ self._save_to_file(agent_id, result_id, result_data)
148
+
149
+ # Store in memory cache
150
+ self._cache_result(result_id, result_data)
151
+
152
+ # Log summary
153
+ if result_status == ResultStatus.SUCCESS:
154
+ time_str = f"{execution_time:.2f}s" if execution_time else "N/A"
155
+ logger.info(
156
+ f"Result {result_id}: SUCCESS in {time_str} "
157
+ f"(repairs: {repairs}, cost: ${cost_usd:.4f})"
158
+ )
159
+ else:
160
+ logger.error(
161
+ f"Result {result_id}: {result_status.value} - {error}"
162
+ )
163
+
164
+ return result_data
165
+
166
+ def get_result(self, result_id: str) -> Optional[Dict[str, Any]]:
167
+ """
168
+ Retrieve result by ID (checks cache first, then file system).
169
+
170
+ Args:
171
+ result_id: Result identifier
172
+
173
+ Returns:
174
+ Result dictionary or None if not found
175
+ """
176
+ # Check in-memory cache first
177
+ if result_id in self._cache:
178
+ logger.debug(f"Result {result_id} retrieved from cache")
179
+ return self._cache[result_id]
180
+
181
+ # Search file system
182
+ result_data = self._load_from_file(result_id)
183
+ if result_data:
184
+ # Cache for future access
185
+ self._cache_result(result_id, result_data)
186
+ logger.debug(f"Result {result_id} loaded from file")
187
+ return result_data
188
+
189
+ logger.warning(f"Result {result_id} not found")
190
+ return None
191
+
192
+ def get_agent_results(self, agent_id: str, limit: int = 10) -> list:
193
+ """
194
+ Get recent results for a specific agent.
195
+
196
+ Args:
197
+ agent_id: Agent identifier
198
+ limit: Maximum number of results to return
199
+
200
+ Returns:
201
+ List of result dictionaries, most recent first
202
+ """
203
+ agent_dir = self.log_directory / agent_id
204
+ if not agent_dir.exists():
205
+ return []
206
+
207
+ # Get all result files for this agent
208
+ result_files = sorted(
209
+ agent_dir.glob("*.json"),
210
+ key=lambda p: p.stat().st_mtime,
211
+ reverse=True
212
+ )[:limit]
213
+
214
+ results = []
215
+ for result_file in result_files:
216
+ try:
217
+ with open(result_file, 'r') as f:
218
+ results.append(json.load(f))
219
+ except Exception as e:
220
+ logger.warning(f"Failed to load {result_file}: {e}")
221
+
222
+ return results
223
+
224
+ def _generate_result_id(self, agent_id: str, timestamp: str) -> str:
225
+ """Generate unique result ID."""
226
+ clean_timestamp = timestamp.replace(':', '-').replace('.', '_')
227
+ return f"{agent_id}_{clean_timestamp}"
228
+
229
+ def _determine_status(
230
+ self,
231
+ status: str,
232
+ error: Optional[str],
233
+ error_category: Optional[ErrorCategory]
234
+ ) -> ResultStatus:
235
+ """Determine detailed result status."""
236
+ if status == "success":
237
+ return ResultStatus.SUCCESS
238
+
239
+ if error_category == ErrorCategory.SYNTAX:
240
+ return ResultStatus.SYNTAX_ERROR
241
+ elif error_category == ErrorCategory.TIMEOUT:
242
+ return ResultStatus.TIMEOUT
243
+ elif error and "error" in error.lower():
244
+ return ResultStatus.RUNTIME_ERROR
245
+
246
+ return ResultStatus.FAILURE
247
+
248
+ def _classify_error(self, error: str) -> ErrorCategory:
249
+ """Classify error into category."""
250
+ error_lower = error.lower()
251
+
252
+ # Syntax errors
253
+ syntax_keywords = ['syntaxerror', 'indentationerror', 'taberror', 'invalid syntax']
254
+ if any(kw in error_lower for kw in syntax_keywords):
255
+ return ErrorCategory.SYNTAX
256
+
257
+ # Timeout errors
258
+ timeout_keywords = ['timeout', 'timed out', 'time limit exceeded']
259
+ if any(kw in error_lower for kw in timeout_keywords):
260
+ return ErrorCategory.TIMEOUT
261
+
262
+ # Network errors
263
+ network_keywords = ['connection', 'network', 'httpx', 'aiohttp', 'socket']
264
+ if any(kw in error_lower for kw in network_keywords):
265
+ return ErrorCategory.NETWORK
266
+
267
+ # Resource errors
268
+ resource_keywords = ['memory', 'resource', 'quota', 'limit exceeded']
269
+ if any(kw in error_lower for kw in resource_keywords):
270
+ return ErrorCategory.RESOURCE
271
+
272
+ # Runtime errors (default for unknown errors)
273
+ return ErrorCategory.RUNTIME
274
+
275
+ def _save_to_file(self, agent_id: str, result_id: str, result_data: Dict):
276
+ """Save result to file system."""
277
+ agent_dir = self.log_directory / agent_id
278
+ agent_dir.mkdir(parents=True, exist_ok=True)
279
+
280
+ result_file = agent_dir / f"{result_id}.json"
281
+
282
+ try:
283
+ with open(result_file, 'w') as f:
284
+ json.dump(result_data, f, indent=2, default=str)
285
+ logger.debug(f"Saved result to {result_file}")
286
+ except Exception as e:
287
+ logger.error(f"Failed to save result to file: {e}")
288
+
289
+ def _load_from_file(self, result_id: str) -> Optional[Dict]:
290
+ """Load result from file system by searching all agent directories."""
291
+ for agent_dir in self.log_directory.iterdir():
292
+ if not agent_dir.is_dir():
293
+ continue
294
+
295
+ result_file = agent_dir / f"{result_id}.json"
296
+ if result_file.exists():
297
+ try:
298
+ with open(result_file, 'r') as f:
299
+ return json.load(f)
300
+ except Exception as e:
301
+ logger.warning(f"Failed to load {result_file}: {e}")
302
+
303
+ return None
304
+
305
+ def _cache_result(self, result_id: str, result_data: Dict):
306
+ """Cache result in memory with LRU eviction."""
307
+ # Add to cache
308
+ self._cache[result_id] = result_data
309
+
310
+ # Track access order
311
+ if result_id in self._cache_order:
312
+ self._cache_order.remove(result_id)
313
+ self._cache_order.append(result_id)
314
+
315
+ # Evict oldest if cache is full
316
+ while len(self._cache) > self.max_cache_size:
317
+ oldest_id = self._cache_order.pop(0)
318
+ del self._cache[oldest_id]
319
+ logger.debug(f"Evicted {oldest_id} from cache (LRU)")
320
+
321
+
322
+ def create_result_handler(log_directory: str = "./logs") -> ResultHandler:
323
+ """
324
+ Factory function to create result handler.
325
+
326
+ Args:
327
+ log_directory: Base directory for result storage
328
+
329
+ Returns:
330
+ ResultHandler instance
331
+ """
332
+ return ResultHandler(log_directory)