@amaster.ai/pi-telemetry 0.1.2-beta.0 → 0.1.2-beta.10

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,4 +1,4 @@
1
- import { createHash, randomUUID } from 'node:crypto';
1
+ import { createHash } from 'node:crypto';
2
2
  import { Langfuse } from 'langfuse';
3
3
  import { NoopRuntimeEventExporter, } from './index.js';
4
4
  const DEFAULT_LANGFUSE_BASE_URL = 'https://cloud.langfuse.com';
@@ -53,7 +53,7 @@ export class LangfuseSdkRuntimeEventExporter {
53
53
  case 'chat_turn_started': {
54
54
  const body = {
55
55
  id: rootSpanId,
56
- name: 'copilot-chat-turn',
56
+ name: 'chat-turn',
57
57
  startTime: event.createdAt,
58
58
  input: event.details?.input,
59
59
  level: 'DEFAULT',
@@ -78,14 +78,14 @@ export class LangfuseSdkRuntimeEventExporter {
78
78
  this.closeSdkSubagentBatchSpans(traceId, event);
79
79
  trace.update({
80
80
  sessionId: event.sessionId,
81
- name: 'copilot-chat-turn',
81
+ name: '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: 'chat-turn',
89
89
  startTime: event.createdAt,
90
90
  metadata: lifecycleMetadata(event),
91
91
  });
@@ -96,7 +96,6 @@ export class LangfuseSdkRuntimeEventExporter {
96
96
  ...(event.error ? { statusMessage: event.error } : {}),
97
97
  metadata: lifecycleMetadata(event),
98
98
  });
99
- this.spans.delete(rootKey);
100
99
  break;
101
100
  }
102
101
  case 'chat_turn_steered':
@@ -119,7 +118,7 @@ export class LangfuseSdkRuntimeEventExporter {
119
118
  case 'subagent_spawned':
120
119
  case 'subagent_started': {
121
120
  const key = subagentSpanKey(event);
122
- const rootSpan = this.ensureSdkRootSpan(trace, rootKey, event);
121
+ this.ensureSdkRootSpan(trace, rootKey, event);
123
122
  const body = {
124
123
  id: langfuseSpanId(traceId, key),
125
124
  name: 'subagent',
@@ -146,7 +145,7 @@ export class LangfuseSdkRuntimeEventExporter {
146
145
  case 'subagent_cancelled': {
147
146
  const key = subagentSpanKey(event);
148
147
  const output = event.details?.output ?? (event.error ? { error: event.error } : undefined);
149
- const rootSpan = this.ensureSdkRootSpan(trace, rootKey, event);
148
+ this.ensureSdkRootSpan(trace, rootKey, event);
150
149
  const span = this.spans.get(key) ??
151
150
  this.getSdkSubagentParent(trace, rootKey, event).span({
152
151
  id: langfuseSpanId(traceId, key),
@@ -161,7 +160,6 @@ export class LangfuseSdkRuntimeEventExporter {
161
160
  ...(event.error ? { statusMessage: event.error } : {}),
162
161
  metadata: lifecycleMetadata(event),
163
162
  });
164
- this.spans.delete(key);
165
163
  break;
166
164
  }
167
165
  default:
@@ -280,7 +278,7 @@ export class LangfuseSdkRuntimeEventExporter {
280
278
  const trace = this.client.trace({
281
279
  id: langfuseTraceId(traceId),
282
280
  sessionId: event.sessionId,
283
- name: 'copilot-chat-turn',
281
+ name: 'chat-turn',
284
282
  timestamp: event.createdAt,
285
283
  input: !isToolEvent(event) && !isLlmGenerationEvent(event) ? event.details?.input : undefined,
286
284
  metadata: isToolEvent(event)
@@ -336,7 +334,6 @@ export class LangfuseSdkRuntimeEventExporter {
336
334
  ...(event.error ? { statusMessage: event.error } : {}),
337
335
  metadata: lifecycleMetadata(event),
338
336
  });
339
- this.spans.delete(key);
340
337
  }
341
338
  }
342
339
  ensureSdkRootSpan(trace, rootKey, event) {
@@ -347,7 +344,7 @@ export class LangfuseSdkRuntimeEventExporter {
347
344
  const traceId = requireTraceId(event.traceId);
348
345
  const span = trace.span({
349
346
  id: langfuseSpanId(traceId, rootKey),
350
- name: 'copilot-chat-turn',
347
+ name: 'chat-turn',
351
348
  startTime: event.createdAt,
352
349
  metadata: isToolEvent(event)
353
350
  ? toolMetadata(event)
@@ -359,97 +356,6 @@ export class LangfuseSdkRuntimeEventExporter {
359
356
  return span;
360
357
  }
361
358
  }
362
- export class LangfuseHttpRuntimeEventExporter {
363
- config;
364
- endpoint;
365
- authHeader;
366
- fetchImpl;
367
- queue = [];
368
- flushTimer;
369
- flushing;
370
- constructor(config, fetchImpl) {
371
- this.config = config;
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
- });
384
- }
385
- async publish(event) {
386
- const redactedEvent = applyTelemetryRedaction(this.config, event);
387
- if (!redactedEvent) {
388
- return;
389
- }
390
- const mapped = mapRuntimeEventToLangfuse(redactedEvent);
391
- if (mapped.length === 0) {
392
- return;
393
- }
394
- this.queue.push(...mapped);
395
- if (this.queue.length >= this.config.flushAt) {
396
- await this.flush();
397
- return;
398
- }
399
- this.scheduleFlush();
400
- }
401
- async flush() {
402
- if (this.flushing) {
403
- await this.flushing;
404
- return;
405
- }
406
- if (this.flushTimer) {
407
- clearTimeout(this.flushTimer);
408
- this.flushTimer = undefined;
409
- }
410
- const batch = this.queue.splice(0, this.queue.length);
411
- if (batch.length === 0) {
412
- return;
413
- }
414
- this.flushing = this.sendBatch(batch)
415
- .catch((error) => {
416
- this.queue.unshift(...batch);
417
- throw error;
418
- })
419
- .finally(() => {
420
- this.flushing = undefined;
421
- });
422
- await this.flushing;
423
- }
424
- async close() {
425
- await this.flush();
426
- }
427
- scheduleFlush() {
428
- if (this.flushTimer) {
429
- return;
430
- }
431
- this.flushTimer = setTimeout(() => {
432
- this.flushTimer = undefined;
433
- void this.flush().catch(() => undefined);
434
- }, this.config.flushIntervalMs);
435
- this.flushTimer.unref();
436
- }
437
- async sendBatch(batch) {
438
- const response = await this.fetchImpl(this.endpoint, {
439
- method: 'POST',
440
- headers: {
441
- authorization: this.authHeader,
442
- 'content-type': 'application/json',
443
- 'x-langfuse-ingestion-version': '4',
444
- },
445
- body: JSON.stringify({ batch }),
446
- });
447
- if (!response.ok) {
448
- const text = await response.text().catch(() => '');
449
- throw new Error(`Langfuse ingestion failed with ${response.status}${text ? `: ${text}` : ''}`);
450
- }
451
- }
452
- }
453
359
  export class OtelRuntimeEventExporter {
454
360
  config;
455
361
  endpoint;
@@ -545,31 +451,22 @@ export function createRuntimeEventExporterFromEnv(env) {
545
451
  if (!config.enabled) {
546
452
  return new NoopRuntimeEventExporter();
547
453
  }
548
- if (config.transport === 'sdk') {
549
- return new LangfuseSdkRuntimeEventExporter(config);
550
- }
551
- return new LangfuseHttpRuntimeEventExporter(config);
454
+ return new LangfuseSdkRuntimeEventExporter(config);
552
455
  }
553
456
  export function createLangfuseExporter(telemetryConfig) {
554
457
  const config = resolveLangfuseExporterConfig(telemetryConfig);
555
458
  if (!config.enabled) {
556
459
  return new NoopRuntimeEventExporter();
557
460
  }
558
- if (config.transport === 'sdk') {
559
- return new LangfuseSdkRuntimeEventExporter(config);
560
- }
561
- return new LangfuseHttpRuntimeEventExporter(config);
461
+ return new LangfuseSdkRuntimeEventExporter(config);
562
462
  }
563
463
  export function resolveLangfuseExporterConfig(telemetryConfig) {
564
464
  const lf = telemetryConfig.langfuse;
565
465
  const publicKey = lf?.publicKey ?? '';
566
466
  const secretKey = lf?.secretKey ?? '';
567
467
  const credentialsPresent = Boolean(publicKey && secretKey);
568
- const requestedTransport = lf?.transport;
569
- const transport = requestedTransport ?? (credentialsPresent ? 'sdk' : 'ingestion');
570
468
  return {
571
469
  enabled: Boolean(lf?.enabled && credentialsPresent),
572
- transport,
573
470
  publicKey,
574
471
  secretKey,
575
472
  baseUrl: lf?.baseUrl ?? DEFAULT_LANGFUSE_BASE_URL,
@@ -585,13 +482,10 @@ export function resolveLangfuseConfig(env) {
585
482
  const publicKey = trim(env.LANGFUSE_PUBLIC_KEY);
586
483
  const secretKey = trim(env.LANGFUSE_SECRET_KEY);
587
484
  const baseUrl = trim(env.LANGFUSE_BASE_URL) ?? DEFAULT_LANGFUSE_BASE_URL;
588
- const requestedTransport = parseLangfuseTransport(env.LANGFUSE_TRANSPORT);
589
- const transport = requestedTransport ?? (publicKey && secretKey ? 'sdk' : 'ingestion');
590
485
  const credentialsPresent = Boolean(publicKey && secretKey);
591
486
  const serviceVersion = trim(env.TELEMETRY_SERVICE_VERSION);
592
487
  return {
593
488
  enabled: Boolean(enabled && credentialsPresent),
594
- transport,
595
489
  publicKey: publicKey ?? '',
596
490
  secretKey: secretKey ?? '',
597
491
  baseUrl,
@@ -602,171 +496,6 @@ export function resolveLangfuseConfig(env) {
602
496
  includePayloads: parseBooleanWithDefault(env.TELEMETRY_INCLUDE_PAYLOADS ?? env.LANGFUSE_INCLUDE_PAYLOADS, true),
603
497
  };
604
498
  }
605
- export function mapRuntimeEventToLangfuse(event) {
606
- if (!event.traceId) {
607
- return [];
608
- }
609
- if (isLlmGenerationEvent(event)) {
610
- return [mapLlmGenerationEventToLangfuse(event)];
611
- }
612
- if (isToolEvent(event)) {
613
- return [mapToolEventToLangfuse(event)];
614
- }
615
- return mapLifecycleEventToLangfuse(event);
616
- }
617
- function mapLifecycleEventToLangfuse(event) {
618
- const traceId = requireTraceId(event.traceId);
619
- switch (event.type) {
620
- case 'chat_turn_started':
621
- return [
622
- ingestionEvent('trace-create', event.createdAt, {
623
- id: langfuseTraceId(traceId),
624
- sessionId: event.sessionId,
625
- name: 'copilot-chat-turn',
626
- timestamp: event.createdAt,
627
- input: event.details?.input,
628
- metadata: lifecycleMetadata(event),
629
- }),
630
- ];
631
- case 'chat_turn_completed':
632
- case 'chat_turn_failed': {
633
- const output = event.details?.output ?? (event.error ? { error: event.error } : undefined);
634
- return [
635
- ingestionEvent('trace-update', event.createdAt, {
636
- id: langfuseTraceId(traceId),
637
- ...(output !== undefined ? { output } : {}),
638
- metadata: lifecycleMetadata(event),
639
- }),
640
- ];
641
- }
642
- case 'chat_turn_steered':
643
- case 'chat_turn_steer_delivered':
644
- case 'chat_turn_followup_queued':
645
- case 'chat_turn_followup_delivered': {
646
- const spanId = langfuseSpanId(traceId, chatInputSpanKey(event));
647
- return [
648
- ingestionEvent('span-create', event.createdAt, {
649
- id: spanId,
650
- traceId: langfuseTraceId(traceId),
651
- parentObservationId: langfuseSpanId(traceId, chatSpanKey(event)),
652
- name: chatInputObservationName(event),
653
- startTime: event.createdAt,
654
- input: event.details?.input,
655
- metadata: lifecycleMetadata(event),
656
- }),
657
- ingestionEvent('span-update', event.createdAt, {
658
- id: spanId,
659
- traceId: langfuseTraceId(traceId),
660
- endTime: event.createdAt,
661
- output: event.details?.output ?? chatInputLifecycleOutput(event),
662
- metadata: lifecycleMetadata(event),
663
- }),
664
- ];
665
- }
666
- case 'subagent_spawned':
667
- case 'subagent_started': {
668
- const batchKey = subagentBatchSpanKey(event);
669
- return [
670
- ...(batchKey
671
- ? [
672
- ingestionEvent('span-create', event.createdAt, {
673
- id: langfuseSpanId(traceId, batchKey),
674
- traceId: langfuseTraceId(traceId),
675
- parentObservationId: langfuseSpanId(traceId, chatSpanKey(event)),
676
- name: 'subagent fan-out',
677
- startTime: event.createdAt,
678
- input: event.details?.input,
679
- metadata: lifecycleMetadata(event),
680
- }),
681
- ]
682
- : []),
683
- ingestionEvent('span-create', event.createdAt, {
684
- id: langfuseSpanId(traceId, subagentSpanKey(event)),
685
- traceId: langfuseTraceId(traceId),
686
- parentObservationId: langfuseSpanId(traceId, batchKey ?? subagentSpawnToolSpanKey(event) ?? chatSpanKey(event)),
687
- name: 'subagent',
688
- startTime: event.createdAt,
689
- metadata: lifecycleMetadata(event),
690
- }),
691
- ];
692
- }
693
- case 'subagent_completed':
694
- case 'subagent_failed':
695
- case 'subagent_cancelled':
696
- return [
697
- ingestionEvent('span-update', event.createdAt, {
698
- id: langfuseSpanId(traceId, subagentSpanKey(event)),
699
- traceId: langfuseTraceId(traceId),
700
- endTime: event.createdAt,
701
- metadata: lifecycleMetadata(event),
702
- ...(event.error ? { output: { error: event.error } } : {}),
703
- }),
704
- ];
705
- default:
706
- return assertNever(event.type);
707
- }
708
- }
709
- function mapToolEventToLangfuse(event) {
710
- const traceId = requireTraceId(event.traceId);
711
- const spanId = langfuseSpanId(traceId, `tool:${event.sessionId}:${event.toolCallId}:${event.toolName}`);
712
- const parentObservationId = langfuseParentObservationId(traceId, event);
713
- if (event.status === 'started') {
714
- return ingestionEvent('span-create', event.createdAt, {
715
- id: spanId,
716
- traceId: langfuseTraceId(traceId),
717
- parentObservationId,
718
- name: toolObservationName(event),
719
- startTime: event.createdAt,
720
- input: event.args ?? {},
721
- metadata: toolMetadata(event),
722
- });
723
- }
724
- return ingestionEvent('span-update', event.createdAt, {
725
- id: spanId,
726
- traceId: langfuseTraceId(traceId),
727
- endTime: event.createdAt,
728
- metadata: toolMetadata(event),
729
- ...(event.error ? { output: { error: event.error } } : { output: event.details ?? {} }),
730
- });
731
- }
732
- function mapLlmGenerationEventToLangfuse(event) {
733
- const traceId = requireTraceId(event.traceId);
734
- const id = langfuseSpanId(traceId, llmGenerationKey(event));
735
- const parentObservationId = langfuseParentObservationId(traceId, event);
736
- if (event.status === 'started') {
737
- return ingestionEvent('generation-create', event.createdAt, {
738
- id,
739
- traceId: langfuseTraceId(traceId),
740
- parentObservationId,
741
- name: llmGenerationObservationName(event),
742
- startTime: event.createdAt,
743
- model: event.model.model,
744
- modelParameters: {
745
- provider: event.model.provider,
746
- ...(event.model.thinkingLevel ? { thinkingLevel: event.model.thinkingLevel } : {}),
747
- },
748
- input: event.input,
749
- metadata: llmGenerationMetadata(event),
750
- });
751
- }
752
- return ingestionEvent('generation-update', event.createdAt, {
753
- id,
754
- traceId: langfuseTraceId(traceId),
755
- endTime: event.createdAt,
756
- output: event.output ?? (event.error ? { error: event.error } : undefined),
757
- level: event.error ? 'ERROR' : 'DEFAULT',
758
- ...(event.error ? { statusMessage: event.error } : {}),
759
- ...(event.usage
760
- ? { usage: toLangfuseUsage(event.usage), usageDetails: toLangfuseUsageDetails(event.usage) }
761
- : {}),
762
- model: event.model.model,
763
- modelParameters: {
764
- provider: event.model.provider,
765
- ...(event.model.thinkingLevel ? { thinkingLevel: event.model.thinkingLevel } : {}),
766
- },
767
- metadata: llmGenerationMetadata(event),
768
- });
769
- }
770
499
  function mapRuntimeEventToOtelSpans(event, pendingStarts) {
771
500
  if (!event.traceId) {
772
501
  return [];
@@ -787,7 +516,7 @@ function mapLifecycleEventToOtelSpans(event, pendingStarts) {
787
516
  pendingStarts.set(rootKey, {
788
517
  traceId: langfuseTraceId(traceId),
789
518
  spanId: rootSpanId,
790
- name: 'copilot-chat-turn',
519
+ name: 'chat-turn',
791
520
  startTime: event.createdAt,
792
521
  attributes: {
793
522
  ...lifecycleMetadata(event),
@@ -802,7 +531,7 @@ function mapLifecycleEventToOtelSpans(event, pendingStarts) {
802
531
  completeOtelSpan(pendingStarts, rootKey, {
803
532
  traceId: langfuseTraceId(traceId),
804
533
  spanId: rootSpanId,
805
- name: 'copilot-chat-turn',
534
+ name: 'chat-turn',
806
535
  startTime: event.createdAt,
807
536
  attributes: {
808
537
  ...lifecycleMetadata(event),
@@ -1282,14 +1011,6 @@ function flattenUsageAttributes(usage) {
1282
1011
  ...(usage.cost?.total !== undefined ? { 'usage.cost.total': usage.cost.total } : {}),
1283
1012
  };
1284
1013
  }
1285
- function ingestionEvent(type, timestamp, body) {
1286
- return {
1287
- id: randomUUID(),
1288
- timestamp,
1289
- type,
1290
- body,
1291
- };
1292
- }
1293
1014
  function langfuseTraceId(traceId) {
1294
1015
  return stableHex(`trace:${traceId}`, 32);
1295
1016
  }
@@ -1343,10 +1064,6 @@ function parseBooleanWithDefault(value, fallback) {
1343
1064
  }
1344
1065
  return parseBoolean(value);
1345
1066
  }
1346
- function parseLangfuseTransport(value) {
1347
- const normalized = value?.trim().toLowerCase();
1348
- return normalized === 'sdk' || normalized === 'ingestion' ? normalized : undefined;
1349
- }
1350
1067
  function parsePositiveInteger(value, fallback) {
1351
1068
  const parsed = Number(value);
1352
1069
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;