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
kalibr/trace_models.py ADDED
@@ -0,0 +1,201 @@
1
+ """
2
+ Unified Trace Event Models - Generated from trace_event_schema.json v1.0.0
3
+
4
+ This module provides Pydantic models for trace events that are shared
5
+ between the SDK and backend to ensure schema consistency.
6
+ """
7
+
8
+ from datetime import datetime
9
+ from typing import Any, Dict, Literal, Optional
10
+
11
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
12
+
13
+
14
+ class TraceEvent(BaseModel):
15
+ """
16
+ Unified trace event model for LLM observability.
17
+
18
+ Compatible with:
19
+ - Kalibr SDK v1.0.30+
20
+ - Kalibr Backend v1.0+
21
+ - ClickHouse storage schema
22
+
23
+ Phase 4.5: Strict validation enabled to enforce data quality.
24
+ """
25
+
26
+ model_config = ConfigDict(
27
+ # Phase 4.5: Relaxed strict mode to allow type coercion (str -> datetime)
28
+ # but keep extra='forbid' to reject unknown fields
29
+ extra="forbid", # Reject unknown fields - this is the key validation
30
+ validate_assignment=True,
31
+ json_schema_extra={
32
+ "example": {
33
+ "schema_version": "1.0",
34
+ "trace_id": "550e8400-e29b-41d4-a716-446655440000",
35
+ "span_id": "a1b2c3d4-e5f6-47a8-b9c0-123456789abc",
36
+ "parent_span_id": None,
37
+ "tenant_id": "acme-corp",
38
+ "provider": "openai",
39
+ "model_id": "gpt-4o",
40
+ "operation": "chat_completion",
41
+ "duration_ms": 250,
42
+ "input_tokens": 100,
43
+ "output_tokens": 50,
44
+ "cost_usd": 0.000375,
45
+ "status": "success",
46
+ "timestamp": "2025-10-30T12:00:00.000Z",
47
+ }
48
+ },
49
+ )
50
+
51
+ # Schema metadata
52
+ schema_version: Literal["1.0"] = Field(description="Schema version (1.0)")
53
+
54
+ # Identity
55
+ trace_id: str = Field(
56
+ min_length=16,
57
+ max_length=36,
58
+ description="Unique trace identifier (UUID or 16-char alphanumeric)",
59
+ )
60
+ span_id: str = Field(
61
+ min_length=16,
62
+ max_length=36,
63
+ description="Unique span identifier (UUIDv4 format)"
64
+ )
65
+ parent_span_id: Optional[str] = Field(
66
+ None,
67
+ min_length=16,
68
+ max_length=36,
69
+ description="Parent span ID for nested operations (UUIDv4 format)"
70
+ )
71
+
72
+ # Tenant & Context
73
+ tenant_id: str = Field(min_length=1, max_length=64, description="Tenant identifier")
74
+ workflow_id: Optional[str] = Field(
75
+ None, max_length=64, description="Workflow identifier for multi-step operations"
76
+ )
77
+ sandbox_id: Optional[str] = Field(
78
+ None, max_length=64, description="Sandbox/VM/Environment identifier"
79
+ )
80
+ runtime_env: Optional[str] = Field(
81
+ None, max_length=32, description="Runtime environment (vercel_vm, fly_io, local, etc.)"
82
+ )
83
+
84
+ # LLM Details
85
+ provider: Literal["openai", "anthropic", "google", "cohere", "custom"] = Field(
86
+ description="LLM provider"
87
+ )
88
+ model_id: str = Field(
89
+ min_length=1, max_length=64, description="Model identifier (e.g., gpt-4o, claude-3-opus)"
90
+ )
91
+ model_name: Optional[str] = Field(
92
+ None, description="Human-readable model name (optional, defaults to model_id)"
93
+ )
94
+
95
+ # Operation
96
+ operation: str = Field(
97
+ min_length=1,
98
+ max_length=64,
99
+ description="Operation type (e.g., chat_completion, summarize, refine)",
100
+ )
101
+ endpoint: Optional[str] = Field(
102
+ None, max_length=128, description="API endpoint or function name"
103
+ )
104
+
105
+ # Performance
106
+ duration_ms: int = Field(ge=0, description="Total duration in milliseconds")
107
+ latency_ms: Optional[int] = Field(None, description="Legacy field, same as duration_ms")
108
+
109
+ # Tokens
110
+ input_tokens: int = Field(ge=0, description="Number of input tokens")
111
+ output_tokens: int = Field(ge=0, description="Number of output tokens")
112
+ total_tokens: Optional[int] = Field(
113
+ None, description="Total tokens (input + output), computed if not provided"
114
+ )
115
+
116
+ # Cost
117
+ cost_usd: float = Field(ge=0.0, description="Total cost in USD")
118
+ total_cost_usd: Optional[float] = Field(None, description="Legacy field, same as cost_usd")
119
+ unit_price_usd: Optional[float] = Field(None, ge=0.0, description="Price per token in USD")
120
+
121
+ # Status & Errors
122
+ status: Literal["success", "error", "timeout"] = Field(description="Execution status")
123
+ error_type: Optional[str] = Field(
124
+ None, max_length=64, description="Error class name if status is error"
125
+ )
126
+ error_message: Optional[str] = Field(
127
+ None, max_length=512, description="Error message if status is error"
128
+ )
129
+ stack_trace: Optional[str] = Field(None, description="Stack trace for errors (optional)")
130
+
131
+ # Timestamps
132
+ timestamp: datetime = Field(description="Event timestamp (ISO 8601 UTC)")
133
+ ts_start: Optional[datetime] = Field(None, description="Operation start time (ISO 8601 UTC)")
134
+ ts_end: Optional[datetime] = Field(None, description="Operation end time (ISO 8601 UTC)")
135
+
136
+ # Environment
137
+ environment: Optional[Literal["prod", "staging", "dev"]] = Field(
138
+ None, description="Deployment environment"
139
+ )
140
+ service: Optional[str] = Field(None, max_length=64, description="Service name")
141
+
142
+ # User Context
143
+ user_id: Optional[str] = Field(
144
+ None, max_length=64, description="End user identifier (anonymized)"
145
+ )
146
+ request_id: Optional[str] = Field(
147
+ None, max_length=64, description="Request identifier for correlation"
148
+ )
149
+
150
+ # Metadata
151
+ metadata: Optional[Dict[str, Any]] = Field(None, description="Additional custom metadata")
152
+ data_class: Optional[Literal["economic", "performance", "diagnostic"]] = Field(
153
+ None, description="Data classification"
154
+ )
155
+
156
+ # Legacy fields
157
+ vendor: Optional[str] = Field(None, description="Legacy field, same as provider")
158
+
159
+ @field_validator("total_tokens", mode="before")
160
+ @classmethod
161
+ def compute_total_tokens(cls, v, info):
162
+ """Auto-compute total_tokens if not provided."""
163
+ if v is None and "input_tokens" in info.data and "output_tokens" in info.data:
164
+ return info.data["input_tokens"] + info.data["output_tokens"]
165
+ return v
166
+
167
+ @field_validator("model_name", mode="before")
168
+ @classmethod
169
+ def default_model_name(cls, v, info):
170
+ """Default model_name to model_id if not provided."""
171
+ if v is None and "model_id" in info.data:
172
+ return info.data["model_id"]
173
+ return v
174
+
175
+ @field_validator("latency_ms", mode="before")
176
+ @classmethod
177
+ def sync_latency(cls, v, info):
178
+ """Sync latency_ms with duration_ms if not provided."""
179
+ if v is None and "duration_ms" in info.data:
180
+ return info.data["duration_ms"]
181
+ return v
182
+
183
+ @field_validator("total_cost_usd", mode="before")
184
+ @classmethod
185
+ def sync_cost(cls, v, info):
186
+ """Sync total_cost_usd with cost_usd if not provided."""
187
+ if v is None and "cost_usd" in info.data:
188
+ return info.data["cost_usd"]
189
+ return v
190
+
191
+ @field_validator("vendor", mode="before")
192
+ @classmethod
193
+ def sync_vendor(cls, v, info):
194
+ """Sync vendor with provider if not provided."""
195
+ if v is None and "provider" in info.data:
196
+ return info.data["provider"]
197
+ return v
198
+
199
+
200
+ # Convenience type aliases
201
+ TraceEventDict = Dict[str, Any]
kalibr/tracer.py ADDED
@@ -0,0 +1,354 @@
1
+ """Centralized trace management and event creation.
2
+
3
+ This module handles:
4
+ - Trace lifecycle management
5
+ - Span creation and context propagation
6
+ - Event standardization to schema v1.0
7
+ - Error capturing with stack traces
8
+ """
9
+
10
+ import traceback
11
+ import uuid
12
+ from datetime import datetime, timezone
13
+ from typing import Any, Dict, Optional
14
+
15
+ from .context import get_trace_id, new_span_id, trace_context
16
+ from .cost_adapter import CostAdapterFactory
17
+
18
+
19
+ class Tracer:
20
+ """Centralized tracer for creating and managing trace events."""
21
+
22
+ def __init__(
23
+ self,
24
+ tenant_id: str = "default",
25
+ environment: str = "prod",
26
+ service: str = "kalibr-app",
27
+ workflow_id: str = "default-workflow",
28
+ workflow_version: str = "1.0",
29
+ sandbox_id: str = "local",
30
+ runtime_env: str = "local",
31
+ parent_trace_id: Optional[str] = None,
32
+ ):
33
+ """Initialize tracer.
34
+
35
+ Args:
36
+ tenant_id: Tenant identifier
37
+ environment: Environment (prod/staging/dev)
38
+ service: Service name
39
+ workflow_id: Workflow identifier
40
+ workflow_version: Workflow version for A/B testing
41
+ sandbox_id: Sandbox/VM identifier
42
+ runtime_env: Runtime environment
43
+ parent_trace_id: Parent trace ID for nested workflows
44
+ """
45
+ self.tenant_id = tenant_id
46
+ self.environment = environment
47
+ self.service = service
48
+ self.workflow_id = workflow_id
49
+ self.workflow_version = workflow_version
50
+ self.sandbox_id = sandbox_id
51
+ self.runtime_env = runtime_env
52
+ self.parent_trace_id = parent_trace_id
53
+
54
+ def create_span(
55
+ self,
56
+ operation: str,
57
+ vendor: str = "unknown",
58
+ model_name: str = "unknown",
59
+ endpoint: Optional[str] = None,
60
+ ) -> "SpanContext":
61
+ """Create a new span context.
62
+
63
+ Args:
64
+ operation: Operation type (chat_completion, embedding, etc.)
65
+ vendor: Vendor name (openai, anthropic, etc.)
66
+ model_name: Model identifier
67
+ endpoint: API endpoint or function name
68
+
69
+ Returns:
70
+ SpanContext instance for managing span lifecycle
71
+ """
72
+ # Get or create trace ID
73
+ trace_id = get_trace_id()
74
+ if not trace_id:
75
+ trace_id = str(uuid.uuid4())
76
+
77
+ # Create span ID
78
+ span_id = new_span_id()
79
+
80
+ # Get parent span ID from context
81
+ ctx = trace_context.get()
82
+ span_stack = ctx.get("span_stack", [])
83
+ parent_id = span_stack[-1] if span_stack else None
84
+
85
+ # Push span to stack
86
+ ctx["trace_id"] = trace_id
87
+ if "span_stack" not in ctx:
88
+ ctx["span_stack"] = []
89
+ ctx["span_stack"].append(span_id)
90
+ trace_context.set(ctx)
91
+
92
+ return SpanContext(
93
+ tracer=self,
94
+ trace_id=trace_id,
95
+ span_id=span_id,
96
+ parent_id=parent_id,
97
+ operation=operation,
98
+ vendor=vendor,
99
+ model_name=model_name,
100
+ endpoint=endpoint or operation,
101
+ )
102
+
103
+ def create_event(
104
+ self,
105
+ trace_id: str,
106
+ span_id: str,
107
+ parent_id: Optional[str],
108
+ operation: str,
109
+ vendor: str,
110
+ model_name: str,
111
+ endpoint: str,
112
+ timestamp: datetime,
113
+ latency_ms: int,
114
+ status: str,
115
+ tokens_in: int = 0,
116
+ tokens_out: int = 0,
117
+ error_type: Optional[str] = None,
118
+ error_message: Optional[str] = None,
119
+ metadata: Optional[Dict[str, Any]] = None,
120
+ ) -> Dict[str, Any]:
121
+ """Create standardized trace event matching schema v1.0 with billing fields.
122
+
123
+ Args:
124
+ trace_id: Trace identifier
125
+ span_id: Span identifier
126
+ parent_id: Parent span ID (None for root)
127
+ operation: Operation type
128
+ vendor: Vendor name
129
+ model_name: Model identifier
130
+ endpoint: API endpoint or function name
131
+ timestamp: Event timestamp
132
+ latency_ms: Duration in milliseconds
133
+ status: Status (success/error)
134
+ tokens_in: Input token count
135
+ tokens_out: Output token count
136
+ error_type: Error type if status=error
137
+ error_message: Error message if status=error
138
+ metadata: Additional metadata
139
+
140
+ Returns:
141
+ Standardized event dict with billing attribution
142
+ """
143
+ # Compute cost using adapter
144
+ cost_adapter_result = CostAdapterFactory.compute_cost(
145
+ vendor=vendor, model_name=model_name, tokens_in=tokens_in, tokens_out=tokens_out
146
+ )
147
+
148
+ # Calculate unit price (approximate)
149
+ total_tokens = tokens_in + tokens_out
150
+ unit_price_usd = cost_adapter_result / total_tokens if total_tokens > 0 else 0.0
151
+
152
+ # Classify data tier
153
+ if cost_adapter_result > 0 or tokens_in > 0:
154
+ data_class = "economic"
155
+ elif error_type:
156
+ data_class = "system"
157
+ else:
158
+ data_class = "economic"
159
+
160
+ event = {
161
+ "schema_version": "1.0", # Required by validator (legacy)
162
+ "data_class": data_class,
163
+ "trace_id": trace_id,
164
+ "span_id": span_id,
165
+ "parent_id": parent_id,
166
+ "timestamp": timestamp.isoformat(),
167
+ "ts_start": timestamp.isoformat(), # For backward compatibility
168
+ "ts_end": timestamp.isoformat(), # For backward compatibility
169
+ "endpoint": endpoint,
170
+ "service": self.service,
171
+ "vendor": vendor,
172
+ "operation": operation,
173
+ "cost_usd": cost_adapter_result,
174
+ "latency_ms": latency_ms,
175
+ "duration_ms": latency_ms, # For backward compatibility
176
+ "status": status,
177
+ # Billing & Attribution
178
+ "tenant_id": self.tenant_id,
179
+ "workflow_id": self.workflow_id,
180
+ "sandbox_id": self.sandbox_id,
181
+ "runtime_env": self.runtime_env,
182
+ # Workflow Context (NEW)
183
+ "parent_trace_id": self.parent_trace_id,
184
+ "workflow_version": self.workflow_version,
185
+ # Model details
186
+ "provider": vendor, # Same as vendor for compatibility
187
+ "model_id": model_name, # For backward compatibility
188
+ "model_name": model_name,
189
+ # Token usage
190
+ "input_tokens": tokens_in,
191
+ "output_tokens": tokens_out,
192
+ # Cost breakdown
193
+ "unit_price_usd": unit_price_usd,
194
+ "total_cost_usd": cost_adapter_result,
195
+ "execution_cost_usd": 0.0, # Infrastructure cost (enriched by collector)
196
+ "kalibr_fee_usd": 0.0, # Platform fee (enriched by collector)
197
+ # Error tracking
198
+ "error_type": error_type,
199
+ "error_message": error_message,
200
+ "error_class": None, # Enriched by collector
201
+ # Reliability Metrics (NEW)
202
+ "retry_count": metadata.get("retry_count", 0) if metadata else 0,
203
+ "queue_latency_ms": metadata.get("queue_latency_ms") if metadata else None,
204
+ "cold_start": metadata.get("cold_start", False) if metadata else False,
205
+ # Metadata
206
+ "metadata": {
207
+ "environment": self.environment,
208
+ "endpoint": endpoint,
209
+ **(metadata or {}),
210
+ },
211
+ }
212
+
213
+ return event
214
+
215
+ def pop_span(self):
216
+ """Pop current span from context stack."""
217
+ ctx = trace_context.get()
218
+ if ctx.get("span_stack"):
219
+ ctx["span_stack"].pop()
220
+ trace_context.set(ctx)
221
+
222
+
223
+ class SpanContext:
224
+ """Context manager for span lifecycle."""
225
+
226
+ def __init__(
227
+ self,
228
+ tracer: Tracer,
229
+ trace_id: str,
230
+ span_id: str,
231
+ parent_id: Optional[str],
232
+ operation: str,
233
+ vendor: str,
234
+ model_name: str,
235
+ endpoint: str,
236
+ ):
237
+ self.tracer = tracer
238
+ self.trace_id = trace_id
239
+ self.span_id = span_id
240
+ self.parent_id = parent_id
241
+ self.operation = operation
242
+ self.vendor = vendor
243
+ self.model_name = model_name
244
+ self.endpoint = endpoint
245
+
246
+ self.ts_start: Optional[datetime] = None
247
+ self.ts_end: Optional[datetime] = None
248
+ self.tokens_in: int = 0
249
+ self.tokens_out: int = 0
250
+ self.status: str = "success"
251
+ self.error_type: Optional[str] = None
252
+ self.error_message: Optional[str] = None
253
+ self.metadata: Dict[str, Any] = {}
254
+
255
+ # NEW: Reliability metrics
256
+ self.retry_count: int = 0
257
+ self.queue_latency_ms: Optional[int] = None
258
+ self.cold_start: bool = False
259
+
260
+ def __enter__(self):
261
+ """Start span."""
262
+ self.ts_start = datetime.now(timezone.utc)
263
+ return self
264
+
265
+ def __exit__(self, exc_type, exc_val, exc_tb):
266
+ """End span and create event."""
267
+ self.ts_end = datetime.now(timezone.utc)
268
+
269
+ # Calculate duration
270
+ latency_ms = int((self.ts_end - self.ts_start).total_seconds() * 1000)
271
+
272
+ # Handle errors
273
+ if exc_type is not None:
274
+ self.status = "error"
275
+ self.error_type = exc_type.__name__
276
+ self.error_message = str(exc_val)
277
+
278
+ # Capture stack trace
279
+ tb_lines = traceback.format_exception(exc_type, exc_val, exc_tb)
280
+ self.metadata["stack_trace"] = "".join(tb_lines)
281
+
282
+ # Create event
283
+ event = self.tracer.create_event(
284
+ trace_id=self.trace_id,
285
+ span_id=self.span_id,
286
+ parent_id=self.parent_id,
287
+ operation=self.operation,
288
+ vendor=self.vendor,
289
+ model_name=self.model_name,
290
+ endpoint=self.endpoint,
291
+ timestamp=self.ts_start,
292
+ latency_ms=latency_ms,
293
+ status=self.status,
294
+ tokens_in=self.tokens_in,
295
+ tokens_out=self.tokens_out,
296
+ error_type=self.error_type,
297
+ error_message=self.error_message,
298
+ metadata=self.metadata,
299
+ )
300
+
301
+ # Store event in context for retrieval
302
+ ctx = trace_context.get()
303
+ if "events" not in ctx:
304
+ ctx["events"] = []
305
+ ctx["events"].append(event)
306
+ trace_context.set(ctx)
307
+
308
+ # Pop span from stack
309
+ self.tracer.pop_span()
310
+
311
+ # Don't suppress exceptions
312
+ return False
313
+
314
+ def set_tokens(self, tokens_in: int, tokens_out: int):
315
+ """Set token counts for span."""
316
+ self.tokens_in = tokens_in
317
+ self.tokens_out = tokens_out
318
+
319
+ def add_metadata(self, key: str, value: Any):
320
+ """Add metadata to span."""
321
+ self.metadata[key] = value
322
+
323
+ def set_reliability_metrics(
324
+ self, retry_count: int = 0, queue_latency_ms: Optional[int] = None, cold_start: bool = False
325
+ ):
326
+ """Set reliability metrics for span.
327
+
328
+ Args:
329
+ retry_count: Number of retries attempted
330
+ queue_latency_ms: Time spent in queue
331
+ cold_start: Whether this was a cold start
332
+ """
333
+ self.retry_count = retry_count
334
+ self.queue_latency_ms = queue_latency_ms
335
+ self.cold_start = cold_start
336
+
337
+ # Also add to metadata for persistence
338
+ self.metadata["retry_count"] = retry_count
339
+ if queue_latency_ms is not None:
340
+ self.metadata["queue_latency_ms"] = queue_latency_ms
341
+ self.metadata["cold_start"] = cold_start
342
+
343
+ def set_error(self, error: Exception):
344
+ """Mark span as error and capture details."""
345
+ self.status = "error"
346
+ self.error_message = str(error)
347
+
348
+ # Capture error type
349
+ if not hasattr(self, "error_type"):
350
+ self.error_type = type(error).__name__
351
+
352
+ # Capture stack trace
353
+ tb_lines = traceback.format_exception(type(error), error, error.__traceback__)
354
+ self.metadata["stack_trace"] = "".join(tb_lines)
kalibr/types.py CHANGED
@@ -1,106 +1,38 @@
1
- """
2
- Enhanced data types for Kalibr app-level framework
3
- """
4
- from pydantic import BaseModel, Field
5
- from typing import Optional, Dict, Any, List, Union, AsyncGenerator
6
- from datetime import datetime
7
- import uuid
8
- import io
1
+ """Type definitions for Kalibr SDK"""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, Optional
5
+
6
+
7
+ @dataclass
8
+ class FileUpload:
9
+ """Represents an uploaded file"""
9
10
 
10
- class FileUpload(BaseModel):
11
- """Enhanced file upload handling for AI model integrations"""
12
11
  filename: str
13
12
  content_type: str
14
13
  size: int
15
14
  content: bytes
16
- upload_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
17
- uploaded_at: datetime = Field(default_factory=datetime.now)
18
-
19
- class Config:
20
- arbitrary_types_allowed = True
21
15
 
22
- class ImageData(BaseModel):
23
- """Image data type for AI vision capabilities"""
24
- filename: str
25
- content_type: str
26
- width: Optional[int] = None
27
- height: Optional[int] = None
28
- format: str # jpeg, png, webp, etc.
29
- content: bytes
30
- image_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
31
-
32
- class Config:
33
- arbitrary_types_allowed = True
34
16
 
35
- class TableData(BaseModel):
36
- """Structured table data for AI analysis"""
37
- headers: List[str]
38
- rows: List[List[Any]]
39
- table_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
40
- metadata: Optional[Dict[str, Any]] = None
17
+ class Session:
18
+ """Session management for stateful interactions"""
41
19
 
42
- class StreamingResponse(BaseModel):
43
- """Base class for streaming responses"""
44
- chunk_id: str
45
- content: Any
46
- is_final: bool = False
47
- timestamp: datetime = Field(default_factory=datetime.now)
20
+ def __init__(self, session_id: str):
21
+ self.session_id = session_id
22
+ self._data: Dict[str, Any] = {}
48
23
 
49
- class Session(BaseModel):
50
- """Session management for stateful interactions"""
51
- session_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
52
- user_id: Optional[str] = None
53
- created_at: datetime = Field(default_factory=datetime.now)
54
- last_accessed: datetime = Field(default_factory=datetime.now)
55
- data: Dict[str, Any] = Field(default_factory=dict)
56
- expires_at: Optional[datetime] = None
57
-
58
- def get(self, key: str, default=None):
24
+ def get(self, key: str, default: Any = None) -> Any:
59
25
  """Get session data"""
60
- return self.data.get(key, default)
61
-
62
- def set(self, key: str, value: Any):
63
- """Set session data"""
64
- self.data[key] = value
65
- self.last_accessed = datetime.now()
66
-
67
- def delete(self, key: str):
68
- """Delete session data"""
69
- if key in self.data:
70
- del self.data[key]
26
+ return self._data.get(key, default)
71
27
 
72
- class AuthenticatedUser(BaseModel):
73
- """Authenticated user context"""
74
- user_id: str
75
- username: str
76
- email: Optional[str] = None
77
- roles: List[str] = Field(default_factory=list)
78
- permissions: List[str] = Field(default_factory=list)
79
- auth_method: str # "jwt", "oauth", "api_key", etc.
80
-
81
- class FileDownload(BaseModel):
82
- """File download response"""
83
- filename: str
84
- content_type: str
85
- content: bytes
86
-
87
- class Config:
88
- arbitrary_types_allowed = True
28
+ def set(self, key: str, value: Any) -> None:
29
+ """Set session data"""
30
+ self._data[key] = value
89
31
 
90
- class AnalysisResult(BaseModel):
91
- """Generic analysis result structure"""
92
- result_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
93
- status: str # "success", "error", "pending"
94
- data: Dict[str, Any] = Field(default_factory=dict)
95
- created_at: datetime = Field(default_factory=datetime.now)
96
- processing_time: Optional[float] = None
97
- metadata: Optional[Dict[str, Any]] = None
32
+ def delete(self, key: str) -> None:
33
+ """Delete session data"""
34
+ self._data.pop(key, None)
98
35
 
99
- class WorkflowState(BaseModel):
100
- """Workflow state management"""
101
- workflow_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
102
- step: str
103
- status: str
104
- data: Dict[str, Any] = Field(default_factory=dict)
105
- created_at: datetime = Field(default_factory=datetime.now)
106
- updated_at: datetime = Field(default_factory=datetime.now)
36
+ def clear(self) -> None:
37
+ """Clear all session data"""
38
+ self._data.clear()