@livekit/agents 1.0.48 → 1.1.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.cjs +27 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +9 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +18 -0
- package/dist/constants.js.map +1 -1
- package/dist/inference/api_protos.d.cts +71 -71
- package/dist/inference/api_protos.d.ts +71 -71
- package/dist/inference/interruption/defaults.cjs +81 -0
- package/dist/inference/interruption/defaults.cjs.map +1 -0
- package/dist/inference/interruption/defaults.d.cts +19 -0
- package/dist/inference/interruption/defaults.d.ts +19 -0
- package/dist/inference/interruption/defaults.d.ts.map +1 -0
- package/dist/inference/interruption/defaults.js +46 -0
- package/dist/inference/interruption/defaults.js.map +1 -0
- package/dist/inference/interruption/errors.cjs +44 -0
- package/dist/inference/interruption/errors.cjs.map +1 -0
- package/dist/inference/interruption/errors.d.cts +12 -0
- package/dist/inference/interruption/errors.d.ts +12 -0
- package/dist/inference/interruption/errors.d.ts.map +1 -0
- package/dist/inference/interruption/errors.js +20 -0
- package/dist/inference/interruption/errors.js.map +1 -0
- package/dist/inference/interruption/http_transport.cjs +147 -0
- package/dist/inference/interruption/http_transport.cjs.map +1 -0
- package/dist/inference/interruption/http_transport.d.cts +63 -0
- package/dist/inference/interruption/http_transport.d.ts +63 -0
- package/dist/inference/interruption/http_transport.d.ts.map +1 -0
- package/dist/inference/interruption/http_transport.js +121 -0
- package/dist/inference/interruption/http_transport.js.map +1 -0
- package/dist/inference/interruption/interruption_cache_entry.cjs +58 -0
- package/dist/inference/interruption/interruption_cache_entry.cjs.map +1 -0
- package/dist/inference/interruption/interruption_cache_entry.d.cts +30 -0
- package/dist/inference/interruption/interruption_cache_entry.d.ts +30 -0
- package/dist/inference/interruption/interruption_cache_entry.d.ts.map +1 -0
- package/dist/inference/interruption/interruption_cache_entry.js +34 -0
- package/dist/inference/interruption/interruption_cache_entry.js.map +1 -0
- package/dist/inference/interruption/interruption_detector.cjs +181 -0
- package/dist/inference/interruption/interruption_detector.cjs.map +1 -0
- package/dist/inference/interruption/interruption_detector.d.cts +59 -0
- package/dist/inference/interruption/interruption_detector.d.ts +59 -0
- package/dist/inference/interruption/interruption_detector.d.ts.map +1 -0
- package/dist/inference/interruption/interruption_detector.js +147 -0
- package/dist/inference/interruption/interruption_detector.js.map +1 -0
- package/dist/inference/interruption/interruption_stream.cjs +368 -0
- package/dist/inference/interruption/interruption_stream.cjs.map +1 -0
- package/dist/inference/interruption/interruption_stream.d.cts +46 -0
- package/dist/inference/interruption/interruption_stream.d.ts +46 -0
- package/dist/inference/interruption/interruption_stream.d.ts.map +1 -0
- package/dist/inference/interruption/interruption_stream.js +344 -0
- package/dist/inference/interruption/interruption_stream.js.map +1 -0
- package/dist/inference/interruption/types.cjs +17 -0
- package/dist/inference/interruption/types.cjs.map +1 -0
- package/dist/inference/interruption/types.d.cts +66 -0
- package/dist/inference/interruption/types.d.ts +66 -0
- package/dist/inference/interruption/types.d.ts.map +1 -0
- package/dist/inference/interruption/types.js +1 -0
- package/dist/inference/interruption/types.js.map +1 -0
- package/dist/inference/interruption/utils.cjs +130 -0
- package/dist/inference/interruption/utils.cjs.map +1 -0
- package/dist/inference/interruption/utils.d.cts +41 -0
- package/dist/inference/interruption/utils.d.ts +41 -0
- package/dist/inference/interruption/utils.d.ts.map +1 -0
- package/dist/inference/interruption/utils.js +105 -0
- package/dist/inference/interruption/utils.js.map +1 -0
- package/dist/inference/interruption/utils.test.cjs +105 -0
- package/dist/inference/interruption/utils.test.cjs.map +1 -0
- package/dist/inference/interruption/utils.test.js +104 -0
- package/dist/inference/interruption/utils.test.js.map +1 -0
- package/dist/inference/interruption/ws_transport.cjs +329 -0
- package/dist/inference/interruption/ws_transport.cjs.map +1 -0
- package/dist/inference/interruption/ws_transport.d.cts +33 -0
- package/dist/inference/interruption/ws_transport.d.ts +33 -0
- package/dist/inference/interruption/ws_transport.d.ts.map +1 -0
- package/dist/inference/interruption/ws_transport.js +295 -0
- package/dist/inference/interruption/ws_transport.js.map +1 -0
- package/dist/inference/llm.cjs +14 -10
- package/dist/inference/llm.cjs.map +1 -1
- package/dist/inference/llm.d.cts +2 -1
- package/dist/inference/llm.d.ts +2 -1
- package/dist/inference/llm.d.ts.map +1 -1
- package/dist/inference/llm.js +8 -10
- package/dist/inference/llm.js.map +1 -1
- package/dist/inference/stt.cjs +7 -2
- package/dist/inference/stt.cjs.map +1 -1
- package/dist/inference/stt.d.cts +2 -0
- package/dist/inference/stt.d.ts +2 -0
- package/dist/inference/stt.d.ts.map +1 -1
- package/dist/inference/stt.js +8 -3
- package/dist/inference/stt.js.map +1 -1
- package/dist/inference/tts.cjs +7 -2
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +2 -0
- package/dist/inference/tts.d.ts +2 -0
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +8 -3
- package/dist/inference/tts.js.map +1 -1
- package/dist/inference/utils.cjs +26 -7
- package/dist/inference/utils.cjs.map +1 -1
- package/dist/inference/utils.d.cts +13 -0
- package/dist/inference/utils.d.ts +13 -0
- package/dist/inference/utils.d.ts.map +1 -1
- package/dist/inference/utils.js +18 -2
- package/dist/inference/utils.js.map +1 -1
- package/dist/llm/chat_context.cjs +20 -2
- package/dist/llm/chat_context.cjs.map +1 -1
- package/dist/llm/chat_context.d.cts +19 -1
- package/dist/llm/chat_context.d.ts +19 -1
- package/dist/llm/chat_context.d.ts.map +1 -1
- package/dist/llm/chat_context.js +20 -2
- package/dist/llm/chat_context.js.map +1 -1
- 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.map +1 -1
- package/dist/llm/llm.cjs +16 -1
- package/dist/llm/llm.cjs.map +1 -1
- package/dist/llm/llm.d.cts +9 -0
- package/dist/llm/llm.d.ts +9 -0
- package/dist/llm/llm.d.ts.map +1 -1
- package/dist/llm/llm.js +16 -1
- package/dist/llm/llm.js.map +1 -1
- package/dist/llm/realtime.cjs +3 -0
- package/dist/llm/realtime.cjs.map +1 -1
- package/dist/llm/realtime.d.cts +1 -0
- package/dist/llm/realtime.d.ts +1 -0
- package/dist/llm/realtime.d.ts.map +1 -1
- package/dist/llm/realtime.js +3 -0
- package/dist/llm/realtime.js.map +1 -1
- package/dist/metrics/base.cjs.map +1 -1
- package/dist/metrics/base.d.cts +45 -1
- package/dist/metrics/base.d.ts +45 -1
- package/dist/metrics/base.d.ts.map +1 -1
- package/dist/metrics/index.cjs +5 -0
- package/dist/metrics/index.cjs.map +1 -1
- package/dist/metrics/index.d.cts +2 -1
- package/dist/metrics/index.d.ts +2 -1
- package/dist/metrics/index.d.ts.map +1 -1
- package/dist/metrics/index.js +6 -0
- package/dist/metrics/index.js.map +1 -1
- package/dist/metrics/model_usage.cjs +189 -0
- package/dist/metrics/model_usage.cjs.map +1 -0
- package/dist/metrics/model_usage.d.cts +92 -0
- package/dist/metrics/model_usage.d.ts +92 -0
- package/dist/metrics/model_usage.d.ts.map +1 -0
- package/dist/metrics/model_usage.js +164 -0
- package/dist/metrics/model_usage.js.map +1 -0
- package/dist/metrics/model_usage.test.cjs +474 -0
- package/dist/metrics/model_usage.test.cjs.map +1 -0
- package/dist/metrics/model_usage.test.js +476 -0
- package/dist/metrics/model_usage.test.js.map +1 -0
- package/dist/metrics/usage_collector.cjs +3 -0
- package/dist/metrics/usage_collector.cjs.map +1 -1
- package/dist/metrics/usage_collector.d.cts +9 -0
- package/dist/metrics/usage_collector.d.ts +9 -0
- package/dist/metrics/usage_collector.d.ts.map +1 -1
- package/dist/metrics/usage_collector.js +3 -0
- package/dist/metrics/usage_collector.js.map +1 -1
- package/dist/metrics/utils.cjs +9 -0
- package/dist/metrics/utils.cjs.map +1 -1
- package/dist/metrics/utils.d.ts.map +1 -1
- package/dist/metrics/utils.js +9 -0
- package/dist/metrics/utils.js.map +1 -1
- package/dist/stream/multi_input_stream.test.cjs +4 -0
- package/dist/stream/multi_input_stream.test.cjs.map +1 -1
- package/dist/stream/multi_input_stream.test.js +5 -1
- package/dist/stream/multi_input_stream.test.js.map +1 -1
- package/dist/stream/stream_channel.cjs +31 -0
- package/dist/stream/stream_channel.cjs.map +1 -1
- package/dist/stream/stream_channel.d.cts +4 -2
- package/dist/stream/stream_channel.d.ts +4 -2
- package/dist/stream/stream_channel.d.ts.map +1 -1
- package/dist/stream/stream_channel.js +31 -0
- package/dist/stream/stream_channel.js.map +1 -1
- package/dist/stt/stt.cjs +34 -2
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +22 -0
- package/dist/stt/stt.d.ts +22 -0
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +34 -2
- package/dist/stt/stt.js.map +1 -1
- package/dist/telemetry/otel_http_exporter.cjs +24 -5
- package/dist/telemetry/otel_http_exporter.cjs.map +1 -1
- package/dist/telemetry/otel_http_exporter.d.cts +1 -0
- package/dist/telemetry/otel_http_exporter.d.ts +1 -0
- package/dist/telemetry/otel_http_exporter.d.ts.map +1 -1
- package/dist/telemetry/otel_http_exporter.js +24 -5
- package/dist/telemetry/otel_http_exporter.js.map +1 -1
- package/dist/telemetry/trace_types.cjs +5 -5
- package/dist/telemetry/trace_types.cjs.map +1 -1
- package/dist/telemetry/trace_types.d.cts +9 -5
- package/dist/telemetry/trace_types.d.ts +9 -5
- package/dist/telemetry/trace_types.d.ts.map +1 -1
- package/dist/telemetry/trace_types.js +5 -5
- package/dist/telemetry/trace_types.js.map +1 -1
- package/dist/telemetry/traces.cjs +47 -8
- package/dist/telemetry/traces.cjs.map +1 -1
- package/dist/telemetry/traces.d.ts.map +1 -1
- package/dist/telemetry/traces.js +47 -8
- package/dist/telemetry/traces.js.map +1 -1
- package/dist/tts/tts.cjs +64 -2
- package/dist/tts/tts.cjs.map +1 -1
- package/dist/tts/tts.d.cts +34 -0
- package/dist/tts/tts.d.ts +34 -0
- package/dist/tts/tts.d.ts.map +1 -1
- package/dist/tts/tts.js +64 -2
- package/dist/tts/tts.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist/voice/agent.cjs +25 -4
- package/dist/voice/agent.cjs.map +1 -1
- package/dist/voice/agent.d.cts +10 -2
- package/dist/voice/agent.d.ts +10 -2
- package/dist/voice/agent.d.ts.map +1 -1
- package/dist/voice/agent.js +25 -4
- package/dist/voice/agent.js.map +1 -1
- package/dist/voice/agent_activity.cjs +261 -36
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +20 -6
- package/dist/voice/agent_activity.d.ts +20 -6
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +262 -37
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_session.cjs +105 -48
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +90 -20
- package/dist/voice/agent_session.d.ts +90 -20
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +105 -46
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +287 -6
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +42 -3
- package/dist/voice/audio_recognition.d.ts +42 -3
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +289 -7
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/client_events.cjs +554 -0
- package/dist/voice/client_events.cjs.map +1 -0
- package/dist/voice/client_events.d.cts +195 -0
- package/dist/voice/client_events.d.ts +195 -0
- package/dist/voice/client_events.d.ts.map +1 -0
- package/dist/voice/client_events.js +548 -0
- package/dist/voice/client_events.js.map +1 -0
- package/dist/voice/events.cjs +1 -0
- package/dist/voice/events.cjs.map +1 -1
- package/dist/voice/events.d.cts +8 -5
- package/dist/voice/events.d.ts +8 -5
- package/dist/voice/events.d.ts.map +1 -1
- package/dist/voice/events.js +1 -0
- package/dist/voice/events.js.map +1 -1
- package/dist/voice/generation.cjs +43 -8
- package/dist/voice/generation.cjs.map +1 -1
- package/dist/voice/generation.d.cts +3 -3
- package/dist/voice/generation.d.ts +3 -3
- package/dist/voice/generation.d.ts.map +1 -1
- package/dist/voice/generation.js +43 -8
- package/dist/voice/generation.js.map +1 -1
- package/dist/voice/index.cjs +1 -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 +20 -8
- package/dist/voice/report.cjs.map +1 -1
- package/dist/voice/report.d.cts +5 -0
- package/dist/voice/report.d.ts +5 -0
- package/dist/voice/report.d.ts.map +1 -1
- package/dist/voice/report.js +20 -8
- package/dist/voice/report.js.map +1 -1
- package/dist/voice/report.test.cjs +106 -0
- package/dist/voice/report.test.cjs.map +1 -0
- package/dist/voice/report.test.js +105 -0
- package/dist/voice/report.test.js.map +1 -0
- package/dist/voice/room_io/room_io.cjs +5 -39
- package/dist/voice/room_io/room_io.cjs.map +1 -1
- package/dist/voice/room_io/room_io.d.cts +4 -9
- package/dist/voice/room_io/room_io.d.ts +4 -9
- package/dist/voice/room_io/room_io.d.ts.map +1 -1
- package/dist/voice/room_io/room_io.js +5 -40
- package/dist/voice/room_io/room_io.js.map +1 -1
- package/dist/voice/turn_config/endpointing.cjs +33 -0
- package/dist/voice/turn_config/endpointing.cjs.map +1 -0
- package/dist/voice/turn_config/endpointing.d.cts +30 -0
- package/dist/voice/turn_config/endpointing.d.ts +30 -0
- package/dist/voice/turn_config/endpointing.d.ts.map +1 -0
- package/dist/voice/turn_config/endpointing.js +9 -0
- package/dist/voice/turn_config/endpointing.js.map +1 -0
- package/dist/voice/turn_config/interruption.cjs +37 -0
- package/dist/voice/turn_config/interruption.cjs.map +1 -0
- package/dist/voice/turn_config/interruption.d.cts +53 -0
- package/dist/voice/turn_config/interruption.d.ts +53 -0
- package/dist/voice/turn_config/interruption.d.ts.map +1 -0
- package/dist/voice/turn_config/interruption.js +13 -0
- package/dist/voice/turn_config/interruption.js.map +1 -0
- package/dist/voice/turn_config/turn_handling.cjs +35 -0
- package/dist/voice/turn_config/turn_handling.cjs.map +1 -0
- package/dist/voice/turn_config/turn_handling.d.cts +36 -0
- package/dist/voice/turn_config/turn_handling.d.ts +36 -0
- package/dist/voice/turn_config/turn_handling.d.ts.map +1 -0
- package/dist/voice/turn_config/turn_handling.js +11 -0
- package/dist/voice/turn_config/turn_handling.js.map +1 -0
- package/dist/voice/turn_config/utils.cjs +97 -0
- package/dist/voice/turn_config/utils.cjs.map +1 -0
- package/dist/voice/turn_config/utils.d.cts +25 -0
- package/dist/voice/turn_config/utils.d.ts +25 -0
- package/dist/voice/turn_config/utils.d.ts.map +1 -0
- package/dist/voice/turn_config/utils.js +73 -0
- package/dist/voice/turn_config/utils.js.map +1 -0
- package/dist/voice/turn_config/utils.test.cjs +86 -0
- package/dist/voice/turn_config/utils.test.cjs.map +1 -0
- package/dist/voice/turn_config/utils.test.js +85 -0
- package/dist/voice/turn_config/utils.test.js.map +1 -0
- package/dist/voice/wire_format.cjs +798 -0
- package/dist/voice/wire_format.cjs.map +1 -0
- package/dist/voice/wire_format.d.cts +5503 -0
- package/dist/voice/wire_format.d.ts +5503 -0
- package/dist/voice/wire_format.d.ts.map +1 -0
- package/dist/voice/wire_format.js +728 -0
- package/dist/voice/wire_format.js.map +1 -0
- package/package.json +2 -1
- package/src/constants.ts +13 -0
- package/src/inference/interruption/defaults.ts +51 -0
- package/src/inference/interruption/errors.ts +25 -0
- package/src/inference/interruption/http_transport.ts +187 -0
- package/src/inference/interruption/interruption_cache_entry.ts +50 -0
- package/src/inference/interruption/interruption_detector.ts +188 -0
- package/src/inference/interruption/interruption_stream.ts +467 -0
- package/src/inference/interruption/types.ts +84 -0
- package/src/inference/interruption/utils.test.ts +132 -0
- package/src/inference/interruption/utils.ts +137 -0
- package/src/inference/interruption/ws_transport.ts +402 -0
- package/src/inference/llm.ts +9 -12
- package/src/inference/stt.ts +10 -3
- package/src/inference/tts.ts +10 -3
- package/src/inference/utils.ts +29 -1
- package/src/llm/chat_context.ts +40 -2
- package/src/llm/index.ts +1 -0
- package/src/llm/llm.ts +16 -0
- package/src/llm/realtime.ts +4 -0
- package/src/metrics/base.ts +48 -1
- package/src/metrics/index.ts +11 -0
- package/src/metrics/model_usage.test.ts +545 -0
- package/src/metrics/model_usage.ts +262 -0
- package/src/metrics/usage_collector.ts +11 -0
- package/src/metrics/utils.ts +11 -0
- package/src/stream/multi_input_stream.test.ts +6 -1
- package/src/stream/stream_channel.ts +34 -2
- package/src/stt/stt.ts +38 -0
- package/src/telemetry/otel_http_exporter.ts +28 -5
- package/src/telemetry/trace_types.ts +11 -8
- package/src/telemetry/traces.ts +111 -54
- package/src/tts/tts.ts +69 -1
- package/src/voice/agent.ts +30 -3
- package/src/voice/agent_activity.ts +327 -28
- package/src/voice/agent_session.ts +207 -59
- package/src/voice/audio_recognition.ts +385 -9
- package/src/voice/client_events.ts +838 -0
- package/src/voice/events.ts +14 -4
- package/src/voice/generation.ts +52 -9
- package/src/voice/index.ts +1 -0
- package/src/voice/report.test.ts +117 -0
- package/src/voice/report.ts +29 -6
- package/src/voice/room_io/room_io.ts +7 -61
- package/src/voice/turn_config/endpointing.ts +33 -0
- package/src/voice/turn_config/interruption.ts +56 -0
- package/src/voice/turn_config/turn_handling.ts +45 -0
- package/src/voice/turn_config/utils.test.ts +100 -0
- package/src/voice/turn_config/utils.ts +103 -0
- package/src/voice/wire_format.ts +827 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/telemetry/traces.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { MetricsRecordingHeader } from '@livekit/protocol';\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 { SeverityNumber } from '@opentelemetry/api-logs';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\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 FormData from 'form-data';\nimport { AccessToken } from 'livekit-server-sdk';\nimport fs from 'node:fs/promises';\nimport type { ChatContent, ChatItem } from '../llm/index.js';\nimport { enableOtelLogging } from '../log.js';\nimport type { SessionReport } from '../voice/report.js';\nimport { type SimpleLogRecord, SimpleOTLPHttpLogExporter } from './otel_http_exporter.js';\nimport { flushPinoLogs, initPinoCloudExporter } from './pino_otel_transport.js';\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 /** Optional start time for the span in milliseconds (Date.now() format) */\n startTime?: number;\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\n const span = this.tracer.startSpan(\n options.name,\n {\n attributes: options.attributes,\n startTime: options.startTime,\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, startTime: options.startTime };\n\n // Directly return the tracer's startActiveSpan result - it handles async correctly\n return await this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {\n try {\n return await fn(span);\n } finally {\n if (endOnExit) {\n span.end();\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, startTime: options.startTime };\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/**\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 setTracerProvider(tracerProvider);\n\n // Initialize standalone Pino cloud exporter (no OTEL SDK dependency)\n initPinoCloudExporter({\n cloudHostname,\n roomId,\n jobId,\n });\n\n enableOtelLogging();\n } catch (error) {\n console.error('Failed to setup cloud tracer:', error);\n throw error;\n }\n}\n\n/**\n * Flush all pending Pino logs to ensure they are exported.\n * Call this before session/job ends to ensure all logs are sent.\n *\n * @internal\n */\nexport async function flushOtelLogs(): Promise<void> {\n await flushPinoLogs();\n}\n\n/**\n * Convert ChatItem to proto-compatible dictionary format.\n * TODO: Use actual agent_session proto types once @livekit/protocol v1.43.1+ is published\n */\nfunction chatItemToProto(item: ChatItem): Record<string, any> {\n const itemDict: Record<string, any> = {};\n\n if (item.type === 'message') {\n const roleMap: Record<string, string> = {\n developer: 'DEVELOPER',\n system: 'SYSTEM',\n user: 'USER',\n assistant: 'ASSISTANT',\n };\n\n const msg: Record<string, any> = {\n id: item.id,\n role: roleMap[item.role] || item.role.toUpperCase(),\n content: item.content.map((c: ChatContent) => ({ text: c })),\n createdAt: toRFC3339(item.createdAt),\n };\n\n if (item.interrupted) {\n msg.interrupted = item.interrupted;\n }\n\n // TODO(brian): Add extra and transcriptConfidence to ChatMessage\n // if (item.extra && Object.keys(item.extra).length > 0) {\n // msg.extra = item.extra;\n // }\n\n // if (item.transcriptConfidence !== undefined && item.transcriptConfidence !== null) {\n // msg.transcriptConfidence = item.transcriptConfidence;\n // }\n\n // TODO(brian): Add metrics to ChatMessage\n // const metrics = item.metrics || {};\n // if (Object.keys(metrics).length > 0) {\n // msg.metrics = {};\n // if (metrics.started_speaking_at) {\n // msg.metrics.startedSpeakingAt = toRFC3339(metrics.started_speaking_at);\n // }\n // if (metrics.stopped_speaking_at) {\n // msg.metrics.stoppedSpeakingAt = toRFC3339(metrics.stopped_speaking_at);\n // }\n // if (metrics.transcription_delay !== undefined) {\n // msg.metrics.transcriptionDelay = metrics.transcription_delay;\n // }\n // if (metrics.end_of_turn_delay !== undefined) {\n // msg.metrics.endOfTurnDelay = metrics.end_of_turn_delay;\n // }\n // if (metrics.on_user_turn_completed_delay !== undefined) {\n // msg.metrics.onUserTurnCompletedDelay = metrics.on_user_turn_completed_delay;\n // }\n // if (metrics.llm_node_ttft !== undefined) {\n // msg.metrics.llmNodeTtft = metrics.llm_node_ttft;\n // }\n // if (metrics.tts_node_ttfb !== undefined) {\n // msg.metrics.ttsNodeTtfb = metrics.tts_node_ttfb;\n // }\n // if (metrics.e2e_latency !== undefined) {\n // msg.metrics.e2eLatency = metrics.e2e_latency;\n // }\n // }\n\n itemDict.message = msg;\n } else if (item.type === 'function_call') {\n itemDict.functionCall = {\n id: item.id,\n callId: item.callId,\n arguments: item.args,\n name: item.name,\n createdAt: toRFC3339(item.createdAt),\n };\n } else if (item.type === 'function_call_output') {\n itemDict.functionCallOutput = {\n id: item.id,\n name: item.name,\n callId: item.callId,\n output: item.output,\n isError: item.isError,\n createdAt: toRFC3339(item.createdAt),\n };\n } else if (item.type === 'agent_handoff') {\n const handoff: Record<string, any> = {\n id: item.id,\n newAgentId: item.newAgentId,\n createdAt: toRFC3339(item.createdAt),\n };\n if (item.oldAgentId !== undefined && item.oldAgentId !== null && item.oldAgentId !== '') {\n handoff.oldAgentId = item.oldAgentId;\n }\n itemDict.agentHandoff = handoff;\n }\n\n try {\n if (item.type === 'function_call' && typeof itemDict.functionCall?.arguments === 'string') {\n itemDict.functionCall.arguments = JSON.parse(itemDict.functionCall.arguments);\n } else if (\n item.type === 'function_call_output' &&\n typeof itemDict.functionCallOutput?.output === 'string'\n ) {\n itemDict.functionCallOutput.output = JSON.parse(itemDict.functionCallOutput.output);\n }\n } catch {\n // ignore parsing errors\n }\n\n return itemDict;\n}\n\n/**\n * Convert timestamp to RFC3339 format matching Python's _to_rfc3339.\n * Note: TypeScript createdAt is in milliseconds (Date.now()), not seconds like Python.\n * @internal\n */\nfunction toRFC3339(valueMs: number | Date): string {\n // valueMs is already in milliseconds (from Date.now())\n const dt = valueMs instanceof Date ? valueMs : new Date(valueMs);\n // Truncate sub-millisecond precision\n const truncated = new Date(Math.floor(dt.getTime()));\n return truncated.toISOString();\n}\n\n/**\n * Upload session report to LiveKit Cloud observability.\n * @param options - Configuration with agentName, cloudHostname, and report\n */\nexport async function uploadSessionReport(options: {\n agentName: string;\n cloudHostname: string;\n report: SessionReport;\n}): Promise<void> {\n const { agentName, cloudHostname, report } = options;\n\n // Create OTLP HTTP exporter for chat history logs\n // Uses raw HTTP JSON format which is required by LiveKit Cloud\n const logExporter = new SimpleOTLPHttpLogExporter({\n cloudHostname,\n resourceAttributes: {\n room_id: report.roomId,\n job_id: report.jobId,\n },\n scopeName: 'chat_history',\n scopeAttributes: {\n room_id: report.roomId,\n job_id: report.jobId,\n room: report.room,\n },\n });\n\n // Build log records for session report and chat items\n const logRecords: SimpleLogRecord[] = [];\n\n const commonAttrs = {\n room_id: report.roomId,\n job_id: report.jobId,\n 'logger.name': 'chat_history',\n };\n\n logRecords.push({\n body: 'session report',\n timestampMs: report.startedAt || report.timestamp || 0,\n attributes: {\n ...commonAttrs,\n 'session.options': report.options || {},\n 'session.report_timestamp': report.timestamp,\n agent_name: agentName,\n },\n });\n\n // Track last timestamp to ensure monotonic ordering when items have identical timestamps\n // This fixes the issue where function_call and function_call_output with same timestamp\n // get reordered by the dashboard\n let lastTimestamp = 0;\n for (const item of report.chatHistory.items) {\n // Skip null/undefined items\n if (!item) continue;\n\n // Ensure monotonically increasing timestamps for proper ordering\n // Add 0.001ms (1 microsecond) offset when timestamps collide\n // Also handle undefined/NaN timestamps from realtime mode (defensive)\n const hasValidTimestamp = Number.isFinite(item.createdAt);\n let itemTimestamp = hasValidTimestamp ? item.createdAt : Date.now();\n\n if (itemTimestamp <= lastTimestamp) {\n itemTimestamp = lastTimestamp + 0.001; // Add 1 microsecond\n }\n lastTimestamp = itemTimestamp;\n\n const itemProto = chatItemToProto(item);\n let severityNumber = SeverityNumber.UNSPECIFIED;\n let severityText = 'unspecified';\n\n if (item.type === 'function_call_output' && item.isError) {\n severityNumber = SeverityNumber.ERROR;\n severityText = 'error';\n }\n\n logRecords.push({\n body: 'chat item',\n timestampMs: itemTimestamp, // Adjusted for monotonic ordering\n attributes: { 'chat.item': itemProto, ...commonAttrs },\n severityNumber,\n severityText,\n });\n }\n\n await logExporter.export(logRecords);\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 session upload');\n }\n\n const token = new AccessToken(apiKey, apiSecret, { ttl: '6h' });\n token.addObservabilityGrant({ write: true });\n const jwt = await token.toJwt();\n\n const formData = new FormData();\n\n // Add header (protobuf MetricsRecordingHeader)\n const audioStartTime = report.audioRecordingStartedAt ?? 0;\n const headerMsg = new MetricsRecordingHeader({\n roomId: report.roomId,\n duration: BigInt(0), // TODO: Calculate actual duration from report\n startTime: {\n seconds: BigInt(Math.floor(audioStartTime / 1000)),\n nanos: Math.floor((audioStartTime % 1000) * 1e6),\n },\n });\n\n const headerBytes = Buffer.from(headerMsg.toBinary());\n formData.append('header', headerBytes, {\n filename: 'header.binpb',\n contentType: 'application/protobuf',\n knownLength: headerBytes.length,\n header: {\n 'Content-Type': 'application/protobuf',\n 'Content-Length': headerBytes.length.toString(),\n },\n });\n\n // Add chat_history JSON\n const chatHistoryJson = JSON.stringify(report.chatHistory.toJSON({ excludeTimestamp: false }));\n const chatHistoryBuffer = Buffer.from(chatHistoryJson, 'utf-8');\n formData.append('chat_history', chatHistoryBuffer, {\n filename: 'chat_history.json',\n contentType: 'application/json',\n knownLength: chatHistoryBuffer.length,\n header: {\n 'Content-Type': 'application/json',\n 'Content-Length': chatHistoryBuffer.length.toString(),\n },\n });\n\n // Add audio recording file if available\n if (report.audioRecordingPath && report.audioRecordingStartedAt) {\n let audioBytes: Buffer;\n try {\n audioBytes = await fs.readFile(report.audioRecordingPath);\n } catch {\n audioBytes = Buffer.alloc(0);\n }\n\n if (audioBytes.length > 0) {\n formData.append('audio', audioBytes, {\n filename: 'recording.ogg',\n contentType: 'audio/ogg',\n knownLength: audioBytes.length,\n header: {\n 'Content-Type': 'audio/ogg',\n 'Content-Length': audioBytes.length.toString(),\n },\n });\n }\n }\n\n // Upload to LiveKit Cloud using form-data's submit method\n // This properly streams the multipart form with all headers including Content-Length\n return new Promise<void>((resolve, reject) => {\n formData.submit(\n {\n protocol: 'https:',\n host: cloudHostname,\n path: '/observability/recordings/v0',\n method: 'POST',\n headers: {\n Authorization: `Bearer ${jwt}`,\n },\n },\n (err, res) => {\n if (err) {\n reject(new Error(`Failed to upload session report: ${err.message}`));\n return;\n }\n\n if (res.statusCode && res.statusCode >= 400) {\n // Read response body for error details\n let body = '';\n res.on('data', (chunk) => {\n body += chunk.toString();\n });\n res.on('error', (readErr) => {\n reject(\n new Error(\n `Failed to upload session report: ${res.statusCode} ${res.statusMessage} (body read error: ${readErr.message})`,\n ),\n );\n });\n res.on('end', () => {\n reject(\n new Error(\n `Failed to upload session report: ${res.statusCode} ${res.statusMessage} - ${body}`,\n ),\n );\n });\n return;\n }\n\n res.resume(); // Drain the response\n res.on('error', (readErr) => reject(new Error(`Response read error: ${readErr.message}`)));\n res.on('end', () => resolve());\n },\n );\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/telemetry/traces.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { MetricsRecordingHeader } from '@livekit/protocol';\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 { SeverityNumber } from '@opentelemetry/api-logs';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\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 FormData from 'form-data';\nimport { AccessToken } from 'livekit-server-sdk';\nimport fs from 'node:fs/promises';\nimport type { ChatContent, ChatItem, ChatRole } from '../llm/index.js';\nimport { enableOtelLogging } from '../log.js';\nimport { filterZeroValues } from '../metrics/model_usage.js';\nimport type { SessionReport } from '../voice/report.js';\nimport { type SimpleLogRecord, SimpleOTLPHttpLogExporter } from './otel_http_exporter.js';\nimport { flushPinoLogs, initPinoCloudExporter } from './pino_otel_transport.js';\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 /** Optional start time for the span in milliseconds (Date.now() format) */\n startTime?: number;\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\n const span = this.tracer.startSpan(\n options.name,\n {\n attributes: options.attributes,\n startTime: options.startTime,\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, startTime: options.startTime };\n\n // Directly return the tracer's startActiveSpan result - it handles async correctly\n return await this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {\n try {\n return await fn(span);\n } finally {\n if (endOnExit) {\n span.end();\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, startTime: options.startTime };\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/**\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 setTracerProvider(tracerProvider);\n\n // Initialize standalone Pino cloud exporter (no OTEL SDK dependency)\n initPinoCloudExporter({\n cloudHostname,\n roomId,\n jobId,\n });\n\n enableOtelLogging();\n } catch (error) {\n console.error('Failed to setup cloud tracer:', error);\n throw error;\n }\n}\n\n/**\n * Flush all pending Pino logs to ensure they are exported.\n * Call this before session/job ends to ensure all logs are sent.\n *\n * @internal\n */\nexport async function flushOtelLogs(): Promise<void> {\n await flushPinoLogs();\n}\n\n/** Proto-compatible role enum values. */\ntype ProtoRole = 'DEVELOPER' | 'SYSTEM' | 'USER' | 'ASSISTANT';\n\nconst ROLE_MAP: Record<ChatRole, ProtoRole> = {\n developer: 'DEVELOPER',\n system: 'SYSTEM',\n user: 'USER',\n assistant: 'ASSISTANT',\n};\n\ninterface ProtoMetricsReport {\n startedSpeakingAt?: string;\n stoppedSpeakingAt?: string;\n transcriptionDelay?: number;\n endOfTurnDelay?: number;\n onUserTurnCompletedDelay?: number;\n llmNodeTtft?: number;\n ttsNodeTtfb?: number;\n e2eLatency?: number;\n}\n\ninterface ProtoMessage {\n id: string;\n role: ProtoRole;\n content: { text: ChatContent }[];\n createdAt: string;\n interrupted?: boolean;\n extra?: Record<string, unknown>;\n transcriptConfidence?: number;\n metrics?: ProtoMetricsReport;\n}\n\ninterface ProtoFunctionCall {\n id: string;\n callId: string;\n arguments: string | Record<string, unknown>;\n name: string;\n createdAt: string;\n}\n\ninterface ProtoFunctionCallOutput {\n id: string;\n name: string;\n callId: string;\n output: string;\n isError: boolean;\n createdAt: string;\n}\n\ninterface ProtoAgentHandoff {\n id: string;\n newAgentId: string;\n createdAt: string;\n oldAgentId?: string;\n}\n\ninterface ProtoChatItem {\n message?: ProtoMessage;\n functionCall?: ProtoFunctionCall;\n functionCallOutput?: ProtoFunctionCallOutput;\n agentHandoff?: ProtoAgentHandoff;\n}\n\n/**\n * Convert ChatItem to proto-compatible dictionary format.\n * TODO: Use actual agent_session proto types once @livekit/protocol v1.43.1+ is published\n */\nfunction chatItemToProto(item: ChatItem): ProtoChatItem {\n const itemDict: ProtoChatItem = {};\n\n if (item.type === 'message') {\n const msg: ProtoMessage = {\n id: item.id,\n role: ROLE_MAP[item.role] ?? (item.role.toUpperCase() as ProtoRole),\n content: item.content.map((c: ChatContent) => ({ text: c })),\n createdAt: toRFC3339(item.createdAt),\n };\n\n if (item.interrupted) {\n msg.interrupted = item.interrupted;\n }\n\n if (item.extra && Object.keys(item.extra).length > 0) {\n msg.extra = item.extra;\n }\n\n if (item.transcriptConfidence !== undefined) {\n msg.transcriptConfidence = item.transcriptConfidence;\n }\n\n const metrics = item.metrics;\n if (metrics && Object.keys(metrics).length > 0) {\n const protoMetrics: ProtoMetricsReport = {};\n if (metrics.startedSpeakingAt !== undefined) {\n protoMetrics.startedSpeakingAt = toRFC3339(metrics.startedSpeakingAt * 1000);\n }\n if (metrics.stoppedSpeakingAt !== undefined) {\n protoMetrics.stoppedSpeakingAt = toRFC3339(metrics.stoppedSpeakingAt * 1000);\n }\n if (metrics.transcriptionDelay !== undefined) {\n protoMetrics.transcriptionDelay = metrics.transcriptionDelay;\n }\n if (metrics.endOfTurnDelay !== undefined) {\n protoMetrics.endOfTurnDelay = metrics.endOfTurnDelay;\n }\n if (metrics.onUserTurnCompletedDelay !== undefined) {\n protoMetrics.onUserTurnCompletedDelay = metrics.onUserTurnCompletedDelay;\n }\n if (metrics.llmNodeTtft !== undefined) {\n protoMetrics.llmNodeTtft = metrics.llmNodeTtft;\n }\n if (metrics.ttsNodeTtfb !== undefined) {\n protoMetrics.ttsNodeTtfb = metrics.ttsNodeTtfb;\n }\n if (metrics.e2eLatency !== undefined) {\n protoMetrics.e2eLatency = metrics.e2eLatency;\n }\n msg.metrics = protoMetrics;\n }\n\n itemDict.message = msg;\n } else if (item.type === 'function_call') {\n itemDict.functionCall = {\n id: item.id,\n callId: item.callId,\n arguments: item.args,\n name: item.name,\n createdAt: toRFC3339(item.createdAt),\n };\n } else if (item.type === 'function_call_output') {\n itemDict.functionCallOutput = {\n id: item.id,\n name: item.name,\n callId: item.callId,\n output: item.output,\n isError: item.isError,\n createdAt: toRFC3339(item.createdAt),\n };\n } else if (item.type === 'agent_handoff') {\n const handoff: ProtoAgentHandoff = {\n id: item.id,\n newAgentId: item.newAgentId,\n createdAt: toRFC3339(item.createdAt),\n };\n if (item.oldAgentId !== undefined && item.oldAgentId !== null && item.oldAgentId !== '') {\n handoff.oldAgentId = item.oldAgentId;\n }\n itemDict.agentHandoff = handoff;\n }\n\n try {\n if (item.type === 'function_call' && typeof itemDict.functionCall?.arguments === 'string') {\n itemDict.functionCall.arguments = JSON.parse(itemDict.functionCall.arguments);\n } else if (\n item.type === 'function_call_output' &&\n typeof itemDict.functionCallOutput?.output === 'string'\n ) {\n itemDict.functionCallOutput.output = JSON.parse(itemDict.functionCallOutput.output);\n }\n } catch {\n // ignore parsing errors\n }\n\n return itemDict;\n}\n\n/**\n * Convert timestamp to RFC3339 format\n */\nfunction toRFC3339(valueMs: number | Date): string {\n // valueMs is already in milliseconds (from Date.now())\n const dt = valueMs instanceof Date ? valueMs : new Date(valueMs);\n // Truncate sub-millisecond precision\n const truncated = new Date(Math.floor(dt.getTime()));\n return truncated.toISOString();\n}\n\n/**\n * Upload session report to LiveKit Cloud observability.\n * @param options - Configuration with agentName, cloudHostname, and report\n */\nexport async function uploadSessionReport(options: {\n agentName: string;\n cloudHostname: string;\n report: SessionReport;\n}): Promise<void> {\n const { agentName, cloudHostname, report } = options;\n\n // Create OTLP HTTP exporter for chat history logs\n // Uses raw HTTP JSON format which is required by LiveKit Cloud\n const logExporter = new SimpleOTLPHttpLogExporter({\n cloudHostname,\n resourceAttributes: {\n room_id: report.roomId,\n job_id: report.jobId,\n },\n scopeName: 'chat_history',\n scopeAttributes: {\n room_id: report.roomId,\n job_id: report.jobId,\n room: report.room,\n },\n });\n\n // Build log records for session report and chat items\n const logRecords: SimpleLogRecord[] = [];\n\n const commonAttrs = {\n room_id: report.roomId,\n job_id: report.jobId,\n 'logger.name': 'chat_history',\n };\n\n const usage = report.modelUsage?.map(filterZeroValues) || null;\n\n logRecords.push({\n body: 'session report',\n timestampMs: report.startedAt || report.timestamp || 0,\n attributes: {\n ...commonAttrs,\n 'session.options': report.options || {},\n 'session.report_timestamp': report.timestamp,\n agent_name: agentName,\n usage,\n },\n });\n\n // Track last timestamp to ensure monotonic ordering when items have identical timestamps\n // This fixes the issue where function_call and function_call_output with same timestamp\n // get reordered by the dashboard\n let lastTimestamp = 0;\n for (const item of report.chatHistory.items) {\n // Skip null/undefined items\n if (!item) continue;\n\n // Ensure monotonically increasing timestamps for proper ordering\n // Add 0.001ms (1 microsecond) offset when timestamps collide\n // Also handle undefined/NaN timestamps from realtime mode (defensive)\n const hasValidTimestamp = Number.isFinite(item.createdAt);\n let itemTimestamp = hasValidTimestamp ? item.createdAt : Date.now();\n\n if (itemTimestamp <= lastTimestamp) {\n itemTimestamp = lastTimestamp + 0.001; // Add 1 microsecond\n }\n lastTimestamp = itemTimestamp;\n\n const itemProto = chatItemToProto(item);\n let severityNumber = SeverityNumber.UNSPECIFIED;\n let severityText = 'unspecified';\n\n if (item.type === 'function_call_output' && item.isError) {\n severityNumber = SeverityNumber.ERROR;\n severityText = 'error';\n }\n\n logRecords.push({\n body: 'chat item',\n timestampMs: itemTimestamp, // Adjusted for monotonic ordering\n attributes: { 'chat.item': itemProto, ...commonAttrs },\n severityNumber,\n severityText,\n });\n }\n\n await logExporter.export(logRecords);\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 session upload');\n }\n\n const token = new AccessToken(apiKey, apiSecret, { ttl: '6h' });\n token.addObservabilityGrant({ write: true });\n const jwt = await token.toJwt();\n\n const formData = new FormData();\n\n // Add header (protobuf MetricsRecordingHeader)\n const audioStartTime = report.audioRecordingStartedAt ?? 0;\n const headerMsg = new MetricsRecordingHeader({\n roomId: report.roomId,\n duration: BigInt(0), // TODO: Calculate actual duration from report\n startTime: {\n seconds: BigInt(Math.floor(audioStartTime / 1000)),\n nanos: Math.floor((audioStartTime % 1000) * 1e6),\n },\n });\n\n const headerBytes = Buffer.from(headerMsg.toBinary());\n formData.append('header', headerBytes, {\n filename: 'header.binpb',\n contentType: 'application/protobuf',\n knownLength: headerBytes.length,\n header: {\n 'Content-Type': 'application/protobuf',\n 'Content-Length': headerBytes.length.toString(),\n },\n });\n\n // Add chat_history JSON\n const chatHistoryJson = JSON.stringify(report.chatHistory.toJSON({ excludeTimestamp: false }));\n const chatHistoryBuffer = Buffer.from(chatHistoryJson, 'utf-8');\n formData.append('chat_history', chatHistoryBuffer, {\n filename: 'chat_history.json',\n contentType: 'application/json',\n knownLength: chatHistoryBuffer.length,\n header: {\n 'Content-Type': 'application/json',\n 'Content-Length': chatHistoryBuffer.length.toString(),\n },\n });\n\n // Add audio recording file if available\n if (report.audioRecordingPath && report.audioRecordingStartedAt) {\n let audioBytes: Buffer;\n try {\n audioBytes = await fs.readFile(report.audioRecordingPath);\n } catch {\n audioBytes = Buffer.alloc(0);\n }\n\n if (audioBytes.length > 0) {\n formData.append('audio', audioBytes, {\n filename: 'recording.ogg',\n contentType: 'audio/ogg',\n knownLength: audioBytes.length,\n header: {\n 'Content-Type': 'audio/ogg',\n 'Content-Length': audioBytes.length.toString(),\n },\n });\n }\n }\n\n // Upload to LiveKit Cloud using form-data's submit method\n // This properly streams the multipart form with all headers including Content-Length\n return new Promise<void>((resolve, reject) => {\n formData.submit(\n {\n protocol: 'https:',\n host: cloudHostname,\n path: '/observability/recordings/v0',\n method: 'POST',\n headers: {\n Authorization: `Bearer ${jwt}`,\n },\n },\n (err, res) => {\n if (err) {\n reject(new Error(`Failed to upload session report: ${err.message}`));\n return;\n }\n\n if (res.statusCode && res.statusCode >= 400) {\n // Read response body for error details\n let body = '';\n res.on('data', (chunk) => {\n body += chunk.toString();\n });\n res.on('error', (readErr) => {\n reject(\n new Error(\n `Failed to upload session report: ${res.statusCode} ${res.statusMessage} (body read error: ${readErr.message})`,\n ),\n );\n });\n res.on('end', () => {\n reject(\n new Error(\n `Failed to upload session report: ${res.statusCode} ${res.statusMessage} - ${body}`,\n ),\n );\n });\n return;\n }\n\n res.resume(); // Drain the response\n res.on('error', (readErr) => reject(new Error(`Response read error: ${readErr.message}`)));\n res.on('end', () => resolve());\n },\n );\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/tts/tts.cjs
CHANGED
|
@@ -52,6 +52,28 @@ class TTS extends import_node_events.EventEmitter {
|
|
|
52
52
|
get numChannels() {
|
|
53
53
|
return this.#numChannels;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the model name/identifier for this TTS instance.
|
|
57
|
+
*
|
|
58
|
+
* @returns The model name if available, "unknown" otherwise.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* Plugins should override this property to provide their model information.
|
|
62
|
+
*/
|
|
63
|
+
get model() {
|
|
64
|
+
return "unknown";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the provider name for this TTS instance.
|
|
68
|
+
*
|
|
69
|
+
* @returns The provider name if available, "unknown" otherwise.
|
|
70
|
+
*
|
|
71
|
+
* @remarks
|
|
72
|
+
* Plugins should override this property to provide their provider information.
|
|
73
|
+
*/
|
|
74
|
+
get provider() {
|
|
75
|
+
return "unknown";
|
|
76
|
+
}
|
|
55
77
|
async close() {
|
|
56
78
|
return;
|
|
57
79
|
}
|
|
@@ -72,6 +94,8 @@ class SynthesizeStream {
|
|
|
72
94
|
#metricsText = "";
|
|
73
95
|
#monitorMetricsTask;
|
|
74
96
|
#ttsRequestSpan;
|
|
97
|
+
#inputTokens = 0;
|
|
98
|
+
#outputTokens = 0;
|
|
75
99
|
constructor(tts, connOptions = import_types.DEFAULT_API_CONNECT_OPTIONS) {
|
|
76
100
|
this.#tts = tts;
|
|
77
101
|
this.connOptions = connOptions;
|
|
@@ -176,6 +200,17 @@ class SynthesizeStream {
|
|
|
176
200
|
}
|
|
177
201
|
}
|
|
178
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Set token usage for token-based TTS billing (e.g., OpenAI TTS).
|
|
205
|
+
* Plugins should call this method to report token usage.
|
|
206
|
+
*/
|
|
207
|
+
setTokenUsage({
|
|
208
|
+
inputTokens = 0,
|
|
209
|
+
outputTokens = 0
|
|
210
|
+
} = {}) {
|
|
211
|
+
this.#inputTokens = inputTokens;
|
|
212
|
+
this.#outputTokens = outputTokens;
|
|
213
|
+
}
|
|
179
214
|
async monitorMetrics() {
|
|
180
215
|
const startTime = process.hrtime.bigint();
|
|
181
216
|
let audioDurationMs = 0;
|
|
@@ -196,12 +231,20 @@ class SynthesizeStream {
|
|
|
196
231
|
audioDurationMs: roundedAudioDurationMs,
|
|
197
232
|
cancelled: this.abortController.signal.aborted,
|
|
198
233
|
label: this.#tts.label,
|
|
199
|
-
|
|
234
|
+
inputTokens: this.#inputTokens,
|
|
235
|
+
outputTokens: this.#outputTokens,
|
|
236
|
+
streamed: true,
|
|
237
|
+
metadata: {
|
|
238
|
+
modelProvider: this.#tts.provider,
|
|
239
|
+
modelName: this.#tts.model
|
|
240
|
+
}
|
|
200
241
|
};
|
|
201
242
|
if (this.#ttsRequestSpan) {
|
|
202
243
|
this.#ttsRequestSpan.setAttribute(import_telemetry.traceTypes.ATTR_TTS_METRICS, JSON.stringify(metrics));
|
|
203
244
|
}
|
|
204
245
|
this.#tts.emit("metrics_collected", metrics);
|
|
246
|
+
this.#inputTokens = 0;
|
|
247
|
+
this.#outputTokens = 0;
|
|
205
248
|
}
|
|
206
249
|
};
|
|
207
250
|
for await (const audio of this.queue) {
|
|
@@ -285,6 +328,8 @@ class ChunkedStream {
|
|
|
285
328
|
#ttsRequestSpan;
|
|
286
329
|
_connOptions;
|
|
287
330
|
logger = (0, import_log.log)();
|
|
331
|
+
#inputTokens = 0;
|
|
332
|
+
#outputTokens = 0;
|
|
288
333
|
abortController = new AbortController();
|
|
289
334
|
constructor(text, tts, connOptions = import_types.DEFAULT_API_CONNECT_OPTIONS, abortSignal) {
|
|
290
335
|
this.#text = text;
|
|
@@ -365,6 +410,17 @@ class ChunkedStream {
|
|
|
365
410
|
get abortSignal() {
|
|
366
411
|
return this.abortController.signal;
|
|
367
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* Set token usage for token-based TTS billing (e.g., OpenAI TTS).
|
|
415
|
+
* Plugins should call this method to report token usage.
|
|
416
|
+
*/
|
|
417
|
+
setTokenUsage({
|
|
418
|
+
inputTokens = 0,
|
|
419
|
+
outputTokens = 0
|
|
420
|
+
} = {}) {
|
|
421
|
+
this.#inputTokens = inputTokens;
|
|
422
|
+
this.#outputTokens = outputTokens;
|
|
423
|
+
}
|
|
368
424
|
async monitorMetrics() {
|
|
369
425
|
const startTime = process.hrtime.bigint();
|
|
370
426
|
let audioDurationMs = 0;
|
|
@@ -391,7 +447,13 @@ class ChunkedStream {
|
|
|
391
447
|
cancelled: false,
|
|
392
448
|
// TODO(AJS-186): support ChunkedStream with 1.0 - add this.abortController.signal.aborted here
|
|
393
449
|
label: this.#tts.label,
|
|
394
|
-
|
|
450
|
+
inputTokens: this.#inputTokens,
|
|
451
|
+
outputTokens: this.#outputTokens,
|
|
452
|
+
streamed: false,
|
|
453
|
+
metadata: {
|
|
454
|
+
modelProvider: this.#tts.provider,
|
|
455
|
+
modelName: this.#tts.model
|
|
456
|
+
}
|
|
395
457
|
};
|
|
396
458
|
if (this.#ttsRequestSpan) {
|
|
397
459
|
this.#ttsRequestSpan.setAttribute(import_telemetry.traceTypes.ATTR_TTS_METRICS, JSON.stringify(metrics));
|
package/dist/tts/tts.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tts/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport type { Span } from '@opentelemetry/api';\nimport { EventEmitter } from 'node:events';\nimport type { ReadableStream } from 'node:stream/web';\nimport { APIConnectionError, APIError } from '../_exceptions.js';\nimport { log } from '../log.js';\nimport type { TTSMetrics } from '../metrics/base.js';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { recordException, traceTypes, tracer } from '../telemetry/index.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS, intervalForRetry } from '../types.js';\nimport { AsyncIterableQueue, delay, mergeFrames, startSoon, toError } from '../utils.js';\nimport type { TimedString } from '../voice/io.js';\n\n/**\n * SynthesizedAudio is a packet of speech synthesis as returned by the TTS.\n */\nexport interface SynthesizedAudio {\n /** Request ID (one segment could be made up of multiple requests) */\n requestId: string;\n /** Segment ID, each segment is separated by a flush */\n segmentId: string;\n /** Synthesized audio frame */\n frame: AudioFrame;\n /** Current segment of the synthesized audio */\n deltaText?: string;\n /** Whether this is the last frame of the segment (streaming only) */\n final: boolean;\n /**\n * Timed transcripts associated with this audio packet (word-level timestamps).\n */\n timedTranscripts?: TimedString[];\n}\n\n/**\n * Describes the capabilities of the TTS provider.\n *\n * @remarks\n * At present, only `streaming` is supplied to this interface, and the framework only supports\n * providers that do have a streaming endpoint.\n */\nexport interface TTSCapabilities {\n streaming: boolean;\n // Whether this TTS supports aligned transcripts (word-level timestamps).\n alignedTranscript?: boolean;\n}\n\nexport interface TTSError {\n type: 'tts_error';\n timestamp: number;\n label: string;\n error: Error;\n recoverable: boolean;\n}\n\nexport type TTSCallbacks = {\n ['metrics_collected']: (metrics: TTSMetrics) => void;\n ['error']: (error: TTSError) => void;\n};\n\n/**\n * An instance of a text-to-speech adapter.\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child TTS class, which inherits this class's methods.\n */\nexport abstract class TTS extends (EventEmitter as new () => TypedEmitter<TTSCallbacks>) {\n #capabilities: TTSCapabilities;\n #sampleRate: number;\n #numChannels: number;\n abstract label: string;\n\n constructor(sampleRate: number, numChannels: number, capabilities: TTSCapabilities) {\n super();\n this.#capabilities = capabilities;\n this.#sampleRate = sampleRate;\n this.#numChannels = numChannels;\n }\n\n /** Returns this TTS's capabilities */\n get capabilities(): TTSCapabilities {\n return this.#capabilities;\n }\n\n /** Returns the sample rate of audio frames returned by this TTS */\n get sampleRate(): number {\n return this.#sampleRate;\n }\n\n /** Returns the channel count of audio frames returned by this TTS */\n get numChannels(): number {\n return this.#numChannels;\n }\n\n /**\n * Receives text and returns synthesis in the form of a {@link ChunkedStream}\n */\n abstract synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): ChunkedStream;\n\n /**\n * Returns a {@link SynthesizeStream} that can be used to push text and receive audio data\n *\n * @param options - Optional configuration including connection options\n */\n abstract stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream;\n\n async close(): Promise<void> {\n return;\n }\n}\n\n/**\n * An instance of a text-to-speech stream, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child SynthesizeStream class, which inherits this class's methods.\n */\nexport abstract class SynthesizeStream\n implements AsyncIterableIterator<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>\n{\n protected static readonly FLUSH_SENTINEL = Symbol('FLUSH_SENTINEL');\n static readonly END_OF_STREAM = Symbol('END_OF_STREAM');\n protected input = new AsyncIterableQueue<string | typeof SynthesizeStream.FLUSH_SENTINEL>();\n protected queue = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected output = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected closed = false;\n protected connOptions: APIConnectOptions;\n protected abortController = new AbortController();\n\n private deferredInputStream: DeferredReadableStream<\n string | typeof SynthesizeStream.FLUSH_SENTINEL\n >;\n private logger = log();\n\n abstract label: string;\n\n #tts: TTS;\n #metricsPendingTexts: string[] = [];\n #metricsText = '';\n #monitorMetricsTask?: Promise<void>;\n #ttsRequestSpan?: Span;\n\n constructor(tts: TTS, connOptions: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS) {\n this.#tts = tts;\n this.connOptions = connOptions;\n this.deferredInputStream = new DeferredReadableStream();\n this.pumpInput();\n\n this.abortController.signal.addEventListener('abort', () => {\n this.deferredInputStream.detachSource();\n // TODO (AJS-36) clean this up when we refactor with streams\n if (!this.input.closed) this.input.close();\n if (!this.output.closed) this.output.close();\n this.closed = true;\n });\n\n // this is a hack to immitate asyncio.create_task so that mainTask\n // is run **after** the constructor has finished. Otherwise we get\n // runtime error when trying to access class variables in the\n // `run` method.\n startSoon(() => this.mainTask().finally(() => this.queue.close()));\n }\n\n private _mainTaskImpl = async (span: Span) => {\n this.#ttsRequestSpan = span;\n span.setAttributes({\n [traceTypes.ATTR_TTS_STREAMING]: true,\n [traceTypes.ATTR_TTS_LABEL]: this.#tts.label,\n });\n\n for (let i = 0; i < this.connOptions.maxRetry + 1; i++) {\n try {\n return await tracer.startActiveSpan(\n async (attemptSpan) => {\n attemptSpan.setAttribute(traceTypes.ATTR_RETRY_COUNT, i);\n try {\n return await this.run();\n } catch (error) {\n recordException(attemptSpan, toError(error));\n throw error;\n }\n },\n { name: 'tts_request_run' },\n );\n } catch (error) {\n if (error instanceof APIError) {\n const retryInterval = intervalForRetry(this.connOptions, i);\n\n if (this.connOptions.maxRetry === 0 || !error.retryable) {\n this.emitError({ error, recoverable: false });\n throw error;\n } else if (i === this.connOptions.maxRetry) {\n this.emitError({ error, recoverable: false });\n throw new APIConnectionError({\n message: `failed to generate TTS completion after ${this.connOptions.maxRetry + 1} attempts`,\n options: { retryable: false },\n });\n } else {\n // Don't emit error event for recoverable errors during retry loop\n // to avoid ERR_UNHANDLED_ERROR or premature session termination\n this.logger.warn(\n { tts: this.#tts.label, attempt: i + 1, error },\n `failed to synthesize speech, retrying in ${retryInterval}ms`,\n );\n }\n\n if (retryInterval > 0) {\n await delay(retryInterval);\n }\n } else {\n this.emitError({ error: toError(error), recoverable: false });\n throw error;\n }\n }\n }\n };\n\n private mainTask = async () =>\n tracer.startActiveSpan(async (span) => this._mainTaskImpl(span), {\n name: 'tts_request',\n endOnExit: false,\n });\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }) {\n this.#tts.emit('error', {\n type: 'tts_error',\n timestamp: Date.now(),\n label: this.#tts.label,\n error,\n recoverable,\n });\n }\n\n // NOTE(AJS-37): The implementation below uses an AsyncIterableQueue (`this.input`)\n // bridged from a DeferredReadableStream (`this.deferredInputStream`) rather than\n // consuming the stream directly.\n //\n // A full refactor to native Web Streams was considered but is currently deferred.\n // The primary reason is to maintain architectural parity with the Python SDK,\n // which is a key design goal for the project. This ensures a consistent developer\n // experience across both platforms.\n //\n // For more context, see the discussion in GitHub issue # 844.\n protected async pumpInput() {\n const reader = this.deferredInputStream.stream.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done || value === SynthesizeStream.FLUSH_SENTINEL) {\n break;\n }\n this.pushText(value);\n }\n this.endInput();\n } catch (error) {\n this.logger.error(error, 'Error reading deferred input stream');\n } finally {\n reader.releaseLock();\n // Ensure output is closed when the stream ends\n if (!this.#monitorMetricsTask) {\n // No text was received, close the output directly\n this.output.close();\n }\n }\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDurationMs = 0;\n let ttfb: bigint = BigInt(-1);\n let requestId = '';\n\n const emit = () => {\n if (this.#metricsPendingTexts.length) {\n const text = this.#metricsPendingTexts.shift()!;\n const duration = process.hrtime.bigint() - startTime;\n const roundedAudioDurationMs = Math.round(audioDurationMs);\n const metrics: TTSMetrics = {\n type: 'tts_metrics',\n timestamp: Date.now(),\n requestId,\n ttfbMs: ttfb === BigInt(-1) ? -1 : Math.trunc(Number(ttfb / BigInt(1000000))),\n durationMs: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: text.length,\n audioDurationMs: roundedAudioDurationMs,\n cancelled: this.abortController.signal.aborted,\n label: this.#tts.label,\n streamed: false,\n };\n if (this.#ttsRequestSpan) {\n this.#ttsRequestSpan.setAttribute(traceTypes.ATTR_TTS_METRICS, JSON.stringify(metrics));\n }\n this.#tts.emit('metrics_collected', metrics);\n }\n };\n\n for await (const audio of this.queue) {\n if (this.abortController.signal.aborted) {\n break;\n }\n this.output.put(audio);\n if (audio === SynthesizeStream.END_OF_STREAM) continue;\n requestId = audio.requestId;\n if (ttfb === BigInt(-1)) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n audioDurationMs += (audio.frame.samplesPerChannel / audio.frame.sampleRate) * 1000;\n if (audio.final) {\n emit();\n }\n }\n\n if (requestId) {\n emit();\n }\n\n if (this.#ttsRequestSpan) {\n this.#ttsRequestSpan.end();\n this.#ttsRequestSpan = undefined;\n }\n }\n\n protected abstract run(): Promise<void>;\n\n updateInputStream(text: ReadableStream<string>) {\n this.deferredInputStream.setSource(text);\n }\n\n /** Push a string of text to the TTS */\n /** @deprecated Use `updateInputStream` instead */\n pushText(text: string) {\n if (!this.#monitorMetricsTask) {\n this.#monitorMetricsTask = this.monitorMetrics();\n // Close output when metrics task completes\n this.#monitorMetricsTask.finally(() => this.output.close());\n }\n this.#metricsText += text;\n\n if (this.input.closed || this.closed) {\n // Stream was aborted/closed, silently skip\n return;\n }\n\n this.input.put(text);\n }\n\n /** Flush the TTS, causing it to process all pending text */\n flush() {\n if (this.#metricsText) {\n this.#metricsPendingTexts.push(this.#metricsText);\n this.#metricsText = '';\n }\n\n if (this.input.closed || this.closed) {\n // Stream was aborted/closed, silently skip\n return;\n }\n\n this.input.put(SynthesizeStream.FLUSH_SENTINEL);\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n this.flush();\n\n if (this.input.closed || this.closed) {\n // Stream was aborted/closed, silently skip\n return;\n }\n\n this.input.close();\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>> {\n return this.output.next();\n }\n\n get abortSignal(): AbortSignal {\n return this.abortController.signal;\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n this.abortController.abort();\n }\n\n [Symbol.asyncIterator](): SynthesizeStream {\n return this;\n }\n}\n\n/**\n * An instance of a text-to-speech response, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child ChunkedStream class, which inherits this class's methods.\n */\nexport abstract class ChunkedStream implements AsyncIterableIterator<SynthesizedAudio> {\n protected queue = new AsyncIterableQueue<SynthesizedAudio>();\n protected output = new AsyncIterableQueue<SynthesizedAudio>();\n protected closed = false;\n abstract label: string;\n #text: string;\n #tts: TTS;\n #ttsRequestSpan?: Span;\n private _connOptions: APIConnectOptions;\n private logger = log();\n\n protected abortController = new AbortController();\n\n constructor(\n text: string,\n tts: TTS,\n connOptions: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS,\n abortSignal?: AbortSignal,\n ) {\n this.#text = text;\n this.#tts = tts;\n this._connOptions = connOptions;\n\n if (abortSignal) {\n abortSignal.addEventListener('abort', () => this.abortController.abort(), { once: true });\n }\n\n this.monitorMetrics();\n\n // this is a hack to immitate asyncio.create_task so that mainTask\n // is run **after** the constructor has finished. Otherwise we get\n // runtime error when trying to access class variables in the\n // `run` method.\n Promise.resolve().then(() => this.mainTask().finally(() => this.queue.close()));\n }\n\n private _mainTaskImpl = async (span: Span) => {\n this.#ttsRequestSpan = span;\n span.setAttributes({\n [traceTypes.ATTR_TTS_STREAMING]: false,\n [traceTypes.ATTR_TTS_LABEL]: this.#tts.label,\n });\n\n for (let i = 0; i < this._connOptions.maxRetry + 1; i++) {\n try {\n return await tracer.startActiveSpan(\n async (attemptSpan) => {\n attemptSpan.setAttribute(traceTypes.ATTR_RETRY_COUNT, i);\n try {\n return await this.run();\n } catch (error) {\n recordException(attemptSpan, toError(error));\n throw error;\n }\n },\n { name: 'tts_request_run' },\n );\n } catch (error) {\n if (error instanceof APIError) {\n const retryInterval = intervalForRetry(this._connOptions, i);\n\n if (this._connOptions.maxRetry === 0 || !error.retryable) {\n this.emitError({ error, recoverable: false });\n throw error;\n } else if (i === this._connOptions.maxRetry) {\n this.emitError({ error, recoverable: false });\n throw new APIConnectionError({\n message: `failed to generate TTS completion after ${this._connOptions.maxRetry + 1} attempts`,\n options: { retryable: false },\n });\n } else {\n // Don't emit error event for recoverable errors during retry loop\n // to avoid ERR_UNHANDLED_ERROR or premature session termination\n this.logger.warn(\n { tts: this.#tts.label, attempt: i + 1, error },\n `failed to generate TTS completion, retrying in ${retryInterval}ms`,\n );\n }\n\n if (retryInterval > 0) {\n await delay(retryInterval);\n }\n } else {\n this.emitError({ error: toError(error), recoverable: false });\n throw error;\n }\n }\n }\n };\n\n private async mainTask() {\n return tracer.startActiveSpan(async (span) => this._mainTaskImpl(span), {\n name: 'tts_request',\n endOnExit: false,\n });\n }\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }) {\n this.#tts.emit('error', {\n type: 'tts_error',\n timestamp: Date.now(),\n label: this.#tts.label,\n error,\n recoverable,\n });\n }\n\n protected abstract run(): Promise<void>;\n\n get inputText(): string {\n return this.#text;\n }\n\n get abortSignal(): AbortSignal {\n return this.abortController.signal;\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDurationMs = 0;\n let ttfb: bigint = BigInt(-1);\n let requestId = '';\n\n for await (const audio of this.queue) {\n this.output.put(audio);\n requestId = audio.requestId;\n if (ttfb === BigInt(-1)) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n audioDurationMs += (audio.frame.samplesPerChannel / audio.frame.sampleRate) * 1000;\n }\n this.output.close();\n\n const duration = process.hrtime.bigint() - startTime;\n const metrics: TTSMetrics = {\n type: 'tts_metrics',\n timestamp: Date.now(),\n requestId,\n ttfbMs: ttfb === BigInt(-1) ? -1 : Math.trunc(Number(ttfb / BigInt(1000000))),\n durationMs: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: this.#text.length,\n audioDurationMs: Math.round(audioDurationMs),\n cancelled: false, // TODO(AJS-186): support ChunkedStream with 1.0 - add this.abortController.signal.aborted here\n label: this.#tts.label,\n streamed: false,\n };\n\n if (this.#ttsRequestSpan) {\n this.#ttsRequestSpan.setAttribute(traceTypes.ATTR_TTS_METRICS, JSON.stringify(metrics));\n this.#ttsRequestSpan.end();\n this.#ttsRequestSpan = undefined;\n }\n\n this.#tts.emit('metrics_collected', metrics);\n }\n\n /** Collect every frame into one in a single call */\n async collect(): Promise<AudioFrame> {\n const frames = [];\n for await (const event of this) {\n frames.push(event.frame);\n }\n return mergeFrames(frames);\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio>> {\n return this.output.next();\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n if (!this.queue.closed) this.queue.close();\n if (!this.output.closed) this.output.close();\n if (!this.abortController.signal.aborted) this.abortController.abort();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): ChunkedStream {\n return this;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/tts/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport type { Span } from '@opentelemetry/api';\nimport { EventEmitter } from 'node:events';\nimport type { ReadableStream } from 'node:stream/web';\nimport { APIConnectionError, APIError } from '../_exceptions.js';\nimport { log } from '../log.js';\nimport type { TTSMetrics } from '../metrics/base.js';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { recordException, traceTypes, tracer } from '../telemetry/index.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS, intervalForRetry } from '../types.js';\nimport { AsyncIterableQueue, delay, mergeFrames, startSoon, toError } from '../utils.js';\nimport type { TimedString } from '../voice/io.js';\n\n/**\n * SynthesizedAudio is a packet of speech synthesis as returned by the TTS.\n */\nexport interface SynthesizedAudio {\n /** Request ID (one segment could be made up of multiple requests) */\n requestId: string;\n /** Segment ID, each segment is separated by a flush */\n segmentId: string;\n /** Synthesized audio frame */\n frame: AudioFrame;\n /** Current segment of the synthesized audio */\n deltaText?: string;\n /** Whether this is the last frame of the segment (streaming only) */\n final: boolean;\n /**\n * Timed transcripts associated with this audio packet (word-level timestamps).\n */\n timedTranscripts?: TimedString[];\n}\n\n/**\n * Describes the capabilities of the TTS provider.\n *\n * @remarks\n * At present, only `streaming` is supplied to this interface, and the framework only supports\n * providers that do have a streaming endpoint.\n */\nexport interface TTSCapabilities {\n streaming: boolean;\n // Whether this TTS supports aligned transcripts (word-level timestamps).\n alignedTranscript?: boolean;\n}\n\nexport interface TTSError {\n type: 'tts_error';\n timestamp: number;\n label: string;\n error: Error;\n recoverable: boolean;\n}\n\nexport type TTSCallbacks = {\n ['metrics_collected']: (metrics: TTSMetrics) => void;\n ['error']: (error: TTSError) => void;\n};\n\n/**\n * An instance of a text-to-speech adapter.\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child TTS class, which inherits this class's methods.\n */\nexport abstract class TTS extends (EventEmitter as new () => TypedEmitter<TTSCallbacks>) {\n #capabilities: TTSCapabilities;\n #sampleRate: number;\n #numChannels: number;\n abstract label: string;\n\n constructor(sampleRate: number, numChannels: number, capabilities: TTSCapabilities) {\n super();\n this.#capabilities = capabilities;\n this.#sampleRate = sampleRate;\n this.#numChannels = numChannels;\n }\n\n /** Returns this TTS's capabilities */\n get capabilities(): TTSCapabilities {\n return this.#capabilities;\n }\n\n /** Returns the sample rate of audio frames returned by this TTS */\n get sampleRate(): number {\n return this.#sampleRate;\n }\n\n /** Returns the channel count of audio frames returned by this TTS */\n get numChannels(): number {\n return this.#numChannels;\n }\n\n /**\n * Get the model name/identifier for this TTS instance.\n *\n * @returns The model name if available, \"unknown\" otherwise.\n *\n * @remarks\n * Plugins should override this property to provide their model information.\n */\n get model(): string {\n return 'unknown';\n }\n\n /**\n * Get the provider name for this TTS instance.\n *\n * @returns The provider name if available, \"unknown\" otherwise.\n *\n * @remarks\n * Plugins should override this property to provide their provider information.\n */\n get provider(): string {\n return 'unknown';\n }\n\n /**\n * Receives text and returns synthesis in the form of a {@link ChunkedStream}\n */\n abstract synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): ChunkedStream;\n\n /**\n * Returns a {@link SynthesizeStream} that can be used to push text and receive audio data\n *\n * @param options - Optional configuration including connection options\n */\n abstract stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream;\n\n async close(): Promise<void> {\n return;\n }\n}\n\n/**\n * An instance of a text-to-speech stream, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child SynthesizeStream class, which inherits this class's methods.\n */\nexport abstract class SynthesizeStream\n implements AsyncIterableIterator<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>\n{\n protected static readonly FLUSH_SENTINEL = Symbol('FLUSH_SENTINEL');\n static readonly END_OF_STREAM = Symbol('END_OF_STREAM');\n protected input = new AsyncIterableQueue<string | typeof SynthesizeStream.FLUSH_SENTINEL>();\n protected queue = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected output = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected closed = false;\n protected connOptions: APIConnectOptions;\n protected abortController = new AbortController();\n\n private deferredInputStream: DeferredReadableStream<\n string | typeof SynthesizeStream.FLUSH_SENTINEL\n >;\n private logger = log();\n\n abstract label: string;\n\n #tts: TTS;\n #metricsPendingTexts: string[] = [];\n #metricsText = '';\n #monitorMetricsTask?: Promise<void>;\n #ttsRequestSpan?: Span;\n #inputTokens = 0;\n #outputTokens = 0;\n\n constructor(tts: TTS, connOptions: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS) {\n this.#tts = tts;\n this.connOptions = connOptions;\n this.deferredInputStream = new DeferredReadableStream();\n this.pumpInput();\n\n this.abortController.signal.addEventListener('abort', () => {\n this.deferredInputStream.detachSource();\n // TODO (AJS-36) clean this up when we refactor with streams\n if (!this.input.closed) this.input.close();\n if (!this.output.closed) this.output.close();\n this.closed = true;\n });\n\n // this is a hack to immitate asyncio.create_task so that mainTask\n // is run **after** the constructor has finished. Otherwise we get\n // runtime error when trying to access class variables in the\n // `run` method.\n startSoon(() => this.mainTask().finally(() => this.queue.close()));\n }\n\n private _mainTaskImpl = async (span: Span) => {\n this.#ttsRequestSpan = span;\n span.setAttributes({\n [traceTypes.ATTR_TTS_STREAMING]: true,\n [traceTypes.ATTR_TTS_LABEL]: this.#tts.label,\n });\n\n for (let i = 0; i < this.connOptions.maxRetry + 1; i++) {\n try {\n return await tracer.startActiveSpan(\n async (attemptSpan) => {\n attemptSpan.setAttribute(traceTypes.ATTR_RETRY_COUNT, i);\n try {\n return await this.run();\n } catch (error) {\n recordException(attemptSpan, toError(error));\n throw error;\n }\n },\n { name: 'tts_request_run' },\n );\n } catch (error) {\n if (error instanceof APIError) {\n const retryInterval = intervalForRetry(this.connOptions, i);\n\n if (this.connOptions.maxRetry === 0 || !error.retryable) {\n this.emitError({ error, recoverable: false });\n throw error;\n } else if (i === this.connOptions.maxRetry) {\n this.emitError({ error, recoverable: false });\n throw new APIConnectionError({\n message: `failed to generate TTS completion after ${this.connOptions.maxRetry + 1} attempts`,\n options: { retryable: false },\n });\n } else {\n // Don't emit error event for recoverable errors during retry loop\n // to avoid ERR_UNHANDLED_ERROR or premature session termination\n this.logger.warn(\n { tts: this.#tts.label, attempt: i + 1, error },\n `failed to synthesize speech, retrying in ${retryInterval}ms`,\n );\n }\n\n if (retryInterval > 0) {\n await delay(retryInterval);\n }\n } else {\n this.emitError({ error: toError(error), recoverable: false });\n throw error;\n }\n }\n }\n };\n\n private mainTask = async () =>\n tracer.startActiveSpan(async (span) => this._mainTaskImpl(span), {\n name: 'tts_request',\n endOnExit: false,\n });\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }) {\n this.#tts.emit('error', {\n type: 'tts_error',\n timestamp: Date.now(),\n label: this.#tts.label,\n error,\n recoverable,\n });\n }\n\n // NOTE(AJS-37): The implementation below uses an AsyncIterableQueue (`this.input`)\n // bridged from a DeferredReadableStream (`this.deferredInputStream`) rather than\n // consuming the stream directly.\n //\n // A full refactor to native Web Streams was considered but is currently deferred.\n // The primary reason is to maintain architectural parity with the Python SDK,\n // which is a key design goal for the project. This ensures a consistent developer\n // experience across both platforms.\n //\n // For more context, see the discussion in GitHub issue # 844.\n protected async pumpInput() {\n const reader = this.deferredInputStream.stream.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done || value === SynthesizeStream.FLUSH_SENTINEL) {\n break;\n }\n this.pushText(value);\n }\n this.endInput();\n } catch (error) {\n this.logger.error(error, 'Error reading deferred input stream');\n } finally {\n reader.releaseLock();\n // Ensure output is closed when the stream ends\n if (!this.#monitorMetricsTask) {\n // No text was received, close the output directly\n this.output.close();\n }\n }\n }\n\n /**\n * Set token usage for token-based TTS billing (e.g., OpenAI TTS).\n * Plugins should call this method to report token usage.\n */\n protected setTokenUsage({\n inputTokens = 0,\n outputTokens = 0,\n }: { inputTokens?: number; outputTokens?: number } = {}): void {\n this.#inputTokens = inputTokens;\n this.#outputTokens = outputTokens;\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDurationMs = 0;\n let ttfb: bigint = BigInt(-1);\n let requestId = '';\n\n const emit = () => {\n if (this.#metricsPendingTexts.length) {\n const text = this.#metricsPendingTexts.shift()!;\n const duration = process.hrtime.bigint() - startTime;\n const roundedAudioDurationMs = Math.round(audioDurationMs);\n const metrics: TTSMetrics = {\n type: 'tts_metrics',\n timestamp: Date.now(),\n requestId,\n ttfbMs: ttfb === BigInt(-1) ? -1 : Math.trunc(Number(ttfb / BigInt(1000000))),\n durationMs: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: text.length,\n audioDurationMs: roundedAudioDurationMs,\n cancelled: this.abortController.signal.aborted,\n label: this.#tts.label,\n inputTokens: this.#inputTokens,\n outputTokens: this.#outputTokens,\n streamed: true,\n metadata: {\n modelProvider: this.#tts.provider,\n modelName: this.#tts.model,\n },\n };\n if (this.#ttsRequestSpan) {\n this.#ttsRequestSpan.setAttribute(traceTypes.ATTR_TTS_METRICS, JSON.stringify(metrics));\n }\n this.#tts.emit('metrics_collected', metrics);\n\n // Reset token usage after emitting metrics for the next segment\n this.#inputTokens = 0;\n this.#outputTokens = 0;\n }\n };\n\n for await (const audio of this.queue) {\n if (this.abortController.signal.aborted) {\n break;\n }\n this.output.put(audio);\n if (audio === SynthesizeStream.END_OF_STREAM) continue;\n requestId = audio.requestId;\n if (ttfb === BigInt(-1)) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n audioDurationMs += (audio.frame.samplesPerChannel / audio.frame.sampleRate) * 1000;\n if (audio.final) {\n emit();\n }\n }\n\n if (requestId) {\n emit();\n }\n\n if (this.#ttsRequestSpan) {\n this.#ttsRequestSpan.end();\n this.#ttsRequestSpan = undefined;\n }\n }\n\n protected abstract run(): Promise<void>;\n\n updateInputStream(text: ReadableStream<string>) {\n this.deferredInputStream.setSource(text);\n }\n\n /** Push a string of text to the TTS */\n /** @deprecated Use `updateInputStream` instead */\n pushText(text: string) {\n if (!this.#monitorMetricsTask) {\n this.#monitorMetricsTask = this.monitorMetrics();\n // Close output when metrics task completes\n this.#monitorMetricsTask.finally(() => this.output.close());\n }\n this.#metricsText += text;\n\n if (this.input.closed || this.closed) {\n // Stream was aborted/closed, silently skip\n return;\n }\n\n this.input.put(text);\n }\n\n /** Flush the TTS, causing it to process all pending text */\n flush() {\n if (this.#metricsText) {\n this.#metricsPendingTexts.push(this.#metricsText);\n this.#metricsText = '';\n }\n\n if (this.input.closed || this.closed) {\n // Stream was aborted/closed, silently skip\n return;\n }\n\n this.input.put(SynthesizeStream.FLUSH_SENTINEL);\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n this.flush();\n\n if (this.input.closed || this.closed) {\n // Stream was aborted/closed, silently skip\n return;\n }\n\n this.input.close();\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>> {\n return this.output.next();\n }\n\n get abortSignal(): AbortSignal {\n return this.abortController.signal;\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n this.abortController.abort();\n }\n\n [Symbol.asyncIterator](): SynthesizeStream {\n return this;\n }\n}\n\n/**\n * An instance of a text-to-speech response, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child ChunkedStream class, which inherits this class's methods.\n */\nexport abstract class ChunkedStream implements AsyncIterableIterator<SynthesizedAudio> {\n protected queue = new AsyncIterableQueue<SynthesizedAudio>();\n protected output = new AsyncIterableQueue<SynthesizedAudio>();\n protected closed = false;\n abstract label: string;\n #text: string;\n #tts: TTS;\n #ttsRequestSpan?: Span;\n private _connOptions: APIConnectOptions;\n private logger = log();\n #inputTokens = 0;\n #outputTokens = 0;\n\n protected abortController = new AbortController();\n\n constructor(\n text: string,\n tts: TTS,\n connOptions: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS,\n abortSignal?: AbortSignal,\n ) {\n this.#text = text;\n this.#tts = tts;\n this._connOptions = connOptions;\n\n if (abortSignal) {\n abortSignal.addEventListener('abort', () => this.abortController.abort(), { once: true });\n }\n\n this.monitorMetrics();\n\n // this is a hack to immitate asyncio.create_task so that mainTask\n // is run **after** the constructor has finished. Otherwise we get\n // runtime error when trying to access class variables in the\n // `run` method.\n Promise.resolve().then(() => this.mainTask().finally(() => this.queue.close()));\n }\n\n private _mainTaskImpl = async (span: Span) => {\n this.#ttsRequestSpan = span;\n span.setAttributes({\n [traceTypes.ATTR_TTS_STREAMING]: false,\n [traceTypes.ATTR_TTS_LABEL]: this.#tts.label,\n });\n\n for (let i = 0; i < this._connOptions.maxRetry + 1; i++) {\n try {\n return await tracer.startActiveSpan(\n async (attemptSpan) => {\n attemptSpan.setAttribute(traceTypes.ATTR_RETRY_COUNT, i);\n try {\n return await this.run();\n } catch (error) {\n recordException(attemptSpan, toError(error));\n throw error;\n }\n },\n { name: 'tts_request_run' },\n );\n } catch (error) {\n if (error instanceof APIError) {\n const retryInterval = intervalForRetry(this._connOptions, i);\n\n if (this._connOptions.maxRetry === 0 || !error.retryable) {\n this.emitError({ error, recoverable: false });\n throw error;\n } else if (i === this._connOptions.maxRetry) {\n this.emitError({ error, recoverable: false });\n throw new APIConnectionError({\n message: `failed to generate TTS completion after ${this._connOptions.maxRetry + 1} attempts`,\n options: { retryable: false },\n });\n } else {\n // Don't emit error event for recoverable errors during retry loop\n // to avoid ERR_UNHANDLED_ERROR or premature session termination\n this.logger.warn(\n { tts: this.#tts.label, attempt: i + 1, error },\n `failed to generate TTS completion, retrying in ${retryInterval}ms`,\n );\n }\n\n if (retryInterval > 0) {\n await delay(retryInterval);\n }\n } else {\n this.emitError({ error: toError(error), recoverable: false });\n throw error;\n }\n }\n }\n };\n\n private async mainTask() {\n return tracer.startActiveSpan(async (span) => this._mainTaskImpl(span), {\n name: 'tts_request',\n endOnExit: false,\n });\n }\n\n private emitError({ error, recoverable }: { error: Error; recoverable: boolean }) {\n this.#tts.emit('error', {\n type: 'tts_error',\n timestamp: Date.now(),\n label: this.#tts.label,\n error,\n recoverable,\n });\n }\n\n protected abstract run(): Promise<void>;\n\n get inputText(): string {\n return this.#text;\n }\n\n get abortSignal(): AbortSignal {\n return this.abortController.signal;\n }\n\n /**\n * Set token usage for token-based TTS billing (e.g., OpenAI TTS).\n * Plugins should call this method to report token usage.\n */\n protected setTokenUsage({\n inputTokens = 0,\n outputTokens = 0,\n }: { inputTokens?: number; outputTokens?: number } = {}): void {\n this.#inputTokens = inputTokens;\n this.#outputTokens = outputTokens;\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDurationMs = 0;\n let ttfb: bigint = BigInt(-1);\n let requestId = '';\n\n for await (const audio of this.queue) {\n this.output.put(audio);\n requestId = audio.requestId;\n if (ttfb === BigInt(-1)) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n audioDurationMs += (audio.frame.samplesPerChannel / audio.frame.sampleRate) * 1000;\n }\n this.output.close();\n\n const duration = process.hrtime.bigint() - startTime;\n const metrics: TTSMetrics = {\n type: 'tts_metrics',\n timestamp: Date.now(),\n requestId,\n ttfbMs: ttfb === BigInt(-1) ? -1 : Math.trunc(Number(ttfb / BigInt(1000000))),\n durationMs: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: this.#text.length,\n audioDurationMs: Math.round(audioDurationMs),\n cancelled: false, // TODO(AJS-186): support ChunkedStream with 1.0 - add this.abortController.signal.aborted here\n label: this.#tts.label,\n inputTokens: this.#inputTokens,\n outputTokens: this.#outputTokens,\n streamed: false,\n metadata: {\n modelProvider: this.#tts.provider,\n modelName: this.#tts.model,\n },\n };\n\n if (this.#ttsRequestSpan) {\n this.#ttsRequestSpan.setAttribute(traceTypes.ATTR_TTS_METRICS, JSON.stringify(metrics));\n this.#ttsRequestSpan.end();\n this.#ttsRequestSpan = undefined;\n }\n\n this.#tts.emit('metrics_collected', metrics);\n }\n\n /** Collect every frame into one in a single call */\n async collect(): Promise<AudioFrame> {\n const frames = [];\n for await (const event of this) {\n frames.push(event.frame);\n }\n return mergeFrames(frames);\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio>> {\n return this.output.next();\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n if (!this.queue.closed) this.queue.close();\n if (!this.output.closed) this.output.close();\n if (!this.abortController.signal.aborted) this.abortController.abort();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): ChunkedStream {\n return this;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/tts/tts.d.cts
CHANGED
|
@@ -65,6 +65,24 @@ export declare abstract class TTS extends TTS_base {
|
|
|
65
65
|
get sampleRate(): number;
|
|
66
66
|
/** Returns the channel count of audio frames returned by this TTS */
|
|
67
67
|
get numChannels(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Get the model name/identifier for this TTS instance.
|
|
70
|
+
*
|
|
71
|
+
* @returns The model name if available, "unknown" otherwise.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* Plugins should override this property to provide their model information.
|
|
75
|
+
*/
|
|
76
|
+
get model(): string;
|
|
77
|
+
/**
|
|
78
|
+
* Get the provider name for this TTS instance.
|
|
79
|
+
*
|
|
80
|
+
* @returns The provider name if available, "unknown" otherwise.
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* Plugins should override this property to provide their provider information.
|
|
84
|
+
*/
|
|
85
|
+
get provider(): string;
|
|
68
86
|
/**
|
|
69
87
|
* Receives text and returns synthesis in the form of a {@link ChunkedStream}
|
|
70
88
|
*/
|
|
@@ -111,6 +129,14 @@ export declare abstract class SynthesizeStream implements AsyncIterableIterator<
|
|
|
111
129
|
private mainTask;
|
|
112
130
|
private emitError;
|
|
113
131
|
protected pumpInput(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Set token usage for token-based TTS billing (e.g., OpenAI TTS).
|
|
134
|
+
* Plugins should call this method to report token usage.
|
|
135
|
+
*/
|
|
136
|
+
protected setTokenUsage({ inputTokens, outputTokens, }?: {
|
|
137
|
+
inputTokens?: number;
|
|
138
|
+
outputTokens?: number;
|
|
139
|
+
}): void;
|
|
114
140
|
protected monitorMetrics(): Promise<void>;
|
|
115
141
|
protected abstract run(): Promise<void>;
|
|
116
142
|
updateInputStream(text: ReadableStream<string>): void;
|
|
@@ -157,6 +183,14 @@ export declare abstract class ChunkedStream implements AsyncIterableIterator<Syn
|
|
|
157
183
|
protected abstract run(): Promise<void>;
|
|
158
184
|
get inputText(): string;
|
|
159
185
|
get abortSignal(): AbortSignal;
|
|
186
|
+
/**
|
|
187
|
+
* Set token usage for token-based TTS billing (e.g., OpenAI TTS).
|
|
188
|
+
* Plugins should call this method to report token usage.
|
|
189
|
+
*/
|
|
190
|
+
protected setTokenUsage({ inputTokens, outputTokens, }?: {
|
|
191
|
+
inputTokens?: number;
|
|
192
|
+
outputTokens?: number;
|
|
193
|
+
}): void;
|
|
160
194
|
protected monitorMetrics(): Promise<void>;
|
|
161
195
|
/** Collect every frame into one in a single call */
|
|
162
196
|
collect(): Promise<AudioFrame>;
|
package/dist/tts/tts.d.ts
CHANGED
|
@@ -65,6 +65,24 @@ export declare abstract class TTS extends TTS_base {
|
|
|
65
65
|
get sampleRate(): number;
|
|
66
66
|
/** Returns the channel count of audio frames returned by this TTS */
|
|
67
67
|
get numChannels(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Get the model name/identifier for this TTS instance.
|
|
70
|
+
*
|
|
71
|
+
* @returns The model name if available, "unknown" otherwise.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* Plugins should override this property to provide their model information.
|
|
75
|
+
*/
|
|
76
|
+
get model(): string;
|
|
77
|
+
/**
|
|
78
|
+
* Get the provider name for this TTS instance.
|
|
79
|
+
*
|
|
80
|
+
* @returns The provider name if available, "unknown" otherwise.
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* Plugins should override this property to provide their provider information.
|
|
84
|
+
*/
|
|
85
|
+
get provider(): string;
|
|
68
86
|
/**
|
|
69
87
|
* Receives text and returns synthesis in the form of a {@link ChunkedStream}
|
|
70
88
|
*/
|
|
@@ -111,6 +129,14 @@ export declare abstract class SynthesizeStream implements AsyncIterableIterator<
|
|
|
111
129
|
private mainTask;
|
|
112
130
|
private emitError;
|
|
113
131
|
protected pumpInput(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Set token usage for token-based TTS billing (e.g., OpenAI TTS).
|
|
134
|
+
* Plugins should call this method to report token usage.
|
|
135
|
+
*/
|
|
136
|
+
protected setTokenUsage({ inputTokens, outputTokens, }?: {
|
|
137
|
+
inputTokens?: number;
|
|
138
|
+
outputTokens?: number;
|
|
139
|
+
}): void;
|
|
114
140
|
protected monitorMetrics(): Promise<void>;
|
|
115
141
|
protected abstract run(): Promise<void>;
|
|
116
142
|
updateInputStream(text: ReadableStream<string>): void;
|
|
@@ -157,6 +183,14 @@ export declare abstract class ChunkedStream implements AsyncIterableIterator<Syn
|
|
|
157
183
|
protected abstract run(): Promise<void>;
|
|
158
184
|
get inputText(): string;
|
|
159
185
|
get abortSignal(): AbortSignal;
|
|
186
|
+
/**
|
|
187
|
+
* Set token usage for token-based TTS billing (e.g., OpenAI TTS).
|
|
188
|
+
* Plugins should call this method to report token usage.
|
|
189
|
+
*/
|
|
190
|
+
protected setTokenUsage({ inputTokens, outputTokens, }?: {
|
|
191
|
+
inputTokens?: number;
|
|
192
|
+
outputTokens?: number;
|
|
193
|
+
}): void;
|
|
160
194
|
protected monitorMetrics(): Promise<void>;
|
|
161
195
|
/** Collect every frame into one in a single call */
|
|
162
196
|
collect(): Promise<AudioFrame>;
|
package/dist/tts/tts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/tts/tts.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/tts/tts.ts"],"names":[],"mappings":"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"}
|