kalibr 1.0.25__py3-none-any.whl → 1.1.2a0__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 (61) hide show
  1. kalibr/__init__.py +170 -3
  2. kalibr/__main__.py +3 -203
  3. kalibr/capsule_middleware.py +108 -0
  4. kalibr/cli/__init__.py +5 -0
  5. kalibr/cli/capsule_cmd.py +174 -0
  6. kalibr/cli/deploy_cmd.py +114 -0
  7. kalibr/cli/main.py +67 -0
  8. kalibr/cli/run.py +203 -0
  9. kalibr/cli/serve.py +59 -0
  10. kalibr/client.py +293 -0
  11. kalibr/collector.py +173 -0
  12. kalibr/context.py +132 -0
  13. kalibr/cost_adapter.py +222 -0
  14. kalibr/decorators.py +140 -0
  15. kalibr/instrumentation/__init__.py +13 -0
  16. kalibr/instrumentation/anthropic_instr.py +282 -0
  17. kalibr/instrumentation/base.py +108 -0
  18. kalibr/instrumentation/google_instr.py +281 -0
  19. kalibr/instrumentation/openai_instr.py +265 -0
  20. kalibr/instrumentation/registry.py +153 -0
  21. kalibr/kalibr.py +144 -230
  22. kalibr/kalibr_app.py +53 -314
  23. kalibr/middleware/__init__.py +5 -0
  24. kalibr/middleware/auto_tracer.py +356 -0
  25. kalibr/models.py +41 -0
  26. kalibr/redaction.py +44 -0
  27. kalibr/schemas.py +116 -0
  28. kalibr/simple_tracer.py +258 -0
  29. kalibr/tokens.py +52 -0
  30. kalibr/trace_capsule.py +296 -0
  31. kalibr/trace_models.py +201 -0
  32. kalibr/tracer.py +354 -0
  33. kalibr/types.py +25 -93
  34. kalibr/utils.py +198 -0
  35. kalibr-1.1.2a0.dist-info/METADATA +236 -0
  36. kalibr-1.1.2a0.dist-info/RECORD +48 -0
  37. kalibr-1.1.2a0.dist-info/entry_points.txt +2 -0
  38. kalibr-1.1.2a0.dist-info/licenses/LICENSE +21 -0
  39. kalibr-1.1.2a0.dist-info/top_level.txt +4 -0
  40. kalibr_crewai/__init__.py +65 -0
  41. kalibr_crewai/callbacks.py +539 -0
  42. kalibr_crewai/instrumentor.py +513 -0
  43. kalibr_langchain/__init__.py +47 -0
  44. kalibr_langchain/async_callback.py +850 -0
  45. kalibr_langchain/callback.py +1064 -0
  46. kalibr_openai_agents/__init__.py +43 -0
  47. kalibr_openai_agents/processor.py +554 -0
  48. kalibr/deployment.py +0 -41
  49. kalibr/packager.py +0 -43
  50. kalibr/runtime_router.py +0 -138
  51. kalibr/schema_generators.py +0 -159
  52. kalibr/validator.py +0 -70
  53. kalibr-1.0.25.data/data/examples/README.md +0 -173
  54. kalibr-1.0.25.data/data/examples/basic_kalibr_example.py +0 -66
  55. kalibr-1.0.25.data/data/examples/enhanced_kalibr_example.py +0 -347
  56. kalibr-1.0.25.dist-info/METADATA +0 -231
  57. kalibr-1.0.25.dist-info/RECORD +0 -19
  58. kalibr-1.0.25.dist-info/entry_points.txt +0 -2
  59. kalibr-1.0.25.dist-info/licenses/LICENSE +0 -11
  60. kalibr-1.0.25.dist-info/top_level.txt +0 -1
  61. {kalibr-1.0.25.dist-info → kalibr-1.1.2a0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,114 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ import typer
7
+ from rich.console import Console
8
+
9
+ console = Console()
10
+ app = typer.Typer(no_args_is_help=True)
11
+
12
+
13
+ # ────────────────────────────────────────────────────────────────────────────────
14
+ # Utility: Ensure Flyctl CLI exists
15
+ # ────────────────────────────────────────────────────────────────────────────────
16
+ def _ensure_flyctl():
17
+ """Check that Flyctl CLI is installed and in PATH."""
18
+ os.environ["PATH"] += os.pathsep + os.path.expanduser("~/.fly/bin")
19
+ fly_path = shutil.which("flyctl") or shutil.which("fly")
20
+ if not fly_path:
21
+ console.print(
22
+ "❌ [red]Flyctl CLI not found. Install it from https://fly.io/docs/hands-on/install-flyctl/[/red]"
23
+ )
24
+ raise typer.Exit(code=1)
25
+ return fly_path
26
+
27
+
28
+ # ────────────────────────────────────────────────────────────────────────────────
29
+ # Main deploy command
30
+ # ────────────────────────────────────────────────────────────────────────────────
31
+ @app.command()
32
+ def deploy(
33
+ file_path: str = typer.Argument(..., help="Path to your Python app file."),
34
+ runtime: str = typer.Option("fly", "--runtime", help="Runtime target (fly, render, or local)."),
35
+ app_name: str = typer.Option("kalibr-app", "--app-name", help="Name of the Fly.io app."),
36
+ ):
37
+ """
38
+ Deploy a Kalibr app to Fly.io or local runtime.
39
+ """
40
+ console.print(
41
+ f"[bold cyan]─────────────────────────── Deploying {file_path} ───────────────────────────[/bold cyan]"
42
+ )
43
+
44
+ # Verify file exists
45
+ app_path = Path(file_path).resolve()
46
+ if not app_path.exists():
47
+ console.print(f"❌ [red]File not found: {app_path}[/red]")
48
+ raise typer.Exit(code=1)
49
+
50
+ # ── Dockerfile ──────────────────────────────────────────────────────────────
51
+ dockerfile_path = app_path.parent / "Dockerfile"
52
+ if not dockerfile_path.exists():
53
+ console.print("🧱 Generating Dockerfile ...")
54
+ dockerfile_path.write_text(
55
+ f"""FROM python:3.11-slim
56
+ WORKDIR /app
57
+ COPY . .
58
+ RUN pip install kalibr fastapi uvicorn
59
+ CMD ["kalibr", "serve", "{app_path.name}"]
60
+ """
61
+ )
62
+ console.print(f"✅ Created [green]{dockerfile_path}[/green]")
63
+
64
+ # ── fly.toml ────────────────────────────────────────────────────────────────
65
+ fly_toml_path = app_path.parent / "fly.toml"
66
+ fly_toml_contents = f"""
67
+ app = "{app_name}"
68
+ primary_region = "iad"
69
+
70
+ [build]
71
+ image = "python:3.11"
72
+
73
+ [env]
74
+ PORT = "8000"
75
+
76
+ [[services]]
77
+ internal_port = 8000
78
+ protocol = "tcp"
79
+
80
+ [[services.ports]]
81
+ port = 80
82
+ handlers = ["http"]
83
+
84
+ [[services.ports]]
85
+ port = 443
86
+ handlers = ["tls", "http"]
87
+ """.strip()
88
+
89
+ fly_toml_path.write_text(fly_toml_contents)
90
+ console.print(
91
+ f"✅ Ensured [green]{fly_toml_path}[/green] includes app name: [bold]{app_name}[/bold]"
92
+ )
93
+
94
+ # ── Deploy ─────────────────────────────────────────────────────────────────
95
+ fly_path = _ensure_flyctl()
96
+ console.print(f"🚀 Deploying to Fly.io using [green]{fly_path}[/green]...")
97
+
98
+ try:
99
+ subprocess.run(["flyctl", "deploy", "--config", str(fly_toml_path)], check=True)
100
+ console.print(f"✅ [bold green]Deployment complete![/bold green]")
101
+ console.print(f"🌍 Visit your app at [underline]https://{app_name}.fly.dev[/underline]")
102
+ except subprocess.CalledProcessError:
103
+ console.print(f"❌ [red]Fly.io deployment failed[/red]")
104
+
105
+
106
+ # ────────────────────────────────────────────────────────────────────────────────
107
+ # Expose CLI entrypoint
108
+ # ────────────────────────────────────────────────────────────────────────────────
109
+ def get_app():
110
+ return app
111
+
112
+
113
+ # Keep compatibility for `from kalibr.cli.deploy_cmd import deploy`
114
+ deploy = deploy
kalibr/cli/main.py ADDED
@@ -0,0 +1,67 @@
1
+ """Main CLI entry point for Kalibr"""
2
+
3
+ import typer
4
+ from kalibr.cli.capsule_cmd import capsule
5
+ from kalibr.cli.deploy_cmd import deploy
6
+ from kalibr.cli.run import run
7
+
8
+ # Import command functions
9
+ from kalibr.cli.serve import serve
10
+ from rich.console import Console
11
+
12
+ app = typer.Typer(
13
+ name="kalibr",
14
+ help="Kalibr SDK - Multi-Model AI Integration Framework",
15
+ add_completion=False,
16
+ )
17
+
18
+ console = Console()
19
+
20
+ # Register commands directly
21
+ app.command(name="serve")(serve)
22
+ app.command(name="deploy")(deploy)
23
+ app.command(name="capsule")(capsule)
24
+ app.command(name="run")(run)
25
+
26
+
27
+ @app.command()
28
+ def version():
29
+ """Show Kalibr SDK version"""
30
+ from kalibr import __version__
31
+
32
+ console.print(f"[bold]Kalibr SDK version:[/bold] {__version__}")
33
+ console.print("Enhanced multi-model AI integration framework")
34
+ console.print("Supports: GPT Actions, Claude MCP, Gemini Extensions, Copilot Plugins")
35
+ console.print("GitHub: https://github.com/devonakelley/kalibr-sdk")
36
+
37
+
38
+ @app.command()
39
+ def package():
40
+ """Create a deployable MCP bundle (code + schemas + metadata)."""
41
+ console.print("[yellow]📦 Package feature coming soon[/yellow]")
42
+ console.print("This will create a deployment bundle with all schemas.")
43
+
44
+
45
+ @app.command()
46
+ def validate():
47
+ """Validate MCP manifest against minimal JSON schema & version hint."""
48
+ console.print("[yellow]✓ Validation feature coming soon[/yellow]")
49
+
50
+
51
+ @app.command()
52
+ def update_schemas():
53
+ """Stub: instruct users to upgrade SDK and regenerate manifests."""
54
+ console.print("[cyan]ℹ️ To update schemas:[/cyan]")
55
+ console.print("1. pip install --upgrade kalibr")
56
+ console.print("2. Restart your kalibr serve command")
57
+ console.print("3. Schemas will be auto-regenerated")
58
+
59
+
60
+ @app.command()
61
+ def status():
62
+ """Check status of a deployed Kalibr app."""
63
+ console.print("[yellow]📊 Status checking coming soon[/yellow]")
64
+
65
+
66
+ if __name__ == "__main__":
67
+ app()
kalibr/cli/run.py ADDED
@@ -0,0 +1,203 @@
1
+ """
2
+ kalibr run - Managed runtime lifecycle for MCP servers
3
+ Phase 3E - CLI Command
4
+ """
5
+
6
+ import atexit
7
+ import os
8
+ import signal
9
+ import subprocess
10
+ import sys
11
+ import time
12
+ import uuid
13
+ from pathlib import Path
14
+ from typing import Optional
15
+
16
+ import httpx
17
+ import typer
18
+ from rich.console import Console
19
+
20
+ console = Console()
21
+
22
+
23
+ def run(
24
+ file_path: str = typer.Argument(..., help="Path to your Python agent file"),
25
+ runtime: str = typer.Option(
26
+ "local", "--runtime", "-r", help="Runtime provider (local, fly.io)"
27
+ ),
28
+ host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to (local only)"),
29
+ port: int = typer.Option(8000, "--port", "-p", help="Port to bind to (local only)"),
30
+ backend_url: Optional[str] = typer.Option(None, "--backend-url", help="Kalibr backend URL"),
31
+ ):
32
+ """
33
+ Run a Kalibr agent with managed runtime lifecycle.
34
+
35
+ Automatically:
36
+ - Registers runtime in backend
37
+ - Injects tracing middleware
38
+ - Emits capsules on completion
39
+ - Updates runtime status
40
+
41
+ Examples:
42
+
43
+ # Run locally
44
+ kalibr run weather.py
45
+
46
+ # Run on Fly.io
47
+ kalibr run weather.py --runtime fly.io
48
+
49
+ # Custom backend
50
+ kalibr run weather.py --backend-url https://api.kalibr.io
51
+ """
52
+ # Validate file exists
53
+ agent_path = Path(file_path).resolve()
54
+ if not agent_path.exists():
55
+ console.print(f"[red]❌ Error: File '{file_path}' not found[/red]")
56
+ raise typer.Exit(1)
57
+
58
+ # Configure backend
59
+ backend = backend_url or os.getenv("KALIBR_BACKEND_URL", "http://localhost:8001")
60
+ api_key = os.getenv("KALIBR_API_KEY")
61
+ if not api_key:
62
+ console.print("[yellow]⚠️ KALIBR_API_KEY not set. Set it for trace authentication.[/yellow]")
63
+ api_key = ""
64
+
65
+ # Generate runtime metadata
66
+ runtime_id = str(uuid.uuid4())
67
+ context_token = str(uuid.uuid4())
68
+ agent_name = agent_path.stem
69
+
70
+ console.print(f"[bold cyan]🚀 Starting Kalibr Runtime[/bold cyan]")
71
+ console.print(f"[dim]Agent:[/dim] {agent_name}")
72
+ console.print(f"[dim]Runtime ID:[/dim] {runtime_id}")
73
+ console.print(f"[dim]Context Token:[/dim] {context_token}")
74
+ console.print(f"[dim]Provider:[/dim] {runtime}")
75
+
76
+ # Register runtime
77
+ try:
78
+ with httpx.Client(timeout=10.0) as client:
79
+ response = client.post(
80
+ f"{backend}/api/runtimes/register",
81
+ json={
82
+ "runtime_id": runtime_id,
83
+ "agent_name": agent_name,
84
+ "runtime_provider": runtime,
85
+ "context_token": context_token,
86
+ },
87
+ )
88
+ response.raise_for_status()
89
+ console.print(f"[green]✓[/green] Runtime registered")
90
+ except Exception as e:
91
+ console.print(f"[yellow]⚠️ Failed to register runtime: {e}[/yellow]")
92
+ console.print("[yellow]Continuing without backend registration...[/yellow]")
93
+
94
+ # Setup environment variables
95
+ env = os.environ.copy()
96
+ env.update(
97
+ {
98
+ "KALIBR_RUNTIME_ID": runtime_id,
99
+ "KALIBR_CONTEXT_TOKEN": context_token,
100
+ "KALIBR_TRACE_ENABLED": "true",
101
+ "KALIBR_COLLECTOR_URL": f"{backend}/api/ingest",
102
+ "KALIBR_API_KEY": api_key,
103
+ "KALIBR_BACKEND_URL": backend,
104
+ }
105
+ )
106
+
107
+ # Store runtime metadata for cleanup
108
+ runtime_metadata = {
109
+ "runtime_id": runtime_id,
110
+ "backend": backend,
111
+ "api_key": api_key,
112
+ "start_time": time.time(),
113
+ "trace_count": 0,
114
+ }
115
+
116
+ # Register cleanup handler
117
+ def cleanup_runtime():
118
+ """Stop runtime and update backend"""
119
+ try:
120
+ duration = time.time() - runtime_metadata["start_time"]
121
+
122
+ with httpx.Client(timeout=10.0) as client:
123
+ client.patch(
124
+ f"{backend}/api/runtimes/{runtime_id}/stop",
125
+ json={
126
+ "total_traces": runtime_metadata.get("trace_count", 0),
127
+ "total_cost_usd": 0.0, # Will be updated by capsule emission
128
+ "total_latency_ms": int(duration * 1000),
129
+ },
130
+ headers={"X-API-Key": api_key},
131
+ )
132
+ console.print(f"[green]✓[/green] Runtime stopped")
133
+ except Exception as e:
134
+ console.print(f"[yellow]⚠️ Failed to stop runtime: {e}[/yellow]")
135
+
136
+ atexit.register(cleanup_runtime)
137
+
138
+ # Launch based on runtime provider
139
+ if runtime == "local":
140
+ # Local subprocess execution
141
+ console.print(f"\n[bold]Starting agent on {host}:{port}[/bold]")
142
+
143
+ try:
144
+ # Use kalibr serve to run the agent
145
+ cmd = [
146
+ sys.executable,
147
+ "-m",
148
+ "kalibr.cli.main",
149
+ "serve",
150
+ str(agent_path),
151
+ "--host",
152
+ host,
153
+ "--port",
154
+ str(port),
155
+ ]
156
+
157
+ process = subprocess.Popen(
158
+ cmd,
159
+ env=env,
160
+ stdout=subprocess.PIPE,
161
+ stderr=subprocess.STDOUT,
162
+ text=True,
163
+ bufsize=1,
164
+ )
165
+
166
+ # Stream output
167
+ console.print("[dim]─" * 80 + "[/dim]")
168
+ for line in process.stdout:
169
+ print(line, end="")
170
+
171
+ process.wait()
172
+
173
+ except KeyboardInterrupt:
174
+ console.print("\n[yellow]⚠️ Received interrupt, stopping runtime...[/yellow]")
175
+ process.terminate()
176
+ process.wait(timeout=5)
177
+ except Exception as e:
178
+ console.print(f"[red]❌ Runtime error: {e}[/red]")
179
+ raise typer.Exit(1)
180
+
181
+ elif runtime == "fly.io":
182
+ # Fly.io deployment
183
+ console.print(f"\n[bold]Deploying to Fly.io[/bold]")
184
+
185
+ # Use existing deploy command
186
+ from kalibr.cli.deploy_cmd import deploy
187
+
188
+ deploy(
189
+ file_path=str(agent_path),
190
+ runtime="fly",
191
+ app_name=f"kalibr-{agent_name}",
192
+ )
193
+
194
+ console.print(f"[green]✓[/green] Deployed to Fly.io")
195
+
196
+ else:
197
+ console.print(f"[red]❌ Unsupported runtime: {runtime}[/red]")
198
+ console.print("[yellow]Supported runtimes: local, fly.io[/yellow]")
199
+ raise typer.Exit(1)
200
+
201
+ # Cleanup
202
+ cleanup_runtime()
203
+ console.print("\n[bold green]✓ Runtime completed[/bold green]")
kalibr/cli/serve.py ADDED
@@ -0,0 +1,59 @@
1
+ """Serve command for local development"""
2
+
3
+ import importlib.util
4
+ from pathlib import Path
5
+
6
+ import typer
7
+ from rich.console import Console
8
+
9
+ console = Console()
10
+
11
+
12
+ def serve(
13
+ file_path: str = typer.Argument(..., help="Path to your Kalibr app (e.g. app.py)"),
14
+ host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
15
+ port: int = typer.Option(8000, "--port", "-p", help="Port to bind to"),
16
+ ):
17
+ """Serve a Kalibr-powered API locally."""
18
+ path = Path(file_path)
19
+
20
+ if not path.exists():
21
+ console.print(f"[red]❌ Error: File '{file_path}' not found[/red]")
22
+ raise typer.Exit(1)
23
+
24
+ try:
25
+ # Load the module
26
+ spec = importlib.util.spec_from_file_location("user_app", path)
27
+ module = importlib.util.module_from_spec(spec)
28
+ spec.loader.exec_module(module)
29
+
30
+ # Find Kalibr app instance
31
+ app_instance = None
32
+ for attr_name in dir(module):
33
+ attr = getattr(module, attr_name)
34
+ if hasattr(attr, "app") and hasattr(attr.app, "openapi"):
35
+ app_instance = attr
36
+ break
37
+
38
+ if not app_instance:
39
+ console.print(f"[red]❌ Error loading {file_path}: No Kalibr app found[/red]")
40
+ raise typer.Exit(1)
41
+
42
+ # Display startup info
43
+ console.print(f"[bold green]🚀 Starting Kalibr server from {path.name}[/bold green]")
44
+ display_host = "localhost" if host == "0.0.0.0" else host
45
+ console.print(f"📍 GPT (OpenAPI): http://{display_host}:{port}/openapi.json")
46
+ console.print(f"📍 Claude (MCP): http://{display_host}:{port}/mcp.json")
47
+ console.print(f"📍 Swagger UI: http://{display_host}:{port}/docs")
48
+ console.print(
49
+ f"🔌 Actions registered: {[action['name'] for action in app_instance.actions]}"
50
+ )
51
+
52
+ # Run the server
53
+ import uvicorn
54
+
55
+ uvicorn.run(app_instance.app, host=host, port=port)
56
+
57
+ except Exception as e:
58
+ console.print(f"[red]❌ Error loading {file_path}: {str(e)}[/red]")
59
+ raise typer.Exit(1)