kalibr 1.0.28__py3-none-any.whl → 1.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.
- kalibr/__init__.py +170 -3
- kalibr/__main__.py +3 -203
- kalibr/capsule_middleware.py +108 -0
- kalibr/cli/__init__.py +5 -0
- kalibr/cli/capsule_cmd.py +174 -0
- kalibr/cli/deploy_cmd.py +114 -0
- kalibr/cli/main.py +67 -0
- kalibr/cli/run.py +200 -0
- kalibr/cli/serve.py +59 -0
- kalibr/client.py +293 -0
- kalibr/collector.py +173 -0
- kalibr/context.py +132 -0
- kalibr/cost_adapter.py +222 -0
- kalibr/decorators.py +140 -0
- kalibr/instrumentation/__init__.py +13 -0
- kalibr/instrumentation/anthropic_instr.py +282 -0
- kalibr/instrumentation/base.py +108 -0
- kalibr/instrumentation/google_instr.py +281 -0
- kalibr/instrumentation/openai_instr.py +265 -0
- kalibr/instrumentation/registry.py +153 -0
- kalibr/kalibr.py +144 -230
- kalibr/kalibr_app.py +53 -314
- kalibr/middleware/__init__.py +5 -0
- kalibr/middleware/auto_tracer.py +356 -0
- kalibr/models.py +41 -0
- kalibr/redaction.py +44 -0
- kalibr/schemas.py +116 -0
- kalibr/simple_tracer.py +255 -0
- kalibr/tokens.py +52 -0
- kalibr/trace_capsule.py +296 -0
- kalibr/trace_models.py +201 -0
- kalibr/tracer.py +354 -0
- kalibr/types.py +25 -93
- kalibr/utils.py +198 -0
- kalibr-1.1.0.dist-info/METADATA +97 -0
- kalibr-1.1.0.dist-info/RECORD +40 -0
- kalibr-1.1.0.dist-info/entry_points.txt +2 -0
- kalibr-1.1.0.dist-info/licenses/LICENSE +21 -0
- kalibr/deployment.py +0 -41
- kalibr/packager.py +0 -43
- kalibr/runtime_router.py +0 -138
- kalibr/schema_generators.py +0 -159
- kalibr/validator.py +0 -70
- kalibr-1.0.28.data/data/examples/README.md +0 -173
- kalibr-1.0.28.data/data/examples/basic_kalibr_example.py +0 -66
- kalibr-1.0.28.data/data/examples/enhanced_kalibr_example.py +0 -347
- kalibr-1.0.28.dist-info/METADATA +0 -175
- kalibr-1.0.28.dist-info/RECORD +0 -19
- kalibr-1.0.28.dist-info/entry_points.txt +0 -2
- kalibr-1.0.28.dist-info/licenses/LICENSE +0 -11
- {kalibr-1.0.28.dist-info → kalibr-1.1.0.dist-info}/WHEEL +0 -0
- {kalibr-1.0.28.dist-info → kalibr-1.1.0.dist-info}/top_level.txt +0 -0
kalibr/utils.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Utility functions for Kalibr SDK.
|
|
2
|
+
|
|
3
|
+
Helper functions for:
|
|
4
|
+
- Environment configuration
|
|
5
|
+
- Event validation
|
|
6
|
+
- JSON serialization
|
|
7
|
+
- Logging
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
from datetime import date, datetime, time
|
|
13
|
+
from typing import Any, Dict, Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def load_config_from_env() -> Dict[str, str]:
|
|
17
|
+
"""Load Kalibr configuration from environment variables.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Configuration dict with keys:
|
|
21
|
+
- clickhouse_url: ClickHouse connection URL
|
|
22
|
+
- project_name: Project/service name
|
|
23
|
+
- auth_token: API authentication token
|
|
24
|
+
- environment: Environment (prod/staging/dev)
|
|
25
|
+
- tenant_id: Tenant identifier
|
|
26
|
+
- workflow_id: Workflow identifier
|
|
27
|
+
- sandbox_id: Sandbox/VM identifier
|
|
28
|
+
- runtime_env: Runtime environment
|
|
29
|
+
- collector_url: Collector endpoint URL
|
|
30
|
+
"""
|
|
31
|
+
config = {
|
|
32
|
+
"clickhouse_url": os.getenv("CLICKHOUSE_URL", "http://localhost:8123"),
|
|
33
|
+
"project_name": os.getenv("KALIBR_PROJECT_NAME", "kalibr-app"),
|
|
34
|
+
"auth_token": os.getenv("KALIBR_AUTH_TOKEN", ""),
|
|
35
|
+
"api_key": os.getenv("KALIBR_API_KEY", ""),
|
|
36
|
+
"environment": os.getenv("KALIBR_ENVIRONMENT", "prod"),
|
|
37
|
+
"tenant_id": os.getenv("KALIBR_TENANT_ID", "default"),
|
|
38
|
+
"workflow_id": os.getenv("KALIBR_WORKFLOW_ID", "default-workflow"),
|
|
39
|
+
"sandbox_id": os.getenv("SANDBOX_ID", "local"),
|
|
40
|
+
"runtime_env": os.getenv("RUNTIME_ENV", "local"),
|
|
41
|
+
"api_endpoint": os.getenv("KALIBR_API_ENDPOINT", "http://localhost:8001/api/v1/traces"),
|
|
42
|
+
"collector_url": os.getenv("KALIBR_COLLECTOR_URL", "http://localhost:8080/api/ingest"),
|
|
43
|
+
}
|
|
44
|
+
return config
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def validate_event(event: Dict[str, Any]) -> bool:
|
|
48
|
+
"""Validate that event has required fields.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
event: Event dictionary
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if valid, False otherwise
|
|
55
|
+
"""
|
|
56
|
+
required_fields = [
|
|
57
|
+
"trace_id",
|
|
58
|
+
"span_id",
|
|
59
|
+
"timestamp",
|
|
60
|
+
"service",
|
|
61
|
+
"vendor",
|
|
62
|
+
"operation",
|
|
63
|
+
"latency_ms",
|
|
64
|
+
"status",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
for field in required_fields:
|
|
68
|
+
if field not in event:
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def serialize_event(event: Dict[str, Any]) -> str:
|
|
75
|
+
"""Serialize event to JSON string.
|
|
76
|
+
|
|
77
|
+
Handles special types like datetime, date, time.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
event: Event dictionary
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
JSON string
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def default_handler(obj):
|
|
87
|
+
"""Handle non-serializable types."""
|
|
88
|
+
if isinstance(obj, (datetime, date, time)):
|
|
89
|
+
return obj.isoformat()
|
|
90
|
+
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
|
91
|
+
|
|
92
|
+
return json.dumps(event, default=default_handler)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def safe_get_nested(data: Dict, keys: list, default: Any = None) -> Any:
|
|
96
|
+
"""Safely get nested dictionary value.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
data: Dictionary to query
|
|
100
|
+
keys: List of nested keys
|
|
101
|
+
default: Default value if key path doesn't exist
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Value at nested key path, or default
|
|
105
|
+
|
|
106
|
+
Example:
|
|
107
|
+
>>> data = {"a": {"b": {"c": 123}}}
|
|
108
|
+
>>> safe_get_nested(data, ["a", "b", "c"])
|
|
109
|
+
123
|
|
110
|
+
>>> safe_get_nested(data, ["a", "x", "y"], default=0)
|
|
111
|
+
0
|
|
112
|
+
"""
|
|
113
|
+
current = data
|
|
114
|
+
for key in keys:
|
|
115
|
+
if isinstance(current, dict) and key in current:
|
|
116
|
+
current = current[key]
|
|
117
|
+
else:
|
|
118
|
+
return default
|
|
119
|
+
return current
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def truncate_string(s: str, max_length: int = 1000) -> str:
|
|
123
|
+
"""Truncate string to max length with ellipsis.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
s: String to truncate
|
|
127
|
+
max_length: Maximum length
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Truncated string
|
|
131
|
+
"""
|
|
132
|
+
if len(s) <= max_length:
|
|
133
|
+
return s
|
|
134
|
+
return s[: max_length - 3] + "..."
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def format_cost(cost_usd: float) -> str:
|
|
138
|
+
"""Format cost in USD for display.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
cost_usd: Cost in USD
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Formatted string (e.g., "$0.0123")
|
|
145
|
+
"""
|
|
146
|
+
if cost_usd < 0.0001:
|
|
147
|
+
return "$0.0000"
|
|
148
|
+
elif cost_usd < 0.01:
|
|
149
|
+
return f"${cost_usd:.4f}"
|
|
150
|
+
elif cost_usd < 1.0:
|
|
151
|
+
return f"${cost_usd:.3f}"
|
|
152
|
+
else:
|
|
153
|
+
return f"${cost_usd:.2f}"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def format_latency(latency_ms: int) -> str:
|
|
157
|
+
"""Format latency for display.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
latency_ms: Latency in milliseconds
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Formatted string (e.g., "420ms" or "2.3s")
|
|
164
|
+
"""
|
|
165
|
+
if latency_ms < 1000:
|
|
166
|
+
return f"{latency_ms}ms"
|
|
167
|
+
else:
|
|
168
|
+
seconds = latency_ms / 1000
|
|
169
|
+
return f"{seconds:.1f}s"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def get_log_prefix() -> str:
|
|
173
|
+
"""Get log prefix for Kalibr SDK messages.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Log prefix string
|
|
177
|
+
"""
|
|
178
|
+
return "[Kalibr SDK]"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def log_info(message: str):
|
|
182
|
+
"""Log info message."""
|
|
183
|
+
print(f"{get_log_prefix()} ℹ️ {message}")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def log_warning(message: str):
|
|
187
|
+
"""Log warning message."""
|
|
188
|
+
print(f"{get_log_prefix()} ⚠️ {message}")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def log_error(message: str):
|
|
192
|
+
"""Log error message."""
|
|
193
|
+
print(f"{get_log_prefix()} ❌ {message}")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def log_success(message: str):
|
|
197
|
+
"""Log success message."""
|
|
198
|
+
print(f"{get_log_prefix()} ✅ {message}")
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kalibr
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Unified LLM Observability & Multi-Model AI Integration Framework - Deploy to GPT, Claude, Gemini, Copilot with full telemetry
|
|
5
|
+
Author-email: Kalibr Team <team@kalibr.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/devonakelley/kalibr-sdk
|
|
8
|
+
Project-URL: Documentation, https://github.com/devonakelley/kalibr-sdk#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/devonakelley/kalibr-sdk
|
|
10
|
+
Project-URL: Issues, https://github.com/devonakelley/kalibr-sdk/issues
|
|
11
|
+
Keywords: ai,mcp,gpt,claude,gemini,copilot,openai,anthropic,google,microsoft,observability,telemetry,tracing,llm,schema-generation,api,multi-model
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: tiktoken>=0.8.0
|
|
28
|
+
Requires-Dist: fastapi>=0.110.1
|
|
29
|
+
Requires-Dist: uvicorn>=0.25.0
|
|
30
|
+
Requires-Dist: pydantic>=2.6.4
|
|
31
|
+
Requires-Dist: typer>=0.9.0
|
|
32
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
33
|
+
Requires-Dist: rich>=10.0.0
|
|
34
|
+
Requires-Dist: requests>=2.31.0
|
|
35
|
+
Requires-Dist: opentelemetry-api>=1.20.0
|
|
36
|
+
Requires-Dist: opentelemetry-sdk>=1.20.0
|
|
37
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.20.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
40
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# Kalibr Python SDK
|
|
45
|
+
|
|
46
|
+
Production-grade observability for LLM applications.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
```bash
|
|
50
|
+
pip install kalibr
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quickstart
|
|
54
|
+
```python
|
|
55
|
+
from kalibr import trace
|
|
56
|
+
import openai
|
|
57
|
+
|
|
58
|
+
@trace(api_key="your-kalibr-api-key")
|
|
59
|
+
def my_agent():
|
|
60
|
+
response = openai.chat.completions.create(
|
|
61
|
+
model="gpt-4",
|
|
62
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
63
|
+
)
|
|
64
|
+
return response
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Features
|
|
68
|
+
|
|
69
|
+
- ✅ Zero-code instrumentation for OpenAI, Anthropic, Google AI
|
|
70
|
+
- ✅ Automatic parent-child trace relationships
|
|
71
|
+
- ✅ Real-time cost tracking
|
|
72
|
+
- ✅ Token usage monitoring
|
|
73
|
+
- ✅ Performance metrics
|
|
74
|
+
|
|
75
|
+
## CLI Tools
|
|
76
|
+
```bash
|
|
77
|
+
# Run your app locally
|
|
78
|
+
kalibr serve myapp.py
|
|
79
|
+
|
|
80
|
+
# Deploy to Fly.io
|
|
81
|
+
kalibr deploy myapp.py
|
|
82
|
+
|
|
83
|
+
# Fetch trace data
|
|
84
|
+
kalibr capsule <trace-id>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
88
|
+
|
|
89
|
+
See `examples/` directory for complete examples.
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
Full docs at https://docs.kalibr.systems
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
kalibr/__init__.py,sha256=fNOjaXe1sJ9fFgqTsO6GQY4Yf4TCr_T659lxr3FB35Q,5906
|
|
2
|
+
kalibr/__main__.py,sha256=jO96I4pqinwHg7ONRvNVKbySBh5pSIhOAiNrgSQrNlY,110
|
|
3
|
+
kalibr/capsule_middleware.py,sha256=pXG_wORgCqo3wHjtkn_zY4doLyiDmTwJtB7XiZNnbPk,3163
|
|
4
|
+
kalibr/client.py,sha256=t6_vZn_juHrsCg-7QFrkl-pzlqV7ZesQLCh8fnF808Q,9727
|
|
5
|
+
kalibr/collector.py,sha256=rtTKQLe6NkDSblBIfFooQ-ESFcP0Q1HUp4Bcqqg8JFo,5818
|
|
6
|
+
kalibr/context.py,sha256=hBxWXZx0gcmeGqDMS1rstke_DmrujoRBIsfrG26WKUY,3755
|
|
7
|
+
kalibr/cost_adapter.py,sha256=NerJ7ywaJjBn97gVFr7qKX7318e3Kmy2qqeNlGl9nPE,6439
|
|
8
|
+
kalibr/decorators.py,sha256=m-XBXxWMDVrzaNsljACiGmeGhgiHj_MqSfj6OGK3L5I,4380
|
|
9
|
+
kalibr/kalibr.py,sha256=cNXC3W_TX5SvGsy1lRopkwFqsHOpyd1kkVjEMOz1Yr4,6084
|
|
10
|
+
kalibr/kalibr_app.py,sha256=ItZwEh0FZPx9_BE-zPQajC2yxI2y9IHYwJD0k9tbHvY,2773
|
|
11
|
+
kalibr/models.py,sha256=HwD_-iysZMSnCzMQYO1Qcf0aeXySupY7yJeBwl_dLS0,1024
|
|
12
|
+
kalibr/redaction.py,sha256=XibxX4Lv1Ci0opE6Tb5ZI2GLbO0a8E9U66MAg60llnc,1139
|
|
13
|
+
kalibr/schemas.py,sha256=XLZNLkXca6jbj9AF6gDIyGVnIcr1SVOsNYaKvW-wbgE,3669
|
|
14
|
+
kalibr/simple_tracer.py,sha256=LPc56PfGZQT8YXS2Y1eAiOu0CsOEi4-UmWVlq03cZpM,9665
|
|
15
|
+
kalibr/tokens.py,sha256=istjgaxi9S4dMddjuGtoQaTnZYcWLCqdnxRjV86yNXA,1297
|
|
16
|
+
kalibr/trace_capsule.py,sha256=CPMUz5D-fVfao-MozNtSDbgOQKdDAJxTN5KQL6w2Xp8,10154
|
|
17
|
+
kalibr/trace_models.py,sha256=9o7VJQk3gCrvdfXPrNh3Ptkq5sRgA9_qrLLE3jNkSBg,7304
|
|
18
|
+
kalibr/tracer.py,sha256=jwWBpZbGXn6fEv4pw25BLFCH-22QUbyzofPWp1Iwdkk,11911
|
|
19
|
+
kalibr/types.py,sha256=cna4-akpdwfHXfOJCtVIq5lO_jaoG2Am3BRrXi0Vo34,895
|
|
20
|
+
kalibr/utils.py,sha256=IbAgw-Bwxdy9Kc0fm2yIDt2RxWU0gRCzffuz3GRUSnE,5042
|
|
21
|
+
kalibr/cli/__init__.py,sha256=FmRGaDMhM9DhrKg1ONkF0emIrJcjFWjlFBl_oenvpsk,77
|
|
22
|
+
kalibr/cli/capsule_cmd.py,sha256=htxUtRpos_dLs-LnJm6WeGtLYCCc1h7H3WXz7i6DVWc,6082
|
|
23
|
+
kalibr/cli/deploy_cmd.py,sha256=kV4uqCN2IdQev1vPBY5qqIHsEhjGBZ7y_rLx8RGAL_4,5178
|
|
24
|
+
kalibr/cli/main.py,sha256=Ob0Vpg8KPnHluP_23pgbEZpDb_iKnjoN7mW52-2Qr44,1939
|
|
25
|
+
kalibr/cli/run.py,sha256=DrH0Pjc_gcrvvi0cSiQCf_WJNtOh3xd1EWncuNloNCM,6293
|
|
26
|
+
kalibr/cli/serve.py,sha256=71Xha35qrBNkcQxuUkwC-ixbOriHGUIEgxl7C_qERQo,2085
|
|
27
|
+
kalibr/instrumentation/__init__.py,sha256=YnUJ4gUH8WNxdVv5t1amn0l2WUULJG2MuQIL2ZZhn04,354
|
|
28
|
+
kalibr/instrumentation/anthropic_instr.py,sha256=ozXHr8BPMafIbvgaxunskQi9YX5_Gpoiekye77oRc2E,10058
|
|
29
|
+
kalibr/instrumentation/base.py,sha256=eMFBTIQXtG2bZD5st6vzN72ooeHCANZ3SapYzrdijgk,3109
|
|
30
|
+
kalibr/instrumentation/google_instr.py,sha256=f2um7MB2QCT2u9CFV4-vKke-8M0dSXSpZHTcbdxMZyI,10476
|
|
31
|
+
kalibr/instrumentation/openai_instr.py,sha256=UU0Pi1Gq1FqgetYWDacQhNFdjemuPrc0hRTKd-LIDHI,9250
|
|
32
|
+
kalibr/instrumentation/registry.py,sha256=sfQnXhbPOI5LVon2kFhe8KcXQwWmuKW1XUe50B2AaBc,4749
|
|
33
|
+
kalibr/middleware/__init__.py,sha256=qyDUn_irAX67MS-IkuDVxg4RmFnJHDf_BfIT3qfGoBI,115
|
|
34
|
+
kalibr/middleware/auto_tracer.py,sha256=oevrhWBAM4el_F5jFc28ZBFLEklx7XGd40dNFe5VQ8Q,13032
|
|
35
|
+
kalibr-1.1.0.dist-info/licenses/LICENSE,sha256=BYlEPoDdYD3iHuAjt2JYGoxDYQaI1gxab2pR4acoz04,1063
|
|
36
|
+
kalibr-1.1.0.dist-info/METADATA,sha256=U1E1RTfkpgHZSOVSpfP6Sw-GxMf4ilmczPK6VvuZfW4,2841
|
|
37
|
+
kalibr-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
38
|
+
kalibr-1.1.0.dist-info/entry_points.txt,sha256=Kojlc6WRX8V1qS9lOMdDPZpTUVHCtzGtHqXusErgmLY,47
|
|
39
|
+
kalibr-1.1.0.dist-info/top_level.txt,sha256=OkloC5_IfpE4-QwI30aLIYbFZk_-ChABWF7aBGddy28,7
|
|
40
|
+
kalibr-1.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kalibr
|
|
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.
|
kalibr/deployment.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Kalibr Deployment
|
|
3
|
-
-----------------
|
|
4
|
-
Thin wrapper that forwards to the runtime router.
|
|
5
|
-
Keeps a simple API surface for backwards-compat commands.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
from dataclasses import dataclass, field
|
|
10
|
-
from typing import Dict, Any
|
|
11
|
-
from kalibr.runtime_router import deploy as router_deploy
|
|
12
|
-
|
|
13
|
-
@dataclass
|
|
14
|
-
class DeploymentConfig:
|
|
15
|
-
app_name: str
|
|
16
|
-
memory_mb: int = 512
|
|
17
|
-
timeout_seconds: int = 30
|
|
18
|
-
environment_vars: Dict[str, str] = field(default_factory=dict)
|
|
19
|
-
|
|
20
|
-
def deploy_app(file_path: str, config: DeploymentConfig, platform: str = "local") -> Dict[str, Any]:
|
|
21
|
-
# Map older "platform" to runtime names used by router
|
|
22
|
-
runtime = {
|
|
23
|
-
"local": "local",
|
|
24
|
-
"fly": "fly",
|
|
25
|
-
"aws-lambda": "local", # not supported; punt to local
|
|
26
|
-
"render": "render",
|
|
27
|
-
}.get(platform, platform)
|
|
28
|
-
|
|
29
|
-
result = router_deploy(runtime=runtime, app_name=config.app_name, app_file=file_path)
|
|
30
|
-
if result.get("status") in ("success", "started"):
|
|
31
|
-
eps = result.get("endpoints", {})
|
|
32
|
-
return {
|
|
33
|
-
"status": "success",
|
|
34
|
-
"endpoints": {
|
|
35
|
-
"root": eps.get("root", ""),
|
|
36
|
-
"mcp": eps.get("mcp", ""),
|
|
37
|
-
"openapi": eps.get("openapi", ""),
|
|
38
|
-
"health": eps.get("health", ""),
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return {"status": "error", "error": "unknown deploy outcome", "raw": result}
|
kalibr/packager.py
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Packager
|
|
3
|
-
--------
|
|
4
|
-
Create a deployable MCP bundle (code + manifests + metadata).
|
|
5
|
-
This does not host anything; it prepares artifacts for any runtime.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
import shutil
|
|
11
|
-
import json
|
|
12
|
-
import tempfile
|
|
13
|
-
from typing import Dict, Any, Optional
|
|
14
|
-
|
|
15
|
-
DEFAULT_BUNDLE = "kalibr_bundle.zip"
|
|
16
|
-
|
|
17
|
-
def package_app(app_dir: str = ".", output: str = DEFAULT_BUNDLE, models_supported: Optional[list] = None, kalibr_version: str = "unknown") -> str:
|
|
18
|
-
app_dir = Path(app_dir).resolve()
|
|
19
|
-
out_path = Path(output).resolve()
|
|
20
|
-
|
|
21
|
-
# Assemble temp dir with metadata
|
|
22
|
-
with tempfile.TemporaryDirectory() as tmp:
|
|
23
|
-
tmpdir = Path(tmp)
|
|
24
|
-
# Copy source tree
|
|
25
|
-
for item in app_dir.iterdir():
|
|
26
|
-
if item.name == out_path.name:
|
|
27
|
-
continue
|
|
28
|
-
dest = tmpdir / item.name
|
|
29
|
-
if item.is_dir():
|
|
30
|
-
shutil.copytree(item, dest)
|
|
31
|
-
else:
|
|
32
|
-
shutil.copy2(item, dest)
|
|
33
|
-
|
|
34
|
-
# Write bundle metadata
|
|
35
|
-
(tmpdir / "kalibr_manifest.json").write_text(json.dumps({
|
|
36
|
-
"kalibr_version": kalibr_version,
|
|
37
|
-
"models_supported": models_supported or ["mcp", "gpt-actions", "gemini", "copilot"],
|
|
38
|
-
}, indent=2))
|
|
39
|
-
|
|
40
|
-
# Zip
|
|
41
|
-
shutil.make_archive(out_path.with_suffix(""), "zip", tmpdir)
|
|
42
|
-
|
|
43
|
-
return str(out_path)
|
kalibr/runtime_router.py
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Runtime Router
|
|
3
|
-
--------------
|
|
4
|
-
Abstraction over deployment targets without hosting them ourselves.
|
|
5
|
-
Generates minimal configs and invokes the target's CLI/API where possible.
|
|
6
|
-
|
|
7
|
-
Supported:
|
|
8
|
-
- local (uvicorn)
|
|
9
|
-
- fly (fly.io) -> generates fly.toml and basic Dockerfile
|
|
10
|
-
- render -> generates render.yaml
|
|
11
|
-
|
|
12
|
-
Note: We do not ship vendor SDKs. We shell out to their CLIs if present.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
import subprocess
|
|
18
|
-
import shutil
|
|
19
|
-
import os
|
|
20
|
-
import json
|
|
21
|
-
from typing import Dict, Any, Optional, Tuple
|
|
22
|
-
|
|
23
|
-
HERE = Path(__file__).parent
|
|
24
|
-
|
|
25
|
-
def which(cmd: str) -> Optional[str]:
|
|
26
|
-
return shutil.which(cmd)
|
|
27
|
-
|
|
28
|
-
def ensure_file(path: Path, content: str):
|
|
29
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
30
|
-
if not path.exists():
|
|
31
|
-
path.write_text(content)
|
|
32
|
-
|
|
33
|
-
def generate_fly_files(app_name: str) -> Tuple[Path, Path]:
|
|
34
|
-
fly_toml = Path("fly.toml")
|
|
35
|
-
dockerfile = Path("Dockerfile")
|
|
36
|
-
ensure_file(fly_toml, f"""# fly.toml
|
|
37
|
-
app = "{app_name}"
|
|
38
|
-
primary_region = "iad"
|
|
39
|
-
|
|
40
|
-
[build]
|
|
41
|
-
dockerfile = "Dockerfile"
|
|
42
|
-
|
|
43
|
-
[http_service]
|
|
44
|
-
internal_port = 8000
|
|
45
|
-
force_https = true
|
|
46
|
-
auto_stop_machines = "off"
|
|
47
|
-
auto_start_machines = true
|
|
48
|
-
min_machines_running = 1
|
|
49
|
-
""")
|
|
50
|
-
ensure_file(dockerfile, """# Dockerfile
|
|
51
|
-
FROM python:3.11-slim
|
|
52
|
-
WORKDIR /app
|
|
53
|
-
COPY . /app
|
|
54
|
-
RUN pip install --no-cache-dir -U pip && \
|
|
55
|
-
pip install --no-cache-dir fastapi uvicorn typer pydantic requests
|
|
56
|
-
EXPOSE 8000
|
|
57
|
-
CMD ["python", "-m", "kalibr", "serve", "kalibr_app.py", "--host", "0.0.0.0", "--port", "8000", "--base-url", "http://0.0.0.0:8000"]
|
|
58
|
-
""")
|
|
59
|
-
return fly_toml, dockerfile
|
|
60
|
-
|
|
61
|
-
def generate_render_file(service_name: str) -> Path:
|
|
62
|
-
render_yaml = Path("render.yaml")
|
|
63
|
-
ensure_file(render_yaml, f"""# render.yaml
|
|
64
|
-
services:
|
|
65
|
-
- type: web
|
|
66
|
-
name: {service_name}
|
|
67
|
-
env: docker
|
|
68
|
-
plan: free
|
|
69
|
-
dockerfilePath: ./Dockerfile
|
|
70
|
-
autoDeploy: true
|
|
71
|
-
""")
|
|
72
|
-
return render_yaml
|
|
73
|
-
|
|
74
|
-
def deploy_local(app_file: str, host: str = "0.0.0.0", port: int = 8000, base_url: str = "http://localhost:8000") -> Dict[str, Any]:
|
|
75
|
-
# Run uvicorn inline (non-blocking not handled here - CLI uses this to print guidance)
|
|
76
|
-
cmd = ["python", "-m", "kalibr", "serve", app_file, "--host", host, "--port", str(port), "--base-url", base_url]
|
|
77
|
-
print("▶︎", " ".join(cmd))
|
|
78
|
-
subprocess.run(cmd, check=False)
|
|
79
|
-
return {
|
|
80
|
-
"status": "started",
|
|
81
|
-
"endpoints": {
|
|
82
|
-
"root": f"{base_url}/",
|
|
83
|
-
"mcp": f"{base_url}/mcp.json",
|
|
84
|
-
"openapi": f"{base_url}/openapi.json",
|
|
85
|
-
"health": f"{base_url}/health"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
def deploy_fly(app_name: str) -> Dict[str, Any]:
|
|
90
|
-
if not which("flyctl"):
|
|
91
|
-
raise RuntimeError("flyctl is not installed. See https://fly.io/docs/flyctl/install/")
|
|
92
|
-
# Ensure files exist
|
|
93
|
-
generate_fly_files(app_name)
|
|
94
|
-
# Launch or deploy
|
|
95
|
-
print("▶︎ flyctl apps list")
|
|
96
|
-
subprocess.run(["flyctl", "apps", "list"], check=False)
|
|
97
|
-
print(f"▶︎ flyctl deploy --app {app_name}")
|
|
98
|
-
subprocess.run(["flyctl", "deploy", "--app", app_name], check=False)
|
|
99
|
-
url = f"https://{app_name}.fly.dev"
|
|
100
|
-
return {
|
|
101
|
-
"status": "success",
|
|
102
|
-
"endpoints": {
|
|
103
|
-
"root": f"{url}/",
|
|
104
|
-
"mcp": f"{url}/mcp.json",
|
|
105
|
-
"openapi": f"{url}/openapi.json",
|
|
106
|
-
"health": f"{url}/health"
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
def deploy_render(service_name: str) -> Dict[str, Any]:
|
|
111
|
-
# We just generate render.yaml and Dockerfile. User connects repo in Render UI.
|
|
112
|
-
generate_render_file(service_name)
|
|
113
|
-
ensure_file(Path("Dockerfile"), """# Dockerfile for Render
|
|
114
|
-
FROM python:3.11-slim
|
|
115
|
-
WORKDIR /app
|
|
116
|
-
COPY . /app
|
|
117
|
-
RUN pip install --no-cache-dir -U pip && \
|
|
118
|
-
pip install --no-cache-dir fastapi uvicorn typer pydantic requests
|
|
119
|
-
EXPOSE 8000
|
|
120
|
-
CMD ["python", "-m", "kalibr", "serve", "kalibr_app.py", "--host", "0.0.0.0", "--port", "8000", "--base-url", "https://$RENDER_EXTERNAL_URL"]
|
|
121
|
-
""")
|
|
122
|
-
print("📄 Generated render.yaml and Dockerfile. Connect your repo in Render.com and auto-deploy.")
|
|
123
|
-
return {
|
|
124
|
-
"status": "success",
|
|
125
|
-
"endpoints": {},
|
|
126
|
-
"note": "Connect this repository to Render; it will build from render.yaml."
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
def deploy(runtime: str, app_name: str, app_file: str, host: str = "0.0.0.0", port: int = 8000, base_url: str = "http://localhost:8000") -> Dict[str, Any]:
|
|
130
|
-
runtime = runtime.lower()
|
|
131
|
-
if runtime in ("local", "dev"):
|
|
132
|
-
return deploy_local(app_file, host, port, base_url)
|
|
133
|
-
if runtime in ("fly", "flyio"):
|
|
134
|
-
return deploy_fly(app_name)
|
|
135
|
-
if runtime == "render":
|
|
136
|
-
return deploy_render(app_name)
|
|
137
|
-
raise ValueError(f"Unknown runtime: {runtime}")
|
|
138
|
-
|