@elsium-ai/observe 0.2.1 → 0.2.3

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @elsium-ai/observe
2
2
 
3
- Observability, tracing, cost tracking, and audit trail for [ElsiumAI](https://github.com/elsium-ai/elsium-ai).
3
+ Observability, tracing, cost intelligence, metrics, audit trails, provenance tracking, and OpenTelemetry compatibility for [ElsiumAI](https://github.com/elsium-ai/elsium-ai).
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@elsium-ai/observe.svg)](https://www.npmjs.com/package/@elsium-ai/observe)
6
6
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/elsium-ai/elsium-ai/blob/main/LICENSE)
@@ -13,31 +13,1164 @@ npm install @elsium-ai/observe @elsium-ai/core
13
13
 
14
14
  ## What's Inside
15
15
 
16
- - **Tracing** Span-based tracing with nested context propagation
17
- - **Cost Intelligence** — Budget tracking, projections, and loop detection
18
- - **Audit Trail** SHA-256 hash-chained events with tamper-proof integrity verification
19
- - **Provenance Tracking** Full lineage per traceId: prompt, model, config, input, output
20
- - **Audit Middleware** Drop-in middleware for automatic event recording
16
+ | Category | Exports | Description |
17
+ |---|---|---|
18
+ | **Spans** | `createSpan`, `Span`, `SpanData`, `SpanEvent`, `SpanKind`, `SpanStatus`, `SpanHandler` | Low-level span creation with nested context propagation |
19
+ | **Cost Engine** | `createCostEngine`, `registerModelTier`, `CostEngine`, `CostEngineConfig`, `BudgetConfig`, `LoopDetectionConfig`, `CostAlert`, `CostDimension`, `CostIntelligenceReport`, `ModelSuggestion`, `ModelTierEntry` | Budget enforcement, cost projections, loop detection, and model optimization suggestions |
20
+ | **Tracer** | `observe`, `Tracer`, `TracerConfig`, `TracerOutput`, `TracerExporter`, `CostReport` | High-level tracing with sampling, console output, and custom exporters |
21
+ | **Metrics** | `createMetrics`, `MetricsCollector`, `MetricEntry` | Counters, gauges, and histograms for application-level metrics |
22
+ | **Audit Trail** | `createAuditTrail`, `auditMiddleware`, `AuditEventType`, `AuditEvent`, `AuditStorageAdapter`, `AuditQueryFilter`, `AuditIntegrityResult`, `AuditTrailConfig`, `AuditTrail` | SHA-256 hash-chained audit events with tamper detection and middleware |
23
+ | **Provenance** | `createProvenanceTracker`, `ProvenanceRecord`, `ProvenanceTracker` | Full lineage tracking per output: prompt, model, config, input, output |
24
+ | **OpenTelemetry** | `toOTelSpan`, `toOTelExportRequest`, `toTraceparent`, `parseTraceparent`, `injectTraceContext`, `extractTraceContext`, `createOTLPExporter`, `OTelSpan`, `OTelSpanKind`, `OTelStatusCode`, `OTelAttribute`, `OTelAttributeValue`, `OTelEvent`, `OTelResource`, `OTelExportRequest`, `TraceContext`, `OTLPExporterConfig` | W3C Trace Context propagation, OTel span conversion, and OTLP JSON export |
21
25
 
22
- ## Usage
26
+ ---
23
27
 
24
- ```typescript
25
- import { createAuditTrail, auditMiddleware, createProvenanceTracker, observe } from '@elsium-ai/observe'
28
+ ## Spans
29
+
30
+ Low-level building blocks for distributed tracing. Each span records a named operation with timing, metadata, events, and parent-child relationships.
31
+
32
+ ### `SpanKind`
33
+
34
+ ```ts
35
+ type SpanKind = 'llm' | 'tool' | 'agent' | 'workflow' | 'custom'
36
+ ```
37
+
38
+ ### `SpanStatus`
39
+
40
+ ```ts
41
+ type SpanStatus = 'running' | 'ok' | 'error'
42
+ ```
43
+
44
+ ### `SpanEvent`
45
+
46
+ ```ts
47
+ interface SpanEvent {
48
+ name: string
49
+ timestamp: number
50
+ data?: Record<string, unknown>
51
+ }
52
+ ```
53
+
54
+ ### `SpanData`
55
+
56
+ The serializable representation of a span, returned by `span.toJSON()`.
57
+
58
+ ```ts
59
+ interface SpanData {
60
+ id: string
61
+ traceId: string
62
+ parentId?: string
63
+ name: string
64
+ kind: SpanKind
65
+ status: SpanStatus
66
+ startTime: number
67
+ endTime?: number
68
+ durationMs?: number
69
+ metadata: Record<string, unknown>
70
+ events: SpanEvent[]
71
+ }
72
+ ```
73
+
74
+ ### `Span`
75
+
76
+ The live span interface used during operation execution.
77
+
78
+ ```ts
79
+ interface Span {
80
+ readonly id: string
81
+ readonly traceId: string
82
+ readonly name: string
83
+ readonly kind: SpanKind
84
+
85
+ addEvent(name: string, data?: Record<string, unknown>): void
86
+ setMetadata(key: string, value: unknown): void
87
+ end(result?: { status?: SpanStatus; metadata?: Record<string, unknown> }): void
88
+ child(name: string, kind?: SpanKind): Span
89
+ toJSON(): SpanData
90
+ }
91
+ ```
92
+
93
+ ### `SpanHandler`
94
+
95
+ Callback invoked when a span ends.
96
+
97
+ ```ts
98
+ type SpanHandler = (span: SpanData) => void
99
+ ```
100
+
101
+ ### `createSpan()`
102
+
103
+ Creates a new span for tracing an operation.
104
+
105
+ ```ts
106
+ function createSpan(
107
+ name: string,
108
+ options?: {
109
+ traceId?: string
110
+ parentId?: string
111
+ kind?: SpanKind
112
+ onEnd?: SpanHandler
113
+ },
114
+ ): Span
115
+ ```
116
+
117
+ | Parameter | Type | Default | Description |
118
+ |---|---|---|---|
119
+ | `name` | `string` | -- | Human-readable name for the operation |
120
+ | `options.traceId` | `string` | auto-generated | Trace ID to group related spans |
121
+ | `options.parentId` | `string` | `undefined` | Parent span ID for nesting |
122
+ | `options.kind` | `SpanKind` | `'custom'` | Category of work being performed |
123
+ | `options.onEnd` | `SpanHandler` | `undefined` | Callback fired when `span.end()` is called |
124
+
125
+ **Returns:** `Span`
126
+
127
+ ```ts
128
+ import { createSpan } from '@elsium-ai/observe'
129
+
130
+ const span = createSpan('generate-summary', { kind: 'llm' })
131
+ span.setMetadata('model', 'gpt-4o')
132
+ span.addEvent('prompt-sent', { tokens: 120 })
133
+
134
+ // Create a child span for a sub-operation
135
+ const child = span.child('embed-context', 'tool')
136
+ child.end()
137
+
138
+ span.end({ status: 'ok', metadata: { outputTokens: 350 } })
139
+
140
+ console.log(span.toJSON())
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Cost Engine
146
+
147
+ Budget enforcement and cost intelligence for LLM usage. Tracks spend across models, agents, users, and features. Detects runaway loops and suggests cheaper model alternatives.
148
+
149
+ ### `BudgetConfig`
150
+
151
+ ```ts
152
+ interface BudgetConfig {
153
+ totalBudget?: number
154
+ dailyBudget?: number
155
+ perUser?: number
156
+ perFeature?: number
157
+ perAgent?: number
158
+ }
159
+ ```
160
+
161
+ ### `LoopDetectionConfig`
162
+
163
+ ```ts
164
+ interface LoopDetectionConfig {
165
+ maxCallsPerMinute?: number
166
+ maxCostPerMinute?: number
167
+ }
168
+ ```
169
+
170
+ ### `CostAlert`
171
+
172
+ ```ts
173
+ interface CostAlert {
174
+ type: 'threshold' | 'loop_detected' | 'budget_exceeded' | 'projection_warning'
175
+ dimension: string
176
+ currentValue: number
177
+ limit: number
178
+ message: string
179
+ timestamp: number
180
+ }
181
+ ```
182
+
183
+ ### `CostDimension`
184
+
185
+ Aggregated cost data for a single dimension (model, agent, user, or feature).
186
+
187
+ ```ts
188
+ interface CostDimension {
189
+ totalCost: number
190
+ totalTokens: number
191
+ callCount: number
192
+ firstCallAt: number
193
+ lastCallAt: number
194
+ }
195
+ ```
196
+
197
+ ### `CostIntelligenceReport`
198
+
199
+ Full cost intelligence report with projections and recommendations.
200
+
201
+ ```ts
202
+ interface CostIntelligenceReport {
203
+ totalSpend: number
204
+ totalTokens: number
205
+ totalCalls: number
206
+ projectedDailySpend: number
207
+ projectedMonthlySpend: number
208
+ byModel: Record<string, CostDimension>
209
+ byAgent: Record<string, CostDimension>
210
+ byUser: Record<string, CostDimension>
211
+ byFeature: Record<string, CostDimension>
212
+ recommendations: string[]
213
+ alerts: CostAlert[]
214
+ }
215
+ ```
216
+
217
+ ### `ModelSuggestion`
218
+
219
+ A recommendation to switch to a cheaper model.
220
+
221
+ ```ts
222
+ interface ModelSuggestion {
223
+ currentModel: string
224
+ suggestedModel: string
225
+ estimatedSavings: number
226
+ reason: string
227
+ }
228
+ ```
229
+
230
+ ### `ModelTierEntry`
231
+
232
+ Defines the pricing tier for a model.
233
+
234
+ ```ts
235
+ interface ModelTierEntry {
236
+ tier: 'low' | 'mid' | 'high'
237
+ costPerMToken: number
238
+ }
239
+ ```
240
+
241
+ ### `CostEngineConfig`
242
+
243
+ ```ts
244
+ interface CostEngineConfig {
245
+ totalBudget?: number
246
+ dailyBudget?: number
247
+ perUser?: number
248
+ perFeature?: number
249
+ perAgent?: number
250
+ loopDetection?: LoopDetectionConfig
251
+ onAlert?: (alert: CostAlert) => void
252
+ alertThresholds?: number[]
253
+ }
254
+ ```
255
+
256
+ ### `CostEngine`
257
+
258
+ ```ts
259
+ interface CostEngine {
260
+ middleware(): Middleware
261
+ getReport(): CostIntelligenceReport
262
+ suggestModel(currentModel: string, inputTokens: number): ModelSuggestion | null
263
+ trackCall(
264
+ response: LLMResponse,
265
+ dimensions?: { agent?: string; user?: string; feature?: string },
266
+ ): void
267
+ reset(): void
268
+ }
269
+ ```
270
+
271
+ ### `createCostEngine()`
272
+
273
+ Creates a cost engine with budget enforcement, alerting, and cost intelligence.
274
+
275
+ ```ts
276
+ function createCostEngine(config?: CostEngineConfig): CostEngine
277
+ ```
278
+
279
+ | Parameter | Type | Default | Description |
280
+ |---|---|---|---|
281
+ | `config.totalBudget` | `number` | `undefined` | Maximum total spend before calls are rejected |
282
+ | `config.dailyBudget` | `number` | `undefined` | Maximum daily spend rate before alerts fire |
283
+ | `config.perUser` | `number` | `undefined` | Per-user budget limit |
284
+ | `config.perFeature` | `number` | `undefined` | Per-feature budget limit |
285
+ | `config.perAgent` | `number` | `undefined` | Per-agent budget limit |
286
+ | `config.loopDetection` | `LoopDetectionConfig` | `undefined` | Thresholds for detecting runaway loops |
287
+ | `config.onAlert` | `(alert: CostAlert) => void` | `undefined` | Callback for every alert |
288
+ | `config.alertThresholds` | `number[]` | `undefined` | Budget percentage thresholds that trigger alerts (e.g. `[0.5, 0.8, 0.95]`) |
289
+
290
+ **Returns:** `CostEngine`
291
+
292
+ ```ts
293
+ import { createCostEngine } from '@elsium-ai/observe'
294
+
295
+ const engine = createCostEngine({
296
+ totalBudget: 50.0,
297
+ dailyBudget: 5.0,
298
+ perAgent: 10.0,
299
+ loopDetection: { maxCallsPerMinute: 60, maxCostPerMinute: 1.0 },
300
+ alertThresholds: [0.5, 0.8, 0.95],
301
+ onAlert: (alert) => console.warn(alert.message),
302
+ })
303
+
304
+ // Use as middleware in an ElsiumAI gateway
305
+ // gateway.use(engine.middleware())
306
+
307
+ // Or track calls manually
308
+ // engine.trackCall(response, { agent: 'summarizer', user: 'user-123' })
309
+
310
+ // Get a full intelligence report
311
+ const report = engine.getReport()
312
+ console.log('Projected monthly spend:', report.projectedMonthlySpend)
313
+ console.log('Recommendations:', report.recommendations)
314
+
315
+ // Get model optimization suggestions
316
+ const suggestion = engine.suggestModel('claude-opus-4-6', 200)
317
+ if (suggestion) {
318
+ console.log(`Switch to ${suggestion.suggestedModel} for ${suggestion.estimatedSavings.toFixed(0)}% savings`)
319
+ }
320
+ ```
321
+
322
+ ### `registerModelTier()`
323
+
324
+ Registers a custom model with its pricing tier so the cost engine can track it.
325
+
326
+ ```ts
327
+ function registerModelTier(model: string, entry: ModelTierEntry): void
328
+ ```
329
+
330
+ | Parameter | Type | Description |
331
+ |---|---|---|
332
+ | `model` | `string` | The model identifier |
333
+ | `entry` | `ModelTierEntry` | The tier classification and cost per million tokens |
334
+
335
+ ```ts
336
+ import { registerModelTier } from '@elsium-ai/observe'
337
+
338
+ registerModelTier('my-custom-model', { tier: 'mid', costPerMToken: 1.5 })
339
+ ```
340
+
341
+ The cost engine ships with built-in tiers for common models including GPT-4o, GPT-4.1, GPT-5, Claude Sonnet 4.6, Claude Opus 4.6, Claude Haiku 4.5, Gemini 2.0 Flash, Gemini 2.5 Pro, o1, o3, o4-mini, and more.
342
+
343
+ ---
344
+
345
+ ## Tracer
346
+
347
+ High-level tracing API that wraps spans with sampling, console output, cost tracking, and pluggable exporters.
348
+
349
+ ### `TracerOutput`
350
+
351
+ ```ts
352
+ type TracerOutput = 'console' | 'json-file' | TracerExporter
353
+ ```
354
+
355
+ ### `TracerExporter`
356
+
357
+ Interface for custom span exporters.
358
+
359
+ ```ts
360
+ interface TracerExporter {
361
+ name: string
362
+ export(spans: SpanData[]): void | Promise<void>
363
+ }
364
+ ```
365
+
366
+ ### `TracerConfig`
367
+
368
+ ```ts
369
+ interface TracerConfig {
370
+ output?: TracerOutput[]
371
+ costTracking?: boolean
372
+ samplingRate?: number
373
+ maxSpans?: number
374
+ }
375
+ ```
376
+
377
+ ### `CostReport`
378
+
379
+ ```ts
380
+ interface CostReport {
381
+ totalCost: number
382
+ totalTokens: number
383
+ totalInputTokens: number
384
+ totalOutputTokens: number
385
+ callCount: number
386
+ byModel: Record<
387
+ string,
388
+ {
389
+ cost: number
390
+ tokens: number
391
+ calls: number
392
+ }
393
+ >
394
+ }
395
+ ```
396
+
397
+ ### `Tracer`
398
+
399
+ ```ts
400
+ interface Tracer {
401
+ startSpan(name: string, kind?: SpanKind): Span
402
+ getSpans(): SpanData[]
403
+ getCostReport(): CostReport
404
+ trackLLMCall(data: {
405
+ model: string
406
+ inputTokens: number
407
+ outputTokens: number
408
+ cost: number
409
+ latencyMs: number
410
+ }): void
411
+ reset(): void
412
+ flush(): Promise<void>
413
+ }
414
+ ```
415
+
416
+ ### `observe()`
417
+
418
+ Creates a tracer instance for recording spans and LLM call costs.
419
+
420
+ ```ts
421
+ function observe(config?: TracerConfig): Tracer
422
+ ```
423
+
424
+ | Parameter | Type | Default | Description |
425
+ |---|---|---|---|
426
+ | `config.output` | `TracerOutput[]` | `['console']` | Where to send completed spans |
427
+ | `config.costTracking` | `boolean` | `true` | Whether `trackLLMCall` records data |
428
+ | `config.samplingRate` | `number` | `1.0` | Fraction of spans to sample (0.0 to 1.0) |
429
+ | `config.maxSpans` | `number` | `10000` | Maximum spans held in memory before oldest are evicted |
430
+
431
+ **Returns:** `Tracer`
432
+
433
+ ```ts
434
+ import { observe } from '@elsium-ai/observe'
435
+
436
+ const tracer = observe({
437
+ output: ['console'],
438
+ samplingRate: 1.0,
439
+ costTracking: true,
440
+ })
441
+
442
+ // Start a span
443
+ const span = tracer.startSpan('chat-completion', 'llm')
444
+ span.setMetadata('model', 'gpt-4o')
445
+ span.end()
446
+
447
+ // Track an LLM call for cost reporting
448
+ tracer.trackLLMCall({
449
+ model: 'gpt-4o',
450
+ inputTokens: 500,
451
+ outputTokens: 200,
452
+ cost: 0.0035,
453
+ latencyMs: 1200,
454
+ })
455
+
456
+ // Get cost report
457
+ const report = tracer.getCostReport()
458
+ console.log('Total cost:', report.totalCost)
459
+
460
+ // Flush spans to all exporters
461
+ await tracer.flush()
462
+ ```
463
+
464
+ You can provide a custom exporter:
465
+
466
+ ```ts
467
+ import { observe } from '@elsium-ai/observe'
468
+ import type { TracerExporter } from '@elsium-ai/observe'
469
+
470
+ const myExporter: TracerExporter = {
471
+ name: 'my-backend',
472
+ async export(spans) {
473
+ await fetch('https://my-telemetry.example.com/spans', {
474
+ method: 'POST',
475
+ body: JSON.stringify(spans),
476
+ })
477
+ },
478
+ }
479
+
480
+ const tracer = observe({ output: [myExporter] })
481
+ ```
482
+
483
+ ---
484
+
485
+ ## Metrics
486
+
487
+ General-purpose metrics collection with counters, gauges, and histograms.
488
+
489
+ ### `MetricEntry`
490
+
491
+ ```ts
492
+ interface MetricEntry {
493
+ name: string
494
+ type: 'counter' | 'gauge' | 'histogram'
495
+ value: number
496
+ tags: Record<string, string>
497
+ timestamp: number
498
+ }
499
+ ```
500
+
501
+ ### `MetricsCollector`
502
+
503
+ ```ts
504
+ interface MetricsCollector {
505
+ increment(name: string, value?: number, tags?: Record<string, string>): void
506
+ gauge(name: string, value: number, tags?: Record<string, string>): void
507
+ histogram(name: string, value: number, tags?: Record<string, string>): void
508
+ getMetrics(): MetricEntry[]
509
+ reset(): void
510
+ }
511
+ ```
512
+
513
+ ### `createMetrics()`
514
+
515
+ Creates a metrics collector for counters, gauges, and histograms.
516
+
517
+ ```ts
518
+ function createMetrics(options?: { maxEntries?: number }): MetricsCollector
519
+ ```
520
+
521
+ | Parameter | Type | Default | Description |
522
+ |---|---|---|---|
523
+ | `options.maxEntries` | `number` | `50000` | Maximum metric entries held in memory |
524
+
525
+ **Returns:** `MetricsCollector`
526
+
527
+ ```ts
528
+ import { createMetrics } from '@elsium-ai/observe'
529
+
530
+ const metrics = createMetrics()
531
+
532
+ // Increment a counter (default increment is 1)
533
+ metrics.increment('llm.calls', 1, { model: 'gpt-4o' })
534
+
535
+ // Set a gauge to a current value
536
+ metrics.gauge('queue.depth', 42, { queue: 'embeddings' })
537
+
538
+ // Record a histogram observation
539
+ metrics.histogram('llm.latency_ms', 1200, { model: 'gpt-4o' })
540
+
541
+ // Retrieve all recorded entries
542
+ const entries = metrics.getMetrics()
543
+ console.log(`Recorded ${entries.length} metric entries`)
544
+
545
+ // Reset all state
546
+ metrics.reset()
547
+ ```
548
+
549
+ ---
550
+
551
+ ## Audit Trail
552
+
553
+ Tamper-evident audit logging with SHA-256 hash-chaining. Every event is linked to the previous one via its hash, enabling integrity verification of the full event history.
554
+
555
+ ### `AuditEventType`
556
+
557
+ ```ts
558
+ type AuditEventType =
559
+ | 'llm_call'
560
+ | 'tool_execution'
561
+ | 'security_violation'
562
+ | 'budget_alert'
563
+ | 'policy_violation'
564
+ | 'auth_event'
565
+ | 'approval_request'
566
+ | 'approval_decision'
567
+ | 'config_change'
568
+ ```
569
+
570
+ ### `AuditEvent`
571
+
572
+ ```ts
573
+ interface AuditEvent {
574
+ id: string
575
+ sequenceId: number
576
+ type: AuditEventType
577
+ timestamp: number
578
+ actor?: string
579
+ traceId?: string
580
+ data: Record<string, unknown>
581
+ hash: string
582
+ previousHash: string
583
+ }
584
+ ```
585
+
586
+ ### `AuditStorageAdapter`
587
+
588
+ Interface for custom audit storage backends (database, file system, etc.).
589
+
590
+ ```ts
591
+ interface AuditStorageAdapter {
592
+ append(event: AuditEvent): void | Promise<void>
593
+ query(filter: AuditQueryFilter): AuditEvent[] | Promise<AuditEvent[]>
594
+ count(): number | Promise<number>
595
+ verifyIntegrity(): AuditIntegrityResult | Promise<AuditIntegrityResult>
596
+ getLastHash?(): string | Promise<string>
597
+ }
598
+ ```
599
+
600
+ ### `AuditQueryFilter`
601
+
602
+ ```ts
603
+ interface AuditQueryFilter {
604
+ type?: AuditEventType | AuditEventType[]
605
+ actor?: string
606
+ traceId?: string
607
+ fromTimestamp?: number
608
+ toTimestamp?: number
609
+ limit?: number
610
+ offset?: number
611
+ }
612
+ ```
613
+
614
+ ### `AuditIntegrityResult`
615
+
616
+ ```ts
617
+ interface AuditIntegrityResult {
618
+ valid: boolean
619
+ totalEvents: number
620
+ brokenAt?: number
621
+ chainComplete?: boolean
622
+ }
623
+ ```
624
+
625
+ ### `AuditTrailConfig`
626
+
627
+ ```ts
628
+ interface AuditTrailConfig {
629
+ storage?: AuditStorageAdapter | 'memory'
630
+ hashChain?: boolean
631
+ maxEvents?: number
632
+ onError?: (error: unknown) => void
633
+ }
634
+ ```
635
+
636
+ ### `AuditTrail`
637
+
638
+ ```ts
639
+ interface AuditTrail {
640
+ log(
641
+ type: AuditEventType,
642
+ data: Record<string, unknown>,
643
+ options?: { actor?: string; traceId?: string },
644
+ ): void
645
+ query(filter: AuditQueryFilter): Promise<AuditEvent[]>
646
+ verifyIntegrity(): Promise<AuditIntegrityResult>
647
+ readonly count: number
648
+ }
649
+ ```
650
+
651
+ ### `createAuditTrail()`
652
+
653
+ Creates a hash-chained audit trail for tamper-evident event logging.
654
+
655
+ ```ts
656
+ function createAuditTrail(config?: AuditTrailConfig): AuditTrail
657
+ ```
658
+
659
+ | Parameter | Type | Default | Description |
660
+ |---|---|---|---|
661
+ | `config.storage` | `AuditStorageAdapter \| 'memory'` | `'memory'` | Storage backend for audit events |
662
+ | `config.hashChain` | `boolean` | `true` | Enable SHA-256 hash chaining for tamper detection |
663
+ | `config.maxEvents` | `number` | `10000` | Maximum events retained in memory storage |
664
+ | `config.onError` | `(error: unknown) => void` | `undefined` | Error handler for async storage failures |
665
+
666
+ **Returns:** `AuditTrail`
667
+
668
+ ```ts
669
+ import { createAuditTrail } from '@elsium-ai/observe'
670
+
671
+ const audit = createAuditTrail({ hashChain: true })
672
+
673
+ // Log events
674
+ audit.log('llm_call', {
675
+ model: 'gpt-4o',
676
+ inputTokens: 500,
677
+ outputTokens: 200,
678
+ cost: 0.0035,
679
+ }, { actor: 'user-123', traceId: 'trc_abc' })
680
+
681
+ audit.log('tool_execution', {
682
+ tool: 'web-search',
683
+ query: 'latest news',
684
+ resultCount: 10,
685
+ })
686
+
687
+ // Query events
688
+ const llmEvents = await audit.query({ type: 'llm_call', limit: 50 })
689
+ console.log(`Found ${llmEvents.length} LLM call events`)
690
+
691
+ // Verify the hash chain has not been tampered with
692
+ const integrity = await audit.verifyIntegrity()
693
+ console.log('Audit chain valid:', integrity.valid)
694
+ console.log('Total events:', integrity.totalEvents)
695
+ ```
696
+
697
+ ### `auditMiddleware()`
698
+
699
+ Creates an ElsiumAI middleware that automatically logs every LLM call (success or failure) to the given audit trail.
700
+
701
+ ```ts
702
+ function auditMiddleware(auditTrail: AuditTrail): Middleware
703
+ ```
704
+
705
+ | Parameter | Type | Description |
706
+ |---|---|---|
707
+ | `auditTrail` | `AuditTrail` | The audit trail instance to log events to |
708
+
709
+ **Returns:** `Middleware` (from `@elsium-ai/core`)
710
+
711
+ ```ts
712
+ import { createAuditTrail, auditMiddleware } from '@elsium-ai/observe'
26
713
 
27
- // Hash-chained audit trail
28
714
  const audit = createAuditTrail({ hashChain: true })
715
+ const middleware = auditMiddleware(audit)
716
+
717
+ // Use with an ElsiumAI gateway
718
+ // gateway.use(middleware)
719
+ ```
720
+
721
+ The middleware automatically records `llm_call` events containing `provider`, `model`, `inputTokens`, `outputTokens`, `totalTokens`, `cost`, `latencyMs`, and `stopReason`. On errors, it records the error message and `success: false`.
722
+
723
+ ---
724
+
725
+ ## Provenance
726
+
727
+ Tracks the full lineage of every AI-generated output by hashing the prompt, model, config, input, and output. Enables reproducibility audits and output-to-source tracing.
728
+
729
+ ### `ProvenanceRecord`
730
+
731
+ ```ts
732
+ interface ProvenanceRecord {
733
+ id: string
734
+ outputHash: string
735
+ promptVersion: string
736
+ modelVersion: string
737
+ configHash: string
738
+ inputHash: string
739
+ timestamp: number
740
+ traceId?: string
741
+ metadata?: Record<string, unknown>
742
+ }
743
+ ```
744
+
745
+ All hash fields (`outputHash`, `promptVersion`, `modelVersion`, `configHash`, `inputHash`) are SHA-256 hex digests of their respective inputs.
746
+
747
+ ### `ProvenanceTracker`
748
+
749
+ ```ts
750
+ interface ProvenanceTracker {
751
+ record(data: {
752
+ prompt: string
753
+ model: string
754
+ config: Record<string, unknown>
755
+ input: string
756
+ output: string
757
+ traceId?: string
758
+ metadata?: Record<string, unknown>
759
+ }): ProvenanceRecord
760
+ query(filter: {
761
+ outputHash?: string
762
+ promptVersion?: string
763
+ modelVersion?: string
764
+ traceId?: string
765
+ }): ProvenanceRecord[]
766
+ getLineage(outputHash: string): ProvenanceRecord[]
767
+ readonly count: number
768
+ clear(): void
769
+ }
770
+ ```
771
+
772
+ ### `createProvenanceTracker()`
773
+
774
+ Creates a provenance tracker for recording and querying output lineage.
775
+
776
+ ```ts
777
+ function createProvenanceTracker(options?: {
778
+ maxRecords?: number
779
+ }): ProvenanceTracker
780
+ ```
781
+
782
+ | Parameter | Type | Default | Description |
783
+ |---|---|---|---|
784
+ | `options.maxRecords` | `number` | `10000` | Maximum records held in memory |
785
+
786
+ **Returns:** `ProvenanceTracker`
787
+
788
+ ```ts
789
+ import { createProvenanceTracker } from '@elsium-ai/observe'
29
790
 
30
- // Provenance tracking
31
791
  const provenance = createProvenanceTracker()
32
- provenance.record({ prompt, model, config, input, output, traceId })
33
792
 
34
- // Tracing
35
- const tracer = observe({ output: [], samplingRate: 1.0 })
36
- const span = tracer.startSpan('request', 'llm-call')
37
- // ... do work ...
793
+ // Record a generation
794
+ const record = provenance.record({
795
+ prompt: 'Summarize the following article...',
796
+ model: 'gpt-4o',
797
+ config: { temperature: 0.7, maxTokens: 500 },
798
+ input: 'The article text here...',
799
+ output: 'A concise summary of the article.',
800
+ traceId: 'trc_abc',
801
+ })
802
+
803
+ console.log('Output hash:', record.outputHash)
804
+
805
+ // Query records by output hash
806
+ const matches = provenance.query({ outputHash: record.outputHash })
807
+
808
+ // Get the full lineage for a given output (all records sharing the same traceId)
809
+ const lineage = provenance.getLineage(record.outputHash)
810
+ console.log(`Lineage has ${lineage.length} steps`)
811
+
812
+ // Check count and clear
813
+ console.log('Total records:', provenance.count)
814
+ provenance.clear()
815
+ ```
816
+
817
+ ---
818
+
819
+ ## OpenTelemetry
820
+
821
+ Compatibility layer for converting ElsiumAI spans to the OpenTelemetry format, propagating W3C Trace Context headers, and exporting via OTLP JSON to any OTel-compatible backend (Jaeger, Grafana Tempo, Datadog, Honeycomb, etc.).
822
+
823
+ ### `OTelSpanKind`
824
+
825
+ ```ts
826
+ type OTelSpanKind = 0 | 1 | 2 | 3 | 4 | 5
827
+ // 0 = UNSPECIFIED, 1 = INTERNAL, 2 = SERVER, 3 = CLIENT, 4 = PRODUCER, 5 = CONSUMER
828
+ ```
829
+
830
+ ### `OTelStatusCode`
831
+
832
+ ```ts
833
+ type OTelStatusCode = 0 | 1 | 2
834
+ // 0 = UNSET, 1 = OK, 2 = ERROR
835
+ ```
836
+
837
+ ### `OTelAttributeValue`
838
+
839
+ ```ts
840
+ interface OTelAttributeValue {
841
+ stringValue?: string
842
+ intValue?: number
843
+ doubleValue?: number
844
+ boolValue?: boolean
845
+ arrayValue?: { values: OTelAttributeValue[] }
846
+ }
847
+ ```
848
+
849
+ ### `OTelAttribute`
850
+
851
+ ```ts
852
+ interface OTelAttribute {
853
+ key: string
854
+ value: OTelAttributeValue
855
+ }
856
+ ```
857
+
858
+ ### `OTelEvent`
859
+
860
+ ```ts
861
+ interface OTelEvent {
862
+ name: string
863
+ timeUnixNano: string
864
+ attributes: OTelAttribute[]
865
+ }
866
+ ```
867
+
868
+ ### `OTelSpan`
869
+
870
+ The OpenTelemetry-compatible span representation.
871
+
872
+ ```ts
873
+ interface OTelSpan {
874
+ traceId: string
875
+ spanId: string
876
+ parentSpanId?: string
877
+ name: string
878
+ kind: OTelSpanKind
879
+ startTimeUnixNano: string
880
+ endTimeUnixNano: string
881
+ attributes: OTelAttribute[]
882
+ events: OTelEvent[]
883
+ status: {
884
+ code: OTelStatusCode
885
+ message?: string
886
+ }
887
+ }
888
+ ```
889
+
890
+ ### `OTelResource`
891
+
892
+ ```ts
893
+ interface OTelResource {
894
+ attributes: OTelAttribute[]
895
+ }
896
+ ```
897
+
898
+ ### `OTelExportRequest`
899
+
900
+ The full OTLP JSON export payload structure.
901
+
902
+ ```ts
903
+ interface OTelExportRequest {
904
+ resourceSpans: Array<{
905
+ resource: OTelResource
906
+ scopeSpans: Array<{
907
+ scope: {
908
+ name: string
909
+ version: string
910
+ }
911
+ spans: OTelSpan[]
912
+ }>
913
+ }>
914
+ }
915
+ ```
916
+
917
+ ### `TraceContext`
918
+
919
+ Parsed W3C Trace Context.
920
+
921
+ ```ts
922
+ interface TraceContext {
923
+ traceId: string
924
+ spanId: string
925
+ traceFlags: number
926
+ traceState?: string
927
+ }
928
+ ```
929
+
930
+ ### `OTLPExporterConfig`
931
+
932
+ ```ts
933
+ interface OTLPExporterConfig {
934
+ /** OTLP endpoint URL (e.g. http://localhost:4318/v1/traces) */
935
+ endpoint: string
936
+ /** Optional headers (e.g. for auth) */
937
+ headers?: Record<string, string>
938
+ /** Service name for resource attributes */
939
+ serviceName?: string
940
+ /** Service version */
941
+ serviceVersion?: string
942
+ /** Batch size before sending */
943
+ batchSize?: number
944
+ /** Flush interval in ms */
945
+ flushIntervalMs?: number
946
+ }
947
+ ```
948
+
949
+ ### `toOTelSpan()`
950
+
951
+ Converts an ElsiumAI `SpanData` to an OpenTelemetry-compatible span.
952
+
953
+ ```ts
954
+ function toOTelSpan(span: SpanData): OTelSpan
955
+ ```
956
+
957
+ | Parameter | Type | Description |
958
+ |---|---|---|
959
+ | `span` | `SpanData` | The ElsiumAI span to convert |
960
+
961
+ **Returns:** `OTelSpan`
962
+
963
+ ```ts
964
+ import { createSpan, toOTelSpan } from '@elsium-ai/observe'
965
+
966
+ const span = createSpan('my-operation', { kind: 'llm' })
967
+ span.end()
968
+
969
+ const otelSpan = toOTelSpan(span.toJSON())
970
+ console.log(otelSpan.traceId, otelSpan.spanId)
971
+ ```
972
+
973
+ ElsiumAI span kinds are mapped as follows: `llm` to CLIENT (3), `tool`/`agent`/`workflow` to INTERNAL (1), `custom` to UNSPECIFIED (0).
974
+
975
+ ### `toOTelExportRequest()`
976
+
977
+ Builds a full OTLP JSON export request from a batch of spans.
978
+
979
+ ```ts
980
+ function toOTelExportRequest(
981
+ spans: SpanData[],
982
+ options?: {
983
+ serviceName?: string
984
+ serviceVersion?: string
985
+ },
986
+ ): OTelExportRequest
987
+ ```
988
+
989
+ | Parameter | Type | Default | Description |
990
+ |---|---|---|---|
991
+ | `spans` | `SpanData[]` | -- | Batch of spans to export |
992
+ | `options.serviceName` | `string` | `'elsium-ai'` | Service name in resource attributes |
993
+ | `options.serviceVersion` | `string` | `'0.1.0'` | Service version in resource attributes |
994
+
995
+ **Returns:** `OTelExportRequest`
996
+
997
+ ```ts
998
+ import { observe, toOTelExportRequest } from '@elsium-ai/observe'
999
+
1000
+ const tracer = observe({ output: [] })
1001
+ const span = tracer.startSpan('process-request', 'agent')
1002
+ span.end()
1003
+
1004
+ const payload = toOTelExportRequest(tracer.getSpans(), {
1005
+ serviceName: 'my-ai-service',
1006
+ serviceVersion: '1.0.0',
1007
+ })
1008
+
1009
+ // Send to any OTLP-compatible endpoint
1010
+ await fetch('http://localhost:4318/v1/traces', {
1011
+ method: 'POST',
1012
+ headers: { 'Content-Type': 'application/json' },
1013
+ body: JSON.stringify(payload),
1014
+ })
1015
+ ```
1016
+
1017
+ ### `toTraceparent()`
1018
+
1019
+ Creates a W3C `traceparent` header value from a span.
1020
+
1021
+ ```ts
1022
+ function toTraceparent(span: SpanData): string
1023
+ ```
1024
+
1025
+ | Parameter | Type | Description |
1026
+ |---|---|---|
1027
+ | `span` | `SpanData` | The span to derive the traceparent from |
1028
+
1029
+ **Returns:** `string` -- Format: `00-{traceId}-{spanId}-01`
1030
+
1031
+ ```ts
1032
+ import { createSpan, toTraceparent } from '@elsium-ai/observe'
1033
+
1034
+ const span = createSpan('outgoing-call', { kind: 'llm' })
1035
+ span.end()
1036
+
1037
+ const header = toTraceparent(span.toJSON())
1038
+ // e.g. "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
1039
+ ```
1040
+
1041
+ ### `parseTraceparent()`
1042
+
1043
+ Parses a W3C `traceparent` header string into a `TraceContext`.
1044
+
1045
+ ```ts
1046
+ function parseTraceparent(header: string): TraceContext | null
1047
+ ```
1048
+
1049
+ | Parameter | Type | Description |
1050
+ |---|---|---|
1051
+ | `header` | `string` | The traceparent header value |
1052
+
1053
+ **Returns:** `TraceContext | null` -- Returns `null` if the header is malformed or uses an unsupported version.
1054
+
1055
+ ```ts
1056
+ import { parseTraceparent } from '@elsium-ai/observe'
1057
+
1058
+ const ctx = parseTraceparent('00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01')
1059
+ if (ctx) {
1060
+ console.log('Trace ID:', ctx.traceId)
1061
+ console.log('Span ID:', ctx.spanId)
1062
+ console.log('Sampled:', ctx.traceFlags === 1)
1063
+ }
1064
+ ```
1065
+
1066
+ ### `injectTraceContext()`
1067
+
1068
+ Injects the `traceparent` header into an outgoing HTTP headers object for distributed trace propagation.
1069
+
1070
+ ```ts
1071
+ function injectTraceContext(
1072
+ span: SpanData,
1073
+ headers?: Record<string, string>,
1074
+ ): Record<string, string>
1075
+ ```
1076
+
1077
+ | Parameter | Type | Default | Description |
1078
+ |---|---|---|---|
1079
+ | `span` | `SpanData` | -- | The span whose trace context to inject |
1080
+ | `headers` | `Record<string, string>` | `{}` | Existing headers to merge with |
1081
+
1082
+ **Returns:** `Record<string, string>` -- A new headers object with `traceparent` set.
1083
+
1084
+ ```ts
1085
+ import { createSpan, injectTraceContext } from '@elsium-ai/observe'
1086
+
1087
+ const span = createSpan('api-call', { kind: 'llm' })
1088
+ const headers = injectTraceContext(span.toJSON(), {
1089
+ 'Content-Type': 'application/json',
1090
+ Authorization: 'Bearer token',
1091
+ })
1092
+ // headers now includes { traceparent: '00-...-...-01', ... }
1093
+
1094
+ await fetch('https://api.example.com/generate', { headers })
1095
+ span.end()
1096
+ ```
1097
+
1098
+ ### `extractTraceContext()`
1099
+
1100
+ Extracts a `TraceContext` from incoming HTTP headers. Checks both `traceparent` and `Traceparent` keys.
1101
+
1102
+ ```ts
1103
+ function extractTraceContext(
1104
+ headers: Record<string, string | undefined>,
1105
+ ): TraceContext | null
1106
+ ```
1107
+
1108
+ | Parameter | Type | Description |
1109
+ |---|---|---|
1110
+ | `headers` | `Record<string, string \| undefined>` | Incoming HTTP headers |
1111
+
1112
+ **Returns:** `TraceContext | null`
1113
+
1114
+ ```ts
1115
+ import { extractTraceContext, createSpan } from '@elsium-ai/observe'
1116
+
1117
+ // In an HTTP handler
1118
+ function handleRequest(req: { headers: Record<string, string> }) {
1119
+ const parentCtx = extractTraceContext(req.headers)
1120
+
1121
+ const span = createSpan('handle-request', {
1122
+ traceId: parentCtx?.traceId,
1123
+ parentId: parentCtx?.spanId,
1124
+ kind: 'agent',
1125
+ })
1126
+
1127
+ // ... process request ...
1128
+ span.end()
1129
+ }
1130
+ ```
1131
+
1132
+ ### `createOTLPExporter()`
1133
+
1134
+ Creates an OTLP JSON exporter that sends spans to any OTel-compatible backend. Supports batching and automatic periodic flushing.
1135
+
1136
+ ```ts
1137
+ function createOTLPExporter(config: OTLPExporterConfig): TracerExporter
1138
+ ```
1139
+
1140
+ | Parameter | Type | Default | Description |
1141
+ |---|---|---|---|
1142
+ | `config.endpoint` | `string` | -- | OTLP endpoint URL (e.g. `http://localhost:4318/v1/traces`) |
1143
+ | `config.headers` | `Record<string, string>` | `{}` | Optional HTTP headers (e.g. for authentication) |
1144
+ | `config.serviceName` | `string` | `undefined` | Service name for resource attributes |
1145
+ | `config.serviceVersion` | `string` | `undefined` | Service version for resource attributes |
1146
+ | `config.batchSize` | `number` | `100` | Number of spans to buffer before sending a batch |
1147
+ | `config.flushIntervalMs` | `number` | `5000` | Automatic flush interval in milliseconds |
1148
+
1149
+ **Returns:** `TracerExporter`
1150
+
1151
+ ```ts
1152
+ import { observe, createOTLPExporter } from '@elsium-ai/observe'
1153
+
1154
+ const exporter = createOTLPExporter({
1155
+ endpoint: 'http://localhost:4318/v1/traces',
1156
+ serviceName: 'my-ai-service',
1157
+ serviceVersion: '1.0.0',
1158
+ headers: { Authorization: 'Bearer my-token' },
1159
+ batchSize: 50,
1160
+ flushIntervalMs: 10000,
1161
+ })
1162
+
1163
+ const tracer = observe({ output: [exporter] })
1164
+
1165
+ const span = tracer.startSpan('generate', 'llm')
38
1166
  span.end()
1167
+
1168
+ // Spans are batched and sent automatically, or flush manually:
1169
+ await tracer.flush()
39
1170
  ```
40
1171
 
1172
+ ---
1173
+
41
1174
  ## Part of ElsiumAI
42
1175
 
43
1176
  This package is the observability layer of the [ElsiumAI](https://github.com/elsium-ai/elsium-ai) framework. See the [full documentation](https://github.com/elsium-ai/elsium-ai) for guides and examples.
package/dist/audit.d.ts CHANGED
@@ -44,6 +44,8 @@ export interface AuditTrail {
44
44
  actor?: string;
45
45
  traceId?: string;
46
46
  }): void;
47
+ /** Resolves once async initialization (e.g. getLastHash) has completed. */
48
+ ready(): Promise<void>;
47
49
  query(filter: AuditQueryFilter): Promise<AuditEvent[]>;
48
50
  verifyIntegrity(): Promise<AuditIntegrityResult>;
49
51
  readonly count: number;
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,iBAAiB,CAAA;AAEpF,MAAM,MAAM,cAAc,GACvB,UAAU,GACV,gBAAgB,GAChB,oBAAoB,GACpB,cAAc,GACd,kBAAkB,GAClB,YAAY,GACZ,kBAAkB,GAClB,mBAAmB,GACnB,eAAe,CAAA;AAElB,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,cAAc,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IACrE,KAAK,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACjC,eAAe,IAAI,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACvE,WAAW,CAAC,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,cAAc,GAAG,cAAc,EAAE,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,CAAC,EAAE,mBAAmB,GAAG,QAAQ,CAAA;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,UAAU;IAC1B,GAAG,CACF,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5C,IAAI,CAAA;IACP,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IACtD,eAAe,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACtB;AAyFD,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAuEtE;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CA0ClE"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,iBAAiB,CAAA;AAEpF,MAAM,MAAM,cAAc,GACvB,UAAU,GACV,gBAAgB,GAChB,oBAAoB,GACpB,cAAc,GACd,kBAAkB,GAClB,YAAY,GACZ,kBAAkB,GAClB,mBAAmB,GACnB,eAAe,CAAA;AAElB,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,cAAc,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IACrE,KAAK,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACjC,eAAe,IAAI,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACvE,WAAW,CAAC,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,cAAc,GAAG,cAAc,EAAE,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,CAAC,EAAE,mBAAmB,GAAG,QAAQ,CAAA;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,UAAU;IAC1B,GAAG,CACF,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5C,IAAI,CAAA;IACP,2EAA2E;IAC3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IACtD,eAAe,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACtB;AAyFD,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAqGtE;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CA0ClE"}
package/dist/index.js CHANGED
@@ -491,6 +491,7 @@ function createCostEngine(config = {}) {
491
491
  };
492
492
  }
493
493
  // src/tracer.ts
494
+ import { writeFileSync } from "node:fs";
494
495
  var log = createLogger();
495
496
  function observe(config = {}) {
496
497
  const {
@@ -506,7 +507,21 @@ function observe(config = {}) {
506
507
  for (const out of output) {
507
508
  if (out === "console") {
508
509
  handlers.push(consoleHandler);
509
- } else if (out === "json-file") {} else {
510
+ } else if (out === "json-file") {
511
+ exporters.push({
512
+ name: "json-file",
513
+ export(spansToExport) {
514
+ const filename = `.elsium/traces-${Date.now()}.json`;
515
+ try {
516
+ writeFileSync(filename, JSON.stringify(spansToExport, null, 2));
517
+ } catch (err2) {
518
+ log.error("Failed to write trace file", {
519
+ error: err2 instanceof Error ? err2.message : String(err2)
520
+ });
521
+ }
522
+ }
523
+ });
524
+ } else {
510
525
  exporters.push(out);
511
526
  }
512
527
  }
@@ -757,41 +772,55 @@ function createAuditTrail(config) {
757
772
  let sequenceId = 0;
758
773
  let idCounter = 0;
759
774
  let previousHash = "0".repeat(64);
775
+ let isReady = true;
776
+ let readyPromise = Promise.resolve();
760
777
  if (useHashChain && storage.getLastHash) {
761
778
  const lastHash = storage.getLastHash();
762
779
  if (typeof lastHash === "string") {
763
780
  previousHash = lastHash;
764
- } else if (lastHash && typeof lastHash.then === "function") {
765
- lastHash.then((hash) => {
781
+ } else {
782
+ isReady = false;
783
+ readyPromise = lastHash.then((hash) => {
766
784
  if (typeof hash === "string")
767
785
  previousHash = hash;
786
+ isReady = true;
768
787
  });
769
788
  }
770
789
  }
790
+ function appendEntry(type, data, options) {
791
+ sequenceId++;
792
+ idCounter++;
793
+ const event = {
794
+ id: `audit_${idCounter.toString(36)}_${Date.now().toString(36)}`,
795
+ sequenceId,
796
+ type,
797
+ timestamp: Date.now(),
798
+ actor: options?.actor,
799
+ traceId: options?.traceId,
800
+ data,
801
+ previousHash: useHashChain ? previousHash : "0".repeat(64)
802
+ };
803
+ const hash = useHashChain ? computeEventHash(event, event.previousHash) : createHash("sha256").update(JSON.stringify(event)).digest("hex");
804
+ const finalEvent = { ...event, hash };
805
+ if (useHashChain) {
806
+ previousHash = hash;
807
+ }
808
+ const result = storage.append(finalEvent);
809
+ if (result && typeof result.catch === "function") {
810
+ result.catch((err2) => config?.onError?.(err2));
811
+ }
812
+ }
771
813
  return {
772
814
  log(type, data, options) {
773
- sequenceId++;
774
- idCounter++;
775
- const event = {
776
- id: `audit_${idCounter.toString(36)}_${Date.now().toString(36)}`,
777
- sequenceId,
778
- type,
779
- timestamp: Date.now(),
780
- actor: options?.actor,
781
- traceId: options?.traceId,
782
- data,
783
- previousHash: useHashChain ? previousHash : "0".repeat(64)
784
- };
785
- const hash = useHashChain ? computeEventHash(event, event.previousHash) : createHash("sha256").update(JSON.stringify(event)).digest("hex");
786
- const finalEvent = { ...event, hash };
787
- if (useHashChain) {
788
- previousHash = hash;
789
- }
790
- const result = storage.append(finalEvent);
791
- if (result && typeof result.catch === "function") {
792
- result.catch((err2) => config?.onError?.(err2));
815
+ if (isReady) {
816
+ appendEntry(type, data, options);
817
+ } else {
818
+ readyPromise = readyPromise.then(() => appendEntry(type, data, options));
793
819
  }
794
820
  },
821
+ ready() {
822
+ return readyPromise;
823
+ },
795
824
  async query(filter) {
796
825
  return storage.query(filter);
797
826
  },
@@ -1059,6 +1088,16 @@ function createOTLPExporter(config) {
1059
1088
  } else {
1060
1089
  startAutoFlush();
1061
1090
  }
1091
+ },
1092
+ async shutdown() {
1093
+ if (flushTimer) {
1094
+ clearInterval(flushTimer);
1095
+ flushTimer = null;
1096
+ }
1097
+ if (buffer.length > 0) {
1098
+ const batch = buffer.splice(0, buffer.length);
1099
+ await sendBatch(batch);
1100
+ }
1062
1101
  }
1063
1102
  };
1064
1103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,QAAQ,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAM9C,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,YAAY,CAAA;IAClB,iBAAiB,EAAE,MAAM,CAAA;IACzB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,MAAM,EAAE;QACP,IAAI,EAAE,cAAc,CAAA;QACpB,OAAO,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACD;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAGhD,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAGtC,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,kBAAkB,CAAA;CACzB;AAED,MAAM,WAAW,kBAAkB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE;QAAE,MAAM,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAAA;CAC7C;AAED,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,aAAa,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,YAAY;IAC5B,UAAU,EAAE,aAAa,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,iBAAiB;IACjC,aAAa,EAAE,KAAK,CAAC;QACpB,QAAQ,EAAE,YAAY,CAAA;QACtB,UAAU,EAAE,KAAK,CAAC;YACjB,KAAK,EAAE;gBACN,IAAI,EAAE,MAAM,CAAA;gBACZ,OAAO,EAAE,MAAM,CAAA;aACf,CAAA;YACD,KAAK,EAAE,QAAQ,EAAE,CAAA;SACjB,CAAC,CAAA;KACF,CAAC,CAAA;CACF;AA+BD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CA6BnD;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,QAAQ,EAAE,EACjB,OAAO,GAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;CAClB,GACJ,iBAAiB,CA0BnB;AAID,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAMpD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAapE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACzC,YAAY,GAAG,IAAI,CAIrB;AAID,MAAM,WAAW,kBAAkB;IAClC,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sBAAsB;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2BAA2B;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,cAAc,CA4D7E"}
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"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"}
package/dist/tracer.d.ts CHANGED
@@ -9,6 +9,7 @@ export type TracerOutput = 'console' | 'json-file' | TracerExporter;
9
9
  export interface TracerExporter {
10
10
  name: string;
11
11
  export(spans: SpanData[]): void | Promise<void>;
12
+ shutdown?(): void | Promise<void>;
12
13
  }
13
14
  export interface CostReport {
14
15
  totalCost: number;
@@ -1 +1 @@
1
- {"version":3,"file":"tracer.d.ts","sourceRoot":"","sources":["../src/tracer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAe,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAGnE,MAAM,WAAW,YAAY;IAC5B,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;IACvB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,CAAA;AAEnE,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C;AAED,MAAM,WAAW,UAAU;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CACd,MAAM,EACN;QACC,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE,MAAM,CAAA;KACb,CACD,CAAA;CACD;AAED,MAAM,WAAW,MAAM;IACtB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC9C,QAAQ,IAAI,QAAQ,EAAE,CAAA;IACtB,aAAa,IAAI,UAAU,CAAA;IAC3B,YAAY,CAAC,IAAI,EAAE;QAClB,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,EAAE,MAAM,CAAA;KACjB,GAAG,IAAI,CAAA;IACR,KAAK,IAAI,IAAI,CAAA;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED,wBAAgB,OAAO,CAAC,MAAM,GAAE,YAAiB,GAAG,MAAM,CAkGzD"}
1
+ {"version":3,"file":"tracer.d.ts","sourceRoot":"","sources":["../src/tracer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAe,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAGnE,MAAM,WAAW,YAAY;IAC5B,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;IACvB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,CAAA;AAEnE,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,QAAQ,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,UAAU;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CACd,MAAM,EACN;QACC,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE,MAAM,CAAA;KACb,CACD,CAAA;CACD;AAED,MAAM,WAAW,MAAM;IACtB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC9C,QAAQ,IAAI,QAAQ,EAAE,CAAA;IACtB,aAAa,IAAI,UAAU,CAAA;IAC3B,YAAY,CAAC,IAAI,EAAE;QAClB,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,EAAE,MAAM,CAAA;KACjB,GAAG,IAAI,CAAA;IACR,KAAK,IAAI,IAAI,CAAA;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED,wBAAgB,OAAO,CAAC,MAAM,GAAE,YAAiB,GAAG,MAAM,CA8GzD"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@elsium-ai/observe",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Observability, tracing, and cost tracking for ElsiumAI",
5
5
  "license": "MIT",
6
6
  "author": "Eric Utrera <ebutrera9103@gmail.com>",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/elsium-ai/elsium-ai",
9
+ "url": "git+https://github.com/elsium-ai/elsium-ai.git",
10
10
  "directory": "packages/observe"
11
11
  },
12
12
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  "dev": "bun --watch src/index.ts"
27
27
  },
28
28
  "dependencies": {
29
- "@elsium-ai/core": "^0.2.1"
29
+ "@elsium-ai/core": "^0.2.3"
30
30
  },
31
31
  "devDependencies": {
32
32
  "typescript": "^5.7.0"