@struktur/telemetry 2.1.1 → 2.2.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.
@@ -1,145 +1,89 @@
1
- /**
2
- * Langfuse telemetry adapter
3
- *
4
- * Implements TelemetryAdapter for Langfuse using their OpenTelemetry SDK.
5
- */
6
-
7
- import type {
8
- TelemetryAdapter,
9
- SpanContext,
10
- Span,
11
- SpanResult,
12
- TelemetryEvent,
13
- TelemetryContext,
14
- LangfuseConfig,
15
- LLMCallEvent,
16
- ValidationEvent,
17
- ChunkEvent,
18
- ToolCallEvent,
19
- MergeEvent,
20
- ParseEvent,
21
- } from "../../types.js";
22
-
23
- type OtelSpan = {
24
- spanContext: () => { spanId: string; traceId: string };
25
- setStatus: (status: { code: number; message?: string }) => void;
26
- setAttribute: (key: string, value: string | number | boolean | undefined) => void;
27
- setAttributes: (attrs: Record<string, string | number | boolean>) => void;
28
- recordException: (error: Error) => void;
29
- end: () => void;
30
- };
31
-
32
- /**
33
- * Langfuse telemetry adapter using OpenTelemetry
34
- */
35
- export class LangfuseAdapter implements TelemetryAdapter {
36
- readonly name = "langfuse";
37
- readonly version = "1.0.0";
38
-
39
- private config: LangfuseConfig;
40
- private sdk: { shutdown: () => Promise<void> } | null = null;
41
- private activeSpans = new Map<string, OtelSpan>();
42
- private otelApi: typeof import("@opentelemetry/api") | null = null;
43
-
44
- constructor(config: LangfuseConfig) {
1
+ // src/adapters/langfuse/LangfuseAdapter.ts
2
+ var LangfuseAdapter = class {
3
+ name = "langfuse";
4
+ version = "1.0.0";
5
+ config;
6
+ sdk = null;
7
+ activeSpans = /* @__PURE__ */ new Map();
8
+ otelApi = null;
9
+ constructor(config) {
45
10
  this.config = {
46
11
  baseUrl: "https://cloud.langfuse.com",
47
- ...config,
12
+ ...config
48
13
  };
49
14
  }
50
-
51
- async initialize(): Promise<void> {
52
- // Dynamically import Langfuse OTel SDK
15
+ async initialize() {
53
16
  const [{ LangfuseSpanProcessor }, { NodeSDK }, otelApi] = await Promise.all([
54
17
  import("@langfuse/otel"),
55
18
  import("@opentelemetry/sdk-node"),
56
- import("@opentelemetry/api"),
19
+ import("@opentelemetry/api")
57
20
  ]);
58
-
59
21
  this.otelApi = otelApi;
60
-
61
22
  const processor = new LangfuseSpanProcessor({
62
23
  publicKey: this.config.publicKey,
63
24
  secretKey: this.config.secretKey,
64
- baseUrl: this.config.baseUrl,
25
+ baseUrl: this.config.baseUrl
65
26
  });
66
-
67
27
  const sdk = new NodeSDK({
68
- spanProcessors: [processor],
28
+ spanProcessors: [processor]
69
29
  });
70
-
71
30
  sdk.start();
72
31
  this.sdk = sdk;
73
32
  }
74
-
75
- async shutdown(): Promise<void> {
33
+ async shutdown() {
76
34
  if (this.sdk) {
77
35
  await this.sdk.shutdown();
78
36
  }
79
37
  }
80
-
81
- startSpan(context: SpanContext): Span {
38
+ startSpan(context) {
82
39
  if (!this.otelApi) {
83
40
  throw new Error("LangfuseAdapter not initialized");
84
41
  }
85
-
86
42
  const tracer = this.otelApi.trace.getTracer("struktur");
87
-
88
43
  const otelSpan = tracer.startSpan(context.name, {
89
44
  attributes: {
90
45
  "observation.type": context.kind.toLowerCase(),
91
- ...context.attributes,
92
- },
93
- }) as OtelSpan;
94
-
46
+ ...context.attributes
47
+ }
48
+ });
95
49
  const spanContext = otelSpan.spanContext();
96
- const span: Span = {
50
+ const span = {
97
51
  id: spanContext.spanId,
98
52
  traceId: spanContext.traceId,
99
53
  name: context.name,
100
54
  kind: context.kind,
101
55
  startTime: context.startTime ?? Date.now(),
102
- parentId: context.parentSpan?.id,
56
+ parentId: context.parentSpan?.id
103
57
  };
104
-
105
58
  this.activeSpans.set(span.id, otelSpan);
106
59
  return span;
107
60
  }
108
-
109
- endSpan(span: Span, result?: SpanResult): void {
61
+ endSpan(span, result) {
110
62
  const otelSpan = this.activeSpans.get(span.id);
111
63
  if (!otelSpan) return;
112
-
113
64
  if (result) {
114
65
  otelSpan.setStatus({
115
66
  code: result.status === "ok" ? 1 : 2,
116
- message: result.error?.message,
67
+ message: result.error?.message
117
68
  });
118
-
119
- if (result.output !== undefined) {
69
+ if (result.output !== void 0) {
120
70
  try {
121
- const outputStr = typeof result.output === "string"
122
- ? result.output
123
- : JSON.stringify(result.output);
71
+ const outputStr = typeof result.output === "string" ? result.output : JSON.stringify(result.output);
124
72
  otelSpan.setAttribute("output", outputStr);
125
73
  } catch {
126
74
  otelSpan.setAttribute("output", "[object]");
127
75
  }
128
76
  }
129
-
130
- if (result.latencyMs !== undefined) {
77
+ if (result.latencyMs !== void 0) {
131
78
  otelSpan.setAttribute("latency_ms", result.latencyMs);
132
79
  }
133
80
  }
134
-
135
81
  otelSpan.end();
136
82
  this.activeSpans.delete(span.id);
137
83
  }
138
-
139
- recordEvent(span: Span, event: TelemetryEvent): void {
84
+ recordEvent(span, event) {
140
85
  const otelSpan = this.activeSpans.get(span.id);
141
86
  if (!otelSpan) return;
142
-
143
87
  switch (event.type) {
144
88
  case "llm_call":
145
89
  this.recordLLMCall(otelSpan, event);
@@ -161,14 +105,12 @@ export class LangfuseAdapter implements TelemetryAdapter {
161
105
  break;
162
106
  }
163
107
  }
164
-
165
- setAttributes(span: Span, attributes: Record<string, unknown>): void {
108
+ setAttributes(span, attributes) {
166
109
  const otelSpan = this.activeSpans.get(span.id);
167
110
  if (!otelSpan) return;
168
-
169
- const stringAttrs: Record<string, string | number | boolean> = {};
111
+ const stringAttrs = {};
170
112
  for (const [key, value] of Object.entries(attributes)) {
171
- if (value !== undefined && value !== null) {
113
+ if (value !== void 0 && value !== null) {
172
114
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
173
115
  stringAttrs[key] = value;
174
116
  } else {
@@ -180,140 +122,109 @@ export class LangfuseAdapter implements TelemetryAdapter {
180
122
  }
181
123
  }
182
124
  }
183
-
184
125
  otelSpan.setAttributes(stringAttrs);
185
126
  }
186
-
187
- setContext(_context: TelemetryContext): void {
188
- // Langfuse supports session_id, user_id, metadata, tags via span attributes
189
- // These would be set on individual spans
127
+ setContext(_context) {
190
128
  }
191
-
192
- private recordLLMCall(span: OtelSpan, event: LLMCallEvent): void {
193
- const attrs: Record<string, string | number | boolean> = {
129
+ recordLLMCall(span, event) {
130
+ const attrs = {
194
131
  model: event.model,
195
132
  provider: event.provider,
196
133
  input: JSON.stringify(event.input.messages),
197
134
  temperature: event.input.temperature ?? "",
198
- max_tokens: event.input.maxTokens ?? "",
135
+ max_tokens: event.input.maxTokens ?? ""
199
136
  };
200
-
201
137
  if (event.output) {
202
138
  attrs.output = event.output.content;
203
-
204
139
  if (event.output.usage) {
205
140
  attrs["usage.input"] = event.output.usage.input;
206
141
  attrs["usage.output"] = event.output.usage.output;
207
142
  attrs["usage.total"] = event.output.usage.total;
208
143
  }
209
144
  }
210
-
211
145
  attrs.latency_ms = event.latencyMs;
212
-
213
146
  if (event.error) {
214
147
  attrs.error = event.error.message;
215
148
  span.recordException(event.error);
216
149
  }
217
-
218
150
  span.setAttributes(attrs);
219
151
  }
220
-
221
- private recordValidation(span: OtelSpan, event: ValidationEvent): void {
222
- const attrs: Record<string, string | number | boolean> = {
152
+ recordValidation(span, event) {
153
+ const attrs = {
223
154
  attempt: event.attempt,
224
155
  max_attempts: event.maxAttempts,
225
- success: event.success,
156
+ success: event.success
226
157
  };
227
-
228
158
  if (event.errors && event.errors.length > 0) {
229
159
  attrs.errors = JSON.stringify(event.errors);
230
160
  }
231
-
232
- if (event.latencyMs !== undefined) {
161
+ if (event.latencyMs !== void 0) {
233
162
  attrs.latency_ms = event.latencyMs;
234
163
  }
235
-
236
164
  span.setAttributes(attrs);
237
165
  }
238
-
239
- private recordChunk(span: OtelSpan, event: ChunkEvent): void {
240
- const attrs: Record<string, string | number | boolean> = {
166
+ recordChunk(span, event) {
167
+ const attrs = {
241
168
  chunk_index: event.chunkIndex,
242
169
  chunk_total: event.totalChunks,
243
170
  chunk_tokens: event.tokens,
244
- chunk_images: event.images,
171
+ chunk_images: event.images
245
172
  };
246
-
247
173
  if (event.content) {
248
- attrs.chunk_content = event.content.slice(0, 1000);
174
+ attrs.chunk_content = event.content.slice(0, 1e3);
249
175
  }
250
-
251
176
  span.setAttributes(attrs);
252
177
  }
253
-
254
- private recordToolCall(span: OtelSpan, event: ToolCallEvent): void {
255
- const attrs: Record<string, string | number | boolean> = {
178
+ recordToolCall(span, event) {
179
+ const attrs = {
256
180
  tool_name: event.toolName,
257
- tool_args: JSON.stringify(event.args),
181
+ tool_args: JSON.stringify(event.args)
258
182
  };
259
-
260
- if (event.result !== undefined) {
183
+ if (event.result !== void 0) {
261
184
  try {
262
185
  attrs.tool_result = JSON.stringify(event.result);
263
186
  } catch {
264
187
  attrs.tool_result = "[object]";
265
188
  }
266
189
  }
267
-
268
190
  if (event.error) {
269
191
  attrs.tool_error = event.error.message;
270
192
  }
271
-
272
- if (event.latencyMs !== undefined) {
193
+ if (event.latencyMs !== void 0) {
273
194
  attrs.latency_ms = event.latencyMs;
274
195
  }
275
-
276
196
  span.setAttributes(attrs);
277
-
278
197
  if (event.error) {
279
198
  span.recordException(event.error);
280
199
  }
281
200
  }
282
-
283
- private recordMerge(span: OtelSpan, event: MergeEvent): void {
284
- const attrs: Record<string, string | number | boolean> = {
201
+ recordMerge(span, event) {
202
+ const attrs = {
285
203
  strategy: event.strategy,
286
204
  input_count: event.inputCount,
287
- output_count: event.outputCount,
205
+ output_count: event.outputCount
288
206
  };
289
-
290
- if (event.deduped !== undefined) {
207
+ if (event.deduped !== void 0) {
291
208
  attrs.deduped = event.deduped;
292
209
  }
293
-
294
210
  span.setAttributes(attrs);
295
211
  }
296
-
297
- private recordParse(span: OtelSpan, event: ParseEvent): void {
212
+ recordParse(span, event) {
298
213
  span.setAttributes({
299
214
  mime_type: event.mimeType,
300
215
  parser: event.parser,
301
216
  input_size: event.inputSize,
302
217
  output_tokens: event.outputTokens,
303
218
  output_images: event.outputImages,
304
- latency_ms: event.latencyMs,
219
+ latency_ms: event.latencyMs
305
220
  });
306
221
  }
307
- }
308
-
309
- /**
310
- * Create a Langfuse telemetry adapter
311
- *
312
- * @param config - Langfuse configuration
313
- * @returns Langfuse telemetry adapter
314
- */
315
- export function createLangfuseAdapter(config: LangfuseConfig): LangfuseAdapter {
222
+ };
223
+ function createLangfuseAdapter(config) {
316
224
  return new LangfuseAdapter(config);
317
225
  }
318
-
319
- export type { LangfuseConfig };
226
+ export {
227
+ LangfuseAdapter,
228
+ createLangfuseAdapter
229
+ };
230
+ //# sourceMappingURL=langfuse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/langfuse/LangfuseAdapter.ts"],"sourcesContent":["/**\n * Langfuse telemetry adapter\n * \n * Implements TelemetryAdapter for Langfuse using their OpenTelemetry SDK.\n */\n\nimport type {\n TelemetryAdapter,\n SpanContext,\n Span,\n SpanResult,\n TelemetryEvent,\n TelemetryContext,\n LangfuseConfig,\n LLMCallEvent,\n ValidationEvent,\n ChunkEvent,\n ToolCallEvent,\n MergeEvent,\n ParseEvent,\n} from \"../../types.js\";\n\ntype OtelSpan = {\n spanContext: () => { spanId: string; traceId: string };\n setStatus: (status: { code: number; message?: string }) => void;\n setAttribute: (key: string, value: string | number | boolean | undefined) => void;\n setAttributes: (attrs: Record<string, string | number | boolean>) => void;\n recordException: (error: Error) => void;\n end: () => void;\n};\n\n/**\n * Langfuse telemetry adapter using OpenTelemetry\n */\nexport class LangfuseAdapter implements TelemetryAdapter {\n readonly name = \"langfuse\";\n readonly version = \"1.0.0\";\n\n private config: LangfuseConfig;\n private sdk: { shutdown: () => Promise<void> } | null = null;\n private activeSpans = new Map<string, OtelSpan>();\n private otelApi: typeof import(\"@opentelemetry/api\") | null = null;\n\n constructor(config: LangfuseConfig) {\n this.config = {\n baseUrl: \"https://cloud.langfuse.com\",\n ...config,\n };\n }\n\n async initialize(): Promise<void> {\n // Dynamically import Langfuse OTel SDK\n const [{ LangfuseSpanProcessor }, { NodeSDK }, otelApi] = await Promise.all([\n import(\"@langfuse/otel\"),\n import(\"@opentelemetry/sdk-node\"),\n import(\"@opentelemetry/api\"),\n ]);\n\n this.otelApi = otelApi;\n\n const processor = new LangfuseSpanProcessor({\n publicKey: this.config.publicKey,\n secretKey: this.config.secretKey,\n baseUrl: this.config.baseUrl,\n });\n\n const sdk = new NodeSDK({\n spanProcessors: [processor],\n });\n\n sdk.start();\n this.sdk = sdk;\n }\n\n async shutdown(): Promise<void> {\n if (this.sdk) {\n await this.sdk.shutdown();\n }\n }\n\n startSpan(context: SpanContext): Span {\n if (!this.otelApi) {\n throw new Error(\"LangfuseAdapter not initialized\");\n }\n\n const tracer = this.otelApi.trace.getTracer(\"struktur\");\n\n const otelSpan = tracer.startSpan(context.name, {\n attributes: {\n \"observation.type\": context.kind.toLowerCase(),\n ...context.attributes,\n },\n }) as OtelSpan;\n\n const spanContext = otelSpan.spanContext();\n const span: Span = {\n id: spanContext.spanId,\n traceId: spanContext.traceId,\n name: context.name,\n kind: context.kind,\n startTime: context.startTime ?? Date.now(),\n parentId: context.parentSpan?.id,\n };\n\n this.activeSpans.set(span.id, otelSpan);\n return span;\n }\n\n endSpan(span: Span, result?: SpanResult): void {\n const otelSpan = this.activeSpans.get(span.id);\n if (!otelSpan) return;\n\n if (result) {\n otelSpan.setStatus({\n code: result.status === \"ok\" ? 1 : 2,\n message: result.error?.message,\n });\n\n if (result.output !== undefined) {\n try {\n const outputStr = typeof result.output === \"string\"\n ? result.output\n : JSON.stringify(result.output);\n otelSpan.setAttribute(\"output\", outputStr);\n } catch {\n otelSpan.setAttribute(\"output\", \"[object]\");\n }\n }\n\n if (result.latencyMs !== undefined) {\n otelSpan.setAttribute(\"latency_ms\", result.latencyMs);\n }\n }\n\n otelSpan.end();\n this.activeSpans.delete(span.id);\n }\n\n recordEvent(span: Span, event: TelemetryEvent): void {\n const otelSpan = this.activeSpans.get(span.id);\n if (!otelSpan) return;\n\n switch (event.type) {\n case \"llm_call\":\n this.recordLLMCall(otelSpan, event);\n break;\n case \"validation\":\n this.recordValidation(otelSpan, event);\n break;\n case \"chunk\":\n this.recordChunk(otelSpan, event);\n break;\n case \"tool_call\":\n this.recordToolCall(otelSpan, event);\n break;\n case \"merge\":\n this.recordMerge(otelSpan, event);\n break;\n case \"parse\":\n this.recordParse(otelSpan, event);\n break;\n }\n }\n\n setAttributes(span: Span, attributes: Record<string, unknown>): void {\n const otelSpan = this.activeSpans.get(span.id);\n if (!otelSpan) return;\n\n const stringAttrs: Record<string, string | number | boolean> = {};\n for (const [key, value] of Object.entries(attributes)) {\n if (value !== undefined && value !== null) {\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n stringAttrs[key] = value;\n } else {\n try {\n stringAttrs[key] = JSON.stringify(value);\n } catch {\n stringAttrs[key] = String(value);\n }\n }\n }\n }\n\n otelSpan.setAttributes(stringAttrs);\n }\n\n setContext(_context: TelemetryContext): void {\n // Langfuse supports session_id, user_id, metadata, tags via span attributes\n // These would be set on individual spans\n }\n\n private recordLLMCall(span: OtelSpan, event: LLMCallEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n model: event.model,\n provider: event.provider,\n input: JSON.stringify(event.input.messages),\n temperature: event.input.temperature ?? \"\",\n max_tokens: event.input.maxTokens ?? \"\",\n };\n\n if (event.output) {\n attrs.output = event.output.content;\n \n if (event.output.usage) {\n attrs[\"usage.input\"] = event.output.usage.input;\n attrs[\"usage.output\"] = event.output.usage.output;\n attrs[\"usage.total\"] = event.output.usage.total;\n }\n }\n\n attrs.latency_ms = event.latencyMs;\n\n if (event.error) {\n attrs.error = event.error.message;\n span.recordException(event.error);\n }\n\n span.setAttributes(attrs);\n }\n\n private recordValidation(span: OtelSpan, event: ValidationEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n attempt: event.attempt,\n max_attempts: event.maxAttempts,\n success: event.success,\n };\n\n if (event.errors && event.errors.length > 0) {\n attrs.errors = JSON.stringify(event.errors);\n }\n\n if (event.latencyMs !== undefined) {\n attrs.latency_ms = event.latencyMs;\n }\n\n span.setAttributes(attrs);\n }\n\n private recordChunk(span: OtelSpan, event: ChunkEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n chunk_index: event.chunkIndex,\n chunk_total: event.totalChunks,\n chunk_tokens: event.tokens,\n chunk_images: event.images,\n };\n\n if (event.content) {\n attrs.chunk_content = event.content.slice(0, 1000);\n }\n\n span.setAttributes(attrs);\n }\n\n private recordToolCall(span: OtelSpan, event: ToolCallEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n tool_name: event.toolName,\n tool_args: JSON.stringify(event.args),\n };\n\n if (event.result !== undefined) {\n try {\n attrs.tool_result = JSON.stringify(event.result);\n } catch {\n attrs.tool_result = \"[object]\";\n }\n }\n\n if (event.error) {\n attrs.tool_error = event.error.message;\n }\n\n if (event.latencyMs !== undefined) {\n attrs.latency_ms = event.latencyMs;\n }\n\n span.setAttributes(attrs);\n\n if (event.error) {\n span.recordException(event.error);\n }\n }\n\n private recordMerge(span: OtelSpan, event: MergeEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n strategy: event.strategy,\n input_count: event.inputCount,\n output_count: event.outputCount,\n };\n\n if (event.deduped !== undefined) {\n attrs.deduped = event.deduped;\n }\n\n span.setAttributes(attrs);\n }\n\n private recordParse(span: OtelSpan, event: ParseEvent): void {\n span.setAttributes({\n mime_type: event.mimeType,\n parser: event.parser,\n input_size: event.inputSize,\n output_tokens: event.outputTokens,\n output_images: event.outputImages,\n latency_ms: event.latencyMs,\n });\n }\n}\n\n/**\n * Create a Langfuse telemetry adapter\n * \n * @param config - Langfuse configuration\n * @returns Langfuse telemetry adapter\n */\nexport function createLangfuseAdapter(config: LangfuseConfig): LangfuseAdapter {\n return new LangfuseAdapter(config);\n}\n\nexport type { LangfuseConfig };\n"],"mappings":"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","names":[]}