@livekit/agents 1.0.18 → 1.0.19

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.
Files changed (152) hide show
  1. package/dist/index.cjs +3 -0
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +2 -1
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/inference/api_protos.d.cts +12 -12
  9. package/dist/inference/api_protos.d.ts +12 -12
  10. package/dist/inference/tts.cjs +1 -1
  11. package/dist/inference/tts.cjs.map +1 -1
  12. package/dist/inference/tts.js +1 -1
  13. package/dist/inference/tts.js.map +1 -1
  14. package/dist/ipc/job_proc_lazy_main.cjs +6 -2
  15. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  16. package/dist/ipc/job_proc_lazy_main.js +6 -2
  17. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  18. package/dist/job.cjs +31 -0
  19. package/dist/job.cjs.map +1 -1
  20. package/dist/job.d.cts +6 -0
  21. package/dist/job.d.ts +6 -0
  22. package/dist/job.d.ts.map +1 -1
  23. package/dist/job.js +31 -0
  24. package/dist/job.js.map +1 -1
  25. package/dist/llm/chat_context.cjs +33 -0
  26. package/dist/llm/chat_context.cjs.map +1 -1
  27. package/dist/llm/chat_context.d.cts +22 -2
  28. package/dist/llm/chat_context.d.ts +22 -2
  29. package/dist/llm/chat_context.d.ts.map +1 -1
  30. package/dist/llm/chat_context.js +32 -0
  31. package/dist/llm/chat_context.js.map +1 -1
  32. package/dist/llm/index.cjs +2 -0
  33. package/dist/llm/index.cjs.map +1 -1
  34. package/dist/llm/index.d.cts +1 -1
  35. package/dist/llm/index.d.ts +1 -1
  36. package/dist/llm/index.d.ts.map +1 -1
  37. package/dist/llm/index.js +2 -0
  38. package/dist/llm/index.js.map +1 -1
  39. package/dist/llm/llm.cjs.map +1 -1
  40. package/dist/llm/llm.d.ts.map +1 -1
  41. package/dist/llm/llm.js.map +1 -1
  42. package/dist/llm/provider_format/google.test.cjs +48 -0
  43. package/dist/llm/provider_format/google.test.cjs.map +1 -1
  44. package/dist/llm/provider_format/google.test.js +54 -1
  45. package/dist/llm/provider_format/google.test.js.map +1 -1
  46. package/dist/llm/provider_format/index.d.cts +1 -1
  47. package/dist/llm/provider_format/index.d.ts +1 -1
  48. package/dist/llm/provider_format/openai.cjs +1 -2
  49. package/dist/llm/provider_format/openai.cjs.map +1 -1
  50. package/dist/llm/provider_format/openai.js +1 -2
  51. package/dist/llm/provider_format/openai.js.map +1 -1
  52. package/dist/llm/provider_format/openai.test.cjs +32 -0
  53. package/dist/llm/provider_format/openai.test.cjs.map +1 -1
  54. package/dist/llm/provider_format/openai.test.js +38 -1
  55. package/dist/llm/provider_format/openai.test.js.map +1 -1
  56. package/dist/log.cjs.map +1 -1
  57. package/dist/log.d.ts.map +1 -1
  58. package/dist/log.js.map +1 -1
  59. package/dist/telemetry/index.cjs +51 -0
  60. package/dist/telemetry/index.cjs.map +1 -0
  61. package/dist/telemetry/index.d.cts +4 -0
  62. package/dist/telemetry/index.d.ts +4 -0
  63. package/dist/telemetry/index.d.ts.map +1 -0
  64. package/dist/telemetry/index.js +12 -0
  65. package/dist/telemetry/index.js.map +1 -0
  66. package/dist/telemetry/trace_types.cjs +191 -0
  67. package/dist/telemetry/trace_types.cjs.map +1 -0
  68. package/dist/telemetry/trace_types.d.cts +56 -0
  69. package/dist/telemetry/trace_types.d.ts +56 -0
  70. package/dist/telemetry/trace_types.d.ts.map +1 -0
  71. package/dist/telemetry/trace_types.js +113 -0
  72. package/dist/telemetry/trace_types.js.map +1 -0
  73. package/dist/telemetry/traces.cjs +196 -0
  74. package/dist/telemetry/traces.cjs.map +1 -0
  75. package/dist/telemetry/traces.d.cts +97 -0
  76. package/dist/telemetry/traces.d.ts +97 -0
  77. package/dist/telemetry/traces.d.ts.map +1 -0
  78. package/dist/telemetry/traces.js +173 -0
  79. package/dist/telemetry/traces.js.map +1 -0
  80. package/dist/telemetry/utils.cjs +86 -0
  81. package/dist/telemetry/utils.cjs.map +1 -0
  82. package/dist/telemetry/utils.d.cts +5 -0
  83. package/dist/telemetry/utils.d.ts +5 -0
  84. package/dist/telemetry/utils.d.ts.map +1 -0
  85. package/dist/telemetry/utils.js +51 -0
  86. package/dist/telemetry/utils.js.map +1 -0
  87. package/dist/tts/tts.cjs.map +1 -1
  88. package/dist/tts/tts.d.ts.map +1 -1
  89. package/dist/tts/tts.js.map +1 -1
  90. package/dist/voice/agent.cjs +15 -0
  91. package/dist/voice/agent.cjs.map +1 -1
  92. package/dist/voice/agent.d.cts +4 -1
  93. package/dist/voice/agent.d.ts +4 -1
  94. package/dist/voice/agent.d.ts.map +1 -1
  95. package/dist/voice/agent.js +15 -0
  96. package/dist/voice/agent.js.map +1 -1
  97. package/dist/voice/agent_activity.cjs +2 -0
  98. package/dist/voice/agent_activity.cjs.map +1 -1
  99. package/dist/voice/agent_activity.d.ts.map +1 -1
  100. package/dist/voice/agent_activity.js +2 -0
  101. package/dist/voice/agent_activity.js.map +1 -1
  102. package/dist/voice/agent_session.cjs +29 -1
  103. package/dist/voice/agent_session.cjs.map +1 -1
  104. package/dist/voice/agent_session.d.cts +6 -2
  105. package/dist/voice/agent_session.d.ts +6 -2
  106. package/dist/voice/agent_session.d.ts.map +1 -1
  107. package/dist/voice/agent_session.js +30 -2
  108. package/dist/voice/agent_session.js.map +1 -1
  109. package/dist/voice/audio_recognition.cjs.map +1 -1
  110. package/dist/voice/audio_recognition.d.ts.map +1 -1
  111. package/dist/voice/audio_recognition.js.map +1 -1
  112. package/dist/voice/generation.cjs.map +1 -1
  113. package/dist/voice/generation.d.ts.map +1 -1
  114. package/dist/voice/generation.js.map +1 -1
  115. package/dist/voice/index.cjs +2 -0
  116. package/dist/voice/index.cjs.map +1 -1
  117. package/dist/voice/index.d.cts +1 -0
  118. package/dist/voice/index.d.ts +1 -0
  119. package/dist/voice/index.d.ts.map +1 -1
  120. package/dist/voice/index.js +1 -0
  121. package/dist/voice/index.js.map +1 -1
  122. package/dist/voice/report.cjs +69 -0
  123. package/dist/voice/report.cjs.map +1 -0
  124. package/dist/voice/report.d.cts +26 -0
  125. package/dist/voice/report.d.ts +26 -0
  126. package/dist/voice/report.d.ts.map +1 -0
  127. package/dist/voice/report.js +44 -0
  128. package/dist/voice/report.js.map +1 -0
  129. package/package.json +10 -3
  130. package/src/index.ts +2 -1
  131. package/src/inference/tts.ts +1 -1
  132. package/src/ipc/job_proc_lazy_main.ts +10 -2
  133. package/src/job.ts +48 -0
  134. package/src/llm/chat_context.ts +53 -1
  135. package/src/llm/index.ts +1 -0
  136. package/src/llm/llm.ts +2 -0
  137. package/src/llm/provider_format/google.test.ts +72 -1
  138. package/src/llm/provider_format/openai.test.ts +55 -1
  139. package/src/llm/provider_format/openai.ts +3 -2
  140. package/src/log.ts +1 -0
  141. package/src/telemetry/index.ts +10 -0
  142. package/src/telemetry/trace_types.ts +88 -0
  143. package/src/telemetry/traces.ts +266 -0
  144. package/src/telemetry/utils.ts +61 -0
  145. package/src/tts/tts.ts +4 -0
  146. package/src/voice/agent.ts +22 -0
  147. package/src/voice/agent_activity.ts +6 -0
  148. package/src/voice/agent_session.ts +44 -1
  149. package/src/voice/audio_recognition.ts +2 -0
  150. package/src/voice/generation.ts +3 -0
  151. package/src/voice/index.ts +1 -0
  152. package/src/voice/report.ts +77 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/traces.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type Attributes,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';\nimport { Resource } from '@opentelemetry/resources';\nimport type { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\nimport { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';\nimport { AccessToken } from 'livekit-server-sdk';\n\nexport interface StartSpanOptions {\n /** Name of the span */\n name: string;\n /** Optional parent context to use for this span */\n context?: Context;\n /** Attributes to set on the span when it starts */\n attributes?: Attributes;\n /** Whether to end the span when the function exits (default: true) */\n endOnExit?: boolean;\n}\n\n/**\n * A dynamic tracer that allows the tracer provider to be changed at runtime.\n */\nclass DynamicTracer {\n private tracerProvider: TracerProvider;\n private tracer: Tracer;\n private readonly instrumentingModuleName: string;\n\n constructor(instrumentingModuleName: string) {\n this.instrumentingModuleName = instrumentingModuleName;\n this.tracerProvider = trace.getTracerProvider();\n this.tracer = trace.getTracer(instrumentingModuleName);\n }\n\n /**\n * Set a new tracer provider. This updates the underlying tracer instance.\n * @param provider - The new tracer provider to use\n */\n setProvider(provider: TracerProvider): void {\n this.tracerProvider = provider;\n this.tracer = this.tracerProvider.getTracer(this.instrumentingModuleName);\n }\n\n /**\n * Get the underlying OpenTelemetry tracer.\n * Use this to access the full Tracer API when needed.\n */\n getTracer(): Tracer {\n return this.tracer;\n }\n\n /**\n * Start a span manually (without making it active).\n * You must call span.end() when done.\n *\n * @param options - Span configuration including name\n * @returns The created span\n */\n startSpan(options: StartSpanOptions): Span {\n const ctx = options.context || otelContext.active();\n const span = this.tracer.startSpan(\n options.name,\n {\n attributes: options.attributes,\n },\n ctx,\n );\n\n return span;\n }\n\n /**\n * Start a new span and make it active in the current context.\n * The span will automatically be ended when the provided function completes (unless endOnExit=false).\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n async startActiveSpan<T>(fn: (span: Span) => Promise<T>, options: StartSpanOptions): Promise<T> {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n return new Promise((resolve, reject) => {\n this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {\n try {\n const result = await fn(span);\n resolve(result);\n } catch (error) {\n reject(error);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n });\n }\n\n /**\n * Synchronous version of startActiveSpan for non-async operations.\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n startActiveSpanSync<T>(fn: (span: Span) => T, options: StartSpanOptions): T {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n return this.tracer.startActiveSpan(options.name, opts, ctx, (span) => {\n try {\n return fn(span);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n }\n}\n\n/**\n * The global tracer instance used throughout the agents framework.\n * This tracer can have its provider updated at runtime via setTracerProvider().\n */\nexport const tracer = new DynamicTracer('livekit-agents');\n\nclass MetadataSpanProcessor implements SpanProcessor {\n private metadata: Attributes;\n\n constructor(metadata: Attributes) {\n this.metadata = metadata;\n }\n\n onStart(span: Span, _parentContext: Context): void {\n span.setAttributes(this.metadata);\n }\n\n onEnd(_span: ReadableSpan): void {}\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\n// TODO(brian): PR4 - Add MetadataLogProcessor for structured logging\n\n// TODO(brian): PR4 - Add ExtraDetailsProcessor for structured logging\n\n/**\n * Set the tracer provider for the livekit-agents framework.\n * This should be called before agent session start if using custom tracer providers.\n *\n * @param provider - The tracer provider to use (must be a NodeTracerProvider)\n * @param options - Optional configuration with metadata property to inject into all spans\n *\n * @example\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n * import { setTracerProvider } from '@livekit/agents/telemetry';\n *\n * const provider = new NodeTracerProvider();\n * setTracerProvider(provider, {\n * metadata: { room_id: 'room123', job_id: 'job456' }\n * });\n * ```\n */\nexport function setTracerProvider(\n provider: NodeTracerProvider,\n options?: { metadata?: Attributes },\n): void {\n if (options?.metadata) {\n provider.addSpanProcessor(new MetadataSpanProcessor(options.metadata));\n }\n\n tracer.setProvider(provider);\n}\n\n/**\n * Setup OpenTelemetry tracer for LiveKit Cloud observability.\n * This configures OTLP exporters to send traces to LiveKit Cloud.\n *\n * @param options - Configuration for cloud tracer with roomId, jobId, and cloudHostname properties\n *\n * @internal\n */\nexport async function setupCloudTracer(options: {\n roomId: string;\n jobId: string;\n cloudHostname: string;\n}): Promise<void> {\n const { roomId, jobId, cloudHostname } = options;\n\n const apiKey = process.env.LIVEKIT_API_KEY;\n const apiSecret = process.env.LIVEKIT_API_SECRET;\n\n if (!apiKey || !apiSecret) {\n throw new Error('LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for cloud tracing');\n }\n\n const token = new AccessToken(apiKey, apiSecret, {\n identity: 'livekit-agents-telemetry',\n ttl: '6h',\n });\n token.addObservabilityGrant({ write: true });\n\n try {\n const jwt = await token.toJwt();\n\n const headers = {\n Authorization: `Bearer ${jwt}`,\n };\n\n const metadata: Attributes = {\n room_id: roomId,\n job_id: jobId,\n };\n\n const resource = new Resource({\n [ATTR_SERVICE_NAME]: 'livekit-agents',\n room_id: roomId,\n job_id: jobId,\n });\n\n // Configure OTLP exporter to send traces to LiveKit Cloud\n const spanExporter = new OTLPTraceExporter({\n url: `https://${cloudHostname}/observability/traces/otlp/v0`,\n headers,\n compression: CompressionAlgorithm.GZIP,\n });\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [new MetadataSpanProcessor(metadata), new BatchSpanProcessor(spanExporter)],\n });\n tracerProvider.register();\n\n // Metadata processor is already configured in the constructor above\n setTracerProvider(tracerProvider);\n\n // TODO(brian): PR4 - Add logger provider setup here for structured logging\n // Similar to Python's setup: LoggerProvider, OTLPLogExporter, BatchLogRecordProcessor\n } catch (error) {\n console.error('Failed to setup cloud tracer:', error);\n throw error;\n }\n}\n"],"mappings":"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","names":["otelContext"]}
@@ -0,0 +1,97 @@
1
+ import { type Attributes, type Context, type Span, type Tracer, type TracerProvider } from '@opentelemetry/api';
2
+ import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
3
+ export interface StartSpanOptions {
4
+ /** Name of the span */
5
+ name: string;
6
+ /** Optional parent context to use for this span */
7
+ context?: Context;
8
+ /** Attributes to set on the span when it starts */
9
+ attributes?: Attributes;
10
+ /** Whether to end the span when the function exits (default: true) */
11
+ endOnExit?: boolean;
12
+ }
13
+ /**
14
+ * A dynamic tracer that allows the tracer provider to be changed at runtime.
15
+ */
16
+ declare class DynamicTracer {
17
+ private tracerProvider;
18
+ private tracer;
19
+ private readonly instrumentingModuleName;
20
+ constructor(instrumentingModuleName: string);
21
+ /**
22
+ * Set a new tracer provider. This updates the underlying tracer instance.
23
+ * @param provider - The new tracer provider to use
24
+ */
25
+ setProvider(provider: TracerProvider): void;
26
+ /**
27
+ * Get the underlying OpenTelemetry tracer.
28
+ * Use this to access the full Tracer API when needed.
29
+ */
30
+ getTracer(): Tracer;
31
+ /**
32
+ * Start a span manually (without making it active).
33
+ * You must call span.end() when done.
34
+ *
35
+ * @param options - Span configuration including name
36
+ * @returns The created span
37
+ */
38
+ startSpan(options: StartSpanOptions): Span;
39
+ /**
40
+ * Start a new span and make it active in the current context.
41
+ * The span will automatically be ended when the provided function completes (unless endOnExit=false).
42
+ *
43
+ * @param fn - The function to execute within the span context
44
+ * @param options - Span configuration including name
45
+ * @returns The result of the provided function
46
+ */
47
+ startActiveSpan<T>(fn: (span: Span) => Promise<T>, options: StartSpanOptions): Promise<T>;
48
+ /**
49
+ * Synchronous version of startActiveSpan for non-async operations.
50
+ *
51
+ * @param fn - The function to execute within the span context
52
+ * @param options - Span configuration including name
53
+ * @returns The result of the provided function
54
+ */
55
+ startActiveSpanSync<T>(fn: (span: Span) => T, options: StartSpanOptions): T;
56
+ }
57
+ /**
58
+ * The global tracer instance used throughout the agents framework.
59
+ * This tracer can have its provider updated at runtime via setTracerProvider().
60
+ */
61
+ export declare const tracer: DynamicTracer;
62
+ /**
63
+ * Set the tracer provider for the livekit-agents framework.
64
+ * This should be called before agent session start if using custom tracer providers.
65
+ *
66
+ * @param provider - The tracer provider to use (must be a NodeTracerProvider)
67
+ * @param options - Optional configuration with metadata property to inject into all spans
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
72
+ * import { setTracerProvider } from '@livekit/agents/telemetry';
73
+ *
74
+ * const provider = new NodeTracerProvider();
75
+ * setTracerProvider(provider, {
76
+ * metadata: { room_id: 'room123', job_id: 'job456' }
77
+ * });
78
+ * ```
79
+ */
80
+ export declare function setTracerProvider(provider: NodeTracerProvider, options?: {
81
+ metadata?: Attributes;
82
+ }): void;
83
+ /**
84
+ * Setup OpenTelemetry tracer for LiveKit Cloud observability.
85
+ * This configures OTLP exporters to send traces to LiveKit Cloud.
86
+ *
87
+ * @param options - Configuration for cloud tracer with roomId, jobId, and cloudHostname properties
88
+ *
89
+ * @internal
90
+ */
91
+ export declare function setupCloudTracer(options: {
92
+ roomId: string;
93
+ jobId: string;
94
+ cloudHostname: string;
95
+ }): Promise<void>;
96
+ export {};
97
+ //# sourceMappingURL=traces.d.ts.map
@@ -0,0 +1,97 @@
1
+ import { type Attributes, type Context, type Span, type Tracer, type TracerProvider } from '@opentelemetry/api';
2
+ import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
3
+ export interface StartSpanOptions {
4
+ /** Name of the span */
5
+ name: string;
6
+ /** Optional parent context to use for this span */
7
+ context?: Context;
8
+ /** Attributes to set on the span when it starts */
9
+ attributes?: Attributes;
10
+ /** Whether to end the span when the function exits (default: true) */
11
+ endOnExit?: boolean;
12
+ }
13
+ /**
14
+ * A dynamic tracer that allows the tracer provider to be changed at runtime.
15
+ */
16
+ declare class DynamicTracer {
17
+ private tracerProvider;
18
+ private tracer;
19
+ private readonly instrumentingModuleName;
20
+ constructor(instrumentingModuleName: string);
21
+ /**
22
+ * Set a new tracer provider. This updates the underlying tracer instance.
23
+ * @param provider - The new tracer provider to use
24
+ */
25
+ setProvider(provider: TracerProvider): void;
26
+ /**
27
+ * Get the underlying OpenTelemetry tracer.
28
+ * Use this to access the full Tracer API when needed.
29
+ */
30
+ getTracer(): Tracer;
31
+ /**
32
+ * Start a span manually (without making it active).
33
+ * You must call span.end() when done.
34
+ *
35
+ * @param options - Span configuration including name
36
+ * @returns The created span
37
+ */
38
+ startSpan(options: StartSpanOptions): Span;
39
+ /**
40
+ * Start a new span and make it active in the current context.
41
+ * The span will automatically be ended when the provided function completes (unless endOnExit=false).
42
+ *
43
+ * @param fn - The function to execute within the span context
44
+ * @param options - Span configuration including name
45
+ * @returns The result of the provided function
46
+ */
47
+ startActiveSpan<T>(fn: (span: Span) => Promise<T>, options: StartSpanOptions): Promise<T>;
48
+ /**
49
+ * Synchronous version of startActiveSpan for non-async operations.
50
+ *
51
+ * @param fn - The function to execute within the span context
52
+ * @param options - Span configuration including name
53
+ * @returns The result of the provided function
54
+ */
55
+ startActiveSpanSync<T>(fn: (span: Span) => T, options: StartSpanOptions): T;
56
+ }
57
+ /**
58
+ * The global tracer instance used throughout the agents framework.
59
+ * This tracer can have its provider updated at runtime via setTracerProvider().
60
+ */
61
+ export declare const tracer: DynamicTracer;
62
+ /**
63
+ * Set the tracer provider for the livekit-agents framework.
64
+ * This should be called before agent session start if using custom tracer providers.
65
+ *
66
+ * @param provider - The tracer provider to use (must be a NodeTracerProvider)
67
+ * @param options - Optional configuration with metadata property to inject into all spans
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
72
+ * import { setTracerProvider } from '@livekit/agents/telemetry';
73
+ *
74
+ * const provider = new NodeTracerProvider();
75
+ * setTracerProvider(provider, {
76
+ * metadata: { room_id: 'room123', job_id: 'job456' }
77
+ * });
78
+ * ```
79
+ */
80
+ export declare function setTracerProvider(provider: NodeTracerProvider, options?: {
81
+ metadata?: Attributes;
82
+ }): void;
83
+ /**
84
+ * Setup OpenTelemetry tracer for LiveKit Cloud observability.
85
+ * This configures OTLP exporters to send traces to LiveKit Cloud.
86
+ *
87
+ * @param options - Configuration for cloud tracer with roomId, jobId, and cloudHostname properties
88
+ *
89
+ * @internal
90
+ */
91
+ export declare function setupCloudTracer(options: {
92
+ roomId: string;
93
+ jobId: string;
94
+ cloudHostname: string;
95
+ }): Promise<void>;
96
+ export {};
97
+ //# sourceMappingURL=traces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traces.d.ts","sourceRoot":"","sources":["../../src/telemetry/traces.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,IAAI,EAET,KAAK,MAAM,EACX,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAsB,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAIvF,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mDAAmD;IACnD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,cAAM,aAAa;IACjB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;gBAErC,uBAAuB,EAAE,MAAM;IAM3C;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAK3C;;;OAGG;IACH,SAAS,IAAI,MAAM;IAInB;;;;;;OAMG;IACH,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAa1C;;;;;;;OAOG;IACG,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAqB/F;;;;;;OAMG;IACH,mBAAmB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC;CAe5E;AAED;;;GAGG;AACH,eAAO,MAAM,MAAM,eAAsC,CAAC;AA4B1D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,UAAU,CAAA;CAAE,GAClC,IAAI,CAMN;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDhB"}
@@ -0,0 +1,173 @@
1
+ import {
2
+ context as otelContext,
3
+ trace
4
+ } from "@opentelemetry/api";
5
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
6
+ import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
7
+ import { Resource } from "@opentelemetry/resources";
8
+ import { BatchSpanProcessor, NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
9
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
10
+ import { AccessToken } from "livekit-server-sdk";
11
+ class DynamicTracer {
12
+ tracerProvider;
13
+ tracer;
14
+ instrumentingModuleName;
15
+ constructor(instrumentingModuleName) {
16
+ this.instrumentingModuleName = instrumentingModuleName;
17
+ this.tracerProvider = trace.getTracerProvider();
18
+ this.tracer = trace.getTracer(instrumentingModuleName);
19
+ }
20
+ /**
21
+ * Set a new tracer provider. This updates the underlying tracer instance.
22
+ * @param provider - The new tracer provider to use
23
+ */
24
+ setProvider(provider) {
25
+ this.tracerProvider = provider;
26
+ this.tracer = this.tracerProvider.getTracer(this.instrumentingModuleName);
27
+ }
28
+ /**
29
+ * Get the underlying OpenTelemetry tracer.
30
+ * Use this to access the full Tracer API when needed.
31
+ */
32
+ getTracer() {
33
+ return this.tracer;
34
+ }
35
+ /**
36
+ * Start a span manually (without making it active).
37
+ * You must call span.end() when done.
38
+ *
39
+ * @param options - Span configuration including name
40
+ * @returns The created span
41
+ */
42
+ startSpan(options) {
43
+ const ctx = options.context || otelContext.active();
44
+ const span = this.tracer.startSpan(
45
+ options.name,
46
+ {
47
+ attributes: options.attributes
48
+ },
49
+ ctx
50
+ );
51
+ return span;
52
+ }
53
+ /**
54
+ * Start a new span and make it active in the current context.
55
+ * The span will automatically be ended when the provided function completes (unless endOnExit=false).
56
+ *
57
+ * @param fn - The function to execute within the span context
58
+ * @param options - Span configuration including name
59
+ * @returns The result of the provided function
60
+ */
61
+ async startActiveSpan(fn, options) {
62
+ const ctx = options.context || otelContext.active();
63
+ const endOnExit = options.endOnExit === void 0 ? true : options.endOnExit;
64
+ const opts = { attributes: options.attributes };
65
+ return new Promise((resolve, reject) => {
66
+ this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {
67
+ try {
68
+ const result = await fn(span);
69
+ resolve(result);
70
+ } catch (error) {
71
+ reject(error);
72
+ } finally {
73
+ if (endOnExit) {
74
+ span.end();
75
+ }
76
+ }
77
+ });
78
+ });
79
+ }
80
+ /**
81
+ * Synchronous version of startActiveSpan for non-async operations.
82
+ *
83
+ * @param fn - The function to execute within the span context
84
+ * @param options - Span configuration including name
85
+ * @returns The result of the provided function
86
+ */
87
+ startActiveSpanSync(fn, options) {
88
+ const ctx = options.context || otelContext.active();
89
+ const endOnExit = options.endOnExit === void 0 ? true : options.endOnExit;
90
+ const opts = { attributes: options.attributes };
91
+ return this.tracer.startActiveSpan(options.name, opts, ctx, (span) => {
92
+ try {
93
+ return fn(span);
94
+ } finally {
95
+ if (endOnExit) {
96
+ span.end();
97
+ }
98
+ }
99
+ });
100
+ }
101
+ }
102
+ const tracer = new DynamicTracer("livekit-agents");
103
+ class MetadataSpanProcessor {
104
+ metadata;
105
+ constructor(metadata) {
106
+ this.metadata = metadata;
107
+ }
108
+ onStart(span, _parentContext) {
109
+ span.setAttributes(this.metadata);
110
+ }
111
+ onEnd(_span) {
112
+ }
113
+ shutdown() {
114
+ return Promise.resolve();
115
+ }
116
+ forceFlush() {
117
+ return Promise.resolve();
118
+ }
119
+ }
120
+ function setTracerProvider(provider, options) {
121
+ if (options == null ? void 0 : options.metadata) {
122
+ provider.addSpanProcessor(new MetadataSpanProcessor(options.metadata));
123
+ }
124
+ tracer.setProvider(provider);
125
+ }
126
+ async function setupCloudTracer(options) {
127
+ const { roomId, jobId, cloudHostname } = options;
128
+ const apiKey = process.env.LIVEKIT_API_KEY;
129
+ const apiSecret = process.env.LIVEKIT_API_SECRET;
130
+ if (!apiKey || !apiSecret) {
131
+ throw new Error("LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for cloud tracing");
132
+ }
133
+ const token = new AccessToken(apiKey, apiSecret, {
134
+ identity: "livekit-agents-telemetry",
135
+ ttl: "6h"
136
+ });
137
+ token.addObservabilityGrant({ write: true });
138
+ try {
139
+ const jwt = await token.toJwt();
140
+ const headers = {
141
+ Authorization: `Bearer ${jwt}`
142
+ };
143
+ const metadata = {
144
+ room_id: roomId,
145
+ job_id: jobId
146
+ };
147
+ const resource = new Resource({
148
+ [ATTR_SERVICE_NAME]: "livekit-agents",
149
+ room_id: roomId,
150
+ job_id: jobId
151
+ });
152
+ const spanExporter = new OTLPTraceExporter({
153
+ url: `https://${cloudHostname}/observability/traces/otlp/v0`,
154
+ headers,
155
+ compression: CompressionAlgorithm.GZIP
156
+ });
157
+ const tracerProvider = new NodeTracerProvider({
158
+ resource,
159
+ spanProcessors: [new MetadataSpanProcessor(metadata), new BatchSpanProcessor(spanExporter)]
160
+ });
161
+ tracerProvider.register();
162
+ setTracerProvider(tracerProvider);
163
+ } catch (error) {
164
+ console.error("Failed to setup cloud tracer:", error);
165
+ throw error;
166
+ }
167
+ }
168
+ export {
169
+ setTracerProvider,
170
+ setupCloudTracer,
171
+ tracer
172
+ };
173
+ //# sourceMappingURL=traces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/traces.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type Attributes,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';\nimport { Resource } from '@opentelemetry/resources';\nimport type { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\nimport { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';\nimport { AccessToken } from 'livekit-server-sdk';\n\nexport interface StartSpanOptions {\n /** Name of the span */\n name: string;\n /** Optional parent context to use for this span */\n context?: Context;\n /** Attributes to set on the span when it starts */\n attributes?: Attributes;\n /** Whether to end the span when the function exits (default: true) */\n endOnExit?: boolean;\n}\n\n/**\n * A dynamic tracer that allows the tracer provider to be changed at runtime.\n */\nclass DynamicTracer {\n private tracerProvider: TracerProvider;\n private tracer: Tracer;\n private readonly instrumentingModuleName: string;\n\n constructor(instrumentingModuleName: string) {\n this.instrumentingModuleName = instrumentingModuleName;\n this.tracerProvider = trace.getTracerProvider();\n this.tracer = trace.getTracer(instrumentingModuleName);\n }\n\n /**\n * Set a new tracer provider. This updates the underlying tracer instance.\n * @param provider - The new tracer provider to use\n */\n setProvider(provider: TracerProvider): void {\n this.tracerProvider = provider;\n this.tracer = this.tracerProvider.getTracer(this.instrumentingModuleName);\n }\n\n /**\n * Get the underlying OpenTelemetry tracer.\n * Use this to access the full Tracer API when needed.\n */\n getTracer(): Tracer {\n return this.tracer;\n }\n\n /**\n * Start a span manually (without making it active).\n * You must call span.end() when done.\n *\n * @param options - Span configuration including name\n * @returns The created span\n */\n startSpan(options: StartSpanOptions): Span {\n const ctx = options.context || otelContext.active();\n const span = this.tracer.startSpan(\n options.name,\n {\n attributes: options.attributes,\n },\n ctx,\n );\n\n return span;\n }\n\n /**\n * Start a new span and make it active in the current context.\n * The span will automatically be ended when the provided function completes (unless endOnExit=false).\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n async startActiveSpan<T>(fn: (span: Span) => Promise<T>, options: StartSpanOptions): Promise<T> {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n return new Promise((resolve, reject) => {\n this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {\n try {\n const result = await fn(span);\n resolve(result);\n } catch (error) {\n reject(error);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n });\n }\n\n /**\n * Synchronous version of startActiveSpan for non-async operations.\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n startActiveSpanSync<T>(fn: (span: Span) => T, options: StartSpanOptions): T {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n return this.tracer.startActiveSpan(options.name, opts, ctx, (span) => {\n try {\n return fn(span);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n }\n}\n\n/**\n * The global tracer instance used throughout the agents framework.\n * This tracer can have its provider updated at runtime via setTracerProvider().\n */\nexport const tracer = new DynamicTracer('livekit-agents');\n\nclass MetadataSpanProcessor implements SpanProcessor {\n private metadata: Attributes;\n\n constructor(metadata: Attributes) {\n this.metadata = metadata;\n }\n\n onStart(span: Span, _parentContext: Context): void {\n span.setAttributes(this.metadata);\n }\n\n onEnd(_span: ReadableSpan): void {}\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\n// TODO(brian): PR4 - Add MetadataLogProcessor for structured logging\n\n// TODO(brian): PR4 - Add ExtraDetailsProcessor for structured logging\n\n/**\n * Set the tracer provider for the livekit-agents framework.\n * This should be called before agent session start if using custom tracer providers.\n *\n * @param provider - The tracer provider to use (must be a NodeTracerProvider)\n * @param options - Optional configuration with metadata property to inject into all spans\n *\n * @example\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n * import { setTracerProvider } from '@livekit/agents/telemetry';\n *\n * const provider = new NodeTracerProvider();\n * setTracerProvider(provider, {\n * metadata: { room_id: 'room123', job_id: 'job456' }\n * });\n * ```\n */\nexport function setTracerProvider(\n provider: NodeTracerProvider,\n options?: { metadata?: Attributes },\n): void {\n if (options?.metadata) {\n provider.addSpanProcessor(new MetadataSpanProcessor(options.metadata));\n }\n\n tracer.setProvider(provider);\n}\n\n/**\n * Setup OpenTelemetry tracer for LiveKit Cloud observability.\n * This configures OTLP exporters to send traces to LiveKit Cloud.\n *\n * @param options - Configuration for cloud tracer with roomId, jobId, and cloudHostname properties\n *\n * @internal\n */\nexport async function setupCloudTracer(options: {\n roomId: string;\n jobId: string;\n cloudHostname: string;\n}): Promise<void> {\n const { roomId, jobId, cloudHostname } = options;\n\n const apiKey = process.env.LIVEKIT_API_KEY;\n const apiSecret = process.env.LIVEKIT_API_SECRET;\n\n if (!apiKey || !apiSecret) {\n throw new Error('LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for cloud tracing');\n }\n\n const token = new AccessToken(apiKey, apiSecret, {\n identity: 'livekit-agents-telemetry',\n ttl: '6h',\n });\n token.addObservabilityGrant({ write: true });\n\n try {\n const jwt = await token.toJwt();\n\n const headers = {\n Authorization: `Bearer ${jwt}`,\n };\n\n const metadata: Attributes = {\n room_id: roomId,\n job_id: jobId,\n };\n\n const resource = new Resource({\n [ATTR_SERVICE_NAME]: 'livekit-agents',\n room_id: roomId,\n job_id: jobId,\n });\n\n // Configure OTLP exporter to send traces to LiveKit Cloud\n const spanExporter = new OTLPTraceExporter({\n url: `https://${cloudHostname}/observability/traces/otlp/v0`,\n headers,\n compression: CompressionAlgorithm.GZIP,\n });\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [new MetadataSpanProcessor(metadata), new BatchSpanProcessor(spanExporter)],\n });\n tracerProvider.register();\n\n // Metadata processor is already configured in the constructor above\n setTracerProvider(tracerProvider);\n\n // TODO(brian): PR4 - Add logger provider setup here for structured logging\n // Similar to Python's setup: LoggerProvider, OTLPLogExporter, BatchLogRecordProcessor\n } catch (error) {\n console.error('Failed to setup cloud tracer:', error);\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var utils_exports = {};
30
+ __export(utils_exports, {
31
+ recordException: () => recordException,
32
+ recordRealtimeMetrics: () => recordRealtimeMetrics
33
+ });
34
+ module.exports = __toCommonJS(utils_exports);
35
+ var import_api = require("@opentelemetry/api");
36
+ var traceTypes = __toESM(require("./trace_types.cjs"), 1);
37
+ var import_traces = require("./traces.cjs");
38
+ function recordException(span, error) {
39
+ span.recordException(error);
40
+ span.setStatus({
41
+ code: import_api.SpanStatusCode.ERROR,
42
+ message: error.message
43
+ });
44
+ span.setAttributes({
45
+ [traceTypes.ATTR_EXCEPTION_TYPE]: error.constructor.name,
46
+ [traceTypes.ATTR_EXCEPTION_MESSAGE]: error.message,
47
+ [traceTypes.ATTR_EXCEPTION_TRACE]: error.stack || ""
48
+ });
49
+ }
50
+ function recordRealtimeMetrics(span, metrics) {
51
+ const attrs = {
52
+ [traceTypes.ATTR_GEN_AI_REQUEST_MODEL]: metrics.label || "unknown",
53
+ [traceTypes.ATTR_REALTIME_MODEL_METRICS]: JSON.stringify(metrics),
54
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: metrics.inputTokens,
55
+ [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: metrics.outputTokens,
56
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TEXT_TOKENS]: metrics.inputTokenDetails.textTokens,
57
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_AUDIO_TOKENS]: metrics.inputTokenDetails.audioTokens,
58
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_CACHED_TOKENS]: metrics.inputTokenDetails.cachedTokens,
59
+ [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TEXT_TOKENS]: metrics.outputTokenDetails.textTokens,
60
+ [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_AUDIO_TOKENS]: metrics.outputTokenDetails.audioTokens
61
+ };
62
+ if (metrics.ttftMs !== void 0 && metrics.ttftMs !== -1) {
63
+ const completionStartTime = metrics.timestamp + metrics.ttftMs;
64
+ const completionStartTimeUtc = new Date(completionStartTime).toISOString();
65
+ attrs[traceTypes.ATTR_LANGFUSE_COMPLETION_START_TIME] = completionStartTimeUtc;
66
+ }
67
+ if (span.isRecording()) {
68
+ span.setAttributes(attrs);
69
+ } else {
70
+ const currentContext = import_api.context.active();
71
+ const spanContext = import_api.trace.setSpan(currentContext, span);
72
+ import_traces.tracer.getTracer().startActiveSpan("realtime_metrics", {}, spanContext, (child) => {
73
+ try {
74
+ child.setAttributes(attrs);
75
+ } finally {
76
+ child.end();
77
+ }
78
+ });
79
+ }
80
+ }
81
+ // Annotate the CommonJS export names for ESM import in node:
82
+ 0 && (module.exports = {
83
+ recordException,
84
+ recordRealtimeMetrics
85
+ });
86
+ //# sourceMappingURL=utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { type Span, SpanStatusCode, context as otelContext, trace } from '@opentelemetry/api';\nimport type { RealtimeModelMetrics } from '../metrics/base.js';\nimport * as traceTypes from './trace_types.js';\nimport { tracer } from './traces.js';\n\nexport function recordException(span: Span, error: Error): void {\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n\n // Set exception attributes for better visibility\n // (in case the exception event is not rendered by the backend)\n span.setAttributes({\n [traceTypes.ATTR_EXCEPTION_TYPE]: error.constructor.name,\n [traceTypes.ATTR_EXCEPTION_MESSAGE]: error.message,\n [traceTypes.ATTR_EXCEPTION_TRACE]: error.stack || '',\n });\n}\n\nexport function recordRealtimeMetrics(span: Span, metrics: RealtimeModelMetrics): void {\n const attrs: Record<string, string | number> = {\n [traceTypes.ATTR_GEN_AI_REQUEST_MODEL]: metrics.label || 'unknown',\n [traceTypes.ATTR_REALTIME_MODEL_METRICS]: JSON.stringify(metrics),\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: metrics.inputTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: metrics.outputTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TEXT_TOKENS]: metrics.inputTokenDetails.textTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_AUDIO_TOKENS]: metrics.inputTokenDetails.audioTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_CACHED_TOKENS]: metrics.inputTokenDetails.cachedTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TEXT_TOKENS]: metrics.outputTokenDetails.textTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_AUDIO_TOKENS]: metrics.outputTokenDetails.audioTokens,\n };\n\n // Add LangFuse-specific completion start time if TTFT is available\n if (metrics.ttftMs !== undefined && metrics.ttftMs !== -1) {\n const completionStartTime = metrics.timestamp + metrics.ttftMs;\n // Convert to UTC ISO string for LangFuse compatibility\n const completionStartTimeUtc = new Date(completionStartTime).toISOString();\n attrs[traceTypes.ATTR_LANGFUSE_COMPLETION_START_TIME] = completionStartTimeUtc;\n }\n\n if (span.isRecording()) {\n span.setAttributes(attrs);\n } else {\n const currentContext = otelContext.active();\n const spanContext = trace.setSpan(currentContext, span);\n\n // Create a dedicated child span for orphaned metrics\n tracer.getTracer().startActiveSpan('realtime_metrics', {}, spanContext, (child) => {\n try {\n child.setAttributes(attrs);\n } finally {\n child.end();\n }\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,iBAAyE;AAEzE,iBAA4B;AAC5B,oBAAuB;AAEhB,SAAS,gBAAgB,MAAY,OAAoB;AAC9D,OAAK,gBAAgB,KAAK;AAC1B,OAAK,UAAU;AAAA,IACb,MAAM,0BAAe;AAAA,IACrB,SAAS,MAAM;AAAA,EACjB,CAAC;AAID,OAAK,cAAc;AAAA,IACjB,CAAC,WAAW,mBAAmB,GAAG,MAAM,YAAY;AAAA,IACpD,CAAC,WAAW,sBAAsB,GAAG,MAAM;AAAA,IAC3C,CAAC,WAAW,oBAAoB,GAAG,MAAM,SAAS;AAAA,EACpD,CAAC;AACH;AAEO,SAAS,sBAAsB,MAAY,SAAqC;AACrF,QAAM,QAAyC;AAAA,IAC7C,CAAC,WAAW,yBAAyB,GAAG,QAAQ,SAAS;AAAA,IACzD,CAAC,WAAW,2BAA2B,GAAG,KAAK,UAAU,OAAO;AAAA,IAChE,CAAC,WAAW,8BAA8B,GAAG,QAAQ;AAAA,IACrD,CAAC,WAAW,+BAA+B,GAAG,QAAQ;AAAA,IACtD,CAAC,WAAW,mCAAmC,GAAG,QAAQ,kBAAkB;AAAA,IAC5E,CAAC,WAAW,oCAAoC,GAAG,QAAQ,kBAAkB;AAAA,IAC7E,CAAC,WAAW,qCAAqC,GAAG,QAAQ,kBAAkB;AAAA,IAC9E,CAAC,WAAW,oCAAoC,GAAG,QAAQ,mBAAmB;AAAA,IAC9E,CAAC,WAAW,qCAAqC,GAAG,QAAQ,mBAAmB;AAAA,EACjF;AAGA,MAAI,QAAQ,WAAW,UAAa,QAAQ,WAAW,IAAI;AACzD,UAAM,sBAAsB,QAAQ,YAAY,QAAQ;AAExD,UAAM,yBAAyB,IAAI,KAAK,mBAAmB,EAAE,YAAY;AACzE,UAAM,WAAW,mCAAmC,IAAI;AAAA,EAC1D;AAEA,MAAI,KAAK,YAAY,GAAG;AACtB,SAAK,cAAc,KAAK;AAAA,EAC1B,OAAO;AACL,UAAM,iBAAiB,WAAAA,QAAY,OAAO;AAC1C,UAAM,cAAc,iBAAM,QAAQ,gBAAgB,IAAI;AAGtD,yBAAO,UAAU,EAAE,gBAAgB,oBAAoB,CAAC,GAAG,aAAa,CAAC,UAAU;AACjF,UAAI;AACF,cAAM,cAAc,KAAK;AAAA,MAC3B,UAAE;AACA,cAAM,IAAI;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":["otelContext"]}
@@ -0,0 +1,5 @@
1
+ import { type Span } from '@opentelemetry/api';
2
+ import type { RealtimeModelMetrics } from '../metrics/base.js';
3
+ export declare function recordException(span: Span, error: Error): void;
4
+ export declare function recordRealtimeMetrics(span: Span, metrics: RealtimeModelMetrics): void;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { type Span } from '@opentelemetry/api';
2
+ import type { RealtimeModelMetrics } from '../metrics/base.js';
3
+ export declare function recordException(span: Span, error: Error): void;
4
+ export declare function recordRealtimeMetrics(span: Span, metrics: RealtimeModelMetrics): void;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/telemetry/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,IAAI,EAAiD,MAAM,oBAAoB,CAAC;AAC9F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAI/D,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAc9D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAoCrF"}
@@ -0,0 +1,51 @@
1
+ import { SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
2
+ import * as traceTypes from "./trace_types.js";
3
+ import { tracer } from "./traces.js";
4
+ function recordException(span, error) {
5
+ span.recordException(error);
6
+ span.setStatus({
7
+ code: SpanStatusCode.ERROR,
8
+ message: error.message
9
+ });
10
+ span.setAttributes({
11
+ [traceTypes.ATTR_EXCEPTION_TYPE]: error.constructor.name,
12
+ [traceTypes.ATTR_EXCEPTION_MESSAGE]: error.message,
13
+ [traceTypes.ATTR_EXCEPTION_TRACE]: error.stack || ""
14
+ });
15
+ }
16
+ function recordRealtimeMetrics(span, metrics) {
17
+ const attrs = {
18
+ [traceTypes.ATTR_GEN_AI_REQUEST_MODEL]: metrics.label || "unknown",
19
+ [traceTypes.ATTR_REALTIME_MODEL_METRICS]: JSON.stringify(metrics),
20
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: metrics.inputTokens,
21
+ [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: metrics.outputTokens,
22
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TEXT_TOKENS]: metrics.inputTokenDetails.textTokens,
23
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_AUDIO_TOKENS]: metrics.inputTokenDetails.audioTokens,
24
+ [traceTypes.ATTR_GEN_AI_USAGE_INPUT_CACHED_TOKENS]: metrics.inputTokenDetails.cachedTokens,
25
+ [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TEXT_TOKENS]: metrics.outputTokenDetails.textTokens,
26
+ [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_AUDIO_TOKENS]: metrics.outputTokenDetails.audioTokens
27
+ };
28
+ if (metrics.ttftMs !== void 0 && metrics.ttftMs !== -1) {
29
+ const completionStartTime = metrics.timestamp + metrics.ttftMs;
30
+ const completionStartTimeUtc = new Date(completionStartTime).toISOString();
31
+ attrs[traceTypes.ATTR_LANGFUSE_COMPLETION_START_TIME] = completionStartTimeUtc;
32
+ }
33
+ if (span.isRecording()) {
34
+ span.setAttributes(attrs);
35
+ } else {
36
+ const currentContext = otelContext.active();
37
+ const spanContext = trace.setSpan(currentContext, span);
38
+ tracer.getTracer().startActiveSpan("realtime_metrics", {}, spanContext, (child) => {
39
+ try {
40
+ child.setAttributes(attrs);
41
+ } finally {
42
+ child.end();
43
+ }
44
+ });
45
+ }
46
+ }
47
+ export {
48
+ recordException,
49
+ recordRealtimeMetrics
50
+ };
51
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { type Span, SpanStatusCode, context as otelContext, trace } from '@opentelemetry/api';\nimport type { RealtimeModelMetrics } from '../metrics/base.js';\nimport * as traceTypes from './trace_types.js';\nimport { tracer } from './traces.js';\n\nexport function recordException(span: Span, error: Error): void {\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n\n // Set exception attributes for better visibility\n // (in case the exception event is not rendered by the backend)\n span.setAttributes({\n [traceTypes.ATTR_EXCEPTION_TYPE]: error.constructor.name,\n [traceTypes.ATTR_EXCEPTION_MESSAGE]: error.message,\n [traceTypes.ATTR_EXCEPTION_TRACE]: error.stack || '',\n });\n}\n\nexport function recordRealtimeMetrics(span: Span, metrics: RealtimeModelMetrics): void {\n const attrs: Record<string, string | number> = {\n [traceTypes.ATTR_GEN_AI_REQUEST_MODEL]: metrics.label || 'unknown',\n [traceTypes.ATTR_REALTIME_MODEL_METRICS]: JSON.stringify(metrics),\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: metrics.inputTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: metrics.outputTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_TEXT_TOKENS]: metrics.inputTokenDetails.textTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_AUDIO_TOKENS]: metrics.inputTokenDetails.audioTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_INPUT_CACHED_TOKENS]: metrics.inputTokenDetails.cachedTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TEXT_TOKENS]: metrics.outputTokenDetails.textTokens,\n [traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_AUDIO_TOKENS]: metrics.outputTokenDetails.audioTokens,\n };\n\n // Add LangFuse-specific completion start time if TTFT is available\n if (metrics.ttftMs !== undefined && metrics.ttftMs !== -1) {\n const completionStartTime = metrics.timestamp + metrics.ttftMs;\n // Convert to UTC ISO string for LangFuse compatibility\n const completionStartTimeUtc = new Date(completionStartTime).toISOString();\n attrs[traceTypes.ATTR_LANGFUSE_COMPLETION_START_TIME] = completionStartTimeUtc;\n }\n\n if (span.isRecording()) {\n span.setAttributes(attrs);\n } else {\n const currentContext = otelContext.active();\n const spanContext = trace.setSpan(currentContext, span);\n\n // Create a dedicated child span for orphaned metrics\n tracer.getTracer().startActiveSpan('realtime_metrics', {}, spanContext, (child) => {\n try {\n child.setAttributes(attrs);\n } finally {\n child.end();\n }\n });\n }\n}\n"],"mappings":"AAGA,SAAoB,gBAAgB,WAAW,aAAa,aAAa;AAEzE,YAAY,gBAAgB;AAC5B,SAAS,cAAc;AAEhB,SAAS,gBAAgB,MAAY,OAAoB;AAC9D,OAAK,gBAAgB,KAAK;AAC1B,OAAK,UAAU;AAAA,IACb,MAAM,eAAe;AAAA,IACrB,SAAS,MAAM;AAAA,EACjB,CAAC;AAID,OAAK,cAAc;AAAA,IACjB,CAAC,WAAW,mBAAmB,GAAG,MAAM,YAAY;AAAA,IACpD,CAAC,WAAW,sBAAsB,GAAG,MAAM;AAAA,IAC3C,CAAC,WAAW,oBAAoB,GAAG,MAAM,SAAS;AAAA,EACpD,CAAC;AACH;AAEO,SAAS,sBAAsB,MAAY,SAAqC;AACrF,QAAM,QAAyC;AAAA,IAC7C,CAAC,WAAW,yBAAyB,GAAG,QAAQ,SAAS;AAAA,IACzD,CAAC,WAAW,2BAA2B,GAAG,KAAK,UAAU,OAAO;AAAA,IAChE,CAAC,WAAW,8BAA8B,GAAG,QAAQ;AAAA,IACrD,CAAC,WAAW,+BAA+B,GAAG,QAAQ;AAAA,IACtD,CAAC,WAAW,mCAAmC,GAAG,QAAQ,kBAAkB;AAAA,IAC5E,CAAC,WAAW,oCAAoC,GAAG,QAAQ,kBAAkB;AAAA,IAC7E,CAAC,WAAW,qCAAqC,GAAG,QAAQ,kBAAkB;AAAA,IAC9E,CAAC,WAAW,oCAAoC,GAAG,QAAQ,mBAAmB;AAAA,IAC9E,CAAC,WAAW,qCAAqC,GAAG,QAAQ,mBAAmB;AAAA,EACjF;AAGA,MAAI,QAAQ,WAAW,UAAa,QAAQ,WAAW,IAAI;AACzD,UAAM,sBAAsB,QAAQ,YAAY,QAAQ;AAExD,UAAM,yBAAyB,IAAI,KAAK,mBAAmB,EAAE,YAAY;AACzE,UAAM,WAAW,mCAAmC,IAAI;AAAA,EAC1D;AAEA,MAAI,KAAK,YAAY,GAAG;AACtB,SAAK,cAAc,KAAK;AAAA,EAC1B,OAAO;AACL,UAAM,iBAAiB,YAAY,OAAO;AAC1C,UAAM,cAAc,MAAM,QAAQ,gBAAgB,IAAI;AAGtD,WAAO,UAAU,EAAE,gBAAgB,oBAAoB,CAAC,GAAG,aAAa,CAAC,UAAU;AACjF,UAAI;AACF,cAAM,cAAc,KAAK;AAAA,MAC3B,UAAE;AACA,cAAM,IAAI;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}