@struktur/telemetry 2.1.2 → 2.3.0

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/src/types.ts DELETED
@@ -1,453 +0,0 @@
1
- /**
2
- * Core telemetry types and interfaces for Struktur
3
- *
4
- * This module defines the TelemetryAdapter interface that all telemetry providers
5
- * must implement. The SDK uses this interface to emit telemetry events without
6
- * knowing about specific provider implementations.
7
- */
8
-
9
- /**
10
- * Represents the different kinds of spans that can be created during extraction
11
- */
12
- export type SpanKind =
13
- | "CHAIN" // Extraction pipeline, strategies
14
- | "LLM" // LLM calls (generateText, generateObject)
15
- | "TOOL" // Agent tool calls (bash, read, etc.)
16
- | "AGENT" // Agent strategy execution
17
- | "RETRIEVER" // Document parsing, chunking
18
- | "EMBEDDING" // Vector embeddings (future)
19
- | "RERANKER"; // Reranking (future)
20
-
21
- /**
22
- * Context for creating a new span
23
- */
24
- export interface SpanContext {
25
- /** Human-readable name for the span */
26
- name: string;
27
-
28
- /** Type of span */
29
- kind: SpanKind;
30
-
31
- /** Parent span for creating hierarchical traces */
32
- parentSpan?: Span;
33
-
34
- /** Initial attributes to set on the span */
35
- attributes?: Record<string, unknown>;
36
-
37
- /** Start time (defaults to now) */
38
- startTime?: number;
39
- }
40
-
41
- /**
42
- * Represents an active span in the telemetry system
43
- */
44
- export interface Span {
45
- /** Unique identifier for this span */
46
- id: string;
47
-
48
- /** Trace identifier that groups related spans */
49
- traceId: string;
50
-
51
- /** Human-readable name */
52
- name: string;
53
-
54
- /** Type of span */
55
- kind: SpanKind;
56
-
57
- /** Unix timestamp when span started */
58
- startTime: number;
59
-
60
- /** Parent span ID (if any) */
61
- parentId?: string;
62
- }
63
-
64
- /**
65
- * Result of a completed span
66
- */
67
- export interface SpanResult {
68
- /** Whether the operation succeeded or failed */
69
- status: "ok" | "error";
70
-
71
- /** Error details if status is "error" */
72
- error?: Error;
73
-
74
- /** Output data from the operation */
75
- output?: unknown;
76
-
77
- /** Latency in milliseconds */
78
- latencyMs?: number;
79
- }
80
-
81
- /**
82
- * Context that applies to all spans in a trace
83
- */
84
- export interface TelemetryContext {
85
- /** Session identifier for grouping related extractions */
86
- sessionId?: string;
87
-
88
- /** User identifier */
89
- userId?: string;
90
-
91
- /** Additional metadata */
92
- metadata?: Record<string, unknown>;
93
-
94
- /** Tags for categorization */
95
- tags?: string[];
96
- }
97
-
98
- /**
99
- * Token usage information from LLM calls
100
- */
101
- export interface TokenUsage {
102
- /** Input/prompt tokens */
103
- input: number;
104
-
105
- /** Output/completion tokens */
106
- output: number;
107
-
108
- /** Total tokens (input + output) */
109
- total: number;
110
- }
111
-
112
- /**
113
- * Event emitted when an LLM call is made
114
- */
115
- export interface LLMCallEvent {
116
- type: "llm_call";
117
-
118
- /** Model identifier (e.g., "gpt-4o", "claude-3-opus") */
119
- model: string;
120
-
121
- /** Provider name (e.g., "openai", "anthropic") */
122
- provider: string;
123
-
124
- /** Input parameters */
125
- input: {
126
- /** Messages sent to the LLM */
127
- messages: Array<{ role: string; content: string }>;
128
-
129
- /** Temperature parameter (if set) */
130
- temperature?: number;
131
-
132
- /** Max tokens parameter (if set) */
133
- maxTokens?: number;
134
-
135
- /** JSON schema for structured output (if set) */
136
- schema?: unknown;
137
- };
138
-
139
- /** Output from the LLM (if successful) */
140
- output?: {
141
- /** Raw content from the LLM */
142
- content: string;
143
-
144
- /** Whether this was structured (JSON) output */
145
- structured?: boolean;
146
-
147
- /** Token usage information */
148
- usage?: TokenUsage;
149
- };
150
-
151
- /** Latency in milliseconds */
152
- latencyMs: number;
153
-
154
- /** Error if the call failed */
155
- error?: Error;
156
- }
157
-
158
- /**
159
- * Event emitted during schema validation
160
- */
161
- export interface ValidationEvent {
162
- type: "validation";
163
-
164
- /** Current attempt number */
165
- attempt: number;
166
-
167
- /** Maximum allowed attempts */
168
- maxAttempts: number;
169
-
170
- /** Schema being validated against */
171
- schema: unknown;
172
-
173
- /** Input data being validated */
174
- input: unknown;
175
-
176
- /** Whether validation succeeded */
177
- success: boolean;
178
-
179
- /** Validation errors (if failed) */
180
- errors?: Array<{ path: string; message: string }>;
181
-
182
- /** Latency in milliseconds */
183
- latencyMs?: number;
184
- }
185
-
186
- /**
187
- * Event emitted when chunking documents
188
- */
189
- export interface ChunkEvent {
190
- type: "chunk";
191
-
192
- /** Index of this chunk (0-based) */
193
- chunkIndex: number;
194
-
195
- /** Total number of chunks */
196
- totalChunks: number;
197
-
198
- /** Number of tokens in this chunk */
199
- tokens: number;
200
-
201
- /** Number of images in this chunk */
202
- images: number;
203
-
204
- /** Preview of chunk content (optional) */
205
- content?: string;
206
- }
207
-
208
- /**
209
- * Event emitted when agent tools are called
210
- */
211
- export interface ToolCallEvent {
212
- type: "tool_call";
213
-
214
- /** Name of the tool */
215
- toolName: string;
216
-
217
- /** Arguments passed to the tool */
218
- args: Record<string, unknown>;
219
-
220
- /** Result from the tool (if successful) */
221
- result?: unknown;
222
-
223
- /** Error if the tool failed */
224
- error?: Error;
225
-
226
- /** Latency in milliseconds */
227
- latencyMs?: number;
228
- }
229
-
230
- /**
231
- * Event emitted when merging results from multiple chunks
232
- */
233
- export interface MergeEvent {
234
- type: "merge";
235
-
236
- /** Merge strategy used */
237
- strategy: string;
238
-
239
- /** Number of input items merged */
240
- inputCount: number;
241
-
242
- /** Number of items after merge */
243
- outputCount: number;
244
-
245
- /** Number of items removed during deduplication (if applicable) */
246
- deduped?: number;
247
- }
248
-
249
- /**
250
- * Event emitted when parsing input files
251
- */
252
- export interface ParseEvent {
253
- type: "parse";
254
-
255
- /** MIME type of the file */
256
- mimeType: string;
257
-
258
- /** Parser used (e.g., "pdf-parse", "text") */
259
- parser: string;
260
-
261
- /** Input file size in bytes */
262
- inputSize: number;
263
-
264
- /** Number of tokens in output */
265
- outputTokens: number;
266
-
267
- /** Number of images extracted */
268
- outputImages: number;
269
-
270
- /** Latency in milliseconds */
271
- latencyMs: number;
272
- }
273
-
274
- /**
275
- * All possible telemetry events
276
- */
277
- export type TelemetryEvent =
278
- | LLMCallEvent
279
- | ValidationEvent
280
- | ChunkEvent
281
- | ToolCallEvent
282
- | MergeEvent
283
- | ParseEvent;
284
-
285
- /**
286
- * Interface that all telemetry adapters must implement.
287
- * This allows the SDK to emit telemetry without knowing about specific providers.
288
- */
289
- export interface TelemetryAdapter {
290
- /** Provider name */
291
- readonly name: string;
292
-
293
- /** Adapter version */
294
- readonly version: string;
295
-
296
- /**
297
- * Initialize the telemetry adapter.
298
- * Must be called before any other operations.
299
- */
300
- initialize(): Promise<void>;
301
-
302
- /**
303
- * Shutdown the telemetry adapter.
304
- * Flushes any pending telemetry data.
305
- */
306
- shutdown(): Promise<void>;
307
-
308
- /**
309
- * Start a new span.
310
- * @param context - Span creation context
311
- * @returns The created span
312
- */
313
- startSpan(context: SpanContext): Span;
314
-
315
- /**
316
- * End a span and record its result.
317
- * @param span - Span to end
318
- * @param result - Optional result of the operation
319
- */
320
- endSpan(span: Span, result?: SpanResult): void;
321
-
322
- /**
323
- * Record an event within a span.
324
- * @param span - Active span to record event in
325
- * @param event - Event to record
326
- */
327
- recordEvent(span: Span, event: TelemetryEvent): void;
328
-
329
- /**
330
- * Set attributes on a span.
331
- * @param span - Active span
332
- * @param attributes - Attributes to set
333
- */
334
- setAttributes(span: Span, attributes: Record<string, unknown>): void;
335
-
336
- /**
337
- * Set context that applies to all spans in this trace.
338
- * @param context - Context to set
339
- */
340
- setContext(context: TelemetryContext): void;
341
- }
342
-
343
- /**
344
- * Configuration options for creating a telemetry adapter
345
- */
346
- export interface TelemetryOptions {
347
- /** Provider name */
348
- provider: string;
349
-
350
- /** Provider-specific configuration */
351
- config: Record<string, unknown>;
352
-
353
- /** Whether telemetry is enabled (defaults to true) */
354
- enabled?: boolean;
355
-
356
- /** Sampling rate from 0.0 to 1.0 (1.0 = all traces) */
357
- sampleRate?: number;
358
-
359
- /** Whether to redact PII from traces */
360
- redactPii?: boolean;
361
-
362
- /** Maximum length for input content (truncate if longer) */
363
- maxInputLength?: number;
364
-
365
- /** Maximum length for output content (truncate if longer) */
366
- maxOutputLength?: number;
367
- }
368
-
369
- /**
370
- * Configuration for Phoenix telemetry
371
- */
372
- export interface PhoenixConfig {
373
- /** Project name in Phoenix */
374
- projectName: string;
375
-
376
- /** Phoenix collector endpoint URL (defaults to http://localhost:6006) */
377
- url?: string;
378
-
379
- /** API key for Phoenix Cloud */
380
- apiKey?: string;
381
-
382
- /** Use batch processing (defaults to true for production) */
383
- batch?: boolean;
384
-
385
- /** Additional headers for OTLP requests */
386
- headers?: Record<string, string>;
387
- }
388
-
389
- /**
390
- * Configuration for Langfuse telemetry
391
- */
392
- export interface LangfuseConfig {
393
- /** Langfuse public key */
394
- publicKey: string;
395
-
396
- /** Langfuse secret key */
397
- secretKey: string;
398
-
399
- /** Langfuse base URL (defaults to https://cloud.langfuse.com) */
400
- baseUrl?: string;
401
-
402
- /** Project name (optional) */
403
- projectName?: string;
404
- }
405
-
406
- /**
407
- * No-op adapter for when telemetry is disabled
408
- */
409
- export class NoopTelemetryAdapter implements TelemetryAdapter {
410
- readonly name = "noop";
411
- readonly version = "1.0.0";
412
-
413
- private currentId = 0;
414
- private mockSpans = new Map<string, Span>();
415
-
416
- async initialize(): Promise<void> {
417
- // No-op
418
- }
419
-
420
- async shutdown(): Promise<void> {
421
- // No-op
422
- }
423
-
424
- startSpan(context: SpanContext): Span {
425
- const id = `noop-${++this.currentId}`;
426
- const span: Span = {
427
- id,
428
- traceId: `trace-${this.currentId}`,
429
- name: context.name,
430
- kind: context.kind,
431
- startTime: Date.now(),
432
- parentId: context.parentSpan?.id,
433
- };
434
- this.mockSpans.set(id, span);
435
- return span;
436
- }
437
-
438
- endSpan(span: Span, _result?: SpanResult): void {
439
- this.mockSpans.delete(span.id);
440
- }
441
-
442
- recordEvent(_span: Span, _event: TelemetryEvent): void {
443
- // No-op
444
- }
445
-
446
- setAttributes(_span: Span, _attributes: Record<string, unknown>): void {
447
- // No-op
448
- }
449
-
450
- setContext(_context: TelemetryContext): void {
451
- // No-op
452
- }
453
- }
@@ -1,118 +0,0 @@
1
- /**
2
- * Unit tests for LangfuseAdapter
3
- */
4
-
5
- import { test, expect, describe } from "bun:test";
6
- import {
7
- LangfuseAdapter,
8
- createLangfuseAdapter,
9
- } from "../../src/adapters/langfuse/index.js";
10
- import type {
11
- SpanContext,
12
- LangfuseConfig,
13
- } from "../../src/types.js";
14
-
15
- describe("LangfuseAdapter", () => {
16
- test("should have correct name and version", () => {
17
- const adapter = new LangfuseAdapter({
18
- publicKey: "pk-test",
19
- secretKey: "sk-test",
20
- });
21
-
22
- expect(adapter.name).toBe("langfuse");
23
- expect(adapter.version).toBe("1.0.0");
24
- });
25
-
26
- test("should apply default baseUrl", () => {
27
- const adapter = new LangfuseAdapter({
28
- publicKey: "pk-test",
29
- secretKey: "sk-test",
30
- });
31
-
32
- expect(adapter).toBeDefined();
33
- });
34
-
35
- test("createLangfuseAdapter should create adapter", () => {
36
- const config: LangfuseConfig = {
37
- publicKey: "pk-test",
38
- secretKey: "sk-test",
39
- baseUrl: "https://cloud.langfuse.com",
40
- projectName: "my-project",
41
- };
42
-
43
- const adapter = createLangfuseAdapter(config);
44
-
45
- expect(adapter).toBeDefined();
46
- expect(adapter.name).toBe("langfuse");
47
- });
48
-
49
- test("should throw when startSpan called before initialize", () => {
50
- const adapter = new LangfuseAdapter({
51
- publicKey: "pk-test",
52
- secretKey: "sk-test",
53
- });
54
-
55
- const context: SpanContext = {
56
- name: "test-span",
57
- kind: "CHAIN",
58
- };
59
-
60
- expect(() => adapter.startSpan(context)).toThrow("not initialized");
61
- });
62
-
63
- test("should accept all span kinds", () => {
64
- const adapter = new LangfuseAdapter({
65
- publicKey: "pk-test",
66
- secretKey: "sk-test",
67
- });
68
-
69
- const kinds = ["CHAIN", "LLM", "TOOL", "AGENT", "RETRIEVER"] as const;
70
-
71
- for (const _kind of kinds) {
72
- expect(adapter).toBeDefined();
73
- }
74
- });
75
- });
76
-
77
- describe("LangfuseAdapter config", () => {
78
- test("should require public and secret keys", () => {
79
- const config: LangfuseConfig = {
80
- publicKey: "pk-lf-123",
81
- secretKey: "sk-lf-456",
82
- };
83
-
84
- const adapter = createLangfuseAdapter(config);
85
-
86
- expect(adapter).toBeDefined();
87
- });
88
-
89
- test("should accept all config options", () => {
90
- const config: LangfuseConfig = {
91
- publicKey: "pk-test",
92
- secretKey: "sk-test",
93
- baseUrl: "https://us.cloud.langfuse.com",
94
- projectName: "test-project",
95
- };
96
-
97
- const adapter = createLangfuseAdapter(config);
98
-
99
- expect(adapter).toBeDefined();
100
- });
101
- });
102
-
103
- describe("LangfuseAdapter interface", () => {
104
- test("should expose required methods", () => {
105
- const adapter = new LangfuseAdapter({
106
- publicKey: "pk-test",
107
- secretKey: "sk-test",
108
- });
109
-
110
- expect(typeof adapter.initialize).toBe("function");
111
- expect(typeof adapter.shutdown).toBe("function");
112
- expect(typeof adapter.startSpan).toBe("function");
113
- expect(typeof adapter.endSpan).toBe("function");
114
- expect(typeof adapter.recordEvent).toBe("function");
115
- expect(typeof adapter.setAttributes).toBe("function");
116
- expect(typeof adapter.setContext).toBe("function");
117
- });
118
- });
@@ -1,132 +0,0 @@
1
- /**
2
- * Unit tests for PhoenixAdapter
3
- *
4
- * These tests verify that the PhoenixAdapter correctly implements
5
- * the TelemetryAdapter interface. Since we don't have the actual
6
- * dependencies installed, these tests focus on the adapter's logic.
7
- */
8
-
9
- import { test, expect, describe } from "bun:test";
10
- import {
11
- PhoenixAdapter,
12
- createPhoenixAdapter,
13
- } from "../../src/adapters/phoenix/index.js";
14
- import type {
15
- SpanContext,
16
- SpanResult,
17
- LLMCallEvent,
18
- ValidationEvent,
19
- ChunkEvent,
20
- ToolCallEvent,
21
- MergeEvent,
22
- ParseEvent,
23
- PhoenixConfig,
24
- } from "../../src/types.js";
25
-
26
- describe("PhoenixAdapter", () => {
27
- test("should have correct name and version", () => {
28
- const adapter = new PhoenixAdapter({ projectName: "test" });
29
-
30
- expect(adapter.name).toBe("phoenix");
31
- expect(adapter.version).toBe("1.0.0");
32
- });
33
-
34
- test("should apply default config values", () => {
35
- const adapter = new PhoenixAdapter({ projectName: "test" });
36
-
37
- // Default values should be set
38
- expect(adapter).toBeDefined();
39
- });
40
-
41
- test("createPhoenixAdapter should create adapter", () => {
42
- const config: PhoenixConfig = {
43
- projectName: "my-project",
44
- url: "http://localhost:6006",
45
- apiKey: "test-key",
46
- };
47
-
48
- const adapter = createPhoenixAdapter(config);
49
-
50
- expect(adapter).toBeDefined();
51
- expect(adapter.name).toBe("phoenix");
52
- });
53
-
54
- test("should throw when startSpan called before initialize", () => {
55
- const adapter = new PhoenixAdapter({ projectName: "test" });
56
-
57
- const context: SpanContext = {
58
- name: "test-span",
59
- kind: "CHAIN",
60
- };
61
-
62
- // Should throw because OTel API isn't loaded yet
63
- expect(() => adapter.startSpan(context)).toThrow("not initialized");
64
- });
65
-
66
- test("should accept all span kinds", () => {
67
- const adapter = new PhoenixAdapter({ projectName: "test" });
68
-
69
- // Verify adapter is created for all span kinds
70
- const kinds = ["CHAIN", "LLM", "TOOL", "AGENT", "RETRIEVER", "EMBEDDING", "RERANKER"] as const;
71
-
72
- for (const kind of kinds) {
73
- expect(adapter).toBeDefined();
74
- }
75
- });
76
- });
77
-
78
- describe("PhoenixAdapter config", () => {
79
- test("should merge config with defaults", () => {
80
- const config: PhoenixConfig = {
81
- projectName: "test-project",
82
- url: "https://app.phoenix.arize.com",
83
- apiKey: "phx-api-key",
84
- batch: false,
85
- };
86
-
87
- const adapter = createPhoenixAdapter(config);
88
-
89
- expect(adapter).toBeDefined();
90
- expect(adapter.name).toBe("phoenix");
91
- });
92
-
93
- test("should work with minimal config", () => {
94
- const config: PhoenixConfig = {
95
- projectName: "minimal",
96
- };
97
-
98
- const adapter = createPhoenixAdapter(config);
99
-
100
- expect(adapter).toBeDefined();
101
- });
102
-
103
- test("should handle all optional config fields", () => {
104
- const config: PhoenixConfig = {
105
- projectName: "full-config",
106
- url: "http://localhost:6006",
107
- apiKey: "secret-key",
108
- batch: true,
109
- headers: {
110
- "X-Custom-Header": "value",
111
- },
112
- };
113
-
114
- const adapter = createPhoenixAdapter(config);
115
-
116
- expect(adapter).toBeDefined();
117
- });
118
- });
119
-
120
- describe("PhoenixAdapter interface", () => {
121
- test("should expose required methods", () => {
122
- const adapter = new PhoenixAdapter({ projectName: "test" });
123
-
124
- expect(typeof adapter.initialize).toBe("function");
125
- expect(typeof adapter.shutdown).toBe("function");
126
- expect(typeof adapter.startSpan).toBe("function");
127
- expect(typeof adapter.endSpan).toBe("function");
128
- expect(typeof adapter.recordEvent).toBe("function");
129
- expect(typeof adapter.setAttributes).toBe("function");
130
- expect(typeof adapter.setContext).toBe("function");
131
- });
132
- });