@amaster.ai/pi-telemetry 0.1.0-beta.0 → 0.1.2-beta.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/dist/langfuse.js CHANGED
@@ -1,7 +1,7 @@
1
- import { createHash, randomUUID } from "node:crypto";
2
- import { Langfuse } from "langfuse";
3
- import { NoopRuntimeEventExporter, } from "./index.js";
4
- const DEFAULT_LANGFUSE_BASE_URL = "https://cloud.langfuse.com";
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { Langfuse } from 'langfuse';
3
+ import { NoopRuntimeEventExporter, } from './index.js';
4
+ const DEFAULT_LANGFUSE_BASE_URL = 'https://cloud.langfuse.com';
5
5
  const DEFAULT_FLUSH_AT = 20;
6
6
  const DEFAULT_FLUSH_INTERVAL_MS = 5000;
7
7
  export class LangfuseSdkRuntimeEventExporter {
@@ -50,20 +50,20 @@ export class LangfuseSdkRuntimeEventExporter {
50
50
  const rootKey = chatSpanKey(event);
51
51
  const rootSpanId = langfuseSpanId(traceId, rootKey);
52
52
  switch (event.type) {
53
- case "chat_turn_started": {
53
+ case 'chat_turn_started': {
54
54
  const body = {
55
55
  id: rootSpanId,
56
- name: "copilot-chat-turn",
56
+ name: 'copilot-chat-turn',
57
57
  startTime: event.createdAt,
58
58
  input: event.details?.input,
59
- level: "DEFAULT",
59
+ level: 'DEFAULT',
60
60
  metadata: lifecycleMetadata(event),
61
61
  };
62
62
  const span = this.spans.get(rootKey);
63
63
  if (span) {
64
64
  span.update({
65
65
  input: event.details?.input,
66
- level: "DEFAULT",
66
+ level: 'DEFAULT',
67
67
  metadata: lifecycleMetadata(event),
68
68
  });
69
69
  }
@@ -72,37 +72,37 @@ export class LangfuseSdkRuntimeEventExporter {
72
72
  }
73
73
  break;
74
74
  }
75
- case "chat_turn_completed":
76
- case "chat_turn_failed": {
75
+ case 'chat_turn_completed':
76
+ case 'chat_turn_failed': {
77
77
  const output = event.details?.output ?? (event.error ? { error: event.error } : undefined);
78
78
  this.closeSdkSubagentBatchSpans(traceId, event);
79
79
  trace.update({
80
80
  sessionId: event.sessionId,
81
- name: "copilot-chat-turn",
81
+ name: 'copilot-chat-turn',
82
82
  output,
83
83
  metadata: lifecycleMetadata(event),
84
84
  });
85
85
  const span = this.spans.get(rootKey) ??
86
86
  trace.span({
87
87
  id: rootSpanId,
88
- name: "copilot-chat-turn",
88
+ name: 'copilot-chat-turn',
89
89
  startTime: event.createdAt,
90
90
  metadata: lifecycleMetadata(event),
91
91
  });
92
92
  span.update({
93
93
  output,
94
94
  endTime: event.createdAt,
95
- level: event.error ? "ERROR" : "DEFAULT",
95
+ level: event.error ? 'ERROR' : 'DEFAULT',
96
96
  ...(event.error ? { statusMessage: event.error } : {}),
97
97
  metadata: lifecycleMetadata(event),
98
98
  });
99
99
  this.spans.delete(rootKey);
100
100
  break;
101
101
  }
102
- case "chat_turn_steered":
103
- case "chat_turn_steer_delivered":
104
- case "chat_turn_followup_queued":
105
- case "chat_turn_followup_delivered": {
102
+ case 'chat_turn_steered':
103
+ case 'chat_turn_steer_delivered':
104
+ case 'chat_turn_followup_queued':
105
+ case 'chat_turn_followup_delivered': {
106
106
  const output = event.details?.output ?? chatInputLifecycleOutput(event);
107
107
  this.getSdkSpanParent(trace, rootKey).span({
108
108
  id: langfuseSpanId(traceId, chatInputSpanKey(event)),
@@ -111,28 +111,28 @@ export class LangfuseSdkRuntimeEventExporter {
111
111
  endTime: event.createdAt,
112
112
  input: event.details?.input,
113
113
  output,
114
- level: "DEFAULT",
114
+ level: 'DEFAULT',
115
115
  metadata: lifecycleMetadata(event),
116
116
  });
117
117
  break;
118
118
  }
119
- case "subagent_spawned":
120
- case "subagent_started": {
119
+ case 'subagent_spawned':
120
+ case 'subagent_started': {
121
121
  const key = subagentSpanKey(event);
122
122
  const rootSpan = this.ensureSdkRootSpan(trace, rootKey, event);
123
123
  const body = {
124
124
  id: langfuseSpanId(traceId, key),
125
- name: "subagent",
125
+ name: 'subagent',
126
126
  startTime: event.createdAt,
127
127
  input: event.details?.input,
128
- level: "DEFAULT",
128
+ level: 'DEFAULT',
129
129
  metadata: lifecycleMetadata(event),
130
130
  };
131
131
  const span = this.spans.get(key);
132
132
  if (span) {
133
133
  span.update({
134
134
  input: event.details?.input,
135
- level: "DEFAULT",
135
+ level: 'DEFAULT',
136
136
  metadata: lifecycleMetadata(event),
137
137
  });
138
138
  }
@@ -141,23 +141,23 @@ export class LangfuseSdkRuntimeEventExporter {
141
141
  }
142
142
  break;
143
143
  }
144
- case "subagent_completed":
145
- case "subagent_failed":
146
- case "subagent_cancelled": {
144
+ case 'subagent_completed':
145
+ case 'subagent_failed':
146
+ case 'subagent_cancelled': {
147
147
  const key = subagentSpanKey(event);
148
148
  const output = event.details?.output ?? (event.error ? { error: event.error } : undefined);
149
149
  const rootSpan = this.ensureSdkRootSpan(trace, rootKey, event);
150
150
  const span = this.spans.get(key) ??
151
151
  this.getSdkSubagentParent(trace, rootKey, event).span({
152
152
  id: langfuseSpanId(traceId, key),
153
- name: "subagent",
153
+ name: 'subagent',
154
154
  startTime: event.createdAt,
155
155
  metadata: lifecycleMetadata(event),
156
156
  });
157
157
  span.update({
158
158
  output,
159
159
  endTime: event.createdAt,
160
- level: event.error ? "ERROR" : "DEFAULT",
160
+ level: event.error ? 'ERROR' : 'DEFAULT',
161
161
  ...(event.error ? { statusMessage: event.error } : {}),
162
162
  metadata: lifecycleMetadata(event),
163
163
  });
@@ -175,20 +175,20 @@ export class LangfuseSdkRuntimeEventExporter {
175
175
  const parent = this.getSdkEventParent(trace, rootKey, event);
176
176
  const key = toolSpanKey(event);
177
177
  const id = langfuseSpanId(traceId, key);
178
- if (event.status === "started") {
178
+ if (event.status === 'started') {
179
179
  const body = {
180
180
  id,
181
181
  name: toolObservationName(event),
182
182
  startTime: event.createdAt,
183
183
  input: event.args ? { args: event.args } : undefined,
184
- level: "DEFAULT",
184
+ level: 'DEFAULT',
185
185
  metadata: toolMetadata(event),
186
186
  };
187
187
  const span = this.spans.get(key);
188
188
  if (span) {
189
189
  span.update({
190
190
  input: event.args ? { args: event.args } : undefined,
191
- level: "DEFAULT",
191
+ level: 'DEFAULT',
192
192
  metadata: toolMetadata(event),
193
193
  });
194
194
  }
@@ -208,7 +208,7 @@ export class LangfuseSdkRuntimeEventExporter {
208
208
  span.update({
209
209
  output,
210
210
  endTime: event.createdAt,
211
- level: event.error ? "ERROR" : "DEFAULT",
211
+ level: event.error ? 'ERROR' : 'DEFAULT',
212
212
  ...(event.error ? { statusMessage: event.error } : {}),
213
213
  metadata: toolMetadata(event),
214
214
  });
@@ -221,7 +221,7 @@ export class LangfuseSdkRuntimeEventExporter {
221
221
  const parent = this.getSdkEventParent(trace, rootKey, event);
222
222
  const key = llmGenerationKey(event);
223
223
  const id = langfuseSpanId(traceId, key);
224
- if (event.status === "started") {
224
+ if (event.status === 'started') {
225
225
  const body = {
226
226
  id,
227
227
  name: llmGenerationObservationName(event),
@@ -257,9 +257,11 @@ export class LangfuseSdkRuntimeEventExporter {
257
257
  generation.update({
258
258
  output: event.output ?? (event.error ? { error: event.error } : undefined),
259
259
  endTime: event.createdAt,
260
- level: event.error ? "ERROR" : "DEFAULT",
260
+ level: event.error ? 'ERROR' : 'DEFAULT',
261
261
  ...(event.error ? { statusMessage: event.error } : {}),
262
- ...(event.usage ? { usage: toLangfuseUsage(event.usage), usageDetails: toLangfuseUsageDetails(event.usage) } : {}),
262
+ ...(event.usage
263
+ ? { usage: toLangfuseUsage(event.usage), usageDetails: toLangfuseUsageDetails(event.usage) }
264
+ : {}),
263
265
  model: event.model.model,
264
266
  modelParameters: {
265
267
  provider: event.model.provider,
@@ -278,7 +280,7 @@ export class LangfuseSdkRuntimeEventExporter {
278
280
  const trace = this.client.trace({
279
281
  id: langfuseTraceId(traceId),
280
282
  sessionId: event.sessionId,
281
- name: "copilot-chat-turn",
283
+ name: 'copilot-chat-turn',
282
284
  timestamp: event.createdAt,
283
285
  input: !isToolEvent(event) && !isLlmGenerationEvent(event) ? event.details?.input : undefined,
284
286
  metadata: isToolEvent(event)
@@ -310,10 +312,10 @@ export class LangfuseSdkRuntimeEventExporter {
310
312
  }
311
313
  const batchSpan = rootSpan.span({
312
314
  id: langfuseSpanId(requireTraceId(event.traceId), batchKey),
313
- name: "subagent fan-out",
315
+ name: 'subagent fan-out',
314
316
  startTime: event.createdAt,
315
317
  input: event.details?.input,
316
- level: "DEFAULT",
318
+ level: 'DEFAULT',
317
319
  metadata: lifecycleMetadata(event),
318
320
  });
319
321
  this.spans.set(batchKey, batchSpan);
@@ -330,7 +332,7 @@ export class LangfuseSdkRuntimeEventExporter {
330
332
  }
331
333
  span.update({
332
334
  endTime: event.createdAt,
333
- level: event.error ? "ERROR" : "DEFAULT",
335
+ level: event.error ? 'ERROR' : 'DEFAULT',
334
336
  ...(event.error ? { statusMessage: event.error } : {}),
335
337
  metadata: lifecycleMetadata(event),
336
338
  });
@@ -345,7 +347,7 @@ export class LangfuseSdkRuntimeEventExporter {
345
347
  const traceId = requireTraceId(event.traceId);
346
348
  const span = trace.span({
347
349
  id: langfuseSpanId(traceId, rootKey),
348
- name: "copilot-chat-turn",
350
+ name: 'copilot-chat-turn',
349
351
  startTime: event.createdAt,
350
352
  metadata: isToolEvent(event)
351
353
  ? toolMetadata(event)
@@ -367,16 +369,18 @@ export class LangfuseHttpRuntimeEventExporter {
367
369
  flushing;
368
370
  constructor(config, fetchImpl) {
369
371
  this.config = config;
370
- this.endpoint = `${config.baseUrl.replace(/\/+$/, "")}/api/public/ingestion`;
371
- this.authHeader = `Basic ${Buffer.from(`${config.publicKey}:${config.secretKey}`).toString("base64")}`;
372
- this.fetchImpl = fetchImpl ?? (async (input, init) => {
373
- const response = await fetch(input, init);
374
- return {
375
- ok: response.ok,
376
- status: response.status,
377
- text: () => response.text(),
378
- };
379
- });
372
+ this.endpoint = `${config.baseUrl.replace(/\/+$/, '')}/api/public/ingestion`;
373
+ this.authHeader = `Basic ${Buffer.from(`${config.publicKey}:${config.secretKey}`).toString('base64')}`;
374
+ this.fetchImpl =
375
+ fetchImpl ??
376
+ (async (input, init) => {
377
+ const response = await fetch(input, init);
378
+ return {
379
+ ok: response.ok,
380
+ status: response.status,
381
+ text: () => response.text(),
382
+ };
383
+ });
380
384
  }
381
385
  async publish(event) {
382
386
  const redactedEvent = applyTelemetryRedaction(this.config, event);
@@ -432,17 +436,17 @@ export class LangfuseHttpRuntimeEventExporter {
432
436
  }
433
437
  async sendBatch(batch) {
434
438
  const response = await this.fetchImpl(this.endpoint, {
435
- method: "POST",
439
+ method: 'POST',
436
440
  headers: {
437
441
  authorization: this.authHeader,
438
- "content-type": "application/json",
439
- "x-langfuse-ingestion-version": "4",
442
+ 'content-type': 'application/json',
443
+ 'x-langfuse-ingestion-version': '4',
440
444
  },
441
445
  body: JSON.stringify({ batch }),
442
446
  });
443
447
  if (!response.ok) {
444
- const text = await response.text().catch(() => "");
445
- throw new Error(`Langfuse ingestion failed with ${response.status}${text ? `: ${text}` : ""}`);
448
+ const text = await response.text().catch(() => '');
449
+ throw new Error(`Langfuse ingestion failed with ${response.status}${text ? `: ${text}` : ''}`);
446
450
  }
447
451
  }
448
452
  }
@@ -457,14 +461,16 @@ export class OtelRuntimeEventExporter {
457
461
  constructor(config, fetchImpl) {
458
462
  this.config = config;
459
463
  this.endpoint = normalizeOtelTracesEndpoint(config.endpoint);
460
- this.fetchImpl = fetchImpl ?? (async (input, init) => {
461
- const response = await fetch(input, init);
462
- return {
463
- ok: response.ok,
464
- status: response.status,
465
- text: () => response.text(),
466
- };
467
- });
464
+ this.fetchImpl =
465
+ fetchImpl ??
466
+ (async (input, init) => {
467
+ const response = await fetch(input, init);
468
+ return {
469
+ ok: response.ok,
470
+ status: response.status,
471
+ text: () => response.text(),
472
+ };
473
+ });
468
474
  }
469
475
  async publish(event) {
470
476
  const redactedEvent = applyTelemetryRedaction(this.config, event);
@@ -521,16 +527,16 @@ export class OtelRuntimeEventExporter {
521
527
  }
522
528
  async sendSpans(spans) {
523
529
  const response = await this.fetchImpl(this.endpoint, {
524
- method: "POST",
530
+ method: 'POST',
525
531
  headers: {
526
- "content-type": "application/json",
532
+ 'content-type': 'application/json',
527
533
  ...(this.config.headers ?? {}),
528
534
  },
529
535
  body: JSON.stringify(toOtelTracePayload(spans, this.config)),
530
536
  });
531
537
  if (!response.ok) {
532
- const text = await response.text().catch(() => "");
533
- throw new Error(`${this.config.errorLabel ?? "OTEL export"} failed with ${response.status}${text ? `: ${text}` : ""}`);
538
+ const text = await response.text().catch(() => '');
539
+ throw new Error(`${this.config.errorLabel ?? 'OTEL export'} failed with ${response.status}${text ? `: ${text}` : ''}`);
534
540
  }
535
541
  }
536
542
  }
@@ -539,29 +545,59 @@ export function createRuntimeEventExporterFromEnv(env) {
539
545
  if (!config.enabled) {
540
546
  return new NoopRuntimeEventExporter();
541
547
  }
542
- if (config.transport === "sdk") {
548
+ if (config.transport === 'sdk') {
543
549
  return new LangfuseSdkRuntimeEventExporter(config);
544
550
  }
545
551
  return new LangfuseHttpRuntimeEventExporter(config);
546
552
  }
553
+ export function createLangfuseExporter(telemetryConfig) {
554
+ const config = resolveLangfuseExporterConfig(telemetryConfig);
555
+ if (!config.enabled) {
556
+ return new NoopRuntimeEventExporter();
557
+ }
558
+ if (config.transport === 'sdk') {
559
+ return new LangfuseSdkRuntimeEventExporter(config);
560
+ }
561
+ return new LangfuseHttpRuntimeEventExporter(config);
562
+ }
563
+ export function resolveLangfuseExporterConfig(telemetryConfig) {
564
+ const lf = telemetryConfig.langfuse;
565
+ const publicKey = lf?.publicKey ?? '';
566
+ const secretKey = lf?.secretKey ?? '';
567
+ const credentialsPresent = Boolean(publicKey && secretKey);
568
+ const requestedTransport = lf?.transport;
569
+ const transport = requestedTransport ?? (credentialsPresent ? 'sdk' : 'ingestion');
570
+ return {
571
+ enabled: Boolean(lf?.enabled && credentialsPresent),
572
+ transport,
573
+ publicKey,
574
+ secretKey,
575
+ baseUrl: lf?.baseUrl ?? DEFAULT_LANGFUSE_BASE_URL,
576
+ flushAt: lf?.flushAt ?? DEFAULT_FLUSH_AT,
577
+ flushIntervalMs: lf?.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,
578
+ serviceName: telemetryConfig.serviceName ?? 'pi-server',
579
+ ...(telemetryConfig.serviceVersion ? { serviceVersion: telemetryConfig.serviceVersion } : {}),
580
+ includePayloads: telemetryConfig.includePayloads ?? true,
581
+ };
582
+ }
547
583
  export function resolveLangfuseConfig(env) {
548
584
  const enabled = parseBoolean(env.LANGFUSE_ENABLED);
549
585
  const publicKey = trim(env.LANGFUSE_PUBLIC_KEY);
550
586
  const secretKey = trim(env.LANGFUSE_SECRET_KEY);
551
587
  const baseUrl = trim(env.LANGFUSE_BASE_URL) ?? DEFAULT_LANGFUSE_BASE_URL;
552
588
  const requestedTransport = parseLangfuseTransport(env.LANGFUSE_TRANSPORT);
553
- const transport = requestedTransport ?? (publicKey && secretKey ? "sdk" : "ingestion");
589
+ const transport = requestedTransport ?? (publicKey && secretKey ? 'sdk' : 'ingestion');
554
590
  const credentialsPresent = Boolean(publicKey && secretKey);
555
591
  const serviceVersion = trim(env.TELEMETRY_SERVICE_VERSION);
556
592
  return {
557
593
  enabled: Boolean(enabled && credentialsPresent),
558
594
  transport,
559
- publicKey: publicKey ?? "",
560
- secretKey: secretKey ?? "",
595
+ publicKey: publicKey ?? '',
596
+ secretKey: secretKey ?? '',
561
597
  baseUrl,
562
598
  flushAt: parsePositiveInteger(env.LANGFUSE_FLUSH_AT, DEFAULT_FLUSH_AT),
563
599
  flushIntervalMs: parsePositiveInteger(env.LANGFUSE_FLUSH_INTERVAL_MS, DEFAULT_FLUSH_INTERVAL_MS),
564
- serviceName: trim(env.TELEMETRY_SERVICE_NAME ?? env.OTEL_SERVICE_NAME) ?? "pi-server",
600
+ serviceName: trim(env.TELEMETRY_SERVICE_NAME ?? env.OTEL_SERVICE_NAME) ?? 'pi-server',
565
601
  ...(serviceVersion ? { serviceVersion } : {}),
566
602
  includePayloads: parseBooleanWithDefault(env.TELEMETRY_INCLUDE_PAYLOADS ?? env.LANGFUSE_INCLUDE_PAYLOADS, true),
567
603
  };
@@ -581,31 +617,35 @@ export function mapRuntimeEventToLangfuse(event) {
581
617
  function mapLifecycleEventToLangfuse(event) {
582
618
  const traceId = requireTraceId(event.traceId);
583
619
  switch (event.type) {
584
- case "chat_turn_started":
585
- return [ingestionEvent("trace-create", event.createdAt, {
620
+ case 'chat_turn_started':
621
+ return [
622
+ ingestionEvent('trace-create', event.createdAt, {
586
623
  id: langfuseTraceId(traceId),
587
624
  sessionId: event.sessionId,
588
- name: "copilot-chat-turn",
625
+ name: 'copilot-chat-turn',
589
626
  timestamp: event.createdAt,
590
627
  input: event.details?.input,
591
628
  metadata: lifecycleMetadata(event),
592
- })];
593
- case "chat_turn_completed":
594
- case "chat_turn_failed": {
629
+ }),
630
+ ];
631
+ case 'chat_turn_completed':
632
+ case 'chat_turn_failed': {
595
633
  const output = event.details?.output ?? (event.error ? { error: event.error } : undefined);
596
- return [ingestionEvent("trace-update", event.createdAt, {
634
+ return [
635
+ ingestionEvent('trace-update', event.createdAt, {
597
636
  id: langfuseTraceId(traceId),
598
637
  ...(output !== undefined ? { output } : {}),
599
638
  metadata: lifecycleMetadata(event),
600
- })];
639
+ }),
640
+ ];
601
641
  }
602
- case "chat_turn_steered":
603
- case "chat_turn_steer_delivered":
604
- case "chat_turn_followup_queued":
605
- case "chat_turn_followup_delivered": {
642
+ case 'chat_turn_steered':
643
+ case 'chat_turn_steer_delivered':
644
+ case 'chat_turn_followup_queued':
645
+ case 'chat_turn_followup_delivered': {
606
646
  const spanId = langfuseSpanId(traceId, chatInputSpanKey(event));
607
647
  return [
608
- ingestionEvent("span-create", event.createdAt, {
648
+ ingestionEvent('span-create', event.createdAt, {
609
649
  id: spanId,
610
650
  traceId: langfuseTraceId(traceId),
611
651
  parentObservationId: langfuseSpanId(traceId, chatSpanKey(event)),
@@ -614,7 +654,7 @@ function mapLifecycleEventToLangfuse(event) {
614
654
  input: event.details?.input,
615
655
  metadata: lifecycleMetadata(event),
616
656
  }),
617
- ingestionEvent("span-update", event.createdAt, {
657
+ ingestionEvent('span-update', event.createdAt, {
618
658
  id: spanId,
619
659
  traceId: langfuseTraceId(traceId),
620
660
  endTime: event.createdAt,
@@ -623,43 +663,45 @@ function mapLifecycleEventToLangfuse(event) {
623
663
  }),
624
664
  ];
625
665
  }
626
- case "subagent_spawned":
627
- case "subagent_started": {
666
+ case 'subagent_spawned':
667
+ case 'subagent_started': {
628
668
  const batchKey = subagentBatchSpanKey(event);
629
669
  return [
630
670
  ...(batchKey
631
671
  ? [
632
- ingestionEvent("span-create", event.createdAt, {
672
+ ingestionEvent('span-create', event.createdAt, {
633
673
  id: langfuseSpanId(traceId, batchKey),
634
674
  traceId: langfuseTraceId(traceId),
635
675
  parentObservationId: langfuseSpanId(traceId, chatSpanKey(event)),
636
- name: "subagent fan-out",
676
+ name: 'subagent fan-out',
637
677
  startTime: event.createdAt,
638
678
  input: event.details?.input,
639
679
  metadata: lifecycleMetadata(event),
640
680
  }),
641
681
  ]
642
682
  : []),
643
- ingestionEvent("span-create", event.createdAt, {
683
+ ingestionEvent('span-create', event.createdAt, {
644
684
  id: langfuseSpanId(traceId, subagentSpanKey(event)),
645
685
  traceId: langfuseTraceId(traceId),
646
686
  parentObservationId: langfuseSpanId(traceId, batchKey ?? subagentSpawnToolSpanKey(event) ?? chatSpanKey(event)),
647
- name: "subagent",
687
+ name: 'subagent',
648
688
  startTime: event.createdAt,
649
689
  metadata: lifecycleMetadata(event),
650
690
  }),
651
691
  ];
652
692
  }
653
- case "subagent_completed":
654
- case "subagent_failed":
655
- case "subagent_cancelled":
656
- return [ingestionEvent("span-update", event.createdAt, {
693
+ case 'subagent_completed':
694
+ case 'subagent_failed':
695
+ case 'subagent_cancelled':
696
+ return [
697
+ ingestionEvent('span-update', event.createdAt, {
657
698
  id: langfuseSpanId(traceId, subagentSpanKey(event)),
658
699
  traceId: langfuseTraceId(traceId),
659
700
  endTime: event.createdAt,
660
701
  metadata: lifecycleMetadata(event),
661
702
  ...(event.error ? { output: { error: event.error } } : {}),
662
- })];
703
+ }),
704
+ ];
663
705
  default:
664
706
  return assertNever(event.type);
665
707
  }
@@ -668,8 +710,8 @@ function mapToolEventToLangfuse(event) {
668
710
  const traceId = requireTraceId(event.traceId);
669
711
  const spanId = langfuseSpanId(traceId, `tool:${event.sessionId}:${event.toolCallId}:${event.toolName}`);
670
712
  const parentObservationId = langfuseParentObservationId(traceId, event);
671
- if (event.status === "started") {
672
- return ingestionEvent("span-create", event.createdAt, {
713
+ if (event.status === 'started') {
714
+ return ingestionEvent('span-create', event.createdAt, {
673
715
  id: spanId,
674
716
  traceId: langfuseTraceId(traceId),
675
717
  parentObservationId,
@@ -679,7 +721,7 @@ function mapToolEventToLangfuse(event) {
679
721
  metadata: toolMetadata(event),
680
722
  });
681
723
  }
682
- return ingestionEvent("span-update", event.createdAt, {
724
+ return ingestionEvent('span-update', event.createdAt, {
683
725
  id: spanId,
684
726
  traceId: langfuseTraceId(traceId),
685
727
  endTime: event.createdAt,
@@ -691,8 +733,8 @@ function mapLlmGenerationEventToLangfuse(event) {
691
733
  const traceId = requireTraceId(event.traceId);
692
734
  const id = langfuseSpanId(traceId, llmGenerationKey(event));
693
735
  const parentObservationId = langfuseParentObservationId(traceId, event);
694
- if (event.status === "started") {
695
- return ingestionEvent("generation-create", event.createdAt, {
736
+ if (event.status === 'started') {
737
+ return ingestionEvent('generation-create', event.createdAt, {
696
738
  id,
697
739
  traceId: langfuseTraceId(traceId),
698
740
  parentObservationId,
@@ -707,14 +749,16 @@ function mapLlmGenerationEventToLangfuse(event) {
707
749
  metadata: llmGenerationMetadata(event),
708
750
  });
709
751
  }
710
- return ingestionEvent("generation-update", event.createdAt, {
752
+ return ingestionEvent('generation-update', event.createdAt, {
711
753
  id,
712
754
  traceId: langfuseTraceId(traceId),
713
755
  endTime: event.createdAt,
714
756
  output: event.output ?? (event.error ? { error: event.error } : undefined),
715
- level: event.error ? "ERROR" : "DEFAULT",
757
+ level: event.error ? 'ERROR' : 'DEFAULT',
716
758
  ...(event.error ? { statusMessage: event.error } : {}),
717
- ...(event.usage ? { usage: toLangfuseUsage(event.usage), usageDetails: toLangfuseUsageDetails(event.usage) } : {}),
759
+ ...(event.usage
760
+ ? { usage: toLangfuseUsage(event.usage), usageDetails: toLangfuseUsageDetails(event.usage) }
761
+ : {}),
718
762
  model: event.model.model,
719
763
  modelParameters: {
720
764
  provider: event.model.provider,
@@ -739,32 +783,32 @@ function mapLifecycleEventToOtelSpans(event, pendingStarts) {
739
783
  const rootKey = chatSpanKey(event);
740
784
  const rootSpanId = langfuseSpanId(traceId, rootKey);
741
785
  switch (event.type) {
742
- case "chat_turn_started":
786
+ case 'chat_turn_started':
743
787
  pendingStarts.set(rootKey, {
744
788
  traceId: langfuseTraceId(traceId),
745
789
  spanId: rootSpanId,
746
- name: "copilot-chat-turn",
790
+ name: 'copilot-chat-turn',
747
791
  startTime: event.createdAt,
748
792
  attributes: {
749
793
  ...lifecycleMetadata(event),
750
- ...langfuseObservationAttributes({ input: event.details?.input, level: "DEFAULT" }),
794
+ ...langfuseObservationAttributes({ input: event.details?.input, level: 'DEFAULT' }),
751
795
  ...langfuseTraceAttributes({ input: event.details?.input }),
752
796
  },
753
797
  });
754
798
  return [];
755
- case "chat_turn_completed":
756
- case "chat_turn_failed":
799
+ case 'chat_turn_completed':
800
+ case 'chat_turn_failed':
757
801
  return [
758
802
  completeOtelSpan(pendingStarts, rootKey, {
759
803
  traceId: langfuseTraceId(traceId),
760
804
  spanId: rootSpanId,
761
- name: "copilot-chat-turn",
805
+ name: 'copilot-chat-turn',
762
806
  startTime: event.createdAt,
763
807
  attributes: {
764
808
  ...lifecycleMetadata(event),
765
809
  ...langfuseObservationAttributes({
766
810
  output: event.details?.output ?? (event.error ? { error: event.error } : undefined),
767
- level: event.error ? "ERROR" : "DEFAULT",
811
+ level: event.error ? 'ERROR' : 'DEFAULT',
768
812
  }),
769
813
  ...langfuseTraceAttributes({
770
814
  output: event.details?.output ?? (event.error ? { error: event.error } : undefined),
@@ -772,10 +816,10 @@ function mapLifecycleEventToOtelSpans(event, pendingStarts) {
772
816
  },
773
817
  }, event.createdAt, event.error),
774
818
  ];
775
- case "chat_turn_steered":
776
- case "chat_turn_steer_delivered":
777
- case "chat_turn_followup_queued":
778
- case "chat_turn_followup_delivered":
819
+ case 'chat_turn_steered':
820
+ case 'chat_turn_steer_delivered':
821
+ case 'chat_turn_followup_queued':
822
+ case 'chat_turn_followup_delivered':
779
823
  return [
780
824
  {
781
825
  traceId: langfuseTraceId(traceId),
@@ -789,43 +833,43 @@ function mapLifecycleEventToOtelSpans(event, pendingStarts) {
789
833
  ...langfuseObservationAttributes({
790
834
  input: event.details?.input,
791
835
  output: event.details?.output ?? chatInputLifecycleOutput(event),
792
- level: "DEFAULT",
836
+ level: 'DEFAULT',
793
837
  }),
794
838
  },
795
839
  },
796
840
  ];
797
- case "subagent_spawned":
798
- case "subagent_started": {
841
+ case 'subagent_spawned':
842
+ case 'subagent_started': {
799
843
  const key = subagentSpanKey(event);
800
844
  pendingStarts.set(key, {
801
845
  traceId: langfuseTraceId(traceId),
802
846
  spanId: langfuseSpanId(traceId, key),
803
847
  parentSpanId: langfuseSpanId(traceId, subagentSpawnToolSpanKey(event) ?? rootKey),
804
- name: "subagent",
848
+ name: 'subagent',
805
849
  startTime: event.createdAt,
806
850
  attributes: {
807
851
  ...lifecycleMetadata(event),
808
- ...langfuseObservationAttributes({ input: event.details?.input, level: "DEFAULT" }),
852
+ ...langfuseObservationAttributes({ input: event.details?.input, level: 'DEFAULT' }),
809
853
  },
810
854
  });
811
855
  return [];
812
856
  }
813
- case "subagent_completed":
814
- case "subagent_failed":
815
- case "subagent_cancelled": {
857
+ case 'subagent_completed':
858
+ case 'subagent_failed':
859
+ case 'subagent_cancelled': {
816
860
  const key = subagentSpanKey(event);
817
861
  return [
818
862
  completeOtelSpan(pendingStarts, key, {
819
863
  traceId: langfuseTraceId(traceId),
820
864
  spanId: langfuseSpanId(traceId, key),
821
865
  parentSpanId: langfuseSpanId(traceId, subagentSpawnToolSpanKey(event) ?? rootKey),
822
- name: "subagent",
866
+ name: 'subagent',
823
867
  startTime: event.createdAt,
824
868
  attributes: {
825
869
  ...lifecycleMetadata(event),
826
870
  ...langfuseObservationAttributes({
827
871
  output: event.details?.output ?? (event.error ? { error: event.error } : undefined),
828
- level: event.error ? "ERROR" : "DEFAULT",
872
+ level: event.error ? 'ERROR' : 'DEFAULT',
829
873
  }),
830
874
  },
831
875
  }, event.createdAt, event.error),
@@ -848,17 +892,17 @@ function mapToolEventToOtelSpans(event, pendingStarts) {
848
892
  ...toolMetadata(event),
849
893
  ...langfuseObservationAttributes({
850
894
  output: event.error ? { error: event.error } : event.details,
851
- level: event.error ? "ERROR" : "DEFAULT",
895
+ level: event.error ? 'ERROR' : 'DEFAULT',
852
896
  }),
853
897
  },
854
898
  };
855
- if (event.status === "started") {
899
+ if (event.status === 'started') {
856
900
  pendingStarts.set(key, {
857
901
  ...fallbackStart,
858
902
  attributes: {
859
903
  ...fallbackStart.attributes,
860
904
  ...(event.args ? { args: event.args } : {}),
861
- ...langfuseObservationAttributes({ input: event.args, level: "DEFAULT" }),
905
+ ...langfuseObservationAttributes({ input: event.args, level: 'DEFAULT' }),
862
906
  },
863
907
  });
864
908
  return [];
@@ -876,23 +920,23 @@ function mapLlmGenerationEventToOtelSpans(event, pendingStarts) {
876
920
  startTime: event.createdAt,
877
921
  attributes: {
878
922
  ...llmGenerationMetadata(event),
879
- "langfuse.observation.type": "generation",
923
+ 'langfuse.observation.type': 'generation',
880
924
  model: event.model.model,
881
925
  provider: event.model.provider,
882
926
  ...(event.model.thinkingLevel ? { thinkingLevel: event.model.thinkingLevel } : {}),
883
927
  ...langfuseObservationAttributes({
884
928
  output: event.output ?? (event.error ? { error: event.error } : undefined),
885
- level: event.error ? "ERROR" : "DEFAULT",
929
+ level: event.error ? 'ERROR' : 'DEFAULT',
886
930
  }),
887
931
  ...(event.usage ? flattenUsageAttributes(event.usage) : {}),
888
932
  },
889
933
  };
890
- if (event.status === "started") {
934
+ if (event.status === 'started') {
891
935
  pendingStarts.set(key, {
892
936
  ...fallbackStart,
893
937
  attributes: {
894
938
  ...fallbackStart.attributes,
895
- ...langfuseObservationAttributes({ input: event.input, level: "DEFAULT" }),
939
+ ...langfuseObservationAttributes({ input: event.input, level: 'DEFAULT' }),
896
940
  },
897
941
  });
898
942
  return [];
@@ -914,33 +958,37 @@ function completeOtelSpan(pendingStarts, key, fallbackStart, endTime, error) {
914
958
  }
915
959
  function langfuseObservationAttributes(input) {
916
960
  return {
917
- "langfuse.observation.type": "span",
961
+ 'langfuse.observation.type': 'span',
918
962
  ...(input.input !== undefined
919
963
  ? {
920
- "langfuse.observation.input": toLangfuseObservationPayload(input.input),
921
- "input.value": toLangfuseObservationPayload(input.input),
964
+ 'langfuse.observation.input': toLangfuseObservationPayload(input.input),
965
+ 'input.value': toLangfuseObservationPayload(input.input),
922
966
  }
923
967
  : {}),
924
968
  ...(input.output !== undefined
925
969
  ? {
926
- "langfuse.observation.output": toLangfuseObservationPayload(input.output),
927
- "output.value": toLangfuseObservationPayload(input.output),
970
+ 'langfuse.observation.output': toLangfuseObservationPayload(input.output),
971
+ 'output.value': toLangfuseObservationPayload(input.output),
928
972
  }
929
973
  : {}),
930
- ...(input.level ? { "langfuse.observation.level": input.level } : {}),
974
+ ...(input.level ? { 'langfuse.observation.level': input.level } : {}),
931
975
  };
932
976
  }
933
977
  function langfuseTraceAttributes(input) {
934
978
  return {
935
- ...(input.input !== undefined ? { "langfuse.trace.input": toLangfuseTracePayload(input.input) } : {}),
936
- ...(input.output !== undefined ? { "langfuse.trace.output": toLangfuseTracePayload(input.output) } : {}),
979
+ ...(input.input !== undefined
980
+ ? { 'langfuse.trace.input': toLangfuseTracePayload(input.input) }
981
+ : {}),
982
+ ...(input.output !== undefined
983
+ ? { 'langfuse.trace.output': toLangfuseTracePayload(input.output) }
984
+ : {}),
937
985
  };
938
986
  }
939
987
  function toLangfuseObservationPayload(value) {
940
988
  return JSON.stringify(value);
941
989
  }
942
990
  function toLangfuseTracePayload(value) {
943
- return typeof value === "string" ? value : JSON.stringify(value);
991
+ return typeof value === 'string' ? value : JSON.stringify(value);
944
992
  }
945
993
  function chatSpanKey(event) {
946
994
  const sessionId = event.parentSessionId ?? event.sessionId;
@@ -954,7 +1002,7 @@ function subagentSpanKey(event) {
954
1002
  return `subagent:${event.runId ?? event.childSessionId ?? event.id}`;
955
1003
  }
956
1004
  function subagentBatchSpanKey(event) {
957
- const batchId = event.spawnBatchId ?? stringFromJsonObject(event.details, "spawnBatchId");
1005
+ const batchId = event.spawnBatchId ?? stringFromJsonObject(event.details, 'spawnBatchId');
958
1006
  return event.traceId && batchId ? `subagent-batch:${event.traceId}:${batchId}` : undefined;
959
1007
  }
960
1008
  function telemetryEventSubagentSpanKey(event) {
@@ -983,23 +1031,25 @@ function toolObservationName(event) {
983
1031
  }
984
1032
  function chatInputObservationName(event) {
985
1033
  const prefix = chatInputObservationPrefix(event.type);
986
- const input = typeof event.details?.input === "string" ? truncateObservationSummary(event.details.input) : undefined;
1034
+ const input = typeof event.details?.input === 'string'
1035
+ ? truncateObservationSummary(event.details.input)
1036
+ : undefined;
987
1037
  return input ? `${prefix} [${input}]` : prefix;
988
1038
  }
989
1039
  function chatInputObservationPrefix(type) {
990
- if (type === "chat_turn_steered") {
991
- return "chat-steer";
1040
+ if (type === 'chat_turn_steered') {
1041
+ return 'chat-steer';
992
1042
  }
993
- if (type === "chat_turn_steer_delivered") {
994
- return "chat-steer-delivered";
1043
+ if (type === 'chat_turn_steer_delivered') {
1044
+ return 'chat-steer-delivered';
995
1045
  }
996
- if (type === "chat_turn_followup_delivered") {
997
- return "chat-followup-delivered";
1046
+ if (type === 'chat_turn_followup_delivered') {
1047
+ return 'chat-followup-delivered';
998
1048
  }
999
- return "chat-followup";
1049
+ return 'chat-followup';
1000
1050
  }
1001
1051
  function chatInputLifecycleOutput(event) {
1002
- return event.type === "chat_turn_steer_delivered" || event.type === "chat_turn_followup_delivered"
1052
+ return event.type === 'chat_turn_steer_delivered' || event.type === 'chat_turn_followup_delivered'
1003
1053
  ? { delivered: true, turnMode: event.details?.turnMode }
1004
1054
  : { accepted: true, turnMode: event.details?.turnMode };
1005
1055
  }
@@ -1007,59 +1057,59 @@ function summarizeToolArgsForName(toolName, args) {
1007
1057
  if (!args) {
1008
1058
  return undefined;
1009
1059
  }
1010
- const pathValue = stringArg(args, "path") ?? stringArg(args, "filePath") ?? stringArg(args, "absolutePath");
1060
+ const pathValue = stringArg(args, 'path') ?? stringArg(args, 'filePath') ?? stringArg(args, 'absolutePath');
1011
1061
  if (pathValue) {
1012
1062
  return truncateObservationSummary(pathValue);
1013
1063
  }
1014
- const command = stringArg(args, "command");
1064
+ const command = stringArg(args, 'command');
1015
1065
  if (command) {
1016
1066
  return truncateObservationSummary(command);
1017
1067
  }
1018
- const query = stringArg(args, "query");
1068
+ const query = stringArg(args, 'query');
1019
1069
  if (query) {
1020
1070
  return truncateObservationSummary(query);
1021
1071
  }
1022
- const task = stringArg(args, "task");
1023
- if (task && toolName === "sessions_spawn") {
1072
+ const task = stringArg(args, 'task');
1073
+ if (task && toolName === 'sessions_spawn') {
1024
1074
  return truncateObservationSummary(task);
1025
1075
  }
1026
- const code = stringArg(args, "code");
1076
+ const code = stringArg(args, 'code');
1027
1077
  if (code) {
1028
1078
  return truncateObservationSummary(code);
1029
1079
  }
1030
- const name = stringArg(args, "name");
1031
- if (name && toolName.startsWith("mcp_")) {
1080
+ const name = stringArg(args, 'name');
1081
+ if (name && toolName.startsWith('mcp_')) {
1032
1082
  return truncateObservationSummary(name);
1033
1083
  }
1034
1084
  return undefined;
1035
1085
  }
1036
1086
  function llmGenerationObservationName(event) {
1037
- return `llm-generation [${event.runId || event.childSessionId ? "subagent" : "main"}] [${summarizeLlmGenerationInputForName(event.input)}]`;
1087
+ return `llm-generation [${event.runId || event.childSessionId ? 'subagent' : 'main'}] [${summarizeLlmGenerationInputForName(event.input)}]`;
1038
1088
  }
1039
1089
  function summarizeLlmGenerationInputForName(input) {
1040
- if (typeof input === "string") {
1090
+ if (typeof input === 'string') {
1041
1091
  return truncateObservationSummary(input);
1042
1092
  }
1043
- if (input && typeof input === "object" && !Array.isArray(input)) {
1093
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
1044
1094
  const continuation = input.continuation === true;
1045
- const index = typeof input.llmGenerationIndex === "number" ? input.llmGenerationIndex : undefined;
1046
- const toolResults = typeof input.previousToolResultCount === "number" ? input.previousToolResultCount : undefined;
1095
+ const index = typeof input.llmGenerationIndex === 'number' ? input.llmGenerationIndex : undefined;
1096
+ const toolResults = typeof input.previousToolResultCount === 'number' ? input.previousToolResultCount : undefined;
1047
1097
  if (continuation) {
1048
- return truncateObservationSummary(`continuation${index !== undefined ? ` #${index}` : ""}${toolResults !== undefined ? ` after ${toolResults} tool result(s)` : ""}`);
1098
+ return truncateObservationSummary(`continuation${index !== undefined ? ` #${index}` : ''}${toolResults !== undefined ? ` after ${toolResults} tool result(s)` : ''}`);
1049
1099
  }
1050
1100
  }
1051
- return "request";
1101
+ return 'request';
1052
1102
  }
1053
1103
  function stringArg(args, key) {
1054
1104
  const value = args[key];
1055
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
1105
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
1056
1106
  }
1057
1107
  function stringFromJsonObject(value, key) {
1058
1108
  const field = value?.[key];
1059
- return typeof field === "string" && field.trim() ? field.trim() : undefined;
1109
+ return typeof field === 'string' && field.trim() ? field.trim() : undefined;
1060
1110
  }
1061
1111
  function truncateObservationSummary(value, maxLength = 90) {
1062
- const normalized = value.replace(/\s+/g, " ").trim();
1112
+ const normalized = value.replace(/\s+/g, ' ').trim();
1063
1113
  return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}…` : normalized;
1064
1114
  }
1065
1115
  function ensureEndAfterStart(startTime, endTime) {
@@ -1071,14 +1121,16 @@ function toOtelTracePayload(spans, config) {
1071
1121
  {
1072
1122
  resource: {
1073
1123
  attributes: [
1074
- otelAttribute("service.name", config.serviceName ?? "pi-server"),
1075
- ...(config.serviceVersion ? [otelAttribute("service.version", config.serviceVersion)] : []),
1076
- otelAttribute("telemetry.sdk.name", "@amaster.ai/pi-telemetry"),
1124
+ otelAttribute('service.name', config.serviceName ?? 'pi-server'),
1125
+ ...(config.serviceVersion
1126
+ ? [otelAttribute('service.version', config.serviceVersion)]
1127
+ : []),
1128
+ otelAttribute('telemetry.sdk.name', '@amaster.ai/pi-telemetry'),
1077
1129
  ],
1078
1130
  },
1079
1131
  scopeSpans: [
1080
1132
  {
1081
- scope: { name: "@amaster.ai/pi-telemetry", version: "0.1.0" },
1133
+ scope: { name: '@amaster.ai/pi-telemetry', version: '0.1.0' },
1082
1134
  spans: spans.map(toOtelSpanPayload),
1083
1135
  },
1084
1136
  ],
@@ -1105,13 +1157,13 @@ function otelAttribute(key, value) {
1105
1157
  return { key, value: otelAttributeValue(value) };
1106
1158
  }
1107
1159
  function otelAttributeValue(value) {
1108
- if (typeof value === "boolean") {
1160
+ if (typeof value === 'boolean') {
1109
1161
  return { boolValue: value };
1110
1162
  }
1111
- if (typeof value === "number") {
1163
+ if (typeof value === 'number') {
1112
1164
  return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
1113
1165
  }
1114
- if (typeof value === "string") {
1166
+ if (typeof value === 'string') {
1115
1167
  return { stringValue: value };
1116
1168
  }
1117
1169
  return { stringValue: JSON.stringify(value) };
@@ -1121,13 +1173,13 @@ function toUnixNano(iso) {
1121
1173
  return String(BigInt(Number.isFinite(millis) ? millis : Date.now()) * 1000000n);
1122
1174
  }
1123
1175
  function normalizeOtelTracesEndpoint(endpoint) {
1124
- return endpoint.endsWith("/v1/traces") ? endpoint : `${endpoint.replace(/\/+$/, "")}/v1/traces`;
1176
+ return endpoint.endsWith('/v1/traces') ? endpoint : `${endpoint.replace(/\/+$/, '')}/v1/traces`;
1125
1177
  }
1126
1178
  function shortCorrelationId(value) {
1127
1179
  if (!value) {
1128
1180
  return undefined;
1129
1181
  }
1130
- const normalized = value.startsWith("trace:") ? value.slice("trace:".length) : value;
1182
+ const normalized = value.startsWith('trace:') ? value.slice('trace:'.length) : value;
1131
1183
  const uuid = normalized.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i)?.[0];
1132
1184
  if (uuid) {
1133
1185
  return uuid.slice(0, 8);
@@ -1141,11 +1193,14 @@ function compactSessionId(value) {
1141
1193
  if (!value) {
1142
1194
  return undefined;
1143
1195
  }
1144
- const [root, ...subagents] = value.split(":subagent:");
1196
+ const [root, ...subagents] = value.split(':subagent:');
1145
1197
  if (subagents.length === 0) {
1146
1198
  return value;
1147
1199
  }
1148
- return [root, ...subagents.map((sessionId) => `sub:${shortCorrelationId(sessionId) ?? sessionId}`)].join("/");
1200
+ return [
1201
+ root,
1202
+ ...subagents.map((sessionId) => `sub:${shortCorrelationId(sessionId) ?? sessionId}`),
1203
+ ].join('/');
1149
1204
  }
1150
1205
  function lineageMetadata(event) {
1151
1206
  const sessionId = compactSessionId(event.sessionId);
@@ -1202,7 +1257,7 @@ function toLangfuseUsage(usage) {
1202
1257
  ...(usage.input !== undefined ? { input: usage.input } : {}),
1203
1258
  ...(usage.output !== undefined ? { output: usage.output } : {}),
1204
1259
  ...(usage.totalTokens !== undefined ? { total: usage.totalTokens } : {}),
1205
- unit: "TOKENS",
1260
+ unit: 'TOKENS',
1206
1261
  ...(usage.cost?.input !== undefined ? { inputCost: usage.cost.input } : {}),
1207
1262
  ...(usage.cost?.output !== undefined ? { outputCost: usage.cost.output } : {}),
1208
1263
  ...(usage.cost?.total !== undefined ? { totalCost: usage.cost.total } : {}),
@@ -1219,12 +1274,12 @@ function toLangfuseUsageDetails(usage) {
1219
1274
  }
1220
1275
  function flattenUsageAttributes(usage) {
1221
1276
  return {
1222
- ...(usage.input !== undefined ? { "usage.input": usage.input } : {}),
1223
- ...(usage.output !== undefined ? { "usage.output": usage.output } : {}),
1224
- ...(usage.cacheRead !== undefined ? { "usage.cache_read": usage.cacheRead } : {}),
1225
- ...(usage.cacheWrite !== undefined ? { "usage.cache_write": usage.cacheWrite } : {}),
1226
- ...(usage.totalTokens !== undefined ? { "usage.total_tokens": usage.totalTokens } : {}),
1227
- ...(usage.cost?.total !== undefined ? { "usage.cost.total": usage.cost.total } : {}),
1277
+ ...(usage.input !== undefined ? { 'usage.input': usage.input } : {}),
1278
+ ...(usage.output !== undefined ? { 'usage.output': usage.output } : {}),
1279
+ ...(usage.cacheRead !== undefined ? { 'usage.cache_read': usage.cacheRead } : {}),
1280
+ ...(usage.cacheWrite !== undefined ? { 'usage.cache_write': usage.cacheWrite } : {}),
1281
+ ...(usage.totalTokens !== undefined ? { 'usage.total_tokens': usage.totalTokens } : {}),
1282
+ ...(usage.cost?.total !== undefined ? { 'usage.cost.total': usage.cost.total } : {}),
1228
1283
  };
1229
1284
  }
1230
1285
  function ingestionEvent(type, timestamp, body) {
@@ -1242,7 +1297,7 @@ function langfuseSpanId(traceId, key) {
1242
1297
  return stableHex(`span:${traceId}:${key}`, 16);
1243
1298
  }
1244
1299
  function stableHex(input, length) {
1245
- return createHash("sha256").update(input).digest("hex").slice(0, length);
1300
+ return createHash('sha256').update(input).digest('hex').slice(0, length);
1246
1301
  }
1247
1302
  function applyTelemetryRedaction(config, event) {
1248
1303
  const redacted = config.redactEvent ? config.redactEvent(event) : event;
@@ -1274,13 +1329,13 @@ function redactJsonObjectPayload(input) {
1274
1329
  return rest;
1275
1330
  }
1276
1331
  function isToolEvent(event) {
1277
- return "toolCallId" in event;
1332
+ return 'toolCallId' in event;
1278
1333
  }
1279
1334
  function isLlmGenerationEvent(event) {
1280
- return "llmGenerationId" in event;
1335
+ return 'llmGenerationId' in event;
1281
1336
  }
1282
1337
  function parseBoolean(value) {
1283
- return value === "1" || value === "true" || value === "TRUE" || value === "yes";
1338
+ return value === '1' || value === 'true' || value === 'TRUE' || value === 'yes';
1284
1339
  }
1285
1340
  function parseBooleanWithDefault(value, fallback) {
1286
1341
  if (value === undefined) {
@@ -1290,7 +1345,7 @@ function parseBooleanWithDefault(value, fallback) {
1290
1345
  }
1291
1346
  function parseLangfuseTransport(value) {
1292
1347
  const normalized = value?.trim().toLowerCase();
1293
- return normalized === "sdk" || normalized === "ingestion" ? normalized : undefined;
1348
+ return normalized === 'sdk' || normalized === 'ingestion' ? normalized : undefined;
1294
1349
  }
1295
1350
  function parsePositiveInteger(value, fallback) {
1296
1351
  const parsed = Number(value);
@@ -1305,7 +1360,7 @@ function assertNever(value) {
1305
1360
  }
1306
1361
  function requireTraceId(traceId) {
1307
1362
  if (!traceId) {
1308
- throw new Error("Telemetry event is missing traceId");
1363
+ throw new Error('Telemetry event is missing traceId');
1309
1364
  }
1310
1365
  return traceId;
1311
1366
  }