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
jarviscore/cli/check.py
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JarvisCore Health Check CLI
|
|
3
|
+
|
|
4
|
+
Validates installation, configuration, and LLM connectivity.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python -m jarviscore.cli.check # Basic health check
|
|
8
|
+
python -m jarviscore.cli.check --validate-llm # Test LLM connectivity
|
|
9
|
+
python -m jarviscore.cli.check --verbose # Show detailed info
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import os
|
|
14
|
+
import asyncio
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Dict, List, Tuple
|
|
17
|
+
import importlib.util
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HealthChecker:
|
|
21
|
+
"""Health check orchestrator for JarvisCore setup."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, validate_llm: bool = False, verbose: bool = False):
|
|
24
|
+
self.validate_llm = validate_llm
|
|
25
|
+
self.verbose = verbose
|
|
26
|
+
self.issues: List[str] = []
|
|
27
|
+
self.warnings: List[str] = []
|
|
28
|
+
self.successes: List[str] = []
|
|
29
|
+
|
|
30
|
+
def print_header(self):
|
|
31
|
+
"""Print check header."""
|
|
32
|
+
print("\n" + "="*70)
|
|
33
|
+
print(" JarvisCore Health Check")
|
|
34
|
+
print(" Testing AutoAgent/Prompt-Dev Profile")
|
|
35
|
+
print("="*70 + "\n")
|
|
36
|
+
|
|
37
|
+
def check_python_version(self) -> bool:
|
|
38
|
+
"""Check Python version >= 3.10."""
|
|
39
|
+
version = sys.version_info
|
|
40
|
+
status = version >= (3, 10)
|
|
41
|
+
|
|
42
|
+
if status:
|
|
43
|
+
self.successes.append(f"Python {version.major}.{version.minor}.{version.micro}")
|
|
44
|
+
else:
|
|
45
|
+
self.issues.append(
|
|
46
|
+
f"Python {version.major}.{version.minor} found, but 3.10+ required"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
self._print_status("Python Version", status,
|
|
50
|
+
f"{version.major}.{version.minor}.{version.micro}")
|
|
51
|
+
return status
|
|
52
|
+
|
|
53
|
+
def check_jarviscore_installed(self) -> bool:
|
|
54
|
+
"""Check if jarviscore package is installed."""
|
|
55
|
+
try:
|
|
56
|
+
import jarviscore
|
|
57
|
+
version = getattr(jarviscore, '__version__', '0.1.0')
|
|
58
|
+
self.successes.append(f"JarvisCore {version} installed")
|
|
59
|
+
self._print_status("JarvisCore Package", True, f"v{version}")
|
|
60
|
+
return True
|
|
61
|
+
except ImportError as e:
|
|
62
|
+
self.issues.append(f"JarvisCore not installed: {e}")
|
|
63
|
+
self._print_status("JarvisCore Package", False, "Not found")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
def check_dependencies(self) -> Dict[str, bool]:
|
|
67
|
+
"""Check core dependencies."""
|
|
68
|
+
deps = {
|
|
69
|
+
'pydantic': 'Core validation',
|
|
70
|
+
'pydantic_settings': 'Configuration management',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
results = {}
|
|
74
|
+
print("\n[Dependencies]")
|
|
75
|
+
|
|
76
|
+
for dep, description in deps.items():
|
|
77
|
+
try:
|
|
78
|
+
spec = importlib.util.find_spec(dep)
|
|
79
|
+
installed = spec is not None
|
|
80
|
+
results[dep] = installed
|
|
81
|
+
|
|
82
|
+
if installed:
|
|
83
|
+
self.successes.append(f"{dep}: {description}")
|
|
84
|
+
else:
|
|
85
|
+
self.issues.append(f"{dep} not installed: {description}")
|
|
86
|
+
|
|
87
|
+
self._print_status(f" {dep}", installed, description)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
results[dep] = False
|
|
90
|
+
self.issues.append(f"{dep} check failed: {e}")
|
|
91
|
+
self._print_status(f" {dep}", False, str(e))
|
|
92
|
+
|
|
93
|
+
return results
|
|
94
|
+
|
|
95
|
+
def check_env_file(self) -> Tuple[bool, Path]:
|
|
96
|
+
"""Check if .env file exists."""
|
|
97
|
+
env_paths = [
|
|
98
|
+
Path.cwd() / '.env',
|
|
99
|
+
Path.cwd() / 'jarviscore' / '.env',
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
for env_path in env_paths:
|
|
103
|
+
if env_path.exists():
|
|
104
|
+
self.successes.append(f".env found at {env_path}")
|
|
105
|
+
self._print_status(".env File", True, str(env_path))
|
|
106
|
+
return True, env_path
|
|
107
|
+
|
|
108
|
+
self.warnings.append(".env file not found - using environment variables")
|
|
109
|
+
self._print_status(".env File", False, "Not found (will use environment vars)")
|
|
110
|
+
return False, None
|
|
111
|
+
|
|
112
|
+
def check_llm_config(self) -> Dict[str, bool]:
|
|
113
|
+
"""Check which LLM providers are configured."""
|
|
114
|
+
providers = {
|
|
115
|
+
'Claude': ['CLAUDE_API_KEY', 'ANTHROPIC_API_KEY'],
|
|
116
|
+
'Azure OpenAI': ['AZURE_API_KEY', 'AZURE_OPENAI_KEY'],
|
|
117
|
+
'Gemini': ['GEMINI_API_KEY'],
|
|
118
|
+
'vLLM': ['LLM_ENDPOINT'],
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
configured = {}
|
|
122
|
+
print("\n[LLM Configuration]")
|
|
123
|
+
|
|
124
|
+
for provider, env_vars in providers.items():
|
|
125
|
+
is_configured = any(os.getenv(var) for var in env_vars)
|
|
126
|
+
configured[provider] = is_configured
|
|
127
|
+
|
|
128
|
+
if is_configured:
|
|
129
|
+
key_name = next(var for var in env_vars if os.getenv(var))
|
|
130
|
+
masked = self._mask_api_key(os.getenv(key_name))
|
|
131
|
+
self.successes.append(f"{provider} configured")
|
|
132
|
+
self._print_status(f" {provider}", True, f"{key_name}={masked}")
|
|
133
|
+
else:
|
|
134
|
+
self._print_status(f" {provider}", False, "Not configured")
|
|
135
|
+
|
|
136
|
+
if not any(configured.values()):
|
|
137
|
+
self.issues.append(
|
|
138
|
+
"No LLM provider configured. AutoAgent requires at least one LLM."
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return configured
|
|
142
|
+
|
|
143
|
+
async def validate_llm_connectivity(self, configured: Dict[str, bool]):
|
|
144
|
+
"""Test actual LLM connectivity."""
|
|
145
|
+
print("\n[LLM Connectivity Test]")
|
|
146
|
+
|
|
147
|
+
# Load environment
|
|
148
|
+
from dotenv import load_dotenv
|
|
149
|
+
load_dotenv()
|
|
150
|
+
|
|
151
|
+
# Try each configured provider
|
|
152
|
+
for provider, is_configured in configured.items():
|
|
153
|
+
if not is_configured:
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
if provider == "Claude":
|
|
158
|
+
success = await self._test_claude()
|
|
159
|
+
elif provider == "Azure OpenAI":
|
|
160
|
+
success = await self._test_azure()
|
|
161
|
+
elif provider == "Gemini":
|
|
162
|
+
success = await self._test_gemini()
|
|
163
|
+
elif provider == "vLLM":
|
|
164
|
+
success = await self._test_vllm()
|
|
165
|
+
else:
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
if success:
|
|
169
|
+
self.successes.append(f"{provider} connectivity OK")
|
|
170
|
+
self._print_status(f" {provider} API", True, "Connected")
|
|
171
|
+
else:
|
|
172
|
+
self.issues.append(f"{provider} connection failed")
|
|
173
|
+
self._print_status(f" {provider} API", False, "Connection failed")
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
self.issues.append(f"{provider} test failed: {str(e)}")
|
|
177
|
+
self._print_status(f" {provider} API", False, str(e))
|
|
178
|
+
|
|
179
|
+
async def _test_claude(self) -> bool:
|
|
180
|
+
"""Test Claude API connectivity."""
|
|
181
|
+
try:
|
|
182
|
+
from anthropic import AsyncAnthropic
|
|
183
|
+
|
|
184
|
+
api_key = os.getenv('CLAUDE_API_KEY') or os.getenv('ANTHROPIC_API_KEY')
|
|
185
|
+
if not api_key:
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
client = AsyncAnthropic(api_key=api_key)
|
|
189
|
+
|
|
190
|
+
# Simple test message
|
|
191
|
+
response = await client.messages.create(
|
|
192
|
+
model=os.getenv('CLAUDE_MODEL', 'claude-sonnet-4'),
|
|
193
|
+
max_tokens=10,
|
|
194
|
+
messages=[{"role": "user", "content": "Reply with just 'OK'"}]
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return response.content[0].text.strip().upper() == 'OK'
|
|
198
|
+
|
|
199
|
+
except Exception:
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
async def _test_azure(self) -> bool:
|
|
203
|
+
"""Test Azure OpenAI connectivity."""
|
|
204
|
+
try:
|
|
205
|
+
from openai import AsyncAzureOpenAI
|
|
206
|
+
|
|
207
|
+
client = AsyncAzureOpenAI(
|
|
208
|
+
api_key=os.getenv('AZURE_API_KEY') or os.getenv('AZURE_OPENAI_KEY'),
|
|
209
|
+
api_version=os.getenv('AZURE_API_VERSION', '2024-02-15-preview'),
|
|
210
|
+
azure_endpoint=os.getenv('AZURE_ENDPOINT')
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
response = await client.chat.completions.create(
|
|
214
|
+
model=os.getenv('AZURE_DEPLOYMENT', 'gpt-4o'),
|
|
215
|
+
messages=[{"role": "user", "content": "Reply with just 'OK'"}],
|
|
216
|
+
max_tokens=10
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return response.choices[0].message.content.strip().upper() == 'OK'
|
|
220
|
+
|
|
221
|
+
except Exception:
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
async def _test_gemini(self) -> bool:
|
|
225
|
+
"""Test Gemini connectivity."""
|
|
226
|
+
try:
|
|
227
|
+
import google.generativeai as genai
|
|
228
|
+
|
|
229
|
+
genai.configure(api_key=os.getenv('GEMINI_API_KEY'))
|
|
230
|
+
model = genai.GenerativeModel(os.getenv('GEMINI_MODEL', 'gemini-1.5-flash'))
|
|
231
|
+
|
|
232
|
+
response = await model.generate_content_async("Reply with just 'OK'")
|
|
233
|
+
return 'OK' in response.text.upper()
|
|
234
|
+
|
|
235
|
+
except Exception:
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
async def _test_vllm(self) -> bool:
|
|
239
|
+
"""Test vLLM endpoint connectivity."""
|
|
240
|
+
try:
|
|
241
|
+
import httpx
|
|
242
|
+
|
|
243
|
+
endpoint = os.getenv('LLM_ENDPOINT')
|
|
244
|
+
if not endpoint:
|
|
245
|
+
return False
|
|
246
|
+
|
|
247
|
+
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
248
|
+
response = await client.get(f"{endpoint}/health")
|
|
249
|
+
return response.status_code == 200
|
|
250
|
+
|
|
251
|
+
except Exception:
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
def check_sandbox_config(self):
|
|
255
|
+
"""Check sandbox configuration."""
|
|
256
|
+
print("\n[Sandbox Configuration]")
|
|
257
|
+
|
|
258
|
+
mode = os.getenv('SANDBOX_MODE', 'local')
|
|
259
|
+
self._print_status(" Sandbox Mode", True, mode)
|
|
260
|
+
|
|
261
|
+
if mode == 'remote':
|
|
262
|
+
service_url = os.getenv('SANDBOX_SERVICE_URL')
|
|
263
|
+
if service_url:
|
|
264
|
+
self.successes.append(f"Remote sandbox: {service_url}")
|
|
265
|
+
self._print_status(" Remote Service", True, service_url)
|
|
266
|
+
else:
|
|
267
|
+
self.warnings.append("SANDBOX_MODE=remote but no SANDBOX_SERVICE_URL set")
|
|
268
|
+
self._print_status(" Remote Service", False, "URL not set")
|
|
269
|
+
|
|
270
|
+
def print_summary(self):
|
|
271
|
+
"""Print summary and recommendations."""
|
|
272
|
+
print("\n" + "="*70)
|
|
273
|
+
print(" Summary")
|
|
274
|
+
print("="*70 + "\n")
|
|
275
|
+
|
|
276
|
+
total_checks = len(self.successes) + len(self.issues) + len(self.warnings)
|
|
277
|
+
|
|
278
|
+
if self.successes:
|
|
279
|
+
print(f"✓ {len(self.successes)} checks passed")
|
|
280
|
+
|
|
281
|
+
if self.warnings:
|
|
282
|
+
print(f"⚠ {len(self.warnings)} warnings")
|
|
283
|
+
for warning in self.warnings:
|
|
284
|
+
print(f" - {warning}")
|
|
285
|
+
|
|
286
|
+
if self.issues:
|
|
287
|
+
print(f"\n✗ {len(self.issues)} issues found:")
|
|
288
|
+
for issue in self.issues:
|
|
289
|
+
print(f" - {issue}")
|
|
290
|
+
|
|
291
|
+
print("\n" + "="*70)
|
|
292
|
+
print(" Next Steps")
|
|
293
|
+
print("="*70)
|
|
294
|
+
print("\n1. Copy .env.example to .env:")
|
|
295
|
+
print(" cp .env.example .env")
|
|
296
|
+
print("\n2. Add your LLM API key to .env (choose one):")
|
|
297
|
+
print(" - CLAUDE_API_KEY=sk-ant-...")
|
|
298
|
+
print(" - AZURE_API_KEY=...")
|
|
299
|
+
print(" - GEMINI_API_KEY=...")
|
|
300
|
+
print(" - LLM_ENDPOINT=http://localhost:8000 (for local vLLM)")
|
|
301
|
+
print("\n3. Run health check again:")
|
|
302
|
+
print(" python -m jarviscore.cli.check --validate-llm")
|
|
303
|
+
print("\n4. Try the smoke test:")
|
|
304
|
+
print(" python -m jarviscore.cli.smoketest")
|
|
305
|
+
print()
|
|
306
|
+
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
print("\n✓ All checks passed! Ready to use JarvisCore.\n")
|
|
310
|
+
print("Next steps:")
|
|
311
|
+
print(" 1. Run smoke test: python -m jarviscore.cli.smoketest")
|
|
312
|
+
print(" 2. Try examples: python examples/calculator_agent_example.py")
|
|
313
|
+
print(" 3. Read guide: docs/GETTING_STARTED.md")
|
|
314
|
+
print()
|
|
315
|
+
|
|
316
|
+
return True
|
|
317
|
+
|
|
318
|
+
def _print_status(self, label: str, status: bool, detail: str = ""):
|
|
319
|
+
"""Print a status line with symbol."""
|
|
320
|
+
symbol = "✓" if status else ("⚠" if detail else "✗")
|
|
321
|
+
label_padded = f"{label}:".ljust(30)
|
|
322
|
+
|
|
323
|
+
if self.verbose or not status:
|
|
324
|
+
print(f"{symbol} {label_padded} {detail}")
|
|
325
|
+
else:
|
|
326
|
+
print(f"{symbol} {label_padded} OK")
|
|
327
|
+
|
|
328
|
+
def _mask_api_key(self, key: str) -> str:
|
|
329
|
+
"""Mask API key for display."""
|
|
330
|
+
if not key:
|
|
331
|
+
return "None"
|
|
332
|
+
if len(key) <= 8:
|
|
333
|
+
return "*" * len(key)
|
|
334
|
+
return f"{key[:4]}...{key[-4:]}"
|
|
335
|
+
|
|
336
|
+
async def run(self) -> bool:
|
|
337
|
+
"""Run all health checks."""
|
|
338
|
+
self.print_header()
|
|
339
|
+
|
|
340
|
+
# Basic checks
|
|
341
|
+
print("[System Requirements]")
|
|
342
|
+
python_ok = self.check_python_version()
|
|
343
|
+
jarviscore_ok = self.check_jarviscore_installed()
|
|
344
|
+
|
|
345
|
+
if not python_ok or not jarviscore_ok:
|
|
346
|
+
self.print_summary()
|
|
347
|
+
return False
|
|
348
|
+
|
|
349
|
+
# Dependency checks
|
|
350
|
+
deps_ok = self.check_dependencies()
|
|
351
|
+
|
|
352
|
+
# Configuration checks
|
|
353
|
+
print()
|
|
354
|
+
env_exists, env_path = self.check_env_file()
|
|
355
|
+
|
|
356
|
+
# Load .env if it exists
|
|
357
|
+
if env_exists:
|
|
358
|
+
from dotenv import load_dotenv
|
|
359
|
+
load_dotenv(env_path)
|
|
360
|
+
|
|
361
|
+
llm_configured = self.check_llm_config()
|
|
362
|
+
|
|
363
|
+
# LLM connectivity test (optional)
|
|
364
|
+
if self.validate_llm and any(llm_configured.values()):
|
|
365
|
+
await self.validate_llm_connectivity(llm_configured)
|
|
366
|
+
|
|
367
|
+
# Sandbox config
|
|
368
|
+
self.check_sandbox_config()
|
|
369
|
+
|
|
370
|
+
# Summary
|
|
371
|
+
return self.print_summary()
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def main():
|
|
375
|
+
"""CLI entry point."""
|
|
376
|
+
import argparse
|
|
377
|
+
|
|
378
|
+
parser = argparse.ArgumentParser(
|
|
379
|
+
description='JarvisCore Health Check - Validate your installation'
|
|
380
|
+
)
|
|
381
|
+
parser.add_argument(
|
|
382
|
+
'--validate-llm',
|
|
383
|
+
action='store_true',
|
|
384
|
+
help='Test LLM API connectivity (makes actual API calls)'
|
|
385
|
+
)
|
|
386
|
+
parser.add_argument(
|
|
387
|
+
'--verbose',
|
|
388
|
+
action='store_true',
|
|
389
|
+
help='Show detailed information'
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
args = parser.parse_args()
|
|
393
|
+
|
|
394
|
+
checker = HealthChecker(
|
|
395
|
+
validate_llm=args.validate_llm,
|
|
396
|
+
verbose=args.verbose
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
success = asyncio.run(checker.run())
|
|
400
|
+
sys.exit(0 if success else 1)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
if __name__ == '__main__':
|
|
404
|
+
main()
|