kalibr 1.0.28__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.28.data/data/examples/README.md +0 -173
  54. kalibr-1.0.28.data/data/examples/basic_kalibr_example.py +0 -66
  55. kalibr-1.0.28.data/data/examples/enhanced_kalibr_example.py +0 -347
  56. kalibr-1.0.28.dist-info/METADATA +0 -175
  57. kalibr-1.0.28.dist-info/RECORD +0 -19
  58. kalibr-1.0.28.dist-info/entry_points.txt +0 -2
  59. kalibr-1.0.28.dist-info/licenses/LICENSE +0 -11
  60. kalibr-1.0.28.dist-info/top_level.txt +0 -1
  61. {kalibr-1.0.28.dist-info → kalibr-1.1.2a0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,43 @@
1
+ """Kalibr OpenAI Agents SDK Integration - Observability for OpenAI Agents.
2
+
3
+ This package provides a TracingProcessor that integrates OpenAI's Agents SDK
4
+ with Kalibr's observability platform, capturing:
5
+ - Agent executions and handoffs
6
+ - LLM generation spans with tokens and model info
7
+ - Function/tool invocations
8
+ - Guardrail checks
9
+ - Full trace hierarchy
10
+
11
+ Usage:
12
+ from kalibr_openai_agents import KalibrTracingProcessor, setup_kalibr_tracing
13
+ from agents import Agent, Runner
14
+
15
+ # Option 1: Quick setup (adds to existing processors)
16
+ setup_kalibr_tracing(tenant_id="my-tenant")
17
+
18
+ # Option 2: Manual setup with more control
19
+ from agents.tracing import add_trace_processor
20
+ processor = KalibrTracingProcessor(tenant_id="my-tenant")
21
+ add_trace_processor(processor)
22
+
23
+ # Use OpenAI Agents normally
24
+ agent = Agent(name="Assistant", instructions="You are helpful.")
25
+ result = Runner.run_sync(agent, "Hello!")
26
+
27
+ Environment Variables:
28
+ KALIBR_API_KEY: API key for authentication
29
+ KALIBR_ENDPOINT: Backend endpoint URL
30
+ KALIBR_TENANT_ID: Tenant identifier
31
+ KALIBR_ENVIRONMENT: Environment (prod/staging/dev)
32
+ KALIBR_SERVICE: Service name
33
+ """
34
+
35
+ __version__ = "0.1.0"
36
+
37
+ from .processor import KalibrTracingProcessor, setup_kalibr_tracing
38
+
39
+ __all__ = [
40
+ "KalibrTracingProcessor",
41
+ "setup_kalibr_tracing",
42
+ "__version__",
43
+ ]
@@ -0,0 +1,554 @@
1
+ """Kalibr TracingProcessor for OpenAI Agents SDK.
2
+
3
+ This module implements a TracingProcessor that captures telemetry from
4
+ OpenAI Agents and sends it to the Kalibr backend.
5
+ """
6
+
7
+ import atexit
8
+ import os
9
+ import queue
10
+ import threading
11
+ import time
12
+ from datetime import datetime, timezone
13
+ from typing import Any, Dict, List, Optional
14
+ import uuid
15
+
16
+ import httpx
17
+
18
+ # Import Kalibr cost adapters
19
+ try:
20
+ from kalibr.cost_adapter import CostAdapterFactory
21
+ except ImportError:
22
+ CostAdapterFactory = None
23
+
24
+ # Import tiktoken for token counting fallback
25
+ try:
26
+ import tiktoken
27
+ HAS_TIKTOKEN = True
28
+ except ImportError:
29
+ HAS_TIKTOKEN = False
30
+
31
+
32
+ def _count_tokens(text: str, model: str = "gpt-4") -> int:
33
+ """Count tokens for given text."""
34
+ if not text:
35
+ return 0
36
+
37
+ if HAS_TIKTOKEN:
38
+ try:
39
+ encoding = tiktoken.encoding_for_model(model)
40
+ return len(encoding.encode(str(text)))
41
+ except Exception:
42
+ pass
43
+
44
+ return len(str(text)) // 4
45
+
46
+
47
+ class EventBatcher:
48
+ """Batches events for efficient sending to backend."""
49
+
50
+ _instances: Dict[str, "EventBatcher"] = {}
51
+ _lock = threading.Lock()
52
+
53
+ def __init__(
54
+ self,
55
+ endpoint: str,
56
+ api_key: str,
57
+ batch_size: int = 100,
58
+ flush_interval: float = 2.0,
59
+ ):
60
+ self.endpoint = endpoint
61
+ self.api_key = api_key
62
+ self.batch_size = batch_size
63
+ self.flush_interval = flush_interval
64
+
65
+ self._event_queue: queue.Queue = queue.Queue(maxsize=5000)
66
+ self._client = httpx.Client(timeout=10.0)
67
+ self._shutdown = False
68
+
69
+ self._flush_thread = threading.Thread(target=self._flush_loop, daemon=True)
70
+ self._flush_thread.start()
71
+
72
+ atexit.register(self.shutdown)
73
+
74
+ @classmethod
75
+ def get_instance(
76
+ cls,
77
+ endpoint: str,
78
+ api_key: str,
79
+ batch_size: int = 100,
80
+ flush_interval: float = 2.0,
81
+ ) -> "EventBatcher":
82
+ """Get or create a shared EventBatcher instance."""
83
+ key = f"{endpoint}:{api_key}"
84
+ with cls._lock:
85
+ if key not in cls._instances:
86
+ cls._instances[key] = cls(
87
+ endpoint=endpoint,
88
+ api_key=api_key,
89
+ batch_size=batch_size,
90
+ flush_interval=flush_interval,
91
+ )
92
+ return cls._instances[key]
93
+
94
+ def enqueue(self, event: Dict[str, Any]):
95
+ """Add event to queue."""
96
+ try:
97
+ self._event_queue.put_nowait(event)
98
+ except queue.Full:
99
+ try:
100
+ self._event_queue.get_nowait()
101
+ self._event_queue.put_nowait(event)
102
+ except:
103
+ pass
104
+
105
+ def _flush_loop(self):
106
+ """Background thread to flush events."""
107
+ batch = []
108
+ last_flush = time.time()
109
+
110
+ while not self._shutdown:
111
+ try:
112
+ try:
113
+ event = self._event_queue.get(timeout=0.1)
114
+ batch.append(event)
115
+ except queue.Empty:
116
+ pass
117
+
118
+ now = time.time()
119
+ should_flush = (
120
+ len(batch) >= self.batch_size or
121
+ (batch and now - last_flush >= self.flush_interval)
122
+ )
123
+
124
+ if should_flush:
125
+ self._send_batch(batch)
126
+ batch = []
127
+ last_flush = now
128
+ except Exception:
129
+ pass
130
+
131
+ if batch:
132
+ self._send_batch(batch)
133
+
134
+ def _send_batch(self, batch: List[Dict[str, Any]]):
135
+ """Send batch to backend."""
136
+ if not batch:
137
+ return
138
+
139
+ try:
140
+ payload = {"events": batch}
141
+ headers = {}
142
+ if self.api_key:
143
+ headers["X-API-Key"] = self.api_key
144
+
145
+ self._client.post(self.endpoint, json=payload, headers=headers)
146
+ except Exception:
147
+ pass
148
+
149
+ def shutdown(self):
150
+ """Shutdown batcher."""
151
+ if self._shutdown:
152
+ return
153
+ self._shutdown = True
154
+ if self._flush_thread.is_alive():
155
+ self._flush_thread.join(timeout=5.0)
156
+ self._client.close()
157
+
158
+ def flush(self):
159
+ """Force flush pending events."""
160
+ events = []
161
+ while True:
162
+ try:
163
+ event = self._event_queue.get_nowait()
164
+ events.append(event)
165
+ except queue.Empty:
166
+ break
167
+ if events:
168
+ self._send_batch(events)
169
+
170
+
171
+ class KalibrTracingProcessor:
172
+ """OpenAI Agents SDK TracingProcessor for Kalibr observability.
173
+
174
+ This processor implements the TracingProcessor interface from OpenAI's
175
+ Agents SDK and sends telemetry to the Kalibr backend.
176
+
177
+ Args:
178
+ api_key: Kalibr API key
179
+ endpoint: Backend endpoint URL
180
+ tenant_id: Tenant identifier
181
+ environment: Environment (prod/staging/dev)
182
+ service: Service name
183
+ workflow_id: Workflow identifier
184
+ capture_input: Whether to capture inputs (default: True)
185
+ capture_output: Whether to capture outputs (default: True)
186
+
187
+ Usage:
188
+ from agents.tracing import add_trace_processor
189
+ from kalibr_openai_agents import KalibrTracingProcessor
190
+
191
+ processor = KalibrTracingProcessor(tenant_id="my-tenant")
192
+ add_trace_processor(processor)
193
+ """
194
+
195
+ def __init__(
196
+ self,
197
+ api_key: Optional[str] = None,
198
+ endpoint: Optional[str] = None,
199
+ tenant_id: Optional[str] = None,
200
+ environment: Optional[str] = None,
201
+ service: Optional[str] = None,
202
+ workflow_id: Optional[str] = None,
203
+ capture_input: bool = True,
204
+ capture_output: bool = True,
205
+ ):
206
+ self.api_key = api_key or os.getenv("KALIBR_API_KEY", "")
207
+ self.endpoint = endpoint or os.getenv(
208
+ "KALIBR_ENDPOINT",
209
+ os.getenv("KALIBR_API_ENDPOINT", "http://localhost:8001/api/v1/traces")
210
+ )
211
+ self.tenant_id = tenant_id or os.getenv("KALIBR_TENANT_ID", "default")
212
+ self.environment = environment or os.getenv("KALIBR_ENVIRONMENT", "prod")
213
+ self.service = service or os.getenv("KALIBR_SERVICE", "openai-agents-app")
214
+ self.workflow_id = workflow_id or os.getenv("KALIBR_WORKFLOW_ID", "default-workflow")
215
+ self.capture_input = capture_input
216
+ self.capture_output = capture_output
217
+
218
+ # Get shared batcher
219
+ self._batcher = EventBatcher.get_instance(
220
+ endpoint=self.endpoint,
221
+ api_key=self.api_key,
222
+ )
223
+
224
+ # Track active traces for context
225
+ self._active_traces: Dict[str, Dict[str, Any]] = {}
226
+ self._lock = threading.Lock()
227
+
228
+ def on_trace_start(self, trace: Any) -> None:
229
+ """Called when a trace starts.
230
+
231
+ Args:
232
+ trace: The trace object with trace_id, workflow_name, etc.
233
+ """
234
+ try:
235
+ trace_id = getattr(trace, "trace_id", str(uuid.uuid4()))
236
+ workflow_name = getattr(trace, "workflow_name", "unknown")
237
+
238
+ with self._lock:
239
+ self._active_traces[trace_id] = {
240
+ "trace_id": trace_id,
241
+ "workflow_name": workflow_name,
242
+ "started_at": datetime.now(timezone.utc),
243
+ "spans": [],
244
+ }
245
+ except Exception:
246
+ pass
247
+
248
+ def on_trace_end(self, trace: Any) -> None:
249
+ """Called when a trace ends.
250
+
251
+ Args:
252
+ trace: The completed trace object
253
+ """
254
+ try:
255
+ trace_id = getattr(trace, "trace_id", None)
256
+ if not trace_id:
257
+ return
258
+
259
+ with self._lock:
260
+ trace_data = self._active_traces.pop(trace_id, None)
261
+
262
+ if not trace_data:
263
+ return
264
+
265
+ ended_at = datetime.now(timezone.utc)
266
+ started_at = trace_data.get("started_at", ended_at)
267
+ duration_ms = int((ended_at - started_at).total_seconds() * 1000)
268
+
269
+ # Create trace-level event
270
+ event = {
271
+ "schema_version": "1.0",
272
+ "trace_id": trace_id,
273
+ "span_id": str(uuid.uuid4()),
274
+ "parent_span_id": None,
275
+ "tenant_id": self.tenant_id,
276
+ "workflow_id": self.workflow_id,
277
+ "provider": "openai",
278
+ "model_id": "agents",
279
+ "model_name": trace_data.get("workflow_name", "unknown"),
280
+ "operation": f"trace:{trace_data.get('workflow_name', 'unknown')}",
281
+ "endpoint": "agents.trace",
282
+ "duration_ms": duration_ms,
283
+ "latency_ms": duration_ms,
284
+ "input_tokens": 0,
285
+ "output_tokens": 0,
286
+ "total_tokens": 0,
287
+ "cost_usd": 0.0,
288
+ "total_cost_usd": 0.0,
289
+ "status": "success",
290
+ "timestamp": started_at.isoformat(),
291
+ "ts_start": started_at.isoformat(),
292
+ "ts_end": ended_at.isoformat(),
293
+ "environment": self.environment,
294
+ "service": self.service,
295
+ "runtime_env": os.getenv("RUNTIME_ENV", "local"),
296
+ "sandbox_id": os.getenv("SANDBOX_ID", "local"),
297
+ "metadata": {
298
+ "span_type": "trace",
299
+ "openai_agents": True,
300
+ "workflow_name": trace_data.get("workflow_name"),
301
+ "span_count": len(trace_data.get("spans", [])),
302
+ },
303
+ }
304
+
305
+ self._batcher.enqueue(event)
306
+
307
+ except Exception:
308
+ pass
309
+
310
+ def on_span_start(self, span: Any) -> None:
311
+ """Called when a span starts.
312
+
313
+ Args:
314
+ span: The span object with span_id, trace_id, span_data, etc.
315
+ """
316
+ # We process spans on end to have complete data
317
+ pass
318
+
319
+ def on_span_end(self, span: Any) -> None:
320
+ """Called when a span ends.
321
+
322
+ Args:
323
+ span: The completed span object
324
+ """
325
+ try:
326
+ self._process_span(span)
327
+ except Exception:
328
+ pass
329
+
330
+ def _process_span(self, span: Any):
331
+ """Process a completed span and create Kalibr event."""
332
+ # Extract span attributes
333
+ span_id = getattr(span, "span_id", str(uuid.uuid4()))
334
+ trace_id = getattr(span, "trace_id", str(uuid.uuid4()))
335
+ parent_id = getattr(span, "parent_id", None)
336
+
337
+ # Get timing
338
+ started_at = getattr(span, "started_at", None)
339
+ ended_at = getattr(span, "ended_at", None)
340
+
341
+ if isinstance(started_at, str):
342
+ started_at = datetime.fromisoformat(started_at.replace("Z", "+00:00"))
343
+ if isinstance(ended_at, str):
344
+ ended_at = datetime.fromisoformat(ended_at.replace("Z", "+00:00"))
345
+
346
+ if not started_at:
347
+ started_at = datetime.now(timezone.utc)
348
+ if not ended_at:
349
+ ended_at = datetime.now(timezone.utc)
350
+
351
+ duration_ms = int((ended_at - started_at).total_seconds() * 1000)
352
+
353
+ # Get span data
354
+ span_data = getattr(span, "span_data", None)
355
+
356
+ # Determine span type and extract relevant data
357
+ span_type = "unknown"
358
+ operation = "span"
359
+ model = "unknown"
360
+ input_tokens = 0
361
+ output_tokens = 0
362
+ input_preview = None
363
+ output_preview = None
364
+
365
+ if span_data is not None:
366
+ span_type = type(span_data).__name__
367
+
368
+ # Handle GenerationSpanData (LLM calls)
369
+ if "Generation" in span_type:
370
+ span_type = "generation"
371
+ operation = "llm_generation"
372
+ model = getattr(span_data, "model", "gpt-4")
373
+
374
+ # Extract token counts from response
375
+ response = getattr(span_data, "response", None)
376
+ if response:
377
+ usage = getattr(response, "usage", None)
378
+ if usage:
379
+ input_tokens = getattr(usage, "prompt_tokens", 0) or 0
380
+ output_tokens = getattr(usage, "completion_tokens", 0) or 0
381
+
382
+ # Extract input/output if capturing
383
+ if self.capture_input:
384
+ input_data = getattr(span_data, "input", None)
385
+ if input_data:
386
+ input_preview = str(input_data)[:500]
387
+
388
+ if self.capture_output:
389
+ output_data = getattr(span_data, "output", None)
390
+ if output_data:
391
+ output_preview = str(output_data)[:500]
392
+
393
+ # Handle AgentSpanData
394
+ elif "Agent" in span_type:
395
+ span_type = "agent"
396
+ agent_name = getattr(span_data, "name", "unknown")
397
+ operation = f"agent:{agent_name}"
398
+
399
+ # Handle FunctionSpanData (tool calls)
400
+ elif "Function" in span_type:
401
+ span_type = "function"
402
+ func_name = getattr(span_data, "name", "unknown")
403
+ operation = f"function:{func_name}"
404
+
405
+ if self.capture_input:
406
+ input_data = getattr(span_data, "input", None)
407
+ if input_data:
408
+ input_preview = str(input_data)[:500]
409
+
410
+ if self.capture_output:
411
+ output_data = getattr(span_data, "output", None)
412
+ if output_data:
413
+ output_preview = str(output_data)[:500]
414
+
415
+ # Handle HandoffSpanData
416
+ elif "Handoff" in span_type:
417
+ span_type = "handoff"
418
+ from_agent = getattr(span_data, "from_agent", "unknown")
419
+ to_agent = getattr(span_data, "to_agent", "unknown")
420
+ operation = f"handoff:{from_agent}->{to_agent}"
421
+
422
+ # Handle GuardrailSpanData
423
+ elif "Guardrail" in span_type:
424
+ span_type = "guardrail"
425
+ guardrail_name = getattr(span_data, "name", "unknown")
426
+ operation = f"guardrail:{guardrail_name}"
427
+
428
+ else:
429
+ # Generic span
430
+ operation = f"span:{span_type}"
431
+
432
+ # Calculate cost for generation spans
433
+ cost_usd = 0.0
434
+ if span_type == "generation" and CostAdapterFactory is not None:
435
+ cost_usd = CostAdapterFactory.compute_cost(
436
+ vendor="openai",
437
+ model_name=model,
438
+ tokens_in=input_tokens,
439
+ tokens_out=output_tokens,
440
+ )
441
+
442
+ # Get error info
443
+ error = getattr(span, "error", None)
444
+ status = "error" if error else "success"
445
+ error_type = None
446
+ error_message = None
447
+ if error:
448
+ error_type = type(error).__name__
449
+ error_message = str(error)[:512]
450
+
451
+ # Build event
452
+ event = {
453
+ "schema_version": "1.0",
454
+ "trace_id": trace_id,
455
+ "span_id": span_id,
456
+ "parent_span_id": parent_id,
457
+ "tenant_id": self.tenant_id,
458
+ "workflow_id": self.workflow_id,
459
+ "provider": "openai",
460
+ "model_id": model,
461
+ "model_name": model,
462
+ "operation": operation,
463
+ "endpoint": f"agents.{span_type}",
464
+ "duration_ms": duration_ms,
465
+ "latency_ms": duration_ms,
466
+ "input_tokens": input_tokens,
467
+ "output_tokens": output_tokens,
468
+ "total_tokens": input_tokens + output_tokens,
469
+ "cost_usd": cost_usd,
470
+ "total_cost_usd": cost_usd,
471
+ "status": status,
472
+ "error_type": error_type,
473
+ "error_message": error_message,
474
+ "timestamp": started_at.isoformat(),
475
+ "ts_start": started_at.isoformat(),
476
+ "ts_end": ended_at.isoformat(),
477
+ "environment": self.environment,
478
+ "service": self.service,
479
+ "runtime_env": os.getenv("RUNTIME_ENV", "local"),
480
+ "sandbox_id": os.getenv("SANDBOX_ID", "local"),
481
+ "metadata": {
482
+ "span_type": span_type,
483
+ "openai_agents": True,
484
+ "input_preview": input_preview,
485
+ "output_preview": output_preview,
486
+ },
487
+ }
488
+
489
+ self._batcher.enqueue(event)
490
+
491
+ def shutdown(self) -> None:
492
+ """Shutdown the processor and flush pending events."""
493
+ self._batcher.shutdown()
494
+
495
+ def force_flush(self) -> None:
496
+ """Force flush all pending events."""
497
+ self._batcher.flush()
498
+
499
+
500
+ def setup_kalibr_tracing(
501
+ api_key: Optional[str] = None,
502
+ endpoint: Optional[str] = None,
503
+ tenant_id: Optional[str] = None,
504
+ environment: Optional[str] = None,
505
+ service: Optional[str] = None,
506
+ workflow_id: Optional[str] = None,
507
+ capture_input: bool = True,
508
+ capture_output: bool = True,
509
+ ) -> KalibrTracingProcessor:
510
+ """Set up Kalibr tracing for OpenAI Agents SDK.
511
+
512
+ This is a convenience function that creates a KalibrTracingProcessor
513
+ and adds it to the OpenAI Agents SDK's trace processors.
514
+
515
+ Args:
516
+ api_key: Kalibr API key
517
+ endpoint: Backend endpoint URL
518
+ tenant_id: Tenant identifier
519
+ environment: Environment (prod/staging/dev)
520
+ service: Service name
521
+ workflow_id: Workflow identifier
522
+ capture_input: Whether to capture inputs
523
+ capture_output: Whether to capture outputs
524
+
525
+ Returns:
526
+ The configured KalibrTracingProcessor
527
+
528
+ Usage:
529
+ from kalibr_openai_agents import setup_kalibr_tracing
530
+
531
+ # Quick setup
532
+ processor = setup_kalibr_tracing(tenant_id="my-tenant")
533
+
534
+ # Now use OpenAI Agents normally
535
+ """
536
+ processor = KalibrTracingProcessor(
537
+ api_key=api_key,
538
+ endpoint=endpoint,
539
+ tenant_id=tenant_id,
540
+ environment=environment,
541
+ service=service,
542
+ workflow_id=workflow_id,
543
+ capture_input=capture_input,
544
+ capture_output=capture_output,
545
+ )
546
+
547
+ # Try to add to OpenAI Agents SDK trace processors
548
+ try:
549
+ from agents.tracing import add_trace_processor
550
+ add_trace_processor(processor)
551
+ except ImportError:
552
+ print("[Kalibr] OpenAI Agents SDK not installed, processor created but not registered")
553
+
554
+ return processor
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)