@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.
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/inference/api_protos.d.cts +12 -12
- package/dist/inference/api_protos.d.ts +12 -12
- package/dist/inference/tts.cjs +1 -1
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.js +1 -1
- package/dist/inference/tts.js.map +1 -1
- package/dist/ipc/job_proc_lazy_main.cjs +6 -2
- package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
- package/dist/ipc/job_proc_lazy_main.js +6 -2
- package/dist/ipc/job_proc_lazy_main.js.map +1 -1
- package/dist/job.cjs +31 -0
- package/dist/job.cjs.map +1 -1
- package/dist/job.d.cts +6 -0
- package/dist/job.d.ts +6 -0
- package/dist/job.d.ts.map +1 -1
- package/dist/job.js +31 -0
- package/dist/job.js.map +1 -1
- package/dist/llm/chat_context.cjs +33 -0
- package/dist/llm/chat_context.cjs.map +1 -1
- package/dist/llm/chat_context.d.cts +22 -2
- package/dist/llm/chat_context.d.ts +22 -2
- package/dist/llm/chat_context.d.ts.map +1 -1
- package/dist/llm/chat_context.js +32 -0
- package/dist/llm/chat_context.js.map +1 -1
- package/dist/llm/index.cjs +2 -0
- package/dist/llm/index.cjs.map +1 -1
- package/dist/llm/index.d.cts +1 -1
- package/dist/llm/index.d.ts +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +2 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/llm.cjs.map +1 -1
- package/dist/llm/llm.d.ts.map +1 -1
- package/dist/llm/llm.js.map +1 -1
- package/dist/llm/provider_format/google.test.cjs +48 -0
- package/dist/llm/provider_format/google.test.cjs.map +1 -1
- package/dist/llm/provider_format/google.test.js +54 -1
- package/dist/llm/provider_format/google.test.js.map +1 -1
- package/dist/llm/provider_format/index.d.cts +1 -1
- package/dist/llm/provider_format/index.d.ts +1 -1
- package/dist/llm/provider_format/openai.cjs +1 -2
- package/dist/llm/provider_format/openai.cjs.map +1 -1
- package/dist/llm/provider_format/openai.js +1 -2
- package/dist/llm/provider_format/openai.js.map +1 -1
- package/dist/llm/provider_format/openai.test.cjs +32 -0
- package/dist/llm/provider_format/openai.test.cjs.map +1 -1
- package/dist/llm/provider_format/openai.test.js +38 -1
- package/dist/llm/provider_format/openai.test.js.map +1 -1
- package/dist/log.cjs.map +1 -1
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js.map +1 -1
- package/dist/telemetry/index.cjs +51 -0
- package/dist/telemetry/index.cjs.map +1 -0
- package/dist/telemetry/index.d.cts +4 -0
- package/dist/telemetry/index.d.ts +4 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +12 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/trace_types.cjs +191 -0
- package/dist/telemetry/trace_types.cjs.map +1 -0
- package/dist/telemetry/trace_types.d.cts +56 -0
- package/dist/telemetry/trace_types.d.ts +56 -0
- package/dist/telemetry/trace_types.d.ts.map +1 -0
- package/dist/telemetry/trace_types.js +113 -0
- package/dist/telemetry/trace_types.js.map +1 -0
- package/dist/telemetry/traces.cjs +196 -0
- package/dist/telemetry/traces.cjs.map +1 -0
- package/dist/telemetry/traces.d.cts +97 -0
- package/dist/telemetry/traces.d.ts +97 -0
- package/dist/telemetry/traces.d.ts.map +1 -0
- package/dist/telemetry/traces.js +173 -0
- package/dist/telemetry/traces.js.map +1 -0
- package/dist/telemetry/utils.cjs +86 -0
- package/dist/telemetry/utils.cjs.map +1 -0
- package/dist/telemetry/utils.d.cts +5 -0
- package/dist/telemetry/utils.d.ts +5 -0
- package/dist/telemetry/utils.d.ts.map +1 -0
- package/dist/telemetry/utils.js +51 -0
- package/dist/telemetry/utils.js.map +1 -0
- package/dist/tts/tts.cjs.map +1 -1
- package/dist/tts/tts.d.ts.map +1 -1
- package/dist/tts/tts.js.map +1 -1
- package/dist/voice/agent.cjs +15 -0
- package/dist/voice/agent.cjs.map +1 -1
- package/dist/voice/agent.d.cts +4 -1
- package/dist/voice/agent.d.ts +4 -1
- package/dist/voice/agent.d.ts.map +1 -1
- package/dist/voice/agent.js +15 -0
- package/dist/voice/agent.js.map +1 -1
- package/dist/voice/agent_activity.cjs +2 -0
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +2 -0
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_session.cjs +29 -1
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +6 -2
- package/dist/voice/agent_session.d.ts +6 -2
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +30 -2
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/generation.cjs.map +1 -1
- package/dist/voice/generation.d.ts.map +1 -1
- package/dist/voice/generation.js.map +1 -1
- package/dist/voice/index.cjs +2 -0
- package/dist/voice/index.cjs.map +1 -1
- package/dist/voice/index.d.cts +1 -0
- package/dist/voice/index.d.ts +1 -0
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +1 -0
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/report.cjs +69 -0
- package/dist/voice/report.cjs.map +1 -0
- package/dist/voice/report.d.cts +26 -0
- package/dist/voice/report.d.ts +26 -0
- package/dist/voice/report.d.ts.map +1 -0
- package/dist/voice/report.js +44 -0
- package/dist/voice/report.js.map +1 -0
- package/package.json +10 -3
- package/src/index.ts +2 -1
- package/src/inference/tts.ts +1 -1
- package/src/ipc/job_proc_lazy_main.ts +10 -2
- package/src/job.ts +48 -0
- package/src/llm/chat_context.ts +53 -1
- package/src/llm/index.ts +1 -0
- package/src/llm/llm.ts +2 -0
- package/src/llm/provider_format/google.test.ts +72 -1
- package/src/llm/provider_format/openai.test.ts +55 -1
- package/src/llm/provider_format/openai.ts +3 -2
- package/src/log.ts +1 -0
- package/src/telemetry/index.ts +10 -0
- package/src/telemetry/trace_types.ts +88 -0
- package/src/telemetry/traces.ts +266 -0
- package/src/telemetry/utils.ts +61 -0
- package/src/tts/tts.ts +4 -0
- package/src/voice/agent.ts +22 -0
- package/src/voice/agent_activity.ts +6 -0
- package/src/voice/agent_session.ts +44 -1
- package/src/voice/audio_recognition.ts +2 -0
- package/src/voice/generation.ts +3 -0
- package/src/voice/index.ts +1 -0
- package/src/voice/report.ts +77 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
// LiveKit custom attributes
|
|
6
|
+
export const ATTR_SPEECH_ID = 'lk.speech_id';
|
|
7
|
+
export const ATTR_AGENT_LABEL = 'lk.agent_label';
|
|
8
|
+
export const ATTR_START_TIME = 'lk.start_time';
|
|
9
|
+
export const ATTR_END_TIME = 'lk.end_time';
|
|
10
|
+
export const ATTR_RETRY_COUNT = 'lk.retry_count';
|
|
11
|
+
|
|
12
|
+
export const ATTR_PARTICIPANT_ID = 'lk.participant_id';
|
|
13
|
+
export const ATTR_PARTICIPANT_IDENTITY = 'lk.participant_identity';
|
|
14
|
+
export const ATTR_PARTICIPANT_KIND = 'lk.participant_kind';
|
|
15
|
+
|
|
16
|
+
// session start
|
|
17
|
+
export const ATTR_JOB_ID = 'lk.job_id';
|
|
18
|
+
export const ATTR_AGENT_NAME = 'lk.agent_name';
|
|
19
|
+
export const ATTR_ROOM_NAME = 'lk.room_name';
|
|
20
|
+
export const ATTR_SESSION_OPTIONS = 'lk.session_options';
|
|
21
|
+
|
|
22
|
+
// assistant turn
|
|
23
|
+
export const ATTR_USER_INPUT = 'lk.user_input';
|
|
24
|
+
export const ATTR_INSTRUCTIONS = 'lk.instructions';
|
|
25
|
+
export const ATTR_SPEECH_INTERRUPTED = 'lk.interrupted';
|
|
26
|
+
|
|
27
|
+
// llm node
|
|
28
|
+
export const ATTR_CHAT_CTX = 'lk.chat_ctx';
|
|
29
|
+
export const ATTR_FUNCTION_TOOLS = 'lk.function_tools';
|
|
30
|
+
export const ATTR_RESPONSE_TEXT = 'lk.response.text';
|
|
31
|
+
export const ATTR_RESPONSE_FUNCTION_CALLS = 'lk.response.function_calls';
|
|
32
|
+
|
|
33
|
+
// function tool
|
|
34
|
+
export const ATTR_FUNCTION_TOOL_NAME = 'lk.function_tool.name';
|
|
35
|
+
export const ATTR_FUNCTION_TOOL_ARGS = 'lk.function_tool.arguments';
|
|
36
|
+
export const ATTR_FUNCTION_TOOL_IS_ERROR = 'lk.function_tool.is_error';
|
|
37
|
+
export const ATTR_FUNCTION_TOOL_OUTPUT = 'lk.function_tool.output';
|
|
38
|
+
|
|
39
|
+
// tts node
|
|
40
|
+
export const ATTR_TTS_INPUT_TEXT = 'lk.input_text';
|
|
41
|
+
export const ATTR_TTS_STREAMING = 'lk.tts.streaming';
|
|
42
|
+
export const ATTR_TTS_LABEL = 'lk.tts.label';
|
|
43
|
+
|
|
44
|
+
// eou detection
|
|
45
|
+
export const ATTR_EOU_PROBABILITY = 'lk.eou.probability';
|
|
46
|
+
export const ATTR_EOU_UNLIKELY_THRESHOLD = 'lk.eou.unlikely_threshold';
|
|
47
|
+
export const ATTR_EOU_DELAY = 'lk.eou.endpointing_delay';
|
|
48
|
+
export const ATTR_EOU_LANGUAGE = 'lk.eou.language';
|
|
49
|
+
export const ATTR_USER_TRANSCRIPT = 'lk.user_transcript';
|
|
50
|
+
export const ATTR_TRANSCRIPT_CONFIDENCE = 'lk.transcript_confidence';
|
|
51
|
+
export const ATTR_TRANSCRIPTION_DELAY = 'lk.transcription_delay';
|
|
52
|
+
export const ATTR_END_OF_TURN_DELAY = 'lk.end_of_turn_delay';
|
|
53
|
+
|
|
54
|
+
// metrics
|
|
55
|
+
export const ATTR_LLM_METRICS = 'lk.llm_metrics';
|
|
56
|
+
export const ATTR_TTS_METRICS = 'lk.tts_metrics';
|
|
57
|
+
export const ATTR_REALTIME_MODEL_METRICS = 'lk.realtime_model_metrics';
|
|
58
|
+
|
|
59
|
+
// OpenTelemetry GenAI attributes
|
|
60
|
+
// OpenTelemetry specification: https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/
|
|
61
|
+
export const ATTR_GEN_AI_OPERATION_NAME = 'gen_ai.operation.name';
|
|
62
|
+
export const ATTR_GEN_AI_REQUEST_MODEL = 'gen_ai.request.model';
|
|
63
|
+
export const ATTR_GEN_AI_USAGE_INPUT_TOKENS = 'gen_ai.usage.input_tokens';
|
|
64
|
+
export const ATTR_GEN_AI_USAGE_OUTPUT_TOKENS = 'gen_ai.usage.output_tokens';
|
|
65
|
+
|
|
66
|
+
// Unofficial OpenTelemetry GenAI attributes, recognized by LangFuse
|
|
67
|
+
// https://langfuse.com/integrations/native/opentelemetry#usage
|
|
68
|
+
// but not yet in the official OpenTelemetry specification.
|
|
69
|
+
export const ATTR_GEN_AI_USAGE_INPUT_TEXT_TOKENS = 'gen_ai.usage.input_text_tokens';
|
|
70
|
+
export const ATTR_GEN_AI_USAGE_INPUT_AUDIO_TOKENS = 'gen_ai.usage.input_audio_tokens';
|
|
71
|
+
export const ATTR_GEN_AI_USAGE_INPUT_CACHED_TOKENS = 'gen_ai.usage.input_cached_tokens';
|
|
72
|
+
export const ATTR_GEN_AI_USAGE_OUTPUT_TEXT_TOKENS = 'gen_ai.usage.output_text_tokens';
|
|
73
|
+
export const ATTR_GEN_AI_USAGE_OUTPUT_AUDIO_TOKENS = 'gen_ai.usage.output_audio_tokens';
|
|
74
|
+
|
|
75
|
+
// OpenTelemetry GenAI event names (for structured logging)
|
|
76
|
+
export const EVENT_GEN_AI_SYSTEM_MESSAGE = 'gen_ai.system.message';
|
|
77
|
+
export const EVENT_GEN_AI_USER_MESSAGE = 'gen_ai.user.message';
|
|
78
|
+
export const EVENT_GEN_AI_ASSISTANT_MESSAGE = 'gen_ai.assistant.message';
|
|
79
|
+
export const EVENT_GEN_AI_TOOL_MESSAGE = 'gen_ai.tool.message';
|
|
80
|
+
export const EVENT_GEN_AI_CHOICE = 'gen_ai.choice';
|
|
81
|
+
|
|
82
|
+
// Exception attributes
|
|
83
|
+
export const ATTR_EXCEPTION_TRACE = 'exception.stacktrace';
|
|
84
|
+
export const ATTR_EXCEPTION_TYPE = 'exception.type';
|
|
85
|
+
export const ATTR_EXCEPTION_MESSAGE = 'exception.message';
|
|
86
|
+
|
|
87
|
+
// Platform-specific attributes
|
|
88
|
+
export const ATTR_LANGFUSE_COMPLETION_START_TIME = 'langfuse.observation.completion_start_time';
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import {
|
|
5
|
+
type Attributes,
|
|
6
|
+
type Context,
|
|
7
|
+
type Span,
|
|
8
|
+
type SpanOptions,
|
|
9
|
+
type Tracer,
|
|
10
|
+
type TracerProvider,
|
|
11
|
+
context as otelContext,
|
|
12
|
+
trace,
|
|
13
|
+
} from '@opentelemetry/api';
|
|
14
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
15
|
+
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';
|
|
16
|
+
import { Resource } from '@opentelemetry/resources';
|
|
17
|
+
import type { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
18
|
+
import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
19
|
+
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
20
|
+
import { AccessToken } from 'livekit-server-sdk';
|
|
21
|
+
|
|
22
|
+
export interface StartSpanOptions {
|
|
23
|
+
/** Name of the span */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Optional parent context to use for this span */
|
|
26
|
+
context?: Context;
|
|
27
|
+
/** Attributes to set on the span when it starts */
|
|
28
|
+
attributes?: Attributes;
|
|
29
|
+
/** Whether to end the span when the function exits (default: true) */
|
|
30
|
+
endOnExit?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A dynamic tracer that allows the tracer provider to be changed at runtime.
|
|
35
|
+
*/
|
|
36
|
+
class DynamicTracer {
|
|
37
|
+
private tracerProvider: TracerProvider;
|
|
38
|
+
private tracer: Tracer;
|
|
39
|
+
private readonly instrumentingModuleName: string;
|
|
40
|
+
|
|
41
|
+
constructor(instrumentingModuleName: string) {
|
|
42
|
+
this.instrumentingModuleName = instrumentingModuleName;
|
|
43
|
+
this.tracerProvider = trace.getTracerProvider();
|
|
44
|
+
this.tracer = trace.getTracer(instrumentingModuleName);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Set a new tracer provider. This updates the underlying tracer instance.
|
|
49
|
+
* @param provider - The new tracer provider to use
|
|
50
|
+
*/
|
|
51
|
+
setProvider(provider: TracerProvider): void {
|
|
52
|
+
this.tracerProvider = provider;
|
|
53
|
+
this.tracer = this.tracerProvider.getTracer(this.instrumentingModuleName);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get the underlying OpenTelemetry tracer.
|
|
58
|
+
* Use this to access the full Tracer API when needed.
|
|
59
|
+
*/
|
|
60
|
+
getTracer(): Tracer {
|
|
61
|
+
return this.tracer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Start a span manually (without making it active).
|
|
66
|
+
* You must call span.end() when done.
|
|
67
|
+
*
|
|
68
|
+
* @param options - Span configuration including name
|
|
69
|
+
* @returns The created span
|
|
70
|
+
*/
|
|
71
|
+
startSpan(options: StartSpanOptions): Span {
|
|
72
|
+
const ctx = options.context || otelContext.active();
|
|
73
|
+
const span = this.tracer.startSpan(
|
|
74
|
+
options.name,
|
|
75
|
+
{
|
|
76
|
+
attributes: options.attributes,
|
|
77
|
+
},
|
|
78
|
+
ctx,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return span;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Start a new span and make it active in the current context.
|
|
86
|
+
* The span will automatically be ended when the provided function completes (unless endOnExit=false).
|
|
87
|
+
*
|
|
88
|
+
* @param fn - The function to execute within the span context
|
|
89
|
+
* @param options - Span configuration including name
|
|
90
|
+
* @returns The result of the provided function
|
|
91
|
+
*/
|
|
92
|
+
async startActiveSpan<T>(fn: (span: Span) => Promise<T>, options: StartSpanOptions): Promise<T> {
|
|
93
|
+
const ctx = options.context || otelContext.active();
|
|
94
|
+
const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true
|
|
95
|
+
const opts: SpanOptions = { attributes: options.attributes };
|
|
96
|
+
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {
|
|
99
|
+
try {
|
|
100
|
+
const result = await fn(span);
|
|
101
|
+
resolve(result);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
reject(error);
|
|
104
|
+
} finally {
|
|
105
|
+
if (endOnExit) {
|
|
106
|
+
span.end();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Synchronous version of startActiveSpan for non-async operations.
|
|
115
|
+
*
|
|
116
|
+
* @param fn - The function to execute within the span context
|
|
117
|
+
* @param options - Span configuration including name
|
|
118
|
+
* @returns The result of the provided function
|
|
119
|
+
*/
|
|
120
|
+
startActiveSpanSync<T>(fn: (span: Span) => T, options: StartSpanOptions): T {
|
|
121
|
+
const ctx = options.context || otelContext.active();
|
|
122
|
+
const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true
|
|
123
|
+
const opts: SpanOptions = { attributes: options.attributes };
|
|
124
|
+
|
|
125
|
+
return this.tracer.startActiveSpan(options.name, opts, ctx, (span) => {
|
|
126
|
+
try {
|
|
127
|
+
return fn(span);
|
|
128
|
+
} finally {
|
|
129
|
+
if (endOnExit) {
|
|
130
|
+
span.end();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The global tracer instance used throughout the agents framework.
|
|
139
|
+
* This tracer can have its provider updated at runtime via setTracerProvider().
|
|
140
|
+
*/
|
|
141
|
+
export const tracer = new DynamicTracer('livekit-agents');
|
|
142
|
+
|
|
143
|
+
class MetadataSpanProcessor implements SpanProcessor {
|
|
144
|
+
private metadata: Attributes;
|
|
145
|
+
|
|
146
|
+
constructor(metadata: Attributes) {
|
|
147
|
+
this.metadata = metadata;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
onStart(span: Span, _parentContext: Context): void {
|
|
151
|
+
span.setAttributes(this.metadata);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
onEnd(_span: ReadableSpan): void {}
|
|
155
|
+
|
|
156
|
+
shutdown(): Promise<void> {
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
forceFlush(): Promise<void> {
|
|
161
|
+
return Promise.resolve();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// TODO(brian): PR4 - Add MetadataLogProcessor for structured logging
|
|
166
|
+
|
|
167
|
+
// TODO(brian): PR4 - Add ExtraDetailsProcessor for structured logging
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Set the tracer provider for the livekit-agents framework.
|
|
171
|
+
* This should be called before agent session start if using custom tracer providers.
|
|
172
|
+
*
|
|
173
|
+
* @param provider - The tracer provider to use (must be a NodeTracerProvider)
|
|
174
|
+
* @param options - Optional configuration with metadata property to inject into all spans
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
179
|
+
* import { setTracerProvider } from '@livekit/agents/telemetry';
|
|
180
|
+
*
|
|
181
|
+
* const provider = new NodeTracerProvider();
|
|
182
|
+
* setTracerProvider(provider, {
|
|
183
|
+
* metadata: { room_id: 'room123', job_id: 'job456' }
|
|
184
|
+
* });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export function setTracerProvider(
|
|
188
|
+
provider: NodeTracerProvider,
|
|
189
|
+
options?: { metadata?: Attributes },
|
|
190
|
+
): void {
|
|
191
|
+
if (options?.metadata) {
|
|
192
|
+
provider.addSpanProcessor(new MetadataSpanProcessor(options.metadata));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
tracer.setProvider(provider);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Setup OpenTelemetry tracer for LiveKit Cloud observability.
|
|
200
|
+
* This configures OTLP exporters to send traces to LiveKit Cloud.
|
|
201
|
+
*
|
|
202
|
+
* @param options - Configuration for cloud tracer with roomId, jobId, and cloudHostname properties
|
|
203
|
+
*
|
|
204
|
+
* @internal
|
|
205
|
+
*/
|
|
206
|
+
export async function setupCloudTracer(options: {
|
|
207
|
+
roomId: string;
|
|
208
|
+
jobId: string;
|
|
209
|
+
cloudHostname: string;
|
|
210
|
+
}): Promise<void> {
|
|
211
|
+
const { roomId, jobId, cloudHostname } = options;
|
|
212
|
+
|
|
213
|
+
const apiKey = process.env.LIVEKIT_API_KEY;
|
|
214
|
+
const apiSecret = process.env.LIVEKIT_API_SECRET;
|
|
215
|
+
|
|
216
|
+
if (!apiKey || !apiSecret) {
|
|
217
|
+
throw new Error('LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for cloud tracing');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const token = new AccessToken(apiKey, apiSecret, {
|
|
221
|
+
identity: 'livekit-agents-telemetry',
|
|
222
|
+
ttl: '6h',
|
|
223
|
+
});
|
|
224
|
+
token.addObservabilityGrant({ write: true });
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const jwt = await token.toJwt();
|
|
228
|
+
|
|
229
|
+
const headers = {
|
|
230
|
+
Authorization: `Bearer ${jwt}`,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const metadata: Attributes = {
|
|
234
|
+
room_id: roomId,
|
|
235
|
+
job_id: jobId,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const resource = new Resource({
|
|
239
|
+
[ATTR_SERVICE_NAME]: 'livekit-agents',
|
|
240
|
+
room_id: roomId,
|
|
241
|
+
job_id: jobId,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Configure OTLP exporter to send traces to LiveKit Cloud
|
|
245
|
+
const spanExporter = new OTLPTraceExporter({
|
|
246
|
+
url: `https://${cloudHostname}/observability/traces/otlp/v0`,
|
|
247
|
+
headers,
|
|
248
|
+
compression: CompressionAlgorithm.GZIP,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const tracerProvider = new NodeTracerProvider({
|
|
252
|
+
resource,
|
|
253
|
+
spanProcessors: [new MetadataSpanProcessor(metadata), new BatchSpanProcessor(spanExporter)],
|
|
254
|
+
});
|
|
255
|
+
tracerProvider.register();
|
|
256
|
+
|
|
257
|
+
// Metadata processor is already configured in the constructor above
|
|
258
|
+
setTracerProvider(tracerProvider);
|
|
259
|
+
|
|
260
|
+
// TODO(brian): PR4 - Add logger provider setup here for structured logging
|
|
261
|
+
// Similar to Python's setup: LoggerProvider, OTLPLogExporter, BatchLogRecordProcessor
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error('Failed to setup cloud tracer:', error);
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { type Span, SpanStatusCode, context as otelContext, trace } from '@opentelemetry/api';
|
|
5
|
+
import type { RealtimeModelMetrics } from '../metrics/base.js';
|
|
6
|
+
import * as traceTypes from './trace_types.js';
|
|
7
|
+
import { tracer } from './traces.js';
|
|
8
|
+
|
|
9
|
+
export function recordException(span: Span, error: Error): void {
|
|
10
|
+
span.recordException(error);
|
|
11
|
+
span.setStatus({
|
|
12
|
+
code: SpanStatusCode.ERROR,
|
|
13
|
+
message: error.message,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Set exception attributes for better visibility
|
|
17
|
+
// (in case the exception event is not rendered by the backend)
|
|
18
|
+
span.setAttributes({
|
|
19
|
+
[traceTypes.ATTR_EXCEPTION_TYPE]: error.constructor.name,
|
|
20
|
+
[traceTypes.ATTR_EXCEPTION_MESSAGE]: error.message,
|
|
21
|
+
[traceTypes.ATTR_EXCEPTION_TRACE]: error.stack || '',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function recordRealtimeMetrics(span: Span, metrics: RealtimeModelMetrics): void {
|
|
26
|
+
const attrs: Record<string, string | number> = {
|
|
27
|
+
[traceTypes.ATTR_GEN_AI_REQUEST_MODEL]: metrics.label || 'unknown',
|
|
28
|
+
[traceTypes.ATTR_REALTIME_MODEL_METRICS]: JSON.stringify(metrics),
|
|
29
|
+
[traceTypes.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: metrics.inputTokens,
|
|
30
|
+
[traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: metrics.outputTokens,
|
|
31
|
+
[traceTypes.ATTR_GEN_AI_USAGE_INPUT_TEXT_TOKENS]: metrics.inputTokenDetails.textTokens,
|
|
32
|
+
[traceTypes.ATTR_GEN_AI_USAGE_INPUT_AUDIO_TOKENS]: metrics.inputTokenDetails.audioTokens,
|
|
33
|
+
[traceTypes.ATTR_GEN_AI_USAGE_INPUT_CACHED_TOKENS]: metrics.inputTokenDetails.cachedTokens,
|
|
34
|
+
[traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_TEXT_TOKENS]: metrics.outputTokenDetails.textTokens,
|
|
35
|
+
[traceTypes.ATTR_GEN_AI_USAGE_OUTPUT_AUDIO_TOKENS]: metrics.outputTokenDetails.audioTokens,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Add LangFuse-specific completion start time if TTFT is available
|
|
39
|
+
if (metrics.ttftMs !== undefined && metrics.ttftMs !== -1) {
|
|
40
|
+
const completionStartTime = metrics.timestamp + metrics.ttftMs;
|
|
41
|
+
// Convert to UTC ISO string for LangFuse compatibility
|
|
42
|
+
const completionStartTimeUtc = new Date(completionStartTime).toISOString();
|
|
43
|
+
attrs[traceTypes.ATTR_LANGFUSE_COMPLETION_START_TIME] = completionStartTimeUtc;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (span.isRecording()) {
|
|
47
|
+
span.setAttributes(attrs);
|
|
48
|
+
} else {
|
|
49
|
+
const currentContext = otelContext.active();
|
|
50
|
+
const spanContext = trace.setSpan(currentContext, span);
|
|
51
|
+
|
|
52
|
+
// Create a dedicated child span for orphaned metrics
|
|
53
|
+
tracer.getTracer().startActiveSpan('realtime_metrics', {}, spanContext, (child) => {
|
|
54
|
+
try {
|
|
55
|
+
child.setAttributes(attrs);
|
|
56
|
+
} finally {
|
|
57
|
+
child.end();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/tts/tts.ts
CHANGED
|
@@ -157,8 +157,10 @@ export abstract class SynthesizeStream
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
private async mainTask() {
|
|
160
|
+
// TODO(brian): PR3 - Add span wrapping: tracer.startActiveSpan('tts_request', ..., { endOnExit: false })
|
|
160
161
|
for (let i = 0; i < this._connOptions.maxRetry + 1; i++) {
|
|
161
162
|
try {
|
|
163
|
+
// TODO(brian): PR3 - Add span for retry attempts: tracer.startActiveSpan('tts_request_run', ...)
|
|
162
164
|
return await this.run();
|
|
163
165
|
} catch (error) {
|
|
164
166
|
if (error instanceof APIError) {
|
|
@@ -385,8 +387,10 @@ export abstract class ChunkedStream implements AsyncIterableIterator<Synthesized
|
|
|
385
387
|
}
|
|
386
388
|
|
|
387
389
|
private async mainTask() {
|
|
390
|
+
// TODO(brian): PR3 - Add span wrapping: tracer.startActiveSpan('tts_request', ..., { endOnExit: false })
|
|
388
391
|
for (let i = 0; i < this._connOptions.maxRetry + 1; i++) {
|
|
389
392
|
try {
|
|
393
|
+
// TODO(brian): PR3 - Add span for retry attempts: tracer.startActiveSpan('tts_request_run', ...)
|
|
390
394
|
return await this.run();
|
|
391
395
|
} catch (error) {
|
|
392
396
|
if (error instanceof APIError) {
|
package/src/voice/agent.ts
CHANGED
|
@@ -59,6 +59,7 @@ export interface ModelSettings {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export interface AgentOptions<UserData> {
|
|
62
|
+
id?: string;
|
|
62
63
|
instructions: string;
|
|
63
64
|
chatCtx?: ChatContext;
|
|
64
65
|
tools?: ToolContext<UserData>;
|
|
@@ -72,6 +73,7 @@ export interface AgentOptions<UserData> {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
export class Agent<UserData = any> {
|
|
76
|
+
private _id: string;
|
|
75
77
|
private turnDetection?: TurnDetectionMode;
|
|
76
78
|
private _stt?: STT;
|
|
77
79
|
private _vad?: VAD;
|
|
@@ -91,6 +93,7 @@ export class Agent<UserData = any> {
|
|
|
91
93
|
_tools?: ToolContext<UserData>;
|
|
92
94
|
|
|
93
95
|
constructor({
|
|
96
|
+
id,
|
|
94
97
|
instructions,
|
|
95
98
|
chatCtx,
|
|
96
99
|
tools,
|
|
@@ -100,6 +103,21 @@ export class Agent<UserData = any> {
|
|
|
100
103
|
llm,
|
|
101
104
|
tts,
|
|
102
105
|
}: AgentOptions<UserData>) {
|
|
106
|
+
if (id) {
|
|
107
|
+
this._id = id;
|
|
108
|
+
} else {
|
|
109
|
+
// Convert class name to snake_case
|
|
110
|
+
const className = this.constructor.name;
|
|
111
|
+
if (className === 'Agent') {
|
|
112
|
+
this._id = 'default_agent';
|
|
113
|
+
} else {
|
|
114
|
+
this._id = className
|
|
115
|
+
.replace(/([A-Z])/g, '_$1')
|
|
116
|
+
.toLowerCase()
|
|
117
|
+
.replace(/^_/, '');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
103
121
|
this._instructions = instructions;
|
|
104
122
|
this._tools = { ...tools };
|
|
105
123
|
this._chatCtx = chatCtx
|
|
@@ -152,6 +170,10 @@ export class Agent<UserData = any> {
|
|
|
152
170
|
return new ReadonlyChatContext(this._chatCtx.items);
|
|
153
171
|
}
|
|
154
172
|
|
|
173
|
+
get id(): string {
|
|
174
|
+
return this._id;
|
|
175
|
+
}
|
|
176
|
+
|
|
155
177
|
get instructions(): string {
|
|
156
178
|
return this._instructions;
|
|
157
179
|
}
|
|
@@ -202,6 +202,8 @@ export class AgentActivity implements RecognitionHooks {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
async start(): Promise<void> {
|
|
205
|
+
// TODO(brian): PR3 - Add span: startSpan = tracer.startSpan('start_agent_activity', { attributes: { 'lk.agent_label': this.agent.label } })
|
|
206
|
+
// TODO(brian): PR3 - Wrap prewarm calls with trace.useSpan(startSpan, endOnExit: false)
|
|
205
207
|
const unlock = await this.lock.lock();
|
|
206
208
|
try {
|
|
207
209
|
this.agent._agentActivity = this;
|
|
@@ -289,6 +291,7 @@ export class AgentActivity implements RecognitionHooks {
|
|
|
289
291
|
this.started = true;
|
|
290
292
|
|
|
291
293
|
this._mainTask = Task.from(({ signal }) => this.mainTask(signal));
|
|
294
|
+
// TODO(brian): PR3 - Wrap onEnter with tracer.startActiveSpan('on_enter', { attributes: { 'lk.agent_label': this.agent.label }, context: startSpan context })
|
|
292
295
|
this.createSpeechTask({
|
|
293
296
|
task: Task.from(() => this.agent.onEnter()),
|
|
294
297
|
name: 'AgentActivity_onEnter',
|
|
@@ -1251,6 +1254,7 @@ export class AgentActivity implements RecognitionHooks {
|
|
|
1251
1254
|
}
|
|
1252
1255
|
}
|
|
1253
1256
|
|
|
1257
|
+
// TODO(brian): PR3 - Wrap entire pipelineReplyTask() method with tracer.startActiveSpan('agent_turn')
|
|
1254
1258
|
private async pipelineReplyTask(
|
|
1255
1259
|
speechHandle: SpeechHandle,
|
|
1256
1260
|
chatCtx: ChatContext,
|
|
@@ -2092,12 +2096,14 @@ export class AgentActivity implements RecognitionHooks {
|
|
|
2092
2096
|
this.wakeupMainTask();
|
|
2093
2097
|
}
|
|
2094
2098
|
|
|
2099
|
+
// TODO(brian): PR3 - Wrap entire drain() method with tracer.startActiveSpan('drain_agent_activity', { attributes: { 'lk.agent_label': this.agent.label } })
|
|
2095
2100
|
async drain(): Promise<void> {
|
|
2096
2101
|
const unlock = await this.lock.lock();
|
|
2097
2102
|
try {
|
|
2098
2103
|
if (this._draining) return;
|
|
2099
2104
|
|
|
2100
2105
|
this.cancelPreemptiveGeneration();
|
|
2106
|
+
// TODO(brian): PR3 - Wrap onExit with tracer.startActiveSpan('on_exit', { attributes: { 'lk.agent_label': this.agent.label } })
|
|
2101
2107
|
this.createSpeechTask({
|
|
2102
2108
|
task: Task.from(() => this.agent.onExit()),
|
|
2103
2109
|
name: 'AgentActivity_onExit',
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type TTSModelString,
|
|
15
15
|
} from '../inference/index.js';
|
|
16
16
|
import { getJobContext } from '../job.js';
|
|
17
|
-
import { ChatContext, ChatMessage } from '../llm/chat_context.js';
|
|
17
|
+
import { AgentHandoffItem, ChatContext, ChatMessage } from '../llm/chat_context.js';
|
|
18
18
|
import type { LLM, RealtimeModel, RealtimeModelError, ToolChoice } from '../llm/index.js';
|
|
19
19
|
import type { LLMError } from '../llm/llm.js';
|
|
20
20
|
import { log } from '../log.js';
|
|
@@ -26,6 +26,7 @@ import type { Agent } from './agent.js';
|
|
|
26
26
|
import { AgentActivity } from './agent_activity.js';
|
|
27
27
|
import type { _TurnDetector } from './audio_recognition.js';
|
|
28
28
|
import {
|
|
29
|
+
type AgentEvent,
|
|
29
30
|
AgentSessionEventTypes,
|
|
30
31
|
type AgentState,
|
|
31
32
|
type AgentStateChangedEvent,
|
|
@@ -127,6 +128,9 @@ export class AgentSession<
|
|
|
127
128
|
private closingTask: Promise<void> | null = null;
|
|
128
129
|
private userAwayTimer: NodeJS.Timeout | null = null;
|
|
129
130
|
|
|
131
|
+
/** @internal */
|
|
132
|
+
_recordedEvents: AgentEvent[] = [];
|
|
133
|
+
|
|
130
134
|
constructor(opts: AgentSessionOptions<UserData>) {
|
|
131
135
|
super();
|
|
132
136
|
|
|
@@ -174,6 +178,15 @@ export class AgentSession<
|
|
|
174
178
|
this.on(AgentSessionEventTypes.UserInputTranscribed, this._onUserInputTranscribed.bind(this));
|
|
175
179
|
}
|
|
176
180
|
|
|
181
|
+
emit<K extends keyof AgentSessionCallbacks>(
|
|
182
|
+
event: K,
|
|
183
|
+
...args: Parameters<AgentSessionCallbacks[K]>
|
|
184
|
+
): boolean {
|
|
185
|
+
const eventData = args[0] as AgentEvent;
|
|
186
|
+
this._recordedEvents.push(eventData);
|
|
187
|
+
return super.emit(event, ...args);
|
|
188
|
+
}
|
|
189
|
+
|
|
177
190
|
get input(): AgentInput {
|
|
178
191
|
return this._input;
|
|
179
192
|
}
|
|
@@ -199,15 +212,20 @@ export class AgentSession<
|
|
|
199
212
|
}
|
|
200
213
|
|
|
201
214
|
async start({
|
|
215
|
+
// TODO(brian): PR2 - Add setupCloudTracer() call if on LiveKit Cloud with recording enabled
|
|
216
|
+
// TODO(brian): PR3 - Add span: this._sessionSpan = tracer.startSpan('agent_session'), store as instance property
|
|
217
|
+
// TODO(brian): PR4 - Add setupCloudLogger() call in setupCloudTracer() to setup OTEL logging with Pino bridge
|
|
202
218
|
agent,
|
|
203
219
|
room,
|
|
204
220
|
inputOptions,
|
|
205
221
|
outputOptions,
|
|
222
|
+
record = true,
|
|
206
223
|
}: {
|
|
207
224
|
agent: Agent;
|
|
208
225
|
room: Room;
|
|
209
226
|
inputOptions?: Partial<RoomInputOptions>;
|
|
210
227
|
outputOptions?: Partial<RoomOutputOptions>;
|
|
228
|
+
record?: boolean;
|
|
211
229
|
}): Promise<void> {
|
|
212
230
|
if (this.started) {
|
|
213
231
|
return;
|
|
@@ -247,6 +265,17 @@ export class AgentSession<
|
|
|
247
265
|
this.logger.debug('Auto-connecting to room via job context');
|
|
248
266
|
tasks.push(ctx.connect());
|
|
249
267
|
}
|
|
268
|
+
|
|
269
|
+
if (record) {
|
|
270
|
+
if (ctx._primaryAgentSession === undefined) {
|
|
271
|
+
ctx._primaryAgentSession = this;
|
|
272
|
+
} else {
|
|
273
|
+
throw new Error(
|
|
274
|
+
'Only one `AgentSession` can be the primary at a time. If you want to ignore primary designation, use session.start(record=False).',
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
250
279
|
// TODO(AJS-265): add shutdown callback to job context
|
|
251
280
|
tasks.push(this.updateActivity(this.agent));
|
|
252
281
|
|
|
@@ -341,6 +370,8 @@ export class AgentSession<
|
|
|
341
370
|
// TODO(AJS-129): add lock to agent activity core lifecycle
|
|
342
371
|
this.nextActivity = new AgentActivity(agent, this);
|
|
343
372
|
|
|
373
|
+
const previousActivity = this.activity;
|
|
374
|
+
|
|
344
375
|
if (this.activity) {
|
|
345
376
|
await this.activity.drain();
|
|
346
377
|
await this.activity.close();
|
|
@@ -349,6 +380,14 @@ export class AgentSession<
|
|
|
349
380
|
this.activity = this.nextActivity;
|
|
350
381
|
this.nextActivity = undefined;
|
|
351
382
|
|
|
383
|
+
this._chatCtx.insert(
|
|
384
|
+
new AgentHandoffItem({
|
|
385
|
+
oldAgentId: previousActivity?.agent.id,
|
|
386
|
+
newAgentId: agent.id,
|
|
387
|
+
}),
|
|
388
|
+
);
|
|
389
|
+
this.logger.debug({ previousActivity, agent }, 'Agent handoff inserted into chat context');
|
|
390
|
+
|
|
352
391
|
await this.activity.start();
|
|
353
392
|
|
|
354
393
|
if (this._input.audio) {
|
|
@@ -419,6 +458,8 @@ export class AgentSession<
|
|
|
419
458
|
return;
|
|
420
459
|
}
|
|
421
460
|
|
|
461
|
+
// TODO(brian): PR3 - Add span: if state === 'speaking' && !this._agentSpeakingSpan, create tracer.startSpan('agent_speaking') with participant attributes
|
|
462
|
+
// TODO(brian): PR3 - Add span: if state !== 'speaking' && this._agentSpeakingSpan, end and clear this._agentSpeakingSpan
|
|
422
463
|
const oldState = this._agentState;
|
|
423
464
|
this._agentState = state;
|
|
424
465
|
|
|
@@ -441,6 +482,8 @@ export class AgentSession<
|
|
|
441
482
|
return;
|
|
442
483
|
}
|
|
443
484
|
|
|
485
|
+
// TODO(brian): PR3 - Add span: if state === 'speaking' && !this._userSpeakingSpan, create tracer.startSpan('user_speaking') with participant attributes
|
|
486
|
+
// TODO(brian): PR3 - Add span: if state !== 'speaking' && this._userSpeakingSpan, end and clear this._userSpeakingSpan
|
|
444
487
|
const oldState = this.userState;
|
|
445
488
|
this.userState = state;
|
|
446
489
|
|
|
@@ -57,6 +57,8 @@ export interface AudioRecognitionOptions {
|
|
|
57
57
|
maxEndpointingDelay: number;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// TODO(brian): PR3 - Add span: private _userTurnSpan?: Span, create lazily in _ensureUserTurnSpan() method (tracer.startSpan('user_turn') with participant attributes)
|
|
61
|
+
// TODO(brian): PR3 - Add span: 'eou_detection' span when running EOU detection (in runEOUDetection method)
|
|
60
62
|
export class AudioRecognition {
|
|
61
63
|
private hooks: RecognitionHooks;
|
|
62
64
|
private stt?: STTNode;
|
package/src/voice/generation.ts
CHANGED
|
@@ -377,6 +377,7 @@ export function updateInstructions(options: {
|
|
|
377
377
|
}
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
// TODO(brian): PR3 - Add @tracer.startActiveSpan('llm_node') decorator/wrapper
|
|
380
381
|
export function performLLMInference(
|
|
381
382
|
node: LLMNode,
|
|
382
383
|
chatCtx: ChatContext,
|
|
@@ -467,6 +468,7 @@ export function performLLMInference(
|
|
|
467
468
|
];
|
|
468
469
|
}
|
|
469
470
|
|
|
471
|
+
// TODO(brian): PR3 - Add @tracer.startActiveSpan('tts_node') decorator/wrapper
|
|
470
472
|
export function performTTSInference(
|
|
471
473
|
node: TTSNode,
|
|
472
474
|
text: ReadableStream<string>,
|
|
@@ -650,6 +652,7 @@ export function performAudioForwarding(
|
|
|
650
652
|
];
|
|
651
653
|
}
|
|
652
654
|
|
|
655
|
+
// TODO(brian): PR3 - Add @tracer.startActiveSpan('function_tool') wrapper for each tool execution
|
|
653
656
|
export function performToolExecutions({
|
|
654
657
|
session,
|
|
655
658
|
speechHandle,
|