kalibr 1.0.16__py3-none-any.whl → 1.0.18__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.
kalibr/__init__.py CHANGED
@@ -0,0 +1,8 @@
1
+ """Kalibr SDK - Multi-Model AI Integration Framework"""
2
+
3
+ from kalibr.kalibr import Kalibr
4
+ # KalibrApp will be imported once it's properly implemented
5
+ # from kalibr.kalibr_app import KalibrApp
6
+
7
+ __version__ = "1.0.18"
8
+ __all__ = ["Kalibr"] # "KalibrApp" will be added once implemented
kalibr/__main__.py CHANGED
@@ -1,31 +1,620 @@
1
1
  import typer
2
- from kalibr.kalibr_app import serve_app, deploy_app
3
-
4
- cli = typer.Typer(help="Kalibr Connect CLI")
5
-
6
- @cli.command("serve")
7
- def serve(file: str):
8
- """Run a Kalibr app locally."""
9
- serve_app(file)
10
-
11
- @cli.command("deploy")
12
- def deploy(file: str):
13
- """Deploy a Kalibr app."""
14
- deploy_app(file)
15
-
16
- @cli.command("usage")
17
- def usage():
18
- """Show usage guide."""
19
- print("""
20
- Kalibr Connect Commands:
21
- kalibr-connect serve <file> Run a Kalibr app locally.
22
- kalibr-connect deploy <file> Deploy your Kalibr app.
23
- kalibr-connect usage Show this usage guide.
24
- """)
2
+ import uvicorn
3
+ import sys
4
+ import importlib.util
5
+ from pathlib import Path
6
+ import os
7
+ import requests
8
+ import json
9
+ import zipfile
10
+ import tempfile
11
+
12
+ # Initialize a Typer application
13
+ app = typer.Typer()
14
+
15
+ @app.command()
16
+ def serve(
17
+ file: str = typer.Argument("kalibr_app.py", help="Python file with Kalibr app"),
18
+ host: str = typer.Option("0.0.0.0", "--host", "-h"),
19
+ port: int = typer.Option(8000, "--port", "-p"),
20
+ base_url: str = typer.Option("http://localhost:8000", "--base-url", "-b"),
21
+ app_mode: bool = typer.Option(False, "--app-mode", "-a", help="Use enhanced KalibrApp instead of basic Kalibr")
22
+ ):
23
+ """Serve a Kalibr-powered API locally."""
24
+
25
+ # Resolve the file path to an absolute path
26
+ file_path = Path(file).resolve()
27
+ # Check if the specified file exists
28
+ if not file_path.exists():
29
+ print(f"❌ Error: {file} not found")
30
+ raise typer.Exit(1)
31
+
32
+ # Create a module spec from the file location
33
+ spec = importlib.util.spec_from_file_location("user_app", file_path)
34
+ if not spec or not spec.loader:
35
+ print(f"❌ Error: Could not load {file}")
36
+ raise typer.Exit(1)
37
+
38
+ # Create a new module from the spec
39
+ module = importlib.util.module_from_spec(spec)
40
+ # Add the module to sys.modules so it can be imported
41
+ sys.modules["user_app"] = module
42
+
43
+ try:
44
+ # Execute the module's code
45
+ spec.loader.exec_module(module)
46
+ except Exception as e:
47
+ print(f"❌ Error loading {file}: {e}")
48
+ raise typer.Exit(1)
49
+
50
+ # Import Kalibr classes
51
+ from kalibr import Kalibr
52
+ # KalibrApp import will be enabled once it's properly implemented
53
+ # from kalibr import KalibrApp
54
+ KalibrApp = None # Placeholder until implemented
55
+ kalibr_instance = None
56
+
57
+ # Iterate through the attributes of the loaded module
58
+ for attr_name in dir(module):
59
+ attr = getattr(module, attr_name)
60
+ # Check if the attribute is an instance of Kalibr (or KalibrApp when implemented)
61
+ if isinstance(attr, Kalibr) or (KalibrApp and isinstance(attr, KalibrApp)):
62
+ kalibr_instance = attr
63
+ # Override the base_url of the Kalibr instance if the --base-url flag was provided
64
+ kalibr_instance.base_url = base_url
65
+ break
66
+
67
+ # If no Kalibr instance was found, raise an error
68
+ if not kalibr_instance:
69
+ print(f"❌ Error: No Kalibr/KalibrApp instance found in {file}")
70
+ raise typer.Exit(1)
71
+
72
+ # Get the FastAPI application from the Kalibr instance
73
+ fastapi_app = kalibr_instance.get_app()
74
+
75
+ # Print server information
76
+ is_enhanced = KalibrApp is not None and isinstance(kalibr_instance, KalibrApp)
77
+ print(f"🚀 Starting {'Enhanced ' if is_enhanced else ''}Kalibr server from {file}")
78
+ print(f"📍 GPT (OpenAPI): {base_url}/openapi.json")
79
+ print(f"📍 Claude (MCP): {base_url}/mcp.json")
80
+
81
+ if is_enhanced:
82
+ print(f"📍 Gemini: {base_url}/schemas/gemini")
83
+ print(f"📍 Copilot: {base_url}/schemas/copilot")
84
+ print(f"📍 Supported Models: {base_url}/models/supported")
85
+ print(f"📍 Health Check: {base_url}/health")
86
+
87
+ print(f"📍 Swagger UI: {base_url}/docs")
88
+ print(f"🔌 Actions registered: {list(kalibr_instance.actions.keys())}")
89
+
90
+ if is_enhanced:
91
+ print(f"📁 File handlers: {list(kalibr_instance.file_handlers.keys())}")
92
+ print(f"🌊 Stream actions: {list(kalibr_instance.stream_actions.keys())}")
93
+ print(f"⚡ Workflows: {list(kalibr_instance.workflows.keys())}")
94
+
95
+ # Run the FastAPI application using uvicorn
96
+ uvicorn.run(fastapi_app, host=host, port=port)
97
+
98
+ @app.command()
99
+ def deploy(
100
+ file: str = typer.Argument(..., help="Python file to deploy"),
101
+ app_name: str = typer.Option("", "--name", "-n", help="App name (defaults to filename)"),
102
+ platform: str = typer.Option("fly", "--platform", "-p", help="Deployment platform (fly, aws-lambda)"),
103
+ memory: int = typer.Option(512, "--memory", help="Memory allocation in MB"),
104
+ timeout: int = typer.Option(30, "--timeout", help="Timeout in seconds"),
105
+ env_file: str = typer.Option("", "--env-file", help="Environment variables file")
106
+ ):
107
+ """Deploy a Kalibr app to your own cloud infrastructure."""
108
+
109
+ from kalibr.deployment import deploy_app, DeploymentConfig
110
+
111
+ # Check if file exists
112
+ file_path = Path(file)
113
+ if not file_path.exists():
114
+ print(f"❌ Error: {file} not found")
115
+ raise typer.Exit(1)
116
+
117
+ # Generate app name from filename if not provided
118
+ if not app_name:
119
+ app_name = file_path.stem.replace('_', '-').replace('.', '-')
120
+
121
+ print(f"🚀 Deploying {file} as '{app_name}' to {platform}...")
122
+
123
+ # Load environment variables
124
+ env_vars = {}
125
+ if env_file and Path(env_file).exists():
126
+ with open(env_file) as f:
127
+ for line in f:
128
+ if '=' in line and not line.startswith('#'):
129
+ key, value = line.strip().split('=', 1)
130
+ env_vars[key] = value
131
+
132
+ # Create deployment config
133
+ config = DeploymentConfig(
134
+ app_name=app_name,
135
+ memory_mb=memory,
136
+ timeout_seconds=timeout,
137
+ environment_vars=env_vars
138
+ )
139
+
140
+ try:
141
+ result = deploy_app(str(file_path), config, platform)
142
+
143
+ if result["status"] == "success":
144
+ print("🎉 Deployment successful!")
145
+ print("\n📍 Your app is live at:")
146
+ for name, url in result["endpoints"].items():
147
+ print(f" {name.upper()}: {url}")
148
+
149
+ print(f"\n🔗 Test it now:")
150
+ print(f" curl {result['endpoints']['health']}")
151
+
152
+ print(f"\n🤖 Connect to AI models:")
153
+ print(f" GPT Actions: {result['endpoints']['openapi']}")
154
+ print(f" Claude MCP: {result['endpoints']['mcp']}")
155
+
156
+ else:
157
+ print(f"❌ Deployment failed: {result.get('error', 'Unknown error')}")
158
+ if result.get('logs'):
159
+ print(f"\n📋 Logs:")
160
+ print(result['logs'])
161
+ raise typer.Exit(1)
162
+
163
+ except Exception as e:
164
+ print(f"❌ Deployment error: {e}")
165
+ raise typer.Exit(1)
166
+
167
+ @app.command()
168
+ def status(
169
+ app_url: str = typer.Argument(..., help="URL of deployed app"),
170
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed information")
171
+ ):
172
+ """Check status of a deployed Kalibr app."""
173
+
174
+ try:
175
+ # Check health endpoint
176
+ health_url = f"{app_url.rstrip('/')}/health"
177
+ response = requests.get(health_url, timeout=10)
178
+
179
+ if response.status_code == 200:
180
+ health_data = response.json()
181
+ print(f"✅ App is healthy at {app_url}")
182
+ print(f" Version: {health_data.get('version', 'unknown')}")
183
+ print(f" Features: {health_data.get('features', {})}")
184
+
185
+ if verbose:
186
+ # Check available schemas
187
+ schemas = ["mcp.json", "openapi.json", "schemas/gemini", "schemas/copilot"]
188
+ print(f"\n📊 Available AI model schemas:")
189
+
190
+ for schema in schemas:
191
+ schema_url = f"{app_url.rstrip('/')}/{schema}"
192
+ try:
193
+ schema_response = requests.get(schema_url, timeout=5)
194
+ status_emoji = "✅" if schema_response.status_code == 200 else "❌"
195
+ model_name = schema.replace(".json", "").replace("schemas/", "")
196
+ print(f" {status_emoji} {model_name.upper()}: {schema_url}")
197
+ except:
198
+ print(f" ❌ {schema}: Connection failed")
199
+ else:
200
+ print(f"❌ App at {app_url} is not responding (HTTP {response.status_code})")
201
+ raise typer.Exit(1)
202
+
203
+ except requests.exceptions.RequestException as e:
204
+ print(f"❌ Cannot connect to {app_url}: {e}")
205
+ raise typer.Exit(1)
206
+
207
+ @app.command()
208
+ def list_platforms():
209
+ """List available deployment platforms and their requirements."""
210
+
211
+ print("🚀 Available Deployment Platforms:")
212
+ print()
213
+
214
+ platforms = [
215
+ {
216
+ "name": "Fly.io",
217
+ "command": "fly",
218
+ "requirements": [
219
+ "Install flyctl: https://fly.io/docs/flyctl/install/",
220
+ "Login: flyctl auth login",
221
+ "Create app: flyctl apps create your-app-name"
222
+ ],
223
+ "example": "kalibr deploy my_app.py --platform fly --name my-api"
224
+ },
225
+ {
226
+ "name": "AWS Lambda",
227
+ "command": "aws-lambda",
228
+ "requirements": [
229
+ "Install AWS CLI: https://aws.amazon.com/cli/",
230
+ "Configure credentials: aws configure",
231
+ "Set environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY"
232
+ ],
233
+ "example": "kalibr deploy my_app.py --platform aws-lambda --name my-api"
234
+ }
235
+ ]
236
+
237
+ for platform in platforms:
238
+ print(f"📦 {platform['name']}")
239
+ print(f" Command: --platform {platform['command']}")
240
+ print(" Setup Requirements:")
241
+ for req in platform['requirements']:
242
+ print(f" • {req}")
243
+ print(f" Example: {platform['example']}")
244
+ print()
245
+
246
+ print("💡 Tips:")
247
+ print(" • Use --env-file to load environment variables")
248
+ print(" • Check deployment status with: kalibr status <app-url>")
249
+ print(" • Test locally first with: kalibr serve my_app.py")
250
+
251
+ @app.command()
252
+ def setup(
253
+ platform: str = typer.Argument(..., help="Platform to setup (fly, aws)")
254
+ ):
255
+ """Setup deployment platform credentials and configuration."""
256
+
257
+ if platform == "fly":
258
+ print("🚀 Setting up Fly.io deployment...")
259
+ print()
260
+ print("📋 Required steps:")
261
+ print("1. Install flyctl:")
262
+ print(" • macOS: brew install flyctl")
263
+ print(" • Windows: iwr https://fly.io/install.ps1 -useb | iex")
264
+ print(" • Linux: curl -L https://fly.io/install.sh | sh")
265
+ print()
266
+ print("2. Create account and login:")
267
+ print(" flyctl auth signup")
268
+ print(" # or")
269
+ print(" flyctl auth login")
270
+ print()
271
+ print("3. Create your app:")
272
+ print(" flyctl apps create your-app-name")
273
+ print()
274
+ print("✅ After setup, deploy with:")
275
+ print(" kalibr deploy my_app.py --platform fly --name your-app-name")
276
+
277
+ elif platform == "aws":
278
+ print("☁️ Setting up AWS deployment...")
279
+ print()
280
+ print("📋 Required steps:")
281
+ print("1. Install AWS CLI:")
282
+ print(" • https://aws.amazon.com/cli/")
283
+ print()
284
+ print("2. Configure credentials:")
285
+ print(" aws configure")
286
+ print(" # Enter your Access Key ID and Secret Access Key")
287
+ print()
288
+ print("3. Set environment variables (alternative):")
289
+ print(" export AWS_ACCESS_KEY_ID=your_access_key")
290
+ print(" export AWS_SECRET_ACCESS_KEY=your_secret_key")
291
+ print(" export AWS_DEFAULT_REGION=us-east-1")
292
+ print()
293
+ print("✅ After setup, deploy with:")
294
+ print(" kalibr deploy my_app.py --platform aws-lambda --name your-app")
295
+
296
+ else:
297
+ print(f"❌ Unknown platform: {platform}")
298
+ print("Available platforms: fly, aws")
299
+ print("Run 'kalibr list-platforms' for more details")
300
+ raise typer.Exit(1)
301
+
302
+ @app.command()
303
+ def init(
304
+ template: str = typer.Option("basic", "--template", "-t", help="Template type (basic, enhanced, auth, analytics)"),
305
+ name: str = typer.Option("My API", "--name", "-n", help="API name")
306
+ ):
307
+ """Generate a starter Kalibr app with various templates."""
308
+
309
+ templates = {
310
+ "basic": {
311
+ "filename": "kalibr_app.py",
312
+ "content": f'''from kalibr import Kalibr
313
+
314
+ # Create your Kalibr API
315
+ app = Kalibr(title="{name}", base_url="http://localhost:8000")
316
+
317
+ @app.action("hello", "Say hello to someone")
318
+ def hello(name: str = "World"):
319
+ return {{"message": f"Hello, {{name}}!"}}
320
+
321
+ @app.action("add_contact", "Add a CRM contact")
322
+ def add_contact(name: str, email: str):
323
+ return {{"status": "success", "contact": {{"name": name, "email": email}}}}
324
+
325
+ @app.action("calculate", "Perform basic math operations")
326
+ def calculate(operation: str, a: float, b: float):
327
+ operations = {{
328
+ "add": a + b,
329
+ "subtract": a - b,
330
+ "multiply": a * b,
331
+ "divide": a / b if b != 0 else "Cannot divide by zero"
332
+ }}
333
+ return {{"result": operations.get(operation, "Invalid operation")}}
334
+
335
+ # Deploy with: kalibr deploy kalibr_app.py --platform fly --name my-api
336
+ ''',
337
+ "description": "Basic Kalibr app with simple actions"
338
+ },
339
+
340
+ "enhanced": {
341
+ "filename": "enhanced_app.py",
342
+ "content": f'''from kalibr import KalibrApp
343
+ from kalibr.types import FileUpload, Session, StreamingResponse
344
+ import asyncio
345
+
346
+ # Create enhanced Kalibr app
347
+ app = KalibrApp(title="{name}", base_url="http://localhost:8000")
348
+
349
+ @app.action("hello", "Say hello with enhanced features")
350
+ def hello(name: str = "World"):
351
+ return {{"message": f"Hello, {{name}}!", "timestamp": "2024-01-01T00:00:00Z"}}
352
+
353
+ @app.file_handler("analyze_file", [".txt", ".json", ".csv"])
354
+ async def analyze_file(file: FileUpload):
355
+ # Analyze uploaded files
356
+ content = file.content.decode('utf-8')
357
+ return {{
358
+ "filename": file.filename,
359
+ "size": file.size,
360
+ "lines": len(content.split('\\n')),
361
+ "words": len(content.split()),
362
+ "upload_id": file.upload_id
363
+ }}
364
+
365
+ @app.session_action("save_preference", "Save user preference")
366
+ async def save_preference(session: Session, key: str, value: str):
367
+ session.set(key, value)
368
+ return {{"saved": {{key: value}}, "session_id": session.session_id}}
369
+
370
+ @app.stream_action("count_progress", "Stream counting progress")
371
+ async def count_progress(max_count: int = 10):
372
+ for i in range(max_count + 1):
373
+ yield {{
374
+ "count": i,
375
+ "progress": i / max_count * 100,
376
+ "message": f"Progress: {{i}}/{{max_count}}"
377
+ }}
378
+ await asyncio.sleep(0.5)
379
+
380
+ # Deploy with: kalibr deploy enhanced_app.py --platform fly --name my-enhanced-api
381
+ ''',
382
+ "description": "Enhanced app with file uploads, sessions, and streaming"
383
+ },
384
+
385
+ "auth": {
386
+ "filename": "auth_app.py",
387
+ "content": f'''from kalibr import KalibrApp
388
+ from kalibr.auth_helpers import KalibrAuth, InMemoryUserStore, KalibrUser
389
+ from fastapi import Depends, HTTPException
390
+ import os
391
+
392
+ # Create app with authentication
393
+ app = KalibrApp(title="{name} (with Auth)", base_url="http://localhost:8000")
394
+
395
+ # Setup authentication
396
+ auth = KalibrAuth(secret_key=os.environ.get("SECRET_KEY", "your-secret-key-here"))
397
+ user_store = InMemoryUserStore()
398
+
399
+ # Create dependency for protected routes
400
+ async def get_current_user(token_payload = Depends(auth.create_auth_dependency(user_store.get_user))):
401
+ return token_payload
402
+
403
+ # Public endpoints
404
+ @app.action("public_hello", "Public greeting (no auth required)")
405
+ def public_hello(name: str = "World"):
406
+ return {{"message": f"Hello, {{name}}! This is a public endpoint."}}
407
+
408
+ @app.action("register", "Register a new user")
409
+ async def register(username: str, email: str, password: str):
410
+ try:
411
+ user = user_store.create_user(username, email, password)
412
+ token = auth.create_access_token({{"sub": user.id}})
413
+ return {{
414
+ "message": "User registered successfully",
415
+ "user": {{"id": user.id, "username": user.username}},
416
+ "token": token
417
+ }}
418
+ except ValueError as e:
419
+ raise HTTPException(400, str(e))
420
+
421
+ @app.action("login", "Login user and get token")
422
+ async def login(email: str, password: str):
423
+ user = user_store.get_user_by_email(email)
424
+ if not user or not user_store.verify_password(user.id, password):
425
+ raise HTTPException(401, "Invalid credentials")
426
+
427
+ token = auth.create_access_token({{"sub": user.id}})
428
+ return {{
429
+ "message": "Login successful",
430
+ "user": {{"id": user.id, "username": user.username}},
431
+ "token": token
432
+ }}
433
+
434
+ # Protected endpoints
435
+ @app.action("protected_hello", "Protected greeting (requires auth)")
436
+ async def protected_hello(name: str = "User", current_user: KalibrUser = Depends(get_current_user)):
437
+ return {{
438
+ "message": f"Hello, {{name}}! You are authenticated as {{current_user.username}}",
439
+ "user_id": current_user.id
440
+ }}
441
+
442
+ # Deploy with: kalibr deploy auth_app.py --platform fly --name my-auth-api --env-file .env
443
+ # Create .env file with: SECRET_KEY=your-actual-secret-key-here
444
+ ''',
445
+ "description": "App with built-in user authentication and protected routes"
446
+ },
447
+
448
+ "analytics": {
449
+ "filename": "analytics_app.py",
450
+ "content": f'''from kalibr import KalibrApp
451
+ from kalibr.analytics import kalibr_analytics, MetricsCollector
452
+ from kalibr.types import FileUpload
453
+
454
+ # Create app with built-in analytics
455
+ @kalibr_analytics(storage="file", auto_track=True)
456
+ class AnalyticsApp(KalibrApp):
457
+ def __init__(self):
458
+ super().__init__(title="{name} (with Analytics)", base_url="http://localhost:8000")
459
+
460
+ app = AnalyticsApp()
461
+
462
+ @app.action("hello", "Say hello with automatic analytics tracking")
463
+ def hello(name: str = "World"):
464
+ # This call is automatically tracked for analytics
465
+ return {{"message": f"Hello, {{name}}!"}}
466
+
467
+ @app.action("process_data", "Process some data with custom analytics")
468
+ def process_data(data: str, process_type: str = "basic"):
469
+ # Record custom analytics event
470
+ app.record_custom_event("data_processing",
471
+ data_length=len(data),
472
+ process_type=process_type)
473
+
474
+ result = {{
475
+ "processed": True,
476
+ "input_length": len(data),
477
+ "output_length": len(data) * 2, # Simulated processing
478
+ "type": process_type
479
+ }}
480
+ return result
481
+
482
+ @app.action("get_analytics", "Get analytics for this app")
483
+ def get_analytics():
484
+ # Get built-in analytics
485
+ analytics = app.get_analytics()
486
+ return {{
487
+ "analytics": analytics,
488
+ "note": "Analytics are automatically tracked for all action calls"
489
+ }}
490
+
491
+ # File handler with analytics
492
+ @app.file_handler("upload_with_analytics", [".txt", ".json"])
493
+ async def upload_with_analytics(file: FileUpload):
494
+ # Custom analytics for file uploads
495
+ app.record_custom_event("file_upload",
496
+ filename=file.filename,
497
+ file_size=file.size,
498
+ file_type=file.content_type)
499
+
500
+ return {{
501
+ "uploaded": file.filename,
502
+ "size": file.size,
503
+ "analytics_recorded": True
504
+ }}
505
+
506
+ # Deploy with: kalibr deploy analytics_app.py --platform fly --name my-analytics-api
507
+ # Analytics data will be saved to kalibr_analytics.jsonl
508
+ ''',
509
+ "description": "App with built-in analytics and metrics tracking"
510
+ }
511
+ }
512
+
513
+ if template not in templates:
514
+ print(f"❌ Unknown template: {template}")
515
+ print(f"Available templates: {', '.join(templates.keys())}")
516
+ raise typer.Exit(1)
517
+
518
+ template_info = templates[template]
519
+ filename = template_info["filename"]
520
+
521
+ # Write the template file
522
+ with open(filename, "w") as f:
523
+ f.write(template_info["content"])
524
+
525
+ print(f"✅ Created {filename}")
526
+ print(f"📝 {template_info['description']}")
527
+ print()
528
+ print(f"🚀 Next steps:")
529
+ print(f" 1. Test locally: kalibr serve {filename}")
530
+ print(f" 2. Deploy: kalibr deploy {filename} --platform fly --name my-app")
531
+ print(f" 3. Check status: kalibr status https://my-app.fly.dev")
532
+
533
+ if template == "auth":
534
+ print(f"\n🔒 Don't forget to:")
535
+ print(f" • Create .env file with SECRET_KEY=your-secret-key")
536
+ print(f" • Use --env-file .env when deploying")
537
+
538
+ if template == "analytics":
539
+ print(f"\n📊 Analytics features:")
540
+ print(f" • Automatic tracking of all API calls")
541
+ print(f" • Custom event recording")
542
+ print(f" • Built-in metrics endpoint")
543
+
544
+ @app.command()
545
+ def test(
546
+ url: str = typer.Option("http://localhost:8000", "--url", "-u", help="Base URL of Kalibr server"),
547
+ action: str = typer.Option("", "--action", "-a", help="Specific action to test")
548
+ ):
549
+ """Test a running Kalibr server."""
550
+ import requests
551
+ import json
552
+
553
+ try:
554
+ # Test health endpoint
555
+ health_response = requests.get(f"{url}/health")
556
+ if health_response.status_code == 200:
557
+ health_data = health_response.json()
558
+ print("✅ Server is healthy!")
559
+ print(f"Version: {health_data.get('version', 'unknown')}")
560
+ print(f"Features: {health_data.get('features', {})}")
561
+
562
+ # Get available actions
563
+ root_response = requests.get(f"{url}/")
564
+ if root_response.status_code == 200:
565
+ root_data = root_response.json()
566
+ actions = root_data.get("actions", [])
567
+ print(f"📋 Available actions: {actions}")
568
+
569
+ if action and action in actions:
570
+ # Test specific action
571
+ test_data = {"test": "data"}
572
+ action_response = requests.post(f"{url}/proxy/{action}", json=test_data)
573
+ print(f"🧪 Testing {action}: {action_response.status_code}")
574
+ print(f"Response: {action_response.json()}")
575
+
576
+ # Test schema endpoints
577
+ schemas = ["mcp", "openapi", "gemini", "copilot"]
578
+ print("\n📊 Schema endpoints:")
579
+ for schema_type in schemas:
580
+ schema_url = f"{url}/schemas/{schema_type}" if schema_type in ["gemini", "copilot"] else f"{url}/{schema_type}.json"
581
+ try:
582
+ schema_response = requests.get(schema_url)
583
+ status = "✅" if schema_response.status_code == 200 else "❌"
584
+ print(f"{status} {schema_type}: {schema_url}")
585
+ except:
586
+ print(f"❌ {schema_type}: {schema_url}")
587
+
588
+ except requests.ConnectionError:
589
+ print(f"❌ Could not connect to {url}")
590
+ print("Make sure the Kalibr server is running")
591
+
592
+ @app.command()
593
+ def version():
594
+ """Show Kalibr version information."""
595
+ try:
596
+ from kalibr import __version__
597
+ except (ImportError, AttributeError):
598
+ __version__ = "unknown"
599
+
600
+ print(f"Kalibr SDK version: {__version__}")
601
+ print("Enhanced multi-model AI integration framework")
602
+ print("Supports: GPT Actions, Claude MCP, Gemini Extensions, Copilot Plugins")
603
+ print("GitHub: https://github.com/devonakelley/kalibr-sdk")
25
604
 
26
605
  def main():
27
- """Entry point for console_scripts."""
28
- cli()
606
+ # Load config file if it exists for token
607
+ config_file = Path.home() / ".kalibr" / "config.json"
608
+ if config_file.exists():
609
+ try:
610
+ config = json.loads(config_file.read_text())
611
+ if "api_token" in config and not os.environ.get("KALIBR_TOKEN"):
612
+ os.environ["KALIBR_TOKEN"] = config["api_token"]
613
+ except:
614
+ pass # Ignore config file errors
615
+
616
+ # Run the Typer application
617
+ app()
29
618
 
30
619
  if __name__ == "__main__":
31
- main()
620
+ main()
kalibr/deployment.py ADDED
@@ -0,0 +1,26 @@
1
+ """
2
+ Kalibr Deployment Stub
3
+ -----------------------
4
+ This module provides a lightweight interface for future deployment providers.
5
+
6
+ Current supported strategy:
7
+ - Local (via Uvicorn)
8
+
9
+ Planned:
10
+ - Fly.io
11
+ - Render
12
+ - Railway
13
+
14
+ No AWS dependencies are required or used.
15
+ """
16
+
17
+ import subprocess
18
+
19
+ def deploy_local(file_path: str):
20
+ """Serve the Kalibr app locally using Uvicorn."""
21
+ subprocess.run(["uvicorn", f"{file_path}:app", "--reload"], check=False)
22
+
23
+ def deploy_placeholder(file_path: str):
24
+ """Placeholder for future remote deployment support."""
25
+ print(f"🚀 Deployment placeholder: {file_path}")
26
+ print("Coming soon: Fly.io and Render deployment support.")