@dtelecom/agents-js 0.3.4 → 0.3.5

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.
@@ -1519,7 +1519,6 @@ var DtelecomTTS = class {
1519
1519
  if (this.flushState === state) {
1520
1520
  this.flushState = null;
1521
1521
  }
1522
- this._resolveWsDone?.();
1523
1522
  }
1524
1523
  }
1525
1524
  /** Ensure a WebSocket connection exists and is open. */
@@ -1593,12 +1592,14 @@ var DtelecomTTS = class {
1593
1592
  }
1594
1593
  this.ws = null;
1595
1594
  this.connectPromise = null;
1595
+ this._resolveWsDone?.();
1596
1596
  reject(error);
1597
1597
  });
1598
1598
  ws.on("close", (code, reason) => {
1599
1599
  log7.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);
1600
1600
  this.ws = null;
1601
1601
  this.connectPromise = null;
1602
+ this._resolveWsDone?.();
1602
1603
  const state = this.flushState;
1603
1604
  if (state) {
1604
1605
  state.done = true;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/index.ts","../../src/providers/deepgram-stt.ts","../../src/core/base-stt-stream.ts","../../src/utils/logger.ts","../../src/providers/openrouter-llm.ts","../../src/providers/openai-llm.ts","../../src/providers/cartesia-tts.ts","../../src/providers/deepgram-tts.ts","../../src/providers/dtelecom-stt.ts","../../src/providers/dtelecom-tts.ts"],"sourcesContent":["export { DeepgramSTT } from './deepgram-stt';\nexport type { DeepgramSTTOptions } from './deepgram-stt';\n\nexport { OpenRouterLLM } from './openrouter-llm';\nexport type { OpenRouterLLMOptions } from './openrouter-llm';\n\nexport { OpenAILLM } from './openai-llm';\nexport type { OpenAILLMOptions } from './openai-llm';\n\nexport { CartesiaTTS } from './cartesia-tts';\nexport type { CartesiaTTSOptions } from './cartesia-tts';\n\nexport { DeepgramTTS } from './deepgram-tts';\nexport type { DeepgramTTSOptions } from './deepgram-tts';\n\nexport { DtelecomSTT } from './dtelecom-stt';\nexport type { DtelecomSTTOptions } from './dtelecom-stt';\n\nexport { DtelecomTTS } from './dtelecom-tts';\nexport type { DtelecomTTSOptions, VoiceConfig as DtelecomVoiceConfig } from './dtelecom-tts';\n","/**\n * DeepgramSTT — real-time streaming STT via Deepgram WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/listen?... with config as query params\n * - Auth via Authorization header: \"Token <apiKey>\"\n * - Send audio as binary WebSocket frames (PCM16 16kHz mono)\n * - Receive JSON: { type: \"Results\", channel: { alternatives: [{ transcript }] }, is_final, speech_final }\n * - Send KeepAlive every 5s when no audio is being sent\n * - Send CloseStream to gracefully shut down\n *\n * End-of-utterance strategy:\n * Buffer all is_final=true transcripts. Emit the buffered utterance as a\n * single final TranscriptionResult when speech_final=true OR UtteranceEnd\n * arrives. Interim results (is_final=false) are emitted immediately for\n * real-time feedback.\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramSTT');\n\nconst DEEPGRAM_WS_URL = 'wss://api.deepgram.com/v1/listen';\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DeepgramSTTOptions {\n apiKey: string;\n /** Deepgram model (default: 'nova-3') */\n model?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Enable interim results (default: true) */\n interimResults?: boolean;\n /** Enable punctuation (default: true) */\n punctuate?: boolean;\n /** Endpointing in ms (default: 300). Set to false to disable. */\n endpointing?: number | false;\n /** Keywords to boost recognition (e.g. ['dTelecom:5', 'WebRTC:3']) */\n keywords?: string[];\n /** Enable smart formatting (default: false) */\n smartFormat?: boolean;\n /** Utterance end timeout in ms (default: 1000). Requires interimResults. */\n utteranceEndMs?: number;\n}\n\nexport class DeepgramSTT implements STTPlugin {\n private readonly options: Required<Pick<DeepgramSTTOptions, 'apiKey'>> & DeepgramSTTOptions;\n\n constructor(options: DeepgramSTTOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramSTT requires an apiKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'en';\n return new DeepgramSTTStream(this.options, language);\n }\n}\n\nclass DeepgramSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly apiKey: string;\n private readonly wsUrl: string;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private lastAudioSentAt = 0;\n /** Buffer of is_final=true transcripts for the current utterance */\n private utteranceBuffer: string[] = [];\n /** Timestamp of the last non-empty interim result (approximates end of speech) */\n private lastInterimAt = 0;\n\n constructor(options: DeepgramSTTOptions, language: string) {\n super();\n this.apiKey = options.apiKey;\n this.wsUrl = buildWsUrl(options, language);\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n this.lastAudioSentAt = performance.now();\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n // Graceful shutdown — ask server to flush remaining audio\n try {\n this.ws.send(JSON.stringify({ type: 'CloseStream' }));\n } catch {\n // Ignore send errors during shutdown\n }\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DeepgramSTT stream closed');\n }\n\n private connect(): void {\n log.debug(`Connecting to Deepgram: ${this.wsUrl.replace(/token=[^&]+/, 'token=***')}`);\n\n this.ws = new WebSocket(this.wsUrl, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n this.ws.on('open', () => {\n log.info('Deepgram WebSocket connected');\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Deepgram message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('Deepgram WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('Deepgram connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'Results') {\n this.handleResults(msg);\n } else if (type === 'UtteranceEnd') {\n this.flushUtterance();\n } else if (type === 'Metadata') {\n log.debug('Deepgram session metadata received');\n } else if (type === 'SpeechStarted') {\n log.debug('Speech started detected');\n }\n }\n\n private handleResults(msg: Record<string, unknown>): void {\n const channel = msg.channel as { alternatives?: Array<{ transcript?: string; confidence?: number }> } | undefined;\n const transcript = channel?.alternatives?.[0]?.transcript ?? '';\n const confidence = channel?.alternatives?.[0]?.confidence;\n const isFinal = msg.is_final as boolean ?? false;\n const speechFinal = msg.speech_final as boolean ?? false;\n\n if (!transcript) return;\n\n if (!isFinal) {\n // Interim result — emit immediately for real-time feedback.\n // Include any buffered finals as prefix so the UI shows the full utterance.\n this.lastInterimAt = performance.now();\n const fullInterim = this.utteranceBuffer.length > 0\n ? this.utteranceBuffer.join(' ') + ' ' + transcript\n : transcript;\n this.emit('transcription', {\n text: fullInterim,\n isFinal: false,\n confidence: confidence ?? undefined,\n } satisfies TranscriptionResult);\n return;\n }\n\n // is_final=true — buffer this segment\n this.utteranceBuffer.push(transcript);\n\n if (speechFinal) {\n // End of utterance — emit the complete buffered transcript\n this.flushUtterance();\n }\n }\n\n /** Emit the buffered utterance as a single final transcription result. */\n private flushUtterance(): void {\n if (this.utteranceBuffer.length === 0) return;\n\n const now = performance.now();\n const fullText = this.utteranceBuffer.join(' ');\n this.utteranceBuffer = [];\n\n // sttDuration = time from last interim (≈ end of speech) to now (final result)\n // This includes endpointing delay + STT processing + network\n const sttDuration = this.lastInterimAt > 0 ? now - this.lastInterimAt : undefined;\n\n if (sttDuration !== undefined) {\n log.info(`stt_final: ${sttDuration.toFixed(0)}ms \"${fullText.slice(0, 50)}\"`);\n }\n\n this.lastInterimAt = 0;\n\n this.emit('transcription', {\n text: fullText,\n isFinal: true,\n sttDuration,\n } satisfies TranscriptionResult);\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'KeepAlive' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n\n/** Build the Deepgram WebSocket URL with query parameters. */\nfunction buildWsUrl(options: DeepgramSTTOptions, language: string): string {\n const params = new URLSearchParams();\n\n params.set('model', options.model ?? 'nova-3');\n params.set('language', language);\n params.set('encoding', 'linear16');\n params.set('sample_rate', '16000');\n params.set('channels', '1');\n params.set('interim_results', String(options.interimResults ?? true));\n params.set('punctuate', String(options.punctuate ?? true));\n\n if (options.endpointing === false) {\n params.set('endpointing', 'false');\n } else {\n params.set('endpointing', String(options.endpointing ?? 300));\n }\n\n if (options.smartFormat) {\n params.set('smart_format', 'true');\n }\n\n if (options.utteranceEndMs !== undefined) {\n params.set('utterance_end_ms', String(options.utteranceEndMs));\n } else if (options.interimResults !== false) {\n // Default utterance_end_ms when interim results are enabled\n params.set('utterance_end_ms', '1000');\n }\n\n if (options.keywords?.length) {\n for (const kw of options.keywords) {\n params.append('keywords', kw);\n }\n }\n\n return `${DEEPGRAM_WS_URL}?${params.toString()}`;\n}\n","import { EventEmitter } from 'events';\nimport type { STTStream, TranscriptionResult } from './types';\n\n/**\n * Abstract base class for STT streams.\n * Provides typed EventEmitter interface for transcription events.\n * Provider implementations should extend this class.\n */\nexport abstract class BaseSTTStream extends EventEmitter implements STTStream {\n abstract sendAudio(pcm16: Buffer): void;\n abstract close(): Promise<void>;\n\n override on(event: 'transcription', cb: (result: TranscriptionResult) => void): this;\n override on(event: 'error', cb: (error: Error) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n override on(event: string, cb: (...args: any[]) => void): this {\n return super.on(event, cb);\n }\n\n override emit(event: 'transcription', result: TranscriptionResult): boolean;\n override emit(event: 'error', error: Error): boolean;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n override emit(event: string | symbol, ...args: any[]): boolean {\n return super.emit(event, ...args);\n }\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\nconst LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n\n/** Default to 'debug' if DEBUG env var matches our namespace */\nfunction detectLevel(): LogLevel {\n const debug = typeof process !== 'undefined' && process.env?.DEBUG;\n if (debug && (debug === '*' || debug.includes('@dtelecom/agents'))) {\n return 'debug';\n }\n return 'info';\n}\n\nlet globalLevel: LogLevel = detectLevel();\n\nexport function setLogLevel(level: LogLevel): void {\n globalLevel = level;\n}\n\nexport function getLogLevel(): LogLevel {\n return globalLevel;\n}\n\nexport interface Logger {\n debug(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nfunction timestamp(): string {\n const d = new Date();\n const h = String(d.getHours()).padStart(2, '0');\n const m = String(d.getMinutes()).padStart(2, '0');\n const s = String(d.getSeconds()).padStart(2, '0');\n const ms = String(d.getMilliseconds()).padStart(3, '0');\n return `${h}:${m}:${s}.${ms}`;\n}\n\nexport function createLogger(tag: string): Logger {\n const prefix = `[@dtelecom/agents:${tag}]`;\n return {\n debug(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.debug) console.debug(timestamp(), prefix, ...args);\n },\n info(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.info) console.info(timestamp(), prefix, ...args);\n },\n warn(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.warn) console.warn(timestamp(), prefix, ...args);\n },\n error(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.error) console.error(timestamp(), prefix, ...args);\n },\n };\n}\n","/**\n * OpenRouterLLM — streaming LLM via OpenRouter (OpenAI-compatible API).\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenRouterLLM');\n\nconst OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\nexport interface OpenRouterLLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'openai/gpt-4o', 'anthropic/claude-sonnet-4') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** OpenRouter provider routing preferences */\n providerRouting?: {\n /** Sort providers by metric (e.g. 'latency') */\n sort?: string;\n /** Pin to specific providers in order */\n order?: string[];\n /** Allow fallback to other providers if pinned ones fail */\n allowFallbacks?: boolean;\n };\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenRouterLLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly provider?: { sort?: string; order?: string[]; allow_fallbacks?: boolean };\n private readonly responseFormat?: OpenRouterLLMOptions['responseFormat'];\n\n constructor(options: OpenRouterLLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenRouterLLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n\n if (options.providerRouting) {\n this.provider = {\n sort: options.providerRouting.sort,\n order: options.providerRouting.order,\n allow_fallbacks: options.providerRouting.allowFallbacks,\n };\n }\n\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n };\n if (this.provider) {\n body.provider = { ...this.provider };\n }\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n // Ensure the provider enforces structured output parameters\n const prov = (body.provider ?? {}) as Record<string, unknown>;\n prov.require_parameters = true;\n body.provider = prov;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENROUTER_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenRouter API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenRouter response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n // Tracks brace depth to detect complete {...} objects inside the \"segments\" array,\n // then parses each with JSON.parse() — correct and streaming.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false; // true after we see \"segments\" : [\n let objectStart = -1; // index where current { started\n let braceDepth = 0; // nested brace depth inside current object\n let scanIndex = 0; // how far we've scanned in jsonBuffer\n let inString = false; // inside a JSON string literal\n let escaped = false; // previous char was backslash\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n // Detect start of segments array: look for [ after \"segments\"\n if (!inSegmentsArray) {\n if (ch === '[') {\n // Check if \"segments\" precedes this bracket\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n // Inside segments array — track objects\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n // Complete segment object\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n // End of segments array\n inSegmentsArray = false;\n }\n }\n\n // Always advance — state vars (braceDepth, inString, etc.) already\n // track parsing state, so rescanning would double-count characters.\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n // Check abort before blocking on read — prevents hanging when signal\n // was fired while we were yielding tokens to the pipeline\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) continue;\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n // Try to extract any newly completed segments\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n // Usage stats in the final chunk\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * OpenAILLM — streaming LLM via OpenAI API.\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenAILLM');\n\nconst OPENAI_URL = 'https://api.openai.com/v1/chat/completions';\n\nexport interface OpenAILLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'gpt-4o', 'gpt-4o-mini') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenAILLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly responseFormat?: OpenAILLMOptions['responseFormat'];\n\n constructor(options: OpenAILLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenAILLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n stream_options: { include_usage: true },\n };\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENAI_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenAI response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false;\n let objectStart = -1;\n let braceDepth = 0;\n let scanIndex = 0;\n let inString = false;\n let escaped = false;\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n if (!inSegmentsArray) {\n if (ch === '[') {\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n inSegmentsArray = false;\n }\n }\n\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) {\n // Final chunk may have usage without choices\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n continue;\n }\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * CartesiaTTS — real-time streaming TTS via Cartesia WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.cartesia.ai/tts/websocket?api_key=...&cartesia_version=...\n * - Send JSON: { model_id, transcript, voice: { mode: \"id\", id }, output_format, context_id }\n * - Receive JSON: { type: \"chunk\", data: \"<base64 PCM>\" } — audio data\n * - Receive JSON: { type: \"done\", context_id } — synthesis complete\n * - Audio is base64-encoded PCM16 LE at the requested sample rate\n *\n * Uses a persistent WebSocket connection to avoid per-sentence handshake overhead.\n * Each synthesize() call uses a unique context_id for multiplexing.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('CartesiaTTS');\n\nconst CARTESIA_WS_BASE = 'wss://api.cartesia.ai/tts/websocket';\nconst DEFAULT_API_VERSION = '2024-06-10';\nconst DEFAULT_MODEL = 'sonic-3';\n/** Pipeline operates at 48kHz — matches Opus/WebRTC native rate, no resampling */\nconst DEFAULT_SAMPLE_RATE = 48000;\n/** Reconnect after idle timeout (Cartesia closes after 5 min idle) */\nconst RECONNECT_DELAY_MS = 1000;\n\nexport interface CartesiaTTSOptions {\n apiKey: string;\n /** Cartesia voice ID */\n voiceId: string;\n /** Model ID (default: 'sonic-3') */\n modelId?: string;\n /** Output sample rate in Hz (default: 16000) */\n sampleRate?: number;\n /** API version (default: '2024-06-10') */\n apiVersion?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Speech speed multiplier, 0.6-1.5 (default: 1.0). Sonic-3 only. */\n speed?: number;\n /** Emotion string (e.g. 'friendly', 'calm'). Sonic-3 only. */\n emotion?: string;\n}\n\n/** Per-context state for tracking an in-flight synthesis. */\ninterface ContextState {\n chunks: Buffer[];\n done: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class CartesiaTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly voiceId: string;\n private readonly modelId: string;\n private readonly sampleRate: number;\n private readonly apiVersion: string;\n private readonly language?: string;\n private readonly speed: number | undefined;\n private readonly emotion: string | undefined;\n\n private ws: WebSocket | null = null;\n private _connected = false;\n private connectPromise: Promise<void> | null = null;\n /** Active contexts keyed by context_id */\n private contexts = new Map<string, ContextState>();\n private contextCounter = 0;\n\n constructor(options: CartesiaTTSOptions) {\n if (!options.apiKey) {\n throw new Error('CartesiaTTS requires an apiKey');\n }\n if (!options.voiceId) {\n throw new Error('CartesiaTTS requires a voiceId');\n }\n this.apiKey = options.apiKey;\n this.voiceId = options.voiceId;\n this.modelId = options.modelId ?? DEFAULT_MODEL;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n this.apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;\n this.language = options.language;\n this.speed = options.speed;\n this.emotion = options.emotion;\n }\n\n /** Close the WebSocket connection to allow clean process exit. */\n close(): void {\n if (this.ws) {\n log.debug('Closing Cartesia WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this._connected = false;\n this.connectPromise = null;\n }\n\n /** Pre-connect the WebSocket so first synthesize() doesn't pay connection cost. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection();\n\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Cartesia WebSocket not connected');\n }\n\n const contextId = `ctx-${++this.contextCounter}-${Date.now()}`;\n const ctx: ContextState = { chunks: [], done: false, error: null, wake: null };\n this.contexts.set(contextId, ctx);\n\n // Build request\n const request: Record<string, unknown> = {\n model_id: this.modelId,\n transcript: text,\n voice: { mode: 'id', id: this.voiceId },\n output_format: {\n container: 'raw',\n encoding: 'pcm_s16le',\n sample_rate: this.sampleRate,\n },\n context_id: contextId,\n continue: false,\n };\n\n if (this.language) {\n request.language = this.language;\n }\n\n // Sonic-3 generation config\n if (this.speed !== undefined || this.emotion !== undefined) {\n const genConfig: Record<string, unknown> = {};\n if (this.speed !== undefined) genConfig.speed = this.speed;\n if (this.emotion !== undefined) genConfig.emotion = this.emotion;\n request.generation_config = genConfig;\n }\n\n // Handle abort — cancel the context on the server\n const onAbort = () => {\n ctx.done = true;\n ctx.wake?.();\n // Send cancel to server so it stops generating\n if (this.ws?.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify({ context_id: contextId, cancel: true }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send synthesis request\n this.ws.send(JSON.stringify(request));\n\n // Yield audio chunks as they arrive\n try {\n while (true) {\n if (signal?.aborted) break;\n if (ctx.error) throw ctx.error;\n\n if (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n continue;\n }\n\n if (ctx.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n ctx.wake = resolve;\n });\n ctx.wake = null;\n }\n\n // Drain remaining chunks\n while (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.contexts.delete(contextId);\n }\n }\n\n /** Ensure the persistent WebSocket is connected. */\n private ensureConnection(): Promise<void> {\n if (this._connected && this.ws?.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n const url = `${CARTESIA_WS_BASE}?api_key=${this.apiKey}&cartesia_version=${this.apiVersion}`;\n log.debug('Connecting to Cartesia...');\n\n this.ws = new WebSocket(url);\n\n this.ws.on('open', () => {\n this._connected = true;\n this.connectPromise = null;\n log.info('Cartesia WebSocket connected');\n resolve();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Cartesia message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('Cartesia WebSocket error:', error);\n // Propagate error to all active contexts\n for (const ctx of this.contexts.values()) {\n ctx.error = error;\n ctx.wake?.();\n }\n this._connected = false;\n this.connectPromise = null;\n reject(error);\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Cartesia WebSocket closed: ${code} ${reason.toString()}`);\n this._connected = false;\n this.connectPromise = null;\n // Mark all active contexts as done\n for (const ctx of this.contexts.values()) {\n ctx.done = true;\n ctx.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const contextId = msg.context_id as string | undefined;\n if (!contextId) return;\n\n const ctx = this.contexts.get(contextId);\n if (!ctx) return; // Stale context — already cleaned up\n\n const type = msg.type as string;\n\n if (type === 'chunk') {\n const b64 = msg.data as string;\n if (b64) {\n const pcm = Buffer.from(b64, 'base64');\n ctx.chunks.push(pcm);\n ctx.wake?.();\n }\n } else if (type === 'done') {\n log.debug(`Cartesia synthesis done for ${contextId} (${ctx.chunks.length} chunks pending)`);\n ctx.done = true;\n ctx.wake?.();\n } else if (type === 'error') {\n const errorMsg = msg.error as string ?? 'Unknown Cartesia error';\n log.error(`Cartesia error for ${contextId}: ${errorMsg}`);\n ctx.error = new Error(`Cartesia TTS error: ${errorMsg}`);\n ctx.wake?.();\n }\n }\n}\n","/**\n * DeepgramTTS — real-time streaming TTS via Deepgram Aura-2 WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/speak?model={model}&encoding=linear16&sample_rate={rate}\n * - Auth: Authorization: Token <key> header\n * - Send: {\"type\":\"Speak\",\"text\":\"...\"} then {\"type\":\"Flush\"}\n * - Receive: binary frames (raw PCM16) until {\"type\":\"Flushed\"} JSON\n * - Cancel: {\"type\":\"Clear\"} then {\"type\":\"Flush\"}, wait for Flushed\n *\n * Supports multi-language via connection pool (one WS per language).\n * Uses SSML <lang> tags to route text segments to the correct voice.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramTTS');\n\nconst DEEPGRAM_WS_BASE = 'wss://api.deepgram.com/v1/speak';\nconst DEFAULT_SAMPLE_RATE = 48000;\n\nexport interface DeepgramTTSOptions {\n apiKey: string;\n /** Single model string OR language->model map for multi-language */\n model: string | Record<string, string>;\n /** Default language for untagged text (default: 'en' or first key) */\n defaultLanguage?: string;\n /** Sample rate (default: 48000 — matches pipeline) */\n sampleRate?: number;\n}\n\ninterface LangSegment {\n lang: string;\n text: string;\n}\n\n/** Per-connection state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n flushed: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\n/**\n * Parse SSML <lang> tags into segments.\n *\n * Input: `Great job! <lang xml:lang=\"es\">Ahora repite: buenos días.</lang>`\n * Output: [{lang:'en', text:'Great job!'}, {lang:'es', text:'Ahora repite: buenos días.'}]\n *\n * Text outside tags uses defaultLang. Handles no tags, adjacent tags, nested text.\n * Malformed input is treated as default language.\n */\nexport function parseLangSegments(text: string, defaultLang: string): LangSegment[] {\n const segments: LangSegment[] = [];\n let i = 0;\n let currentText = '';\n\n while (i < text.length) {\n // Look for opening <lang tag\n if (text[i] === '<' && text.startsWith('<lang ', i)) {\n // Flush accumulated default-language text\n if (currentText) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n currentText = '';\n }\n\n // Find xml:lang=\"...\"\n const xmlLangStart = text.indexOf('xml:lang=\"', i);\n if (xmlLangStart === -1) {\n // Malformed — treat rest as default\n currentText += text[i];\n i++;\n continue;\n }\n\n const langStart = xmlLangStart + 'xml:lang=\"'.length;\n const langEnd = text.indexOf('\"', langStart);\n if (langEnd === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n const lang = text.substring(langStart, langEnd);\n\n // Find end of opening tag \">\"\n const tagClose = text.indexOf('>', langEnd);\n if (tagClose === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n // Find closing </lang>\n const closingTag = '</lang>';\n const closingStart = text.indexOf(closingTag, tagClose + 1);\n if (closingStart === -1) {\n // No closing tag — treat the rest after opening tag as this language\n const innerText = text.substring(tagClose + 1).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n i = text.length;\n continue;\n }\n\n const innerText = text.substring(tagClose + 1, closingStart).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n\n i = closingStart + closingTag.length;\n continue;\n }\n\n currentText += text[i];\n i++;\n }\n\n // Flush remaining default-language text\n if (currentText.trim()) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n }\n\n return segments;\n}\n\nexport class DeepgramTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly models: Record<string, string>;\n private readonly defaultLang: string;\n private readonly sampleRate: number;\n private readonly multiLanguage: boolean;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n /** Connection pool: one WebSocket per language code */\n private connections = new Map<string, WebSocket>();\n private connectPromises = new Map<string, Promise<void>>();\n /** Per-connection flush state */\n private flushStates = new Map<string, FlushState>();\n\n constructor(options: DeepgramTTSOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramTTS requires an apiKey');\n }\n\n this.apiKey = options.apiKey;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n\n if (typeof options.model === 'string') {\n // Single-language mode\n this.multiLanguage = false;\n const lang = options.defaultLanguage ?? 'en';\n this.models = { [lang]: options.model };\n this.defaultLang = lang;\n } else {\n // Multi-language mode\n this.multiLanguage = true;\n this.models = { ...options.model };\n const keys = Object.keys(this.models);\n if (keys.length === 0) {\n throw new Error('DeepgramTTS model map must have at least one entry');\n }\n this.defaultLang = options.defaultLanguage ?? keys[0];\n }\n }\n\n /** Close all WebSocket connections to allow clean process exit. */\n close(): void {\n for (const [lang, ws] of this.connections) {\n log.debug(`Closing WebSocket for [${lang}]`);\n ws.close();\n }\n this.connections.clear();\n this.connectPromises.clear();\n this.flushStates.clear();\n }\n\n /** Pre-connect all language WebSocket connections. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connections...');\n const start = performance.now();\n try {\n await Promise.all(Object.keys(this.models).map((lang) => this.ensureConnection(lang)));\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = this.multiLanguage\n ? parseLangSegments(text, this.defaultLang)\n : [{ lang: this.defaultLang, text }];\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 mono: sampleRate * 0.2 * 2 bytes per sample\n const silenceBytes = Math.round(this.sampleRate * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.models[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing [${lang}]: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection(lang);\n\n const ws = this.connections.get(lang);\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n throw new Error(`Deepgram WebSocket not connected for language \"${lang}\"`);\n }\n\n const state: FlushState = { chunks: [], flushed: false, error: null, wake: null };\n this.flushStates.set(lang, state);\n\n // Handle abort — send Clear + Flush to cancel\n const onAbort = () => {\n state.flushed = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'Clear' }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send Speak + Flush\n ws.send(JSON.stringify({ type: 'Speak', text }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.flushed) break;\n\n // Wait for next chunk or Flushed signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.flushStates.delete(lang);\n }\n }\n\n /** Ensure a WebSocket connection exists for the given language. */\n private ensureConnection(lang: string): Promise<void> {\n const existing = this.connections.get(lang);\n if (existing && existing.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n const pending = this.connectPromises.get(lang);\n if (pending) return pending;\n\n const model = this.models[lang];\n if (!model) {\n return Promise.reject(new Error(`No Deepgram model configured for language \"${lang}\"`));\n }\n\n const promise = new Promise<void>((resolve, reject) => {\n const url = `${DEEPGRAM_WS_BASE}?model=${encodeURIComponent(model)}&encoding=linear16&sample_rate=${this.sampleRate}`;\n log.debug(`Connecting to Deepgram for [${lang}]: ${model}`);\n\n const ws = new WebSocket(url, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n ws.on('open', () => {\n this.connections.set(lang, ws);\n this.connectPromises.delete(lang);\n log.info(`Deepgram WebSocket connected for [${lang}] (${model})`);\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushStates.get(lang);\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 audio\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'Flushed') {\n state.flushed = true;\n state.wake?.();\n } else if (msg.type === 'Warning' || msg.type === 'Error') {\n log.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);\n }\n } catch {\n log.warn(`Failed to parse Deepgram message for [${lang}]`);\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error(`Deepgram WebSocket error [${lang}]:`, error);\n const state = this.flushStates.get(lang);\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed [${lang}]: ${code} ${reason.toString()}`);\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n const state = this.flushStates.get(lang);\n if (state) {\n state.flushed = true;\n state.wake?.();\n }\n });\n });\n\n this.connectPromises.set(lang, promise);\n return promise;\n }\n}\n","/**\n * DtelecomSTT — real-time streaming STT via dTelecom STT server (realtime-stt-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"type\":\"config\",\"language\":\"en\"} (or \"auto\" for Parakeet auto-detect)\n * - Wait for ready: {\"type\":\"ready\",\"client_id\":\"...\",\"language\":\"en\"}\n * - Send audio as binary PCM16 16kHz mono frames\n * - Receive transcriptions: {\"type\":\"transcription\",\"text\":\"...\",\"is_final\":true,\"latency_ms\":N}\n * - Receive VAD events: {\"type\":\"vad_event\",\"event\":\"speech_start\"|\"speech_end\"}\n * - Keepalive via {\"type\":\"ping\"} / {\"type\":\"pong\"}\n * - Mid-session reconfigure: send {\"type\":\"config\",\"language\":\"es\",\"model\":\"whisper\"} at any time\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomSTT');\n\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DtelecomSTTOptions {\n /** Server base URL, e.g. \"https://stt-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Initial language (default: \"auto\" for Parakeet auto-detect) */\n language?: string;\n /** Force Whisper model even if Parakeet supports the language */\n forceWhisper?: boolean;\n}\n\nexport class DtelecomSTT implements STTPlugin {\n private readonly options: DtelecomSTTOptions;\n\n constructor(options: DtelecomSTTOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomSTT requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomSTT requires a sessionKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'auto';\n return new DtelecomSTTStream(this.options, language);\n }\n}\n\nclass DtelecomSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly forceWhisper: boolean;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private language: string;\n\n constructor(options: DtelecomSTTOptions, language: string) {\n super();\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.language = language;\n this.forceWhisper = options.forceWhisper ?? false;\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n }\n }\n\n /**\n * Switch language mid-session.\n * Sends a reconfigure message to the server; clears buffers and updates model routing.\n */\n setLanguage(language: string, options?: { forceWhisper?: boolean }): void {\n if (this._closed) return;\n this.language = language;\n\n const config: Record<string, string> = { type: 'config', language };\n if (options?.forceWhisper) {\n config.model = 'whisper';\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(config));\n log.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ', model=whisper' : ''}`);\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DtelecomSTT stream closed');\n }\n\n private connect(): void {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom STT: ${wsUrl}`);\n\n this.ws = new WebSocket(wsUrl);\n\n this.ws.on('open', () => {\n log.info('dTelecom STT WebSocket connected');\n\n // Send initial config with session_key\n const config: Record<string, string> = {\n type: 'config',\n language: this.language,\n session_key: this.sessionKey,\n };\n if (this.forceWhisper) {\n config.model = 'whisper';\n }\n this.ws!.send(JSON.stringify(config));\n });\n\n this.ws.on('message', (data, isBinary) => {\n if (isBinary) return; // Server only sends JSON text messages\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse dTelecom STT message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('dTelecom STT WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('dTelecom STT connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'ready') {\n this.handleReady(msg);\n } else if (type === 'transcription') {\n this.handleTranscription(msg);\n } else if (type === 'vad_event') {\n this.handleVadEvent(msg);\n } else if (type === 'pong') {\n // Keepalive response — no action needed\n } else if (type === 'error') {\n const errData = msg.error;\n const errorMsg = (msg.message as string)\n || (typeof errData === 'string' ? errData : JSON.stringify(errData))\n || 'Unknown STT error';\n log.error(`dTelecom STT error: ${errorMsg}`);\n this.emit('error', new Error(errorMsg));\n }\n }\n\n private handleReady(msg: Record<string, unknown>): void {\n const clientId = msg.client_id as string | undefined;\n const lang = msg.language as string | undefined;\n log.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);\n\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n }\n\n private handleTranscription(msg: Record<string, unknown>): void {\n const text = (msg.text as string) ?? '';\n const isFinal = (msg.is_final as boolean) ?? false;\n const language = msg.language as string | undefined;\n const latencyMs = msg.latency_ms as number | undefined;\n\n if (!text) return;\n\n if (isFinal && latencyMs !== undefined) {\n log.info(`stt_final: ${latencyMs.toFixed(0)}ms \"${text.slice(0, 50)}\"`);\n }\n\n this.emit('transcription', {\n text,\n isFinal,\n language,\n sttDuration: isFinal ? latencyMs : undefined,\n } satisfies TranscriptionResult);\n }\n\n private handleVadEvent(msg: Record<string, unknown>): void {\n const event = msg.event as string;\n log.debug(`VAD event: ${event}`);\n\n if (event === 'speech_start') {\n // Emit empty non-final transcription to signal speech detected.\n // Note: barge-in in the pipeline requires non-empty text, so this won't\n // trigger it directly. Actual interim transcriptions that follow will.\n this.emit('transcription', {\n text: '',\n isFinal: false,\n } satisfies TranscriptionResult);\n }\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n","/**\n * DtelecomTTS — real-time streaming TTS via dTelecom TTS server (realtime-tts-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"config\":{\"voice\":\"af_heart\",\"lang_code\":\"a\",\"speed\":1.0}}\n * - Send text: {\"text\":\"Hello world\"} — uses config defaults\n * - Send text with per-message override: {\"text\":\"Hola\",\"voice\":\"ef_dora\",\"lang_code\":\"e\",\"speed\":1.0}\n * - Receive: {\"type\":\"generating\",\"text\":\"...\"} then binary PCM16 48kHz chunks, then {\"type\":\"done\"}\n * - Cancel: {\"type\":\"clear\"} → {\"type\":\"cleared\"}\n *\n * Key differences from DeepgramTTS:\n * - Single WebSocket connection (not per-language pool)\n * - Per-message voice/language switching instead of separate connections\n * - Server outputs 48kHz PCM16 (resampled from Kokoro's native 24kHz)\n * - Uses SSML <lang> tags to route text segments to correct voice (same as DeepgramTTS)\n */\n\nimport WebSocket from 'ws';\nimport { parseLangSegments } from './deepgram-tts';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomTTS');\n\nexport interface VoiceConfig {\n voice: string;\n langCode: string;\n}\n\nexport interface DtelecomTTSOptions {\n /** Server base URL, e.g. \"https://tts-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Voice config per language: { en: { voice: \"af_heart\", langCode: \"a\" }, es: { voice: \"bf_emma\", langCode: \"b\" } } */\n voices: Record<string, VoiceConfig>;\n /** Default language code (default: \"en\") */\n defaultLanguage?: string;\n /** Speech speed multiplier (default: 1.0) */\n speed?: number;\n}\n\n/** Per-request state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n done: boolean;\n cleared: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class DtelecomTTS implements TTSPlugin {\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly voices: Record<string, VoiceConfig>;\n private readonly defaultLang: string;\n private readonly speed: number;\n\n private ws: WebSocket | null = null;\n private connectPromise: Promise<void> | null = null;\n private flushState: FlushState | null = null;\n /** Resolves when the current WS conversation receives \"done\"/\"cleared\". */\n private _wsDone: Promise<void> = Promise.resolve();\n private _resolveWsDone?: () => void;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n constructor(options: DtelecomTTSOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomTTS requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomTTS requires a sessionKey');\n }\n if (!options.voices || Object.keys(options.voices).length === 0) {\n throw new Error('DtelecomTTS requires at least one voice config');\n }\n\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.voices = { ...options.voices };\n this.defaultLang = options.defaultLanguage ?? Object.keys(this.voices)[0];\n this.speed = options.speed ?? 1.0;\n }\n\n /** Pre-connect WebSocket to TTS server. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Close WebSocket connection. */\n close(): void {\n if (this.ws) {\n log.debug('Closing TTS WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this.connectPromise = null;\n this.flushState = null;\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = parseLangSegments(text, this.defaultLang);\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 48kHz mono: 48000 * 0.2 * 2 = 19200 bytes\n const silenceBytes = Math.round(48000 * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.voices[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n // Wait for previous WS conversation to finish (\"done\"/\"cleared\" received).\n // This lets pipeline prefetch start TTS for the next sentence as soon as\n // the current one finishes generating — while audio is still playing.\n await this._wsDone;\n if (signal?.aborted) return;\n\n // Set up new _wsDone for this conversation\n this._wsDone = new Promise((r) => { this._resolveWsDone = r; });\n\n await this.ensureConnection();\n\n const ws = this.ws;\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n this._resolveWsDone?.();\n throw new Error('dTelecom TTS WebSocket not connected');\n }\n\n const state: FlushState = { chunks: [], done: false, cleared: false, error: null, wake: null };\n this.flushState = state;\n\n // Handle abort — send clear to cancel\n const onAbort = () => {\n state.done = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'clear' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send text with per-message voice/language override\n const voiceConfig = this.voices[lang];\n const msg: Record<string, unknown> = { text };\n if (voiceConfig) {\n msg.voice = voiceConfig.voice;\n msg.lang_code = voiceConfig.langCode;\n msg.speed = this.speed;\n }\n log.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? 'default'} lang_code=${voiceConfig?.langCode ?? 'default'} \"${text.slice(0, 60)}\"`)\n ws.send(JSON.stringify(msg));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n // Only clear flushState if it hasn't been replaced by the next synthesis\n if (this.flushState === state) {\n this.flushState = null;\n }\n this._resolveWsDone?.();\n }\n }\n\n /** Ensure a WebSocket connection exists and is open. */\n private ensureConnection(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom TTS: ${wsUrl}`);\n\n const ws = new WebSocket(wsUrl);\n\n ws.on('open', () => {\n this.ws = ws;\n this.connectPromise = null;\n\n // Send initial config with session_key and default voice\n const defaultVoice = this.voices[this.defaultLang];\n const msg: Record<string, unknown> = {\n session_key: this.sessionKey,\n };\n if (defaultVoice) {\n msg.config = {\n voice: defaultVoice.voice,\n lang_code: defaultVoice.langCode,\n speed: this.speed,\n };\n }\n ws.send(JSON.stringify(msg));\n\n log.info('dTelecom TTS WebSocket connected');\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushState;\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 48kHz audio (server resamples from Kokoro's 24kHz)\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'done') {\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'cleared') {\n state.cleared = true;\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'generating') {\n log.debug(`TTS generating: \"${(msg.text as string)?.slice(0, 40)}\"`);\n } else if (msg.type === 'error') {\n const errorMsg = (msg.message as string) || 'Unknown TTS error';\n log.error(`dTelecom TTS error: ${errorMsg}`);\n state.error = new Error(errorMsg);\n state.wake?.();\n }\n } catch {\n log.warn('Failed to parse dTelecom TTS message');\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('dTelecom TTS WebSocket error:', error);\n const state = this.flushState;\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.ws = null;\n this.connectPromise = null;\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);\n this.ws = null;\n this.connectPromise = null;\n const state = this.flushState;\n if (state) {\n state.done = true;\n state.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n}\n"],"mappings":"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","names":["WebSocket","log","log","import_ws","log","log","WebSocket","import_ws","log","DEFAULT_SAMPLE_RATE","innerText","WebSocket","import_ws","log","KEEPALIVE_INTERVAL_MS","WebSocket","import_ws","log","WebSocket"]}
1
+ {"version":3,"sources":["../../src/providers/index.ts","../../src/providers/deepgram-stt.ts","../../src/core/base-stt-stream.ts","../../src/utils/logger.ts","../../src/providers/openrouter-llm.ts","../../src/providers/openai-llm.ts","../../src/providers/cartesia-tts.ts","../../src/providers/deepgram-tts.ts","../../src/providers/dtelecom-stt.ts","../../src/providers/dtelecom-tts.ts"],"sourcesContent":["export { DeepgramSTT } from './deepgram-stt';\nexport type { DeepgramSTTOptions } from './deepgram-stt';\n\nexport { OpenRouterLLM } from './openrouter-llm';\nexport type { OpenRouterLLMOptions } from './openrouter-llm';\n\nexport { OpenAILLM } from './openai-llm';\nexport type { OpenAILLMOptions } from './openai-llm';\n\nexport { CartesiaTTS } from './cartesia-tts';\nexport type { CartesiaTTSOptions } from './cartesia-tts';\n\nexport { DeepgramTTS } from './deepgram-tts';\nexport type { DeepgramTTSOptions } from './deepgram-tts';\n\nexport { DtelecomSTT } from './dtelecom-stt';\nexport type { DtelecomSTTOptions } from './dtelecom-stt';\n\nexport { DtelecomTTS } from './dtelecom-tts';\nexport type { DtelecomTTSOptions, VoiceConfig as DtelecomVoiceConfig } from './dtelecom-tts';\n","/**\n * DeepgramSTT — real-time streaming STT via Deepgram WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/listen?... with config as query params\n * - Auth via Authorization header: \"Token <apiKey>\"\n * - Send audio as binary WebSocket frames (PCM16 16kHz mono)\n * - Receive JSON: { type: \"Results\", channel: { alternatives: [{ transcript }] }, is_final, speech_final }\n * - Send KeepAlive every 5s when no audio is being sent\n * - Send CloseStream to gracefully shut down\n *\n * End-of-utterance strategy:\n * Buffer all is_final=true transcripts. Emit the buffered utterance as a\n * single final TranscriptionResult when speech_final=true OR UtteranceEnd\n * arrives. Interim results (is_final=false) are emitted immediately for\n * real-time feedback.\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramSTT');\n\nconst DEEPGRAM_WS_URL = 'wss://api.deepgram.com/v1/listen';\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DeepgramSTTOptions {\n apiKey: string;\n /** Deepgram model (default: 'nova-3') */\n model?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Enable interim results (default: true) */\n interimResults?: boolean;\n /** Enable punctuation (default: true) */\n punctuate?: boolean;\n /** Endpointing in ms (default: 300). Set to false to disable. */\n endpointing?: number | false;\n /** Keywords to boost recognition (e.g. ['dTelecom:5', 'WebRTC:3']) */\n keywords?: string[];\n /** Enable smart formatting (default: false) */\n smartFormat?: boolean;\n /** Utterance end timeout in ms (default: 1000). Requires interimResults. */\n utteranceEndMs?: number;\n}\n\nexport class DeepgramSTT implements STTPlugin {\n private readonly options: Required<Pick<DeepgramSTTOptions, 'apiKey'>> & DeepgramSTTOptions;\n\n constructor(options: DeepgramSTTOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramSTT requires an apiKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'en';\n return new DeepgramSTTStream(this.options, language);\n }\n}\n\nclass DeepgramSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly apiKey: string;\n private readonly wsUrl: string;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private lastAudioSentAt = 0;\n /** Buffer of is_final=true transcripts for the current utterance */\n private utteranceBuffer: string[] = [];\n /** Timestamp of the last non-empty interim result (approximates end of speech) */\n private lastInterimAt = 0;\n\n constructor(options: DeepgramSTTOptions, language: string) {\n super();\n this.apiKey = options.apiKey;\n this.wsUrl = buildWsUrl(options, language);\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n this.lastAudioSentAt = performance.now();\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n // Graceful shutdown — ask server to flush remaining audio\n try {\n this.ws.send(JSON.stringify({ type: 'CloseStream' }));\n } catch {\n // Ignore send errors during shutdown\n }\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DeepgramSTT stream closed');\n }\n\n private connect(): void {\n log.debug(`Connecting to Deepgram: ${this.wsUrl.replace(/token=[^&]+/, 'token=***')}`);\n\n this.ws = new WebSocket(this.wsUrl, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n this.ws.on('open', () => {\n log.info('Deepgram WebSocket connected');\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Deepgram message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('Deepgram WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('Deepgram connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'Results') {\n this.handleResults(msg);\n } else if (type === 'UtteranceEnd') {\n this.flushUtterance();\n } else if (type === 'Metadata') {\n log.debug('Deepgram session metadata received');\n } else if (type === 'SpeechStarted') {\n log.debug('Speech started detected');\n }\n }\n\n private handleResults(msg: Record<string, unknown>): void {\n const channel = msg.channel as { alternatives?: Array<{ transcript?: string; confidence?: number }> } | undefined;\n const transcript = channel?.alternatives?.[0]?.transcript ?? '';\n const confidence = channel?.alternatives?.[0]?.confidence;\n const isFinal = msg.is_final as boolean ?? false;\n const speechFinal = msg.speech_final as boolean ?? false;\n\n if (!transcript) return;\n\n if (!isFinal) {\n // Interim result — emit immediately for real-time feedback.\n // Include any buffered finals as prefix so the UI shows the full utterance.\n this.lastInterimAt = performance.now();\n const fullInterim = this.utteranceBuffer.length > 0\n ? this.utteranceBuffer.join(' ') + ' ' + transcript\n : transcript;\n this.emit('transcription', {\n text: fullInterim,\n isFinal: false,\n confidence: confidence ?? undefined,\n } satisfies TranscriptionResult);\n return;\n }\n\n // is_final=true — buffer this segment\n this.utteranceBuffer.push(transcript);\n\n if (speechFinal) {\n // End of utterance — emit the complete buffered transcript\n this.flushUtterance();\n }\n }\n\n /** Emit the buffered utterance as a single final transcription result. */\n private flushUtterance(): void {\n if (this.utteranceBuffer.length === 0) return;\n\n const now = performance.now();\n const fullText = this.utteranceBuffer.join(' ');\n this.utteranceBuffer = [];\n\n // sttDuration = time from last interim (≈ end of speech) to now (final result)\n // This includes endpointing delay + STT processing + network\n const sttDuration = this.lastInterimAt > 0 ? now - this.lastInterimAt : undefined;\n\n if (sttDuration !== undefined) {\n log.info(`stt_final: ${sttDuration.toFixed(0)}ms \"${fullText.slice(0, 50)}\"`);\n }\n\n this.lastInterimAt = 0;\n\n this.emit('transcription', {\n text: fullText,\n isFinal: true,\n sttDuration,\n } satisfies TranscriptionResult);\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'KeepAlive' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n\n/** Build the Deepgram WebSocket URL with query parameters. */\nfunction buildWsUrl(options: DeepgramSTTOptions, language: string): string {\n const params = new URLSearchParams();\n\n params.set('model', options.model ?? 'nova-3');\n params.set('language', language);\n params.set('encoding', 'linear16');\n params.set('sample_rate', '16000');\n params.set('channels', '1');\n params.set('interim_results', String(options.interimResults ?? true));\n params.set('punctuate', String(options.punctuate ?? true));\n\n if (options.endpointing === false) {\n params.set('endpointing', 'false');\n } else {\n params.set('endpointing', String(options.endpointing ?? 300));\n }\n\n if (options.smartFormat) {\n params.set('smart_format', 'true');\n }\n\n if (options.utteranceEndMs !== undefined) {\n params.set('utterance_end_ms', String(options.utteranceEndMs));\n } else if (options.interimResults !== false) {\n // Default utterance_end_ms when interim results are enabled\n params.set('utterance_end_ms', '1000');\n }\n\n if (options.keywords?.length) {\n for (const kw of options.keywords) {\n params.append('keywords', kw);\n }\n }\n\n return `${DEEPGRAM_WS_URL}?${params.toString()}`;\n}\n","import { EventEmitter } from 'events';\nimport type { STTStream, TranscriptionResult } from './types';\n\n/**\n * Abstract base class for STT streams.\n * Provides typed EventEmitter interface for transcription events.\n * Provider implementations should extend this class.\n */\nexport abstract class BaseSTTStream extends EventEmitter implements STTStream {\n abstract sendAudio(pcm16: Buffer): void;\n abstract close(): Promise<void>;\n\n override on(event: 'transcription', cb: (result: TranscriptionResult) => void): this;\n override on(event: 'error', cb: (error: Error) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n override on(event: string, cb: (...args: any[]) => void): this {\n return super.on(event, cb);\n }\n\n override emit(event: 'transcription', result: TranscriptionResult): boolean;\n override emit(event: 'error', error: Error): boolean;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n override emit(event: string | symbol, ...args: any[]): boolean {\n return super.emit(event, ...args);\n }\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\nconst LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n\n/** Default to 'debug' if DEBUG env var matches our namespace */\nfunction detectLevel(): LogLevel {\n const debug = typeof process !== 'undefined' && process.env?.DEBUG;\n if (debug && (debug === '*' || debug.includes('@dtelecom/agents'))) {\n return 'debug';\n }\n return 'info';\n}\n\nlet globalLevel: LogLevel = detectLevel();\n\nexport function setLogLevel(level: LogLevel): void {\n globalLevel = level;\n}\n\nexport function getLogLevel(): LogLevel {\n return globalLevel;\n}\n\nexport interface Logger {\n debug(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nfunction timestamp(): string {\n const d = new Date();\n const h = String(d.getHours()).padStart(2, '0');\n const m = String(d.getMinutes()).padStart(2, '0');\n const s = String(d.getSeconds()).padStart(2, '0');\n const ms = String(d.getMilliseconds()).padStart(3, '0');\n return `${h}:${m}:${s}.${ms}`;\n}\n\nexport function createLogger(tag: string): Logger {\n const prefix = `[@dtelecom/agents:${tag}]`;\n return {\n debug(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.debug) console.debug(timestamp(), prefix, ...args);\n },\n info(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.info) console.info(timestamp(), prefix, ...args);\n },\n warn(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.warn) console.warn(timestamp(), prefix, ...args);\n },\n error(...args: unknown[]) {\n if (LEVELS[globalLevel] <= LEVELS.error) console.error(timestamp(), prefix, ...args);\n },\n };\n}\n","/**\n * OpenRouterLLM — streaming LLM via OpenRouter (OpenAI-compatible API).\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenRouterLLM');\n\nconst OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\nexport interface OpenRouterLLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'openai/gpt-4o', 'anthropic/claude-sonnet-4') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** OpenRouter provider routing preferences */\n providerRouting?: {\n /** Sort providers by metric (e.g. 'latency') */\n sort?: string;\n /** Pin to specific providers in order */\n order?: string[];\n /** Allow fallback to other providers if pinned ones fail */\n allowFallbacks?: boolean;\n };\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenRouterLLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly provider?: { sort?: string; order?: string[]; allow_fallbacks?: boolean };\n private readonly responseFormat?: OpenRouterLLMOptions['responseFormat'];\n\n constructor(options: OpenRouterLLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenRouterLLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n\n if (options.providerRouting) {\n this.provider = {\n sort: options.providerRouting.sort,\n order: options.providerRouting.order,\n allow_fallbacks: options.providerRouting.allowFallbacks,\n };\n }\n\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n };\n if (this.provider) {\n body.provider = { ...this.provider };\n }\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n // Ensure the provider enforces structured output parameters\n const prov = (body.provider ?? {}) as Record<string, unknown>;\n prov.require_parameters = true;\n body.provider = prov;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENROUTER_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenRouter API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenRouter response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n // Tracks brace depth to detect complete {...} objects inside the \"segments\" array,\n // then parses each with JSON.parse() — correct and streaming.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false; // true after we see \"segments\" : [\n let objectStart = -1; // index where current { started\n let braceDepth = 0; // nested brace depth inside current object\n let scanIndex = 0; // how far we've scanned in jsonBuffer\n let inString = false; // inside a JSON string literal\n let escaped = false; // previous char was backslash\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n // Detect start of segments array: look for [ after \"segments\"\n if (!inSegmentsArray) {\n if (ch === '[') {\n // Check if \"segments\" precedes this bracket\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n // Inside segments array — track objects\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n // Complete segment object\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n // End of segments array\n inSegmentsArray = false;\n }\n }\n\n // Always advance — state vars (braceDepth, inString, etc.) already\n // track parsing state, so rescanning would double-count characters.\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n // Check abort before blocking on read — prevents hanging when signal\n // was fired while we were yielding tokens to the pipeline\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) continue;\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n // Try to extract any newly completed segments\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n // Usage stats in the final chunk\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * OpenAILLM — streaming LLM via OpenAI API.\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenAILLM');\n\nconst OPENAI_URL = 'https://api.openai.com/v1/chat/completions';\n\nexport interface OpenAILLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'gpt-4o', 'gpt-4o-mini') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenAILLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly responseFormat?: OpenAILLMOptions['responseFormat'];\n\n constructor(options: OpenAILLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenAILLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n stream_options: { include_usage: true },\n };\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENAI_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenAI response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false;\n let objectStart = -1;\n let braceDepth = 0;\n let scanIndex = 0;\n let inString = false;\n let escaped = false;\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n if (!inSegmentsArray) {\n if (ch === '[') {\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n inSegmentsArray = false;\n }\n }\n\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) {\n // Final chunk may have usage without choices\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n continue;\n }\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * CartesiaTTS — real-time streaming TTS via Cartesia WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.cartesia.ai/tts/websocket?api_key=...&cartesia_version=...\n * - Send JSON: { model_id, transcript, voice: { mode: \"id\", id }, output_format, context_id }\n * - Receive JSON: { type: \"chunk\", data: \"<base64 PCM>\" } — audio data\n * - Receive JSON: { type: \"done\", context_id } — synthesis complete\n * - Audio is base64-encoded PCM16 LE at the requested sample rate\n *\n * Uses a persistent WebSocket connection to avoid per-sentence handshake overhead.\n * Each synthesize() call uses a unique context_id for multiplexing.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('CartesiaTTS');\n\nconst CARTESIA_WS_BASE = 'wss://api.cartesia.ai/tts/websocket';\nconst DEFAULT_API_VERSION = '2024-06-10';\nconst DEFAULT_MODEL = 'sonic-3';\n/** Pipeline operates at 48kHz — matches Opus/WebRTC native rate, no resampling */\nconst DEFAULT_SAMPLE_RATE = 48000;\n/** Reconnect after idle timeout (Cartesia closes after 5 min idle) */\nconst RECONNECT_DELAY_MS = 1000;\n\nexport interface CartesiaTTSOptions {\n apiKey: string;\n /** Cartesia voice ID */\n voiceId: string;\n /** Model ID (default: 'sonic-3') */\n modelId?: string;\n /** Output sample rate in Hz (default: 16000) */\n sampleRate?: number;\n /** API version (default: '2024-06-10') */\n apiVersion?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Speech speed multiplier, 0.6-1.5 (default: 1.0). Sonic-3 only. */\n speed?: number;\n /** Emotion string (e.g. 'friendly', 'calm'). Sonic-3 only. */\n emotion?: string;\n}\n\n/** Per-context state for tracking an in-flight synthesis. */\ninterface ContextState {\n chunks: Buffer[];\n done: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class CartesiaTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly voiceId: string;\n private readonly modelId: string;\n private readonly sampleRate: number;\n private readonly apiVersion: string;\n private readonly language?: string;\n private readonly speed: number | undefined;\n private readonly emotion: string | undefined;\n\n private ws: WebSocket | null = null;\n private _connected = false;\n private connectPromise: Promise<void> | null = null;\n /** Active contexts keyed by context_id */\n private contexts = new Map<string, ContextState>();\n private contextCounter = 0;\n\n constructor(options: CartesiaTTSOptions) {\n if (!options.apiKey) {\n throw new Error('CartesiaTTS requires an apiKey');\n }\n if (!options.voiceId) {\n throw new Error('CartesiaTTS requires a voiceId');\n }\n this.apiKey = options.apiKey;\n this.voiceId = options.voiceId;\n this.modelId = options.modelId ?? DEFAULT_MODEL;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n this.apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;\n this.language = options.language;\n this.speed = options.speed;\n this.emotion = options.emotion;\n }\n\n /** Close the WebSocket connection to allow clean process exit. */\n close(): void {\n if (this.ws) {\n log.debug('Closing Cartesia WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this._connected = false;\n this.connectPromise = null;\n }\n\n /** Pre-connect the WebSocket so first synthesize() doesn't pay connection cost. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection();\n\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Cartesia WebSocket not connected');\n }\n\n const contextId = `ctx-${++this.contextCounter}-${Date.now()}`;\n const ctx: ContextState = { chunks: [], done: false, error: null, wake: null };\n this.contexts.set(contextId, ctx);\n\n // Build request\n const request: Record<string, unknown> = {\n model_id: this.modelId,\n transcript: text,\n voice: { mode: 'id', id: this.voiceId },\n output_format: {\n container: 'raw',\n encoding: 'pcm_s16le',\n sample_rate: this.sampleRate,\n },\n context_id: contextId,\n continue: false,\n };\n\n if (this.language) {\n request.language = this.language;\n }\n\n // Sonic-3 generation config\n if (this.speed !== undefined || this.emotion !== undefined) {\n const genConfig: Record<string, unknown> = {};\n if (this.speed !== undefined) genConfig.speed = this.speed;\n if (this.emotion !== undefined) genConfig.emotion = this.emotion;\n request.generation_config = genConfig;\n }\n\n // Handle abort — cancel the context on the server\n const onAbort = () => {\n ctx.done = true;\n ctx.wake?.();\n // Send cancel to server so it stops generating\n if (this.ws?.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify({ context_id: contextId, cancel: true }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send synthesis request\n this.ws.send(JSON.stringify(request));\n\n // Yield audio chunks as they arrive\n try {\n while (true) {\n if (signal?.aborted) break;\n if (ctx.error) throw ctx.error;\n\n if (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n continue;\n }\n\n if (ctx.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n ctx.wake = resolve;\n });\n ctx.wake = null;\n }\n\n // Drain remaining chunks\n while (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.contexts.delete(contextId);\n }\n }\n\n /** Ensure the persistent WebSocket is connected. */\n private ensureConnection(): Promise<void> {\n if (this._connected && this.ws?.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n const url = `${CARTESIA_WS_BASE}?api_key=${this.apiKey}&cartesia_version=${this.apiVersion}`;\n log.debug('Connecting to Cartesia...');\n\n this.ws = new WebSocket(url);\n\n this.ws.on('open', () => {\n this._connected = true;\n this.connectPromise = null;\n log.info('Cartesia WebSocket connected');\n resolve();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Cartesia message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('Cartesia WebSocket error:', error);\n // Propagate error to all active contexts\n for (const ctx of this.contexts.values()) {\n ctx.error = error;\n ctx.wake?.();\n }\n this._connected = false;\n this.connectPromise = null;\n reject(error);\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Cartesia WebSocket closed: ${code} ${reason.toString()}`);\n this._connected = false;\n this.connectPromise = null;\n // Mark all active contexts as done\n for (const ctx of this.contexts.values()) {\n ctx.done = true;\n ctx.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const contextId = msg.context_id as string | undefined;\n if (!contextId) return;\n\n const ctx = this.contexts.get(contextId);\n if (!ctx) return; // Stale context — already cleaned up\n\n const type = msg.type as string;\n\n if (type === 'chunk') {\n const b64 = msg.data as string;\n if (b64) {\n const pcm = Buffer.from(b64, 'base64');\n ctx.chunks.push(pcm);\n ctx.wake?.();\n }\n } else if (type === 'done') {\n log.debug(`Cartesia synthesis done for ${contextId} (${ctx.chunks.length} chunks pending)`);\n ctx.done = true;\n ctx.wake?.();\n } else if (type === 'error') {\n const errorMsg = msg.error as string ?? 'Unknown Cartesia error';\n log.error(`Cartesia error for ${contextId}: ${errorMsg}`);\n ctx.error = new Error(`Cartesia TTS error: ${errorMsg}`);\n ctx.wake?.();\n }\n }\n}\n","/**\n * DeepgramTTS — real-time streaming TTS via Deepgram Aura-2 WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/speak?model={model}&encoding=linear16&sample_rate={rate}\n * - Auth: Authorization: Token <key> header\n * - Send: {\"type\":\"Speak\",\"text\":\"...\"} then {\"type\":\"Flush\"}\n * - Receive: binary frames (raw PCM16) until {\"type\":\"Flushed\"} JSON\n * - Cancel: {\"type\":\"Clear\"} then {\"type\":\"Flush\"}, wait for Flushed\n *\n * Supports multi-language via connection pool (one WS per language).\n * Uses SSML <lang> tags to route text segments to the correct voice.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramTTS');\n\nconst DEEPGRAM_WS_BASE = 'wss://api.deepgram.com/v1/speak';\nconst DEFAULT_SAMPLE_RATE = 48000;\n\nexport interface DeepgramTTSOptions {\n apiKey: string;\n /** Single model string OR language->model map for multi-language */\n model: string | Record<string, string>;\n /** Default language for untagged text (default: 'en' or first key) */\n defaultLanguage?: string;\n /** Sample rate (default: 48000 — matches pipeline) */\n sampleRate?: number;\n}\n\ninterface LangSegment {\n lang: string;\n text: string;\n}\n\n/** Per-connection state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n flushed: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\n/**\n * Parse SSML <lang> tags into segments.\n *\n * Input: `Great job! <lang xml:lang=\"es\">Ahora repite: buenos días.</lang>`\n * Output: [{lang:'en', text:'Great job!'}, {lang:'es', text:'Ahora repite: buenos días.'}]\n *\n * Text outside tags uses defaultLang. Handles no tags, adjacent tags, nested text.\n * Malformed input is treated as default language.\n */\nexport function parseLangSegments(text: string, defaultLang: string): LangSegment[] {\n const segments: LangSegment[] = [];\n let i = 0;\n let currentText = '';\n\n while (i < text.length) {\n // Look for opening <lang tag\n if (text[i] === '<' && text.startsWith('<lang ', i)) {\n // Flush accumulated default-language text\n if (currentText) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n currentText = '';\n }\n\n // Find xml:lang=\"...\"\n const xmlLangStart = text.indexOf('xml:lang=\"', i);\n if (xmlLangStart === -1) {\n // Malformed — treat rest as default\n currentText += text[i];\n i++;\n continue;\n }\n\n const langStart = xmlLangStart + 'xml:lang=\"'.length;\n const langEnd = text.indexOf('\"', langStart);\n if (langEnd === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n const lang = text.substring(langStart, langEnd);\n\n // Find end of opening tag \">\"\n const tagClose = text.indexOf('>', langEnd);\n if (tagClose === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n // Find closing </lang>\n const closingTag = '</lang>';\n const closingStart = text.indexOf(closingTag, tagClose + 1);\n if (closingStart === -1) {\n // No closing tag — treat the rest after opening tag as this language\n const innerText = text.substring(tagClose + 1).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n i = text.length;\n continue;\n }\n\n const innerText = text.substring(tagClose + 1, closingStart).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n\n i = closingStart + closingTag.length;\n continue;\n }\n\n currentText += text[i];\n i++;\n }\n\n // Flush remaining default-language text\n if (currentText.trim()) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n }\n\n return segments;\n}\n\nexport class DeepgramTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly models: Record<string, string>;\n private readonly defaultLang: string;\n private readonly sampleRate: number;\n private readonly multiLanguage: boolean;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n /** Connection pool: one WebSocket per language code */\n private connections = new Map<string, WebSocket>();\n private connectPromises = new Map<string, Promise<void>>();\n /** Per-connection flush state */\n private flushStates = new Map<string, FlushState>();\n\n constructor(options: DeepgramTTSOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramTTS requires an apiKey');\n }\n\n this.apiKey = options.apiKey;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n\n if (typeof options.model === 'string') {\n // Single-language mode\n this.multiLanguage = false;\n const lang = options.defaultLanguage ?? 'en';\n this.models = { [lang]: options.model };\n this.defaultLang = lang;\n } else {\n // Multi-language mode\n this.multiLanguage = true;\n this.models = { ...options.model };\n const keys = Object.keys(this.models);\n if (keys.length === 0) {\n throw new Error('DeepgramTTS model map must have at least one entry');\n }\n this.defaultLang = options.defaultLanguage ?? keys[0];\n }\n }\n\n /** Close all WebSocket connections to allow clean process exit. */\n close(): void {\n for (const [lang, ws] of this.connections) {\n log.debug(`Closing WebSocket for [${lang}]`);\n ws.close();\n }\n this.connections.clear();\n this.connectPromises.clear();\n this.flushStates.clear();\n }\n\n /** Pre-connect all language WebSocket connections. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connections...');\n const start = performance.now();\n try {\n await Promise.all(Object.keys(this.models).map((lang) => this.ensureConnection(lang)));\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = this.multiLanguage\n ? parseLangSegments(text, this.defaultLang)\n : [{ lang: this.defaultLang, text }];\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 mono: sampleRate * 0.2 * 2 bytes per sample\n const silenceBytes = Math.round(this.sampleRate * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.models[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing [${lang}]: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection(lang);\n\n const ws = this.connections.get(lang);\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n throw new Error(`Deepgram WebSocket not connected for language \"${lang}\"`);\n }\n\n const state: FlushState = { chunks: [], flushed: false, error: null, wake: null };\n this.flushStates.set(lang, state);\n\n // Handle abort — send Clear + Flush to cancel\n const onAbort = () => {\n state.flushed = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'Clear' }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send Speak + Flush\n ws.send(JSON.stringify({ type: 'Speak', text }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.flushed) break;\n\n // Wait for next chunk or Flushed signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.flushStates.delete(lang);\n }\n }\n\n /** Ensure a WebSocket connection exists for the given language. */\n private ensureConnection(lang: string): Promise<void> {\n const existing = this.connections.get(lang);\n if (existing && existing.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n const pending = this.connectPromises.get(lang);\n if (pending) return pending;\n\n const model = this.models[lang];\n if (!model) {\n return Promise.reject(new Error(`No Deepgram model configured for language \"${lang}\"`));\n }\n\n const promise = new Promise<void>((resolve, reject) => {\n const url = `${DEEPGRAM_WS_BASE}?model=${encodeURIComponent(model)}&encoding=linear16&sample_rate=${this.sampleRate}`;\n log.debug(`Connecting to Deepgram for [${lang}]: ${model}`);\n\n const ws = new WebSocket(url, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n ws.on('open', () => {\n this.connections.set(lang, ws);\n this.connectPromises.delete(lang);\n log.info(`Deepgram WebSocket connected for [${lang}] (${model})`);\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushStates.get(lang);\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 audio\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'Flushed') {\n state.flushed = true;\n state.wake?.();\n } else if (msg.type === 'Warning' || msg.type === 'Error') {\n log.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);\n }\n } catch {\n log.warn(`Failed to parse Deepgram message for [${lang}]`);\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error(`Deepgram WebSocket error [${lang}]:`, error);\n const state = this.flushStates.get(lang);\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed [${lang}]: ${code} ${reason.toString()}`);\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n const state = this.flushStates.get(lang);\n if (state) {\n state.flushed = true;\n state.wake?.();\n }\n });\n });\n\n this.connectPromises.set(lang, promise);\n return promise;\n }\n}\n","/**\n * DtelecomSTT — real-time streaming STT via dTelecom STT server (realtime-stt-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"type\":\"config\",\"language\":\"en\"} (or \"auto\" for Parakeet auto-detect)\n * - Wait for ready: {\"type\":\"ready\",\"client_id\":\"...\",\"language\":\"en\"}\n * - Send audio as binary PCM16 16kHz mono frames\n * - Receive transcriptions: {\"type\":\"transcription\",\"text\":\"...\",\"is_final\":true,\"latency_ms\":N}\n * - Receive VAD events: {\"type\":\"vad_event\",\"event\":\"speech_start\"|\"speech_end\"}\n * - Keepalive via {\"type\":\"ping\"} / {\"type\":\"pong\"}\n * - Mid-session reconfigure: send {\"type\":\"config\",\"language\":\"es\",\"model\":\"whisper\"} at any time\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomSTT');\n\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DtelecomSTTOptions {\n /** Server base URL, e.g. \"https://stt-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Initial language (default: \"auto\" for Parakeet auto-detect) */\n language?: string;\n /** Force Whisper model even if Parakeet supports the language */\n forceWhisper?: boolean;\n}\n\nexport class DtelecomSTT implements STTPlugin {\n private readonly options: DtelecomSTTOptions;\n\n constructor(options: DtelecomSTTOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomSTT requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomSTT requires a sessionKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'auto';\n return new DtelecomSTTStream(this.options, language);\n }\n}\n\nclass DtelecomSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly forceWhisper: boolean;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private language: string;\n\n constructor(options: DtelecomSTTOptions, language: string) {\n super();\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.language = language;\n this.forceWhisper = options.forceWhisper ?? false;\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n }\n }\n\n /**\n * Switch language mid-session.\n * Sends a reconfigure message to the server; clears buffers and updates model routing.\n */\n setLanguage(language: string, options?: { forceWhisper?: boolean }): void {\n if (this._closed) return;\n this.language = language;\n\n const config: Record<string, string> = { type: 'config', language };\n if (options?.forceWhisper) {\n config.model = 'whisper';\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(config));\n log.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ', model=whisper' : ''}`);\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DtelecomSTT stream closed');\n }\n\n private connect(): void {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom STT: ${wsUrl}`);\n\n this.ws = new WebSocket(wsUrl);\n\n this.ws.on('open', () => {\n log.info('dTelecom STT WebSocket connected');\n\n // Send initial config with session_key\n const config: Record<string, string> = {\n type: 'config',\n language: this.language,\n session_key: this.sessionKey,\n };\n if (this.forceWhisper) {\n config.model = 'whisper';\n }\n this.ws!.send(JSON.stringify(config));\n });\n\n this.ws.on('message', (data, isBinary) => {\n if (isBinary) return; // Server only sends JSON text messages\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse dTelecom STT message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('dTelecom STT WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('dTelecom STT connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'ready') {\n this.handleReady(msg);\n } else if (type === 'transcription') {\n this.handleTranscription(msg);\n } else if (type === 'vad_event') {\n this.handleVadEvent(msg);\n } else if (type === 'pong') {\n // Keepalive response — no action needed\n } else if (type === 'error') {\n const errData = msg.error;\n const errorMsg = (msg.message as string)\n || (typeof errData === 'string' ? errData : JSON.stringify(errData))\n || 'Unknown STT error';\n log.error(`dTelecom STT error: ${errorMsg}`);\n this.emit('error', new Error(errorMsg));\n }\n }\n\n private handleReady(msg: Record<string, unknown>): void {\n const clientId = msg.client_id as string | undefined;\n const lang = msg.language as string | undefined;\n log.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);\n\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n }\n\n private handleTranscription(msg: Record<string, unknown>): void {\n const text = (msg.text as string) ?? '';\n const isFinal = (msg.is_final as boolean) ?? false;\n const language = msg.language as string | undefined;\n const latencyMs = msg.latency_ms as number | undefined;\n\n if (!text) return;\n\n if (isFinal && latencyMs !== undefined) {\n log.info(`stt_final: ${latencyMs.toFixed(0)}ms \"${text.slice(0, 50)}\"`);\n }\n\n this.emit('transcription', {\n text,\n isFinal,\n language,\n sttDuration: isFinal ? latencyMs : undefined,\n } satisfies TranscriptionResult);\n }\n\n private handleVadEvent(msg: Record<string, unknown>): void {\n const event = msg.event as string;\n log.debug(`VAD event: ${event}`);\n\n if (event === 'speech_start') {\n // Emit empty non-final transcription to signal speech detected.\n // Note: barge-in in the pipeline requires non-empty text, so this won't\n // trigger it directly. Actual interim transcriptions that follow will.\n this.emit('transcription', {\n text: '',\n isFinal: false,\n } satisfies TranscriptionResult);\n }\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n","/**\n * DtelecomTTS — real-time streaming TTS via dTelecom TTS server (realtime-tts-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"config\":{\"voice\":\"af_heart\",\"lang_code\":\"a\",\"speed\":1.0}}\n * - Send text: {\"text\":\"Hello world\"} — uses config defaults\n * - Send text with per-message override: {\"text\":\"Hola\",\"voice\":\"ef_dora\",\"lang_code\":\"e\",\"speed\":1.0}\n * - Receive: {\"type\":\"generating\",\"text\":\"...\"} then binary PCM16 48kHz chunks, then {\"type\":\"done\"}\n * - Cancel: {\"type\":\"clear\"} → {\"type\":\"cleared\"}\n *\n * Key differences from DeepgramTTS:\n * - Single WebSocket connection (not per-language pool)\n * - Per-message voice/language switching instead of separate connections\n * - Server outputs 48kHz PCM16 (resampled from Kokoro's native 24kHz)\n * - Uses SSML <lang> tags to route text segments to correct voice (same as DeepgramTTS)\n */\n\nimport WebSocket from 'ws';\nimport { parseLangSegments } from './deepgram-tts';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomTTS');\n\nexport interface VoiceConfig {\n voice: string;\n langCode: string;\n}\n\nexport interface DtelecomTTSOptions {\n /** Server base URL, e.g. \"https://tts-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Voice config per language: { en: { voice: \"af_heart\", langCode: \"a\" }, es: { voice: \"bf_emma\", langCode: \"b\" } } */\n voices: Record<string, VoiceConfig>;\n /** Default language code (default: \"en\") */\n defaultLanguage?: string;\n /** Speech speed multiplier (default: 1.0) */\n speed?: number;\n}\n\n/** Per-request state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n done: boolean;\n cleared: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class DtelecomTTS implements TTSPlugin {\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly voices: Record<string, VoiceConfig>;\n private readonly defaultLang: string;\n private readonly speed: number;\n\n private ws: WebSocket | null = null;\n private connectPromise: Promise<void> | null = null;\n private flushState: FlushState | null = null;\n /** Resolves when the current WS conversation receives \"done\"/\"cleared\". */\n private _wsDone: Promise<void> = Promise.resolve();\n private _resolveWsDone?: () => void;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n constructor(options: DtelecomTTSOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomTTS requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomTTS requires a sessionKey');\n }\n if (!options.voices || Object.keys(options.voices).length === 0) {\n throw new Error('DtelecomTTS requires at least one voice config');\n }\n\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.voices = { ...options.voices };\n this.defaultLang = options.defaultLanguage ?? Object.keys(this.voices)[0];\n this.speed = options.speed ?? 1.0;\n }\n\n /** Pre-connect WebSocket to TTS server. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Close WebSocket connection. */\n close(): void {\n if (this.ws) {\n log.debug('Closing TTS WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this.connectPromise = null;\n this.flushState = null;\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = parseLangSegments(text, this.defaultLang);\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 48kHz mono: 48000 * 0.2 * 2 = 19200 bytes\n const silenceBytes = Math.round(48000 * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.voices[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n // Wait for previous WS conversation to finish (\"done\"/\"cleared\" received).\n // This lets pipeline prefetch start TTS for the next sentence as soon as\n // the current one finishes generating — while audio is still playing.\n await this._wsDone;\n if (signal?.aborted) return;\n\n // Set up new _wsDone for this conversation\n this._wsDone = new Promise((r) => { this._resolveWsDone = r; });\n\n await this.ensureConnection();\n\n const ws = this.ws;\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n this._resolveWsDone?.();\n throw new Error('dTelecom TTS WebSocket not connected');\n }\n\n const state: FlushState = { chunks: [], done: false, cleared: false, error: null, wake: null };\n this.flushState = state;\n\n // Handle abort — send clear to cancel\n const onAbort = () => {\n state.done = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'clear' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send text with per-message voice/language override\n const voiceConfig = this.voices[lang];\n const msg: Record<string, unknown> = { text };\n if (voiceConfig) {\n msg.voice = voiceConfig.voice;\n msg.lang_code = voiceConfig.langCode;\n msg.speed = this.speed;\n }\n log.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? 'default'} lang_code=${voiceConfig?.langCode ?? 'default'} \"${text.slice(0, 60)}\"`)\n ws.send(JSON.stringify(msg));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n // Only clear flushState if it hasn't been replaced by the next synthesis\n if (this.flushState === state) {\n this.flushState = null;\n }\n }\n }\n\n /** Ensure a WebSocket connection exists and is open. */\n private ensureConnection(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom TTS: ${wsUrl}`);\n\n const ws = new WebSocket(wsUrl);\n\n ws.on('open', () => {\n this.ws = ws;\n this.connectPromise = null;\n\n // Send initial config with session_key and default voice\n const defaultVoice = this.voices[this.defaultLang];\n const msg: Record<string, unknown> = {\n session_key: this.sessionKey,\n };\n if (defaultVoice) {\n msg.config = {\n voice: defaultVoice.voice,\n lang_code: defaultVoice.langCode,\n speed: this.speed,\n };\n }\n ws.send(JSON.stringify(msg));\n\n log.info('dTelecom TTS WebSocket connected');\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushState;\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 48kHz audio (server resamples from Kokoro's 24kHz)\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'done') {\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'cleared') {\n state.cleared = true;\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'generating') {\n log.debug(`TTS generating: \"${(msg.text as string)?.slice(0, 40)}\"`);\n } else if (msg.type === 'error') {\n const errorMsg = (msg.message as string) || 'Unknown TTS error';\n log.error(`dTelecom TTS error: ${errorMsg}`);\n state.error = new Error(errorMsg);\n state.wake?.();\n }\n } catch {\n log.warn('Failed to parse dTelecom TTS message');\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('dTelecom TTS WebSocket error:', error);\n const state = this.flushState;\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.ws = null;\n this.connectPromise = null;\n this._resolveWsDone?.();\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);\n this.ws = null;\n this.connectPromise = null;\n this._resolveWsDone?.();\n const state = this.flushState;\n if (state) {\n state.done = true;\n state.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACkBA,gBAAsB;;;AClBtB,oBAA6B;AAQtB,IAAe,gBAAf,cAAqC,2BAAkC;AAAA;AAAA,EAOnE,GAAG,OAAe,IAAoC;AAC7D,WAAO,MAAM,GAAG,OAAO,EAAE;AAAA,EAC3B;AAAA;AAAA,EAKS,KAAK,UAA2B,MAAsB;AAC7D,WAAO,MAAM,KAAK,OAAO,GAAG,IAAI;AAAA,EAClC;AACF;;;ACvBA,IAAM,SAAmC;AAAA,EACvC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;AAGA,SAAS,cAAwB;AAC/B,QAAM,QAAQ,OAAO,YAAY,eAAe,QAAQ,KAAK;AAC7D,MAAI,UAAU,UAAU,OAAO,MAAM,SAAS,kBAAkB,IAAI;AAClE,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,IAAI,cAAwB,YAAY;AAiBxC,SAAS,YAAoB;AAC3B,QAAM,IAAI,oBAAI,KAAK;AACnB,QAAM,IAAI,OAAO,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AAC9C,QAAM,IAAI,OAAO,EAAE,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AAChD,QAAM,IAAI,OAAO,EAAE,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AAChD,QAAM,KAAK,OAAO,EAAE,gBAAgB,CAAC,EAAE,SAAS,GAAG,GAAG;AACtD,SAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;AAC7B;AAEO,SAAS,aAAa,KAAqB;AAChD,QAAM,SAAS,qBAAqB,GAAG;AACvC,SAAO;AAAA,IACL,SAAS,MAAiB;AACxB,UAAI,OAAO,WAAW,KAAK,OAAO,MAAO,SAAQ,MAAM,UAAU,GAAG,QAAQ,GAAG,IAAI;AAAA,IACrF;AAAA,IACA,QAAQ,MAAiB;AACvB,UAAI,OAAO,WAAW,KAAK,OAAO,KAAM,SAAQ,KAAK,UAAU,GAAG,QAAQ,GAAG,IAAI;AAAA,IACnF;AAAA,IACA,QAAQ,MAAiB;AACvB,UAAI,OAAO,WAAW,KAAK,OAAO,KAAM,SAAQ,KAAK,UAAU,GAAG,QAAQ,GAAG,IAAI;AAAA,IACnF;AAAA,IACA,SAAS,MAAiB;AACxB,UAAI,OAAO,WAAW,KAAK,OAAO,MAAO,SAAQ,MAAM,UAAU,GAAG,QAAQ,GAAG,IAAI;AAAA,IACrF;AAAA,EACF;AACF;;;AFtCA,IAAM,MAAM,aAAa,aAAa;AAEtC,IAAM,kBAAkB;AACxB,IAAM,wBAAwB;AAsBvB,IAAM,cAAN,MAAuC;AAAA,EAC3B;AAAA,EAEjB,YAAY,SAA6B;AACvC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,aAAa,SAAuC;AAClD,UAAM,WAAW,SAAS,YAAY,KAAK,QAAQ,YAAY;AAC/D,WAAO,IAAI,kBAAkB,KAAK,SAAS,QAAQ;AAAA,EACrD;AACF;AAEA,IAAM,oBAAN,cAAgC,cAAc;AAAA,EACpC,KAAuB;AAAA,EACd;AAAA,EACA;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,eAAyB,CAAC;AAAA,EAC1B,iBAAwD;AAAA,EACxD,kBAAkB;AAAA;AAAA,EAElB,kBAA4B,CAAC;AAAA;AAAA,EAE7B,gBAAgB;AAAA,EAExB,YAAY,SAA6B,UAAkB;AACzD,UAAM;AACN,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,WAAW,SAAS,QAAQ;AACzC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,UAAU,OAAqB;AAC7B,QAAI,KAAK,QAAS;AAElB,QAAI,CAAC,KAAK,QAAQ;AAChB,WAAK,aAAa,KAAK,KAAK;AAC5B;AAAA,IACF;AAEA,QAAI,KAAK,IAAI,eAAe,UAAAA,QAAU,MAAM;AAC1C,WAAK,GAAG,KAAK,KAAK;AAClB,WAAK,kBAAkB,YAAY,IAAI;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,eAAe,CAAC;AACrB,SAAK,cAAc;AAEnB,QAAI,KAAK,IAAI,eAAe,UAAAA,QAAU,MAAM;AAE1C,UAAI;AACF,aAAK,GAAG,KAAK,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC,CAAC;AAAA,MACtD,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AAEA,QAAI,MAAM,2BAA2B;AAAA,EACvC;AAAA,EAEQ,UAAgB;AACtB,QAAI,MAAM,2BAA2B,KAAK,MAAM,QAAQ,eAAe,WAAW,CAAC,EAAE;AAErF,SAAK,KAAK,IAAI,UAAAA,QAAU,KAAK,OAAO;AAAA,MAClC,SAAS;AAAA,QACP,eAAe,SAAS,KAAK,MAAM;AAAA,MACrC;AAAA,IACF,CAAC;AAED,SAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,UAAI,KAAK,8BAA8B;AACvC,WAAK,SAAS;AAGd,iBAAW,OAAO,KAAK,cAAc;AACnC,YAAI,KAAK,IAAI,eAAe,UAAAA,QAAU,MAAM;AAC1C,eAAK,GAAG,KAAK,GAAG;AAAA,QAClB;AAAA,MACF;AACA,WAAK,eAAe,CAAC;AAErB,WAAK,eAAe;AAAA,IACtB,CAAC;AAED,SAAK,GAAG,GAAG,WAAW,CAAC,SAAS;AAC9B,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,aAAK,cAAc,GAAG;AAAA,MACxB,SAAS,KAAK;AACZ,YAAI,MAAM,qCAAqC,GAAG;AAAA,MACpD;AAAA,IACF,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,QAAQ;AAC3B,UAAI,MAAM,6BAA6B,GAAG;AAC1C,WAAK,KAAK,SAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,IACxE,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AACpC,UAAI,MAAM,8BAA8B,IAAI,IAAI,OAAO,SAAS,CAAC,EAAE;AACnE,WAAK,SAAS;AACd,WAAK,cAAc;AAGnB,UAAI,CAAC,KAAK,SAAS;AACjB,YAAI,KAAK,iDAAiD;AAC1D,mBAAW,MAAM;AACf,cAAI,CAAC,KAAK,QAAS,MAAK,QAAQ;AAAA,QAClC,GAAG,GAAI;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,cAAc,KAAoC;AACxD,UAAM,OAAO,IAAI;AAEjB,QAAI,SAAS,WAAW;AACtB,WAAK,cAAc,GAAG;AAAA,IACxB,WAAW,SAAS,gBAAgB;AAClC,WAAK,eAAe;AAAA,IACtB,WAAW,SAAS,YAAY;AAC9B,UAAI,MAAM,oCAAoC;AAAA,IAChD,WAAW,SAAS,iBAAiB;AACnC,UAAI,MAAM,yBAAyB;AAAA,IACrC;AAAA,EACF;AAAA,EAEQ,cAAc,KAAoC;AACxD,UAAM,UAAU,IAAI;AACpB,UAAM,aAAa,SAAS,eAAe,CAAC,GAAG,cAAc;AAC7D,UAAM,aAAa,SAAS,eAAe,CAAC,GAAG;AAC/C,UAAM,UAAU,IAAI,YAAuB;AAC3C,UAAM,cAAc,IAAI,gBAA2B;AAEnD,QAAI,CAAC,WAAY;AAEjB,QAAI,CAAC,SAAS;AAGZ,WAAK,gBAAgB,YAAY,IAAI;AACrC,YAAM,cAAc,KAAK,gBAAgB,SAAS,IAC9C,KAAK,gBAAgB,KAAK,GAAG,IAAI,MAAM,aACvC;AACJ,WAAK,KAAK,iBAAiB;AAAA,QACzB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAY,cAAc;AAAA,MAC5B,CAA+B;AAC/B;AAAA,IACF;AAGA,SAAK,gBAAgB,KAAK,UAAU;AAEpC,QAAI,aAAa;AAEf,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA,EAGQ,iBAAuB;AAC7B,QAAI,KAAK,gBAAgB,WAAW,EAAG;AAEvC,UAAM,MAAM,YAAY,IAAI;AAC5B,UAAM,WAAW,KAAK,gBAAgB,KAAK,GAAG;AAC9C,SAAK,kBAAkB,CAAC;AAIxB,UAAM,cAAc,KAAK,gBAAgB,IAAI,MAAM,KAAK,gBAAgB;AAExE,QAAI,gBAAgB,QAAW;AAC7B,UAAI,KAAK,cAAc,YAAY,QAAQ,CAAC,CAAC,OAAO,SAAS,MAAM,GAAG,EAAE,CAAC,GAAG;AAAA,IAC9E;AAEA,SAAK,gBAAgB;AAErB,SAAK,KAAK,iBAAiB;AAAA,MACzB,MAAM;AAAA,MACN,SAAS;AAAA,MACT;AAAA,IACF,CAA+B;AAAA,EACjC;AAAA,EAEQ,iBAAuB;AAC7B,SAAK,cAAc;AACnB,SAAK,iBAAiB,YAAY,MAAM;AACtC,UAAI,KAAK,IAAI,eAAe,UAAAA,QAAU,MAAM;AAC1C,aAAK,GAAG,KAAK,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC,CAAC;AAAA,MACpD;AAAA,IACF,GAAG,qBAAqB;AAAA,EAC1B;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AACF;AAGA,SAAS,WAAW,SAA6B,UAA0B;AACzE,QAAM,SAAS,IAAI,gBAAgB;AAEnC,SAAO,IAAI,SAAS,QAAQ,SAAS,QAAQ;AAC7C,SAAO,IAAI,YAAY,QAAQ;AAC/B,SAAO,IAAI,YAAY,UAAU;AACjC,SAAO,IAAI,eAAe,OAAO;AACjC,SAAO,IAAI,YAAY,GAAG;AAC1B,SAAO,IAAI,mBAAmB,OAAO,QAAQ,kBAAkB,IAAI,CAAC;AACpE,SAAO,IAAI,aAAa,OAAO,QAAQ,aAAa,IAAI,CAAC;AAEzD,MAAI,QAAQ,gBAAgB,OAAO;AACjC,WAAO,IAAI,eAAe,OAAO;AAAA,EACnC,OAAO;AACL,WAAO,IAAI,eAAe,OAAO,QAAQ,eAAe,GAAG,CAAC;AAAA,EAC9D;AAEA,MAAI,QAAQ,aAAa;AACvB,WAAO,IAAI,gBAAgB,MAAM;AAAA,EACnC;AAEA,MAAI,QAAQ,mBAAmB,QAAW;AACxC,WAAO,IAAI,oBAAoB,OAAO,QAAQ,cAAc,CAAC;AAAA,EAC/D,WAAW,QAAQ,mBAAmB,OAAO;AAE3C,WAAO,IAAI,oBAAoB,MAAM;AAAA,EACvC;AAEA,MAAI,QAAQ,UAAU,QAAQ;AAC5B,eAAW,MAAM,QAAQ,UAAU;AACjC,aAAO,OAAO,YAAY,EAAE;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO,GAAG,eAAe,IAAI,OAAO,SAAS,CAAC;AAChD;;;AGnSA,IAAMC,OAAM,aAAa,eAAe;AAExC,IAAM,iBAAiB;AA0BhB,IAAM,gBAAN,MAAyC;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA+B;AACzC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ;AACrB,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,cAAc,QAAQ,eAAe;AAE1C,QAAI,QAAQ,iBAAiB;AAC3B,WAAK,WAAW;AAAA,QACd,MAAM,QAAQ,gBAAgB;AAAA,QAC9B,OAAO,QAAQ,gBAAgB;AAAA,QAC/B,iBAAiB,QAAQ,gBAAgB;AAAA,MAC3C;AAAA,IACF;AAEA,SAAK,iBAAiB,QAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,cAAqC;AAChD,IAAAA,KAAI,KAAK,8BAA8B;AACvC,UAAM,QAAQ,YAAY,IAAI;AAE9B,UAAM,WAAsB;AAAA,MAC1B,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,MACxC,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,IACnC;AAEA,QAAI;AACF,YAAM,MAAM,KAAK,KAAK,QAAQ;AAC9B,uBAAiB,SAAS,KAAK;AAC7B,YAAI,MAAM,SAAS,OAAQ;AAAA,MAC7B;AACA,MAAAA,KAAI,KAAK,2BAA2B,YAAY,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,IAAI;AAAA,IAC/E,SAAS,KAAK;AACZ,MAAAA,KAAI,KAAK,kCAAkC,GAAG;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,OAAO,KAAK,UAAqB,QAAsB,SAAoD;AACzG,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,IACV;AACA,QAAI,KAAK,UAAU;AACjB,WAAK,WAAW,EAAE,GAAG,KAAK,SAAS;AAAA,IACrC;AACA,QAAI,KAAK,kBAAkB,CAAC,SAAS,WAAW;AAC9C,WAAK,kBAAkB,KAAK;AAE5B,YAAM,OAAQ,KAAK,YAAY,CAAC;AAChC,WAAK,qBAAqB;AAC1B,WAAK,WAAW;AAAA,IAClB;AACA,QAAI,SAAS,OAAO,QAAQ;AAC1B,WAAK,QAAQ,QAAQ;AAAA,IACvB;AAEA,IAAAA,KAAI,MAAM,sBAAsB,KAAK,KAAK,cAAc,SAAS,MAAM,EAAE;AAEzE,UAAM,WAAW,MAAM,MAAM,gBAAgB;AAAA,MAC3C,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB,UAAU,KAAK,MAAM;AAAA,MACxC;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK;AACtC,YAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,KAAK,SAAS,EAAE;AAAA,IACzE;AAEA,QAAI,CAAC,SAAS,MAAM;AAClB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAGA,UAAM,SAAS,SAAS,KAAK,UAAU;AACvC,UAAM,UAAU,IAAI,YAAY;AAChC,QAAI,SAAS;AAKb,UAAM,aAAa,CAAC,CAAC,KAAK,kBAAkB,CAAC,SAAS;AACtD,QAAI,aAAa;AACjB,QAAI,kBAAkB;AACtB,QAAI;AACJ,UAAM,mBAAmB,oBAAI,IAA6D;AAG1F,QAAI,kBAAkB;AACtB,QAAI,cAAc;AAClB,QAAI,aAAa;AACjB,QAAI,YAAY;AAChB,QAAI,WAAW;AACf,QAAI,UAAU;AAEd,aAAS,gBAAgB,KAAoD;AAC3E,YAAM,UAAiD,CAAC;AAExD,eAAS,IAAI,WAAW,IAAI,IAAI,QAAQ,KAAK;AAC3C,cAAM,KAAK,IAAI,CAAC;AAEhB,YAAI,SAAS;AACX,oBAAU;AACV;AAAA,QACF;AAEA,YAAI,OAAO,QAAQ,UAAU;AAC3B,oBAAU;AACV;AAAA,QACF;AAEA,YAAI,OAAO,KAAK;AACd,qBAAW,CAAC;AACZ;AAAA,QACF;AAEA,YAAI,SAAU;AAGd,YAAI,CAAC,iBAAiB;AACpB,cAAI,OAAO,KAAK;AAEd,kBAAM,SAAS,IAAI,MAAM,GAAG,CAAC,EAAE,QAAQ;AACvC,gBAAI,OAAO,SAAS,GAAG,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,SAAS,YAAY,GAAG;AAClE,gCAAkB;AAAA,YACpB;AAAA,UACF;AACA;AAAA,QACF;AAGA,YAAI,OAAO,KAAK;AACd,cAAI,eAAe,EAAG,eAAc;AACpC;AAAA,QACF,WAAW,OAAO,KAAK;AACrB;AACA,cAAI,eAAe,KAAK,eAAe,GAAG;AAExC,kBAAM,SAAS,IAAI,MAAM,aAAa,IAAI,CAAC;AAC3C,gBAAI;AACF,oBAAM,MAAM,KAAK,MAAM,MAAM;AAC7B,kBAAI,IAAI,QAAQ,IAAI,MAAM;AACxB,wBAAQ,KAAK,EAAE,MAAM,IAAI,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,cACjD;AAAA,YACF,QAAQ;AAAA,YAER;AACA,0BAAc;AAAA,UAChB;AAAA,QACF,WAAW,OAAO,OAAO,eAAe,GAAG;AAEzC,4BAAkB;AAAA,QACpB;AAAA,MACF;AAIA,kBAAY,IAAI;AAChB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAO,MAAM;AAGX,YAAI,QAAQ,QAAS;AAErB,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,kBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,cAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,iBAAS,MAAM,IAAI,KAAK;AAExB,mBAAW,QAAQ,OAAO;AACxB,gBAAM,UAAU,KAAK,KAAK;AAC1B,cAAI,CAAC,WAAW,CAAC,QAAQ,WAAW,QAAQ,EAAG;AAE/C,gBAAM,OAAO,QAAQ,MAAM,CAAC;AAC5B,cAAI,SAAS,SAAU;AAEvB,cAAI;AACF,kBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,kBAAM,SAAS,OAAO,UAAU,CAAC;AACjC,gBAAI,CAAC,OAAQ;AAEb,kBAAM,QAAQ,OAAO;AACrB,gBAAI,OAAO,SAAS;AAClB,kBAAI,YAAY;AACd,8BAAc,MAAM;AAGpB,sBAAM,WAAW,gBAAgB,UAAU;AAC3C,2BAAW,OAAO,UAAU;AAC1B,wBAAM,EAAE,MAAM,WAAW,SAAS,IAAI;AACtC,oCAAkB;AAAA,gBACpB;AAAA,cACF,OAAO;AACL,sBAAM,EAAE,MAAM,SAAS,OAAO,MAAM,QAAQ;AAAA,cAC9C;AAAA,YACF;AAEA,gBAAI,OAAO,YAAY;AACrB,yBAAW,MAAM,MAAM,YAAuG;AAC5H,sBAAM,MAAM,GAAG;AACf,oBAAI,GAAG,IAAI;AACT,mCAAiB,IAAI,KAAK,EAAE,IAAI,GAAG,IAAI,MAAM,GAAG,UAAU,QAAQ,IAAI,WAAW,GAAG,UAAU,aAAa,GAAG,CAAC;AAAA,gBACjH,OAAO;AACL,wBAAM,WAAW,iBAAiB,IAAI,GAAG;AACzC,sBAAI,UAAU;AACZ,wBAAI,GAAG,UAAU,KAAM,UAAS,QAAQ,GAAG,SAAS;AACpD,wBAAI,GAAG,UAAU,UAAW,UAAS,aAAa,GAAG,SAAS;AAAA,kBAChE;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAGA,gBAAI,OAAO,OAAO;AAChB,0BAAY;AAAA,gBACV,cAAc,OAAO,MAAM;AAAA,gBAC3B,kBAAkB,OAAO,MAAM;AAAA,cACjC;AAAA,YACF;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAAA,IACF,UAAE;AACA,aAAO,YAAY;AAAA,IACrB;AAEA,QAAI,cAAc,CAAC,mBAAmB,WAAW,SAAS,GAAG;AAC3D,MAAAA,KAAI,KAAK,wCAAwC,WAAW,MAAM,GAAG,GAAG,CAAC,GAAG;AAAA,IAC9E;AAEA,eAAW,CAAC,EAAE,EAAE,KAAK,kBAAkB;AACrC,YAAM,EAAE,MAAM,aAAa,UAAU,GAAG;AAAA,IAC1C;AAEA,UAAM,EAAE,MAAM,QAAQ,GAAI,YAAY,EAAE,OAAO,UAAU,IAAI,CAAC,EAAG;AAAA,EACnE;AACF;;;ACrSA,IAAMC,OAAM,aAAa,WAAW;AAEpC,IAAM,aAAa;AAiBZ,IAAM,YAAN,MAAqC;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA2B;AACrC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ;AACrB,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,iBAAiB,QAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,cAAqC;AAChD,IAAAA,KAAI,KAAK,8BAA8B;AACvC,UAAM,QAAQ,YAAY,IAAI;AAE9B,UAAM,WAAsB;AAAA,MAC1B,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,MACxC,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,IACnC;AAEA,QAAI;AACF,YAAM,MAAM,KAAK,KAAK,QAAQ;AAC9B,uBAAiB,SAAS,KAAK;AAC7B,YAAI,MAAM,SAAS,OAAQ;AAAA,MAC7B;AACA,MAAAA,KAAI,KAAK,2BAA2B,YAAY,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,IAAI;AAAA,IAC/E,SAAS,KAAK;AACZ,MAAAA,KAAI,KAAK,kCAAkC,GAAG;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,OAAO,KAAK,UAAqB,QAAsB,SAAoD;AACzG,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR,gBAAgB,EAAE,eAAe,KAAK;AAAA,IACxC;AACA,QAAI,KAAK,kBAAkB,CAAC,SAAS,WAAW;AAC9C,WAAK,kBAAkB,KAAK;AAAA,IAC9B;AACA,QAAI,SAAS,OAAO,QAAQ;AAC1B,WAAK,QAAQ,QAAQ;AAAA,IACvB;AAEA,IAAAA,KAAI,MAAM,sBAAsB,KAAK,KAAK,cAAc,SAAS,MAAM,EAAE;AAEzE,UAAM,WAAW,MAAM,MAAM,YAAY;AAAA,MACvC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB,UAAU,KAAK,MAAM;AAAA,MACxC;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK;AACtC,YAAM,IAAI,MAAM,oBAAoB,SAAS,MAAM,KAAK,SAAS,EAAE;AAAA,IACrE;AAEA,QAAI,CAAC,SAAS,MAAM;AAClB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAGA,UAAM,SAAS,SAAS,KAAK,UAAU;AACvC,UAAM,UAAU,IAAI,YAAY;AAChC,QAAI,SAAS;AAGb,UAAM,aAAa,CAAC,CAAC,KAAK,kBAAkB,CAAC,SAAS;AACtD,QAAI,aAAa;AACjB,QAAI,kBAAkB;AACtB,QAAI;AACJ,UAAM,mBAAmB,oBAAI,IAA6D;AAG1F,QAAI,kBAAkB;AACtB,QAAI,cAAc;AAClB,QAAI,aAAa;AACjB,QAAI,YAAY;AAChB,QAAI,WAAW;AACf,QAAI,UAAU;AAEd,aAAS,gBAAgB,KAAoD;AAC3E,YAAM,UAAiD,CAAC;AAExD,eAAS,IAAI,WAAW,IAAI,IAAI,QAAQ,KAAK;AAC3C,cAAM,KAAK,IAAI,CAAC;AAEhB,YAAI,SAAS;AACX,oBAAU;AACV;AAAA,QACF;AAEA,YAAI,OAAO,QAAQ,UAAU;AAC3B,oBAAU;AACV;AAAA,QACF;AAEA,YAAI,OAAO,KAAK;AACd,qBAAW,CAAC;AACZ;AAAA,QACF;AAEA,YAAI,SAAU;AAEd,YAAI,CAAC,iBAAiB;AACpB,cAAI,OAAO,KAAK;AACd,kBAAM,SAAS,IAAI,MAAM,GAAG,CAAC,EAAE,QAAQ;AACvC,gBAAI,OAAO,SAAS,GAAG,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,SAAS,YAAY,GAAG;AAClE,gCAAkB;AAAA,YACpB;AAAA,UACF;AACA;AAAA,QACF;AAEA,YAAI,OAAO,KAAK;AACd,cAAI,eAAe,EAAG,eAAc;AACpC;AAAA,QACF,WAAW,OAAO,KAAK;AACrB;AACA,cAAI,eAAe,KAAK,eAAe,GAAG;AACxC,kBAAM,SAAS,IAAI,MAAM,aAAa,IAAI,CAAC;AAC3C,gBAAI;AACF,oBAAM,MAAM,KAAK,MAAM,MAAM;AAC7B,kBAAI,IAAI,QAAQ,IAAI,MAAM;AACxB,wBAAQ,KAAK,EAAE,MAAM,IAAI,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,cACjD;AAAA,YACF,QAAQ;AAAA,YAER;AACA,0BAAc;AAAA,UAChB;AAAA,QACF,WAAW,OAAO,OAAO,eAAe,GAAG;AACzC,4BAAkB;AAAA,QACpB;AAAA,MACF;AAEA,kBAAY,IAAI;AAChB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAO,MAAM;AACX,YAAI,QAAQ,QAAS;AAErB,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,kBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,cAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,iBAAS,MAAM,IAAI,KAAK;AAExB,mBAAW,QAAQ,OAAO;AACxB,gBAAM,UAAU,KAAK,KAAK;AAC1B,cAAI,CAAC,WAAW,CAAC,QAAQ,WAAW,QAAQ,EAAG;AAE/C,gBAAM,OAAO,QAAQ,MAAM,CAAC;AAC5B,cAAI,SAAS,SAAU;AAEvB,cAAI;AACF,kBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,kBAAM,SAAS,OAAO,UAAU,CAAC;AACjC,gBAAI,CAAC,QAAQ;AAEX,kBAAI,OAAO,OAAO;AAChB,4BAAY;AAAA,kBACV,cAAc,OAAO,MAAM;AAAA,kBAC3B,kBAAkB,OAAO,MAAM;AAAA,gBACjC;AAAA,cACF;AACA;AAAA,YACF;AAEA,kBAAM,QAAQ,OAAO;AACrB,gBAAI,OAAO,SAAS;AAClB,kBAAI,YAAY;AACd,8BAAc,MAAM;AAEpB,sBAAM,WAAW,gBAAgB,UAAU;AAC3C,2BAAW,OAAO,UAAU;AAC1B,wBAAM,EAAE,MAAM,WAAW,SAAS,IAAI;AACtC,oCAAkB;AAAA,gBACpB;AAAA,cACF,OAAO;AACL,sBAAM,EAAE,MAAM,SAAS,OAAO,MAAM,QAAQ;AAAA,cAC9C;AAAA,YACF;AAEA,gBAAI,OAAO,YAAY;AACrB,yBAAW,MAAM,MAAM,YAAuG;AAC5H,sBAAM,MAAM,GAAG;AACf,oBAAI,GAAG,IAAI;AACT,mCAAiB,IAAI,KAAK,EAAE,IAAI,GAAG,IAAI,MAAM,GAAG,UAAU,QAAQ,IAAI,WAAW,GAAG,UAAU,aAAa,GAAG,CAAC;AAAA,gBACjH,OAAO;AACL,wBAAM,WAAW,iBAAiB,IAAI,GAAG;AACzC,sBAAI,UAAU;AACZ,wBAAI,GAAG,UAAU,KAAM,UAAS,QAAQ,GAAG,SAAS;AACpD,wBAAI,GAAG,UAAU,UAAW,UAAS,aAAa,GAAG,SAAS;AAAA,kBAChE;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAEA,gBAAI,OAAO,OAAO;AAChB,0BAAY;AAAA,gBACV,cAAc,OAAO,MAAM;AAAA,gBAC3B,kBAAkB,OAAO,MAAM;AAAA,cACjC;AAAA,YACF;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAAA,IACF,UAAE;AACA,aAAO,YAAY;AAAA,IACrB;AAEA,QAAI,cAAc,CAAC,mBAAmB,WAAW,SAAS,GAAG;AAC3D,MAAAA,KAAI,KAAK,wCAAwC,WAAW,MAAM,GAAG,GAAG,CAAC,GAAG;AAAA,IAC9E;AAEA,eAAW,CAAC,EAAE,EAAE,KAAK,kBAAkB;AACrC,YAAM,EAAE,MAAM,aAAa,UAAU,GAAG;AAAA,IAC1C;AAEA,UAAM,EAAE,MAAM,QAAQ,GAAI,YAAY,EAAE,OAAO,UAAU,IAAI,CAAC,EAAG;AAAA,EACnE;AACF;;;ACpQA,IAAAC,aAAsB;AAItB,IAAMC,OAAM,aAAa,aAAa;AAEtC,IAAM,mBAAmB;AACzB,IAAM,sBAAsB;AAC5B,IAAM,gBAAgB;AAEtB,IAAM,sBAAsB;AA8BrB,IAAM,cAAN,MAAuC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,KAAuB;AAAA,EACvB,aAAa;AAAA,EACb,iBAAuC;AAAA;AAAA,EAEvC,WAAW,oBAAI,IAA0B;AAAA,EACzC,iBAAiB;AAAA,EAEzB,YAAY,SAA6B;AACvC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AACA,QAAI,CAAC,QAAQ,SAAS;AACpB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU,QAAQ;AACvB,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,WAAW,QAAQ;AACxB,SAAK,QAAQ,QAAQ;AACrB,SAAK,UAAU,QAAQ;AAAA,EACzB;AAAA;AAAA,EAGA,QAAc;AACZ,QAAI,KAAK,IAAI;AACX,MAAAC,KAAI,MAAM,4BAA4B;AACtC,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AACA,SAAK,aAAa;AAClB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,SAAwB;AAC5B,IAAAA,KAAI,KAAK,8BAA8B;AACvC,UAAM,QAAQ,YAAY,IAAI;AAC9B,QAAI;AACF,YAAM,KAAK,iBAAiB;AAC5B,MAAAA,KAAI,KAAK,2BAA2B,YAAY,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,IAAI;AAAA,IAC/E,SAAS,KAAK;AACZ,MAAAA,KAAI,KAAK,kCAAkC,GAAG;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,OAAO,WAAW,MAAc,QAA8C;AAC5E,IAAAA,KAAI,MAAM,kBAAkB,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG;AAEhD,UAAM,KAAK,iBAAiB;AAE5B,QAAI,CAAC,KAAK,MAAM,KAAK,GAAG,eAAe,WAAAC,QAAU,MAAM;AACrD,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAEA,UAAM,YAAY,OAAO,EAAE,KAAK,cAAc,IAAI,KAAK,IAAI,CAAC;AAC5D,UAAM,MAAoB,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,OAAO,MAAM,MAAM,KAAK;AAC7E,SAAK,SAAS,IAAI,WAAW,GAAG;AAGhC,UAAM,UAAmC;AAAA,MACvC,UAAU,KAAK;AAAA,MACf,YAAY;AAAA,MACZ,OAAO,EAAE,MAAM,MAAM,IAAI,KAAK,QAAQ;AAAA,MACtC,eAAe;AAAA,QACb,WAAW;AAAA,QACX,UAAU;AAAA,QACV,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ;AAEA,QAAI,KAAK,UAAU;AACjB,cAAQ,WAAW,KAAK;AAAA,IAC1B;AAGA,QAAI,KAAK,UAAU,UAAa,KAAK,YAAY,QAAW;AAC1D,YAAM,YAAqC,CAAC;AAC5C,UAAI,KAAK,UAAU,OAAW,WAAU,QAAQ,KAAK;AACrD,UAAI,KAAK,YAAY,OAAW,WAAU,UAAU,KAAK;AACzD,cAAQ,oBAAoB;AAAA,IAC9B;AAGA,UAAM,UAAU,MAAM;AACpB,UAAI,OAAO;AACX,UAAI,OAAO;AAEX,UAAI,KAAK,IAAI,eAAe,WAAAA,QAAU,MAAM;AAC1C,YAAI;AACF,eAAK,GAAG,KAAK,KAAK,UAAU,EAAE,YAAY,WAAW,QAAQ,KAAK,CAAC,CAAC;AAAA,QACtE,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAGzD,SAAK,GAAG,KAAK,KAAK,UAAU,OAAO,CAAC;AAGpC,QAAI;AACF,aAAO,MAAM;AACX,YAAI,QAAQ,QAAS;AACrB,YAAI,IAAI,MAAO,OAAM,IAAI;AAEzB,YAAI,IAAI,OAAO,SAAS,GAAG;AACzB,gBAAM,IAAI,OAAO,MAAM;AACvB;AAAA,QACF;AAEA,YAAI,IAAI,KAAM;AAGd,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,cAAI,OAAO;AAAA,QACb,CAAC;AACD,YAAI,OAAO;AAAA,MACb;AAGA,aAAO,IAAI,OAAO,SAAS,GAAG;AAC5B,cAAM,IAAI,OAAO,MAAM;AAAA,MACzB;AAAA,IACF,UAAE;AACA,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,WAAK,SAAS,OAAO,SAAS;AAAA,IAChC;AAAA,EACF;AAAA;AAAA,EAGQ,mBAAkC;AACxC,QAAI,KAAK,cAAc,KAAK,IAAI,eAAe,WAAAA,QAAU,MAAM;AAC7D,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAGA,QAAI,KAAK,eAAgB,QAAO,KAAK;AAErC,SAAK,iBAAiB,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,YAAM,MAAM,GAAG,gBAAgB,YAAY,KAAK,MAAM,qBAAqB,KAAK,UAAU;AAC1F,MAAAD,KAAI,MAAM,2BAA2B;AAErC,WAAK,KAAK,IAAI,WAAAC,QAAU,GAAG;AAE3B,WAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,aAAK,aAAa;AAClB,aAAK,iBAAiB;AACtB,QAAAD,KAAI,KAAK,8BAA8B;AACvC,gBAAQ;AAAA,MACV,CAAC;AAED,WAAK,GAAG,GAAG,WAAW,CAAC,SAAS;AAC9B,YAAI;AACF,gBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,eAAK,cAAc,GAAG;AAAA,QACxB,SAAS,KAAK;AACZ,UAAAA,KAAI,MAAM,qCAAqC,GAAG;AAAA,QACpD;AAAA,MACF,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,QAAQ;AAC3B,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,QAAAA,KAAI,MAAM,6BAA6B,KAAK;AAE5C,mBAAW,OAAO,KAAK,SAAS,OAAO,GAAG;AACxC,cAAI,QAAQ;AACZ,cAAI,OAAO;AAAA,QACb;AACA,aAAK,aAAa;AAClB,aAAK,iBAAiB;AACtB,eAAO,KAAK;AAAA,MACd,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AACpC,QAAAA,KAAI,MAAM,8BAA8B,IAAI,IAAI,OAAO,SAAS,CAAC,EAAE;AACnE,aAAK,aAAa;AAClB,aAAK,iBAAiB;AAEtB,mBAAW,OAAO,KAAK,SAAS,OAAO,GAAG;AACxC,cAAI,OAAO;AACX,cAAI,OAAO;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,cAAc,KAAoC;AACxD,UAAM,YAAY,IAAI;AACtB,QAAI,CAAC,UAAW;AAEhB,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AACvC,QAAI,CAAC,IAAK;AAEV,UAAM,OAAO,IAAI;AAEjB,QAAI,SAAS,SAAS;AACpB,YAAM,MAAM,IAAI;AAChB,UAAI,KAAK;AACP,cAAM,MAAM,OAAO,KAAK,KAAK,QAAQ;AACrC,YAAI,OAAO,KAAK,GAAG;AACnB,YAAI,OAAO;AAAA,MACb;AAAA,IACF,WAAW,SAAS,QAAQ;AAC1B,MAAAA,KAAI,MAAM,+BAA+B,SAAS,KAAK,IAAI,OAAO,MAAM,kBAAkB;AAC1F,UAAI,OAAO;AACX,UAAI,OAAO;AAAA,IACb,WAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,IAAI,SAAmB;AACxC,MAAAA,KAAI,MAAM,sBAAsB,SAAS,KAAK,QAAQ,EAAE;AACxD,UAAI,QAAQ,IAAI,MAAM,uBAAuB,QAAQ,EAAE;AACvD,UAAI,OAAO;AAAA,IACb;AAAA,EACF;AACF;;;AC9QA,IAAAE,aAAsB;AAItB,IAAMC,OAAM,aAAa,aAAa;AAEtC,IAAM,mBAAmB;AACzB,IAAMC,uBAAsB;AAkCrB,SAAS,kBAAkB,MAAc,aAAoC;AAClF,QAAM,WAA0B,CAAC;AACjC,MAAI,IAAI;AACR,MAAI,cAAc;AAElB,SAAO,IAAI,KAAK,QAAQ;AAEtB,QAAI,KAAK,CAAC,MAAM,OAAO,KAAK,WAAW,UAAU,CAAC,GAAG;AAEnD,UAAI,aAAa;AACf,iBAAS,KAAK,EAAE,MAAM,aAAa,MAAM,YAAY,KAAK,EAAE,CAAC;AAC7D,sBAAc;AAAA,MAChB;AAGA,YAAM,eAAe,KAAK,QAAQ,cAAc,CAAC;AACjD,UAAI,iBAAiB,IAAI;AAEvB,uBAAe,KAAK,CAAC;AACrB;AACA;AAAA,MACF;AAEA,YAAM,YAAY,eAAe,aAAa;AAC9C,YAAM,UAAU,KAAK,QAAQ,KAAK,SAAS;AAC3C,UAAI,YAAY,IAAI;AAClB,uBAAe,KAAK,CAAC;AACrB;AACA;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,UAAU,WAAW,OAAO;AAG9C,YAAM,WAAW,KAAK,QAAQ,KAAK,OAAO;AAC1C,UAAI,aAAa,IAAI;AACnB,uBAAe,KAAK,CAAC;AACrB;AACA;AAAA,MACF;AAGA,YAAM,aAAa;AACnB,YAAM,eAAe,KAAK,QAAQ,YAAY,WAAW,CAAC;AAC1D,UAAI,iBAAiB,IAAI;AAEvB,cAAMC,aAAY,KAAK,UAAU,WAAW,CAAC,EAAE,KAAK;AACpD,YAAIA,YAAW;AACb,mBAAS,KAAK,EAAE,MAAM,MAAMA,WAAU,CAAC;AAAA,QACzC;AACA,YAAI,KAAK;AACT;AAAA,MACF;AAEA,YAAM,YAAY,KAAK,UAAU,WAAW,GAAG,YAAY,EAAE,KAAK;AAClE,UAAI,WAAW;AACb,iBAAS,KAAK,EAAE,MAAM,MAAM,UAAU,CAAC;AAAA,MACzC;AAEA,UAAI,eAAe,WAAW;AAC9B;AAAA,IACF;AAEA,mBAAe,KAAK,CAAC;AACrB;AAAA,EACF;AAGA,MAAI,YAAY,KAAK,GAAG;AACtB,aAAS,KAAK,EAAE,MAAM,aAAa,MAAM,YAAY,KAAK,EAAE,CAAC;AAAA,EAC/D;AAEA,SAAO;AACT;AAEO,IAAM,cAAN,MAAuC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGjB,IAAI,kBAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGQ,cAAc,oBAAI,IAAuB;AAAA,EACzC,kBAAkB,oBAAI,IAA2B;AAAA;AAAA,EAEjD,cAAc,oBAAI,IAAwB;AAAA,EAElD,YAAY,SAA6B;AACvC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AAEA,SAAK,SAAS,QAAQ;AACtB,SAAK,aAAa,QAAQ,cAAcD;AAExC,QAAI,OAAO,QAAQ,UAAU,UAAU;AAErC,WAAK,gBAAgB;AACrB,YAAM,OAAO,QAAQ,mBAAmB;AACxC,WAAK,SAAS,EAAE,CAAC,IAAI,GAAG,QAAQ,MAAM;AACtC,WAAK,cAAc;AAAA,IACrB,OAAO;AAEL,WAAK,gBAAgB;AACrB,WAAK,SAAS,EAAE,GAAG,QAAQ,MAAM;AACjC,YAAM,OAAO,OAAO,KAAK,KAAK,MAAM;AACpC,UAAI,KAAK,WAAW,GAAG;AACrB,cAAM,IAAI,MAAM,oDAAoD;AAAA,MACtE;AACA,WAAK,cAAc,QAAQ,mBAAmB,KAAK,CAAC;AAAA,IACtD;AAAA,EACF;AAAA;AAAA,EAGA,QAAc;AACZ,eAAW,CAAC,MAAM,EAAE,KAAK,KAAK,aAAa;AACzC,MAAAD,KAAI,MAAM,0BAA0B,IAAI,GAAG;AAC3C,SAAG,MAAM;AAAA,IACX;AACA,SAAK,YAAY,MAAM;AACvB,SAAK,gBAAgB,MAAM;AAC3B,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA;AAAA,EAGA,MAAM,SAAwB;AAC5B,IAAAA,KAAI,KAAK,+BAA+B;AACxC,UAAM,QAAQ,YAAY,IAAI;AAC9B,QAAI;AACF,YAAM,QAAQ,IAAI,OAAO,KAAK,KAAK,MAAM,EAAE,IAAI,CAAC,SAAS,KAAK,iBAAiB,IAAI,CAAC,CAAC;AACrF,MAAAA,KAAI,KAAK,2BAA2B,YAAY,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,IAAI;AAAA,IAC/E,SAAS,KAAK;AACZ,MAAAA,KAAI,KAAK,kCAAkC,GAAG;AAAA,IAChD;AAAA,EACF;AAAA;AAAA,EAGA,UAAU,MAAsB;AAC9B,WAAO,kBAAkB,MAAM,KAAK,WAAW,EAC5C,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,GAAG,EACR,QAAQ,QAAQ,GAAG,EACnB,KAAK;AAAA,EACV;AAAA,EAEA,OAAO,WAAW,MAAc,QAA8C;AAC5E,QAAI,QAAQ,QAAS;AAErB,UAAM,cAAc,KAAK,gBACrB,kBAAkB,MAAM,KAAK,WAAW,IACxC,CAAC,EAAE,MAAM,KAAK,aAAa,KAAK,CAAC;AAKrC,UAAM,WAA+B,CAAC;AACtC,eAAW,OAAO,aAAa;AAC7B,UAAI,CAAC,IAAI,KAAK,KAAK,EAAG;AACtB,UAAI,CAAC,SAAS,KAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG;AACnD,iBAAS,SAAS,SAAS,CAAC,EAAE,QAAQ,IAAI;AAAA,MAC5C,OAAO;AACL,iBAAS,KAAK,GAAG;AAAA,MACnB;AAAA,IACF;AAIA,UAAM,eAAe,KAAK,MAAM,KAAK,aAAa,GAAG,IAAI;AACzD,UAAM,UAAU,OAAO,MAAM,YAAY;AAEzC,QAAI,WAA0B;AAC9B,eAAW,WAAW,UAAU;AAC9B,UAAI,QAAQ,QAAS;AACrB,YAAM,OAAO,KAAK,OAAO,QAAQ,IAAI,IAAI,QAAQ,OAAO,KAAK;AAG7D,UAAI,aAAa,QAAQ,SAAS,UAAU;AAC1C,cAAM;AAAA,MACR;AACA,iBAAW;AAEX,aAAO,KAAK,kBAAkB,MAAM,QAAQ,MAAM,MAAM;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,OAAe,kBACb,MACA,MACA,QACwB;AACxB,IAAAA,KAAI,MAAM,iBAAiB,IAAI,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG;AAE1D,UAAM,KAAK,iBAAiB,IAAI;AAEhC,UAAM,KAAK,KAAK,YAAY,IAAI,IAAI;AACpC,QAAI,CAAC,MAAM,GAAG,eAAe,WAAAG,QAAU,MAAM;AAC3C,YAAM,IAAI,MAAM,kDAAkD,IAAI,GAAG;AAAA,IAC3E;AAEA,UAAM,QAAoB,EAAE,QAAQ,CAAC,GAAG,SAAS,OAAO,OAAO,MAAM,MAAM,KAAK;AAChF,SAAK,YAAY,IAAI,MAAM,KAAK;AAGhC,UAAM,UAAU,MAAM;AACpB,YAAM,UAAU;AAChB,YAAM,OAAO;AACb,UAAI,GAAG,eAAe,WAAAA,QAAU,MAAM;AACpC,YAAI;AACF,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC;AACzC,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC;AAAA,QAC3C,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAGzD,OAAG,KAAK,KAAK,UAAU,EAAE,MAAM,SAAS,KAAK,CAAC,CAAC;AAC/C,OAAG,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC;AAEzC,QAAI;AACF,aAAO,MAAM;AACX,YAAI,QAAQ,QAAS;AACrB,YAAI,MAAM,MAAO,OAAM,MAAM;AAE7B,YAAI,MAAM,OAAO,SAAS,GAAG;AAC3B,gBAAM,MAAM,OAAO,MAAM;AACzB;AAAA,QACF;AAEA,YAAI,MAAM,QAAS;AAGnB,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,gBAAM,OAAO;AAAA,QACf,CAAC;AACD,cAAM,OAAO;AAAA,MACf;AAGA,aAAO,MAAM,OAAO,SAAS,GAAG;AAC9B,cAAM,MAAM,OAAO,MAAM;AAAA,MAC3B;AAAA,IACF,UAAE;AACA,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,WAAK,YAAY,OAAO,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAGQ,iBAAiB,MAA6B;AACpD,UAAM,WAAW,KAAK,YAAY,IAAI,IAAI;AAC1C,QAAI,YAAY,SAAS,eAAe,WAAAA,QAAU,MAAM;AACtD,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAGA,UAAM,UAAU,KAAK,gBAAgB,IAAI,IAAI;AAC7C,QAAI,QAAS,QAAO;AAEpB,UAAM,QAAQ,KAAK,OAAO,IAAI;AAC9B,QAAI,CAAC,OAAO;AACV,aAAO,QAAQ,OAAO,IAAI,MAAM,8CAA8C,IAAI,GAAG,CAAC;AAAA,IACxF;AAEA,UAAM,UAAU,IAAI,QAAc,CAAC,SAAS,WAAW;AACrD,YAAM,MAAM,GAAG,gBAAgB,UAAU,mBAAmB,KAAK,CAAC,kCAAkC,KAAK,UAAU;AACnH,MAAAH,KAAI,MAAM,+BAA+B,IAAI,MAAM,KAAK,EAAE;AAE1D,YAAM,KAAK,IAAI,WAAAG,QAAU,KAAK;AAAA,QAC5B,SAAS;AAAA,UACP,eAAe,SAAS,KAAK,MAAM;AAAA,QACrC;AAAA,MACF,CAAC;AAED,SAAG,GAAG,QAAQ,MAAM;AAClB,aAAK,YAAY,IAAI,MAAM,EAAE;AAC7B,aAAK,gBAAgB,OAAO,IAAI;AAChC,QAAAH,KAAI,KAAK,qCAAqC,IAAI,MAAM,KAAK,GAAG;AAChE,gBAAQ;AAAA,MACV,CAAC;AAED,SAAG,GAAG,WAAW,CAAC,MAAM,aAAa;AACnC,cAAM,QAAQ,KAAK,YAAY,IAAI,IAAI;AACvC,YAAI,CAAC,MAAO;AAEZ,YAAI,UAAU;AAEZ,gBAAM,MAAM,OAAO,SAAS,IAAI,IAAI,OAAO,OAAO,KAAK,IAAmB;AAC1E,gBAAM,OAAO,KAAK,GAAG;AACrB,gBAAM,OAAO;AAAA,QACf,OAAO;AAEL,cAAI;AACF,kBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,gBAAI,IAAI,SAAS,WAAW;AAC1B,oBAAM,UAAU;AAChB,oBAAM,OAAO;AAAA,YACf,WAAW,IAAI,SAAS,aAAa,IAAI,SAAS,SAAS;AACzD,cAAAA,KAAI,KAAK,aAAa,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,eAAe,IAAI,WAAW,KAAK,UAAU,GAAG,CAAC,EAAE;AAAA,YACrG;AAAA,UACF,QAAQ;AACN,YAAAA,KAAI,KAAK,yCAAyC,IAAI,GAAG;AAAA,UAC3D;AAAA,QACF;AAAA,MACF,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,QAAAA,KAAI,MAAM,6BAA6B,IAAI,MAAM,KAAK;AACtD,cAAM,QAAQ,KAAK,YAAY,IAAI,IAAI;AACvC,YAAI,OAAO;AACT,gBAAM,QAAQ;AACd,gBAAM,OAAO;AAAA,QACf;AACA,aAAK,YAAY,OAAO,IAAI;AAC5B,aAAK,gBAAgB,OAAO,IAAI;AAChC,eAAO,KAAK;AAAA,MACd,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AAC/B,QAAAA,KAAI,MAAM,8BAA8B,IAAI,MAAM,IAAI,IAAI,OAAO,SAAS,CAAC,EAAE;AAC7E,aAAK,YAAY,OAAO,IAAI;AAC5B,aAAK,gBAAgB,OAAO,IAAI;AAChC,cAAM,QAAQ,KAAK,YAAY,IAAI,IAAI;AACvC,YAAI,OAAO;AACT,gBAAM,UAAU;AAChB,gBAAM,OAAO;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,SAAK,gBAAgB,IAAI,MAAM,OAAO;AACtC,WAAO;AAAA,EACT;AACF;;;AC/XA,IAAAI,aAAsB;AAKtB,IAAMC,OAAM,aAAa,aAAa;AAEtC,IAAMC,yBAAwB;AAavB,IAAM,cAAN,MAAuC;AAAA,EAC3B;AAAA,EAEjB,YAAY,SAA6B;AACvC,QAAI,CAAC,QAAQ,WAAW;AACtB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,QAAI,CAAC,QAAQ,YAAY;AACvB,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,aAAa,SAAuC;AAClD,UAAM,WAAW,SAAS,YAAY,KAAK,QAAQ,YAAY;AAC/D,WAAO,IAAI,kBAAkB,KAAK,SAAS,QAAQ;AAAA,EACrD;AACF;AAEA,IAAM,oBAAN,cAAgC,cAAc;AAAA,EACpC,KAAuB;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,eAAyB,CAAC;AAAA,EAC1B,iBAAwD;AAAA,EACxD;AAAA,EAER,YAAY,SAA6B,UAAkB;AACzD,UAAM;AACN,SAAK,YAAY,QAAQ;AACzB,SAAK,aAAa,QAAQ;AAC1B,SAAK,WAAW;AAChB,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,UAAU,OAAqB;AAC7B,QAAI,KAAK,QAAS;AAElB,QAAI,CAAC,KAAK,QAAQ;AAChB,WAAK,aAAa,KAAK,KAAK;AAC5B;AAAA,IACF;AAEA,QAAI,KAAK,IAAI,eAAe,WAAAC,QAAU,MAAM;AAC1C,WAAK,GAAG,KAAK,KAAK;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,UAAkB,SAA4C;AACxE,QAAI,KAAK,QAAS;AAClB,SAAK,WAAW;AAEhB,UAAM,SAAiC,EAAE,MAAM,UAAU,SAAS;AAClE,QAAI,SAAS,cAAc;AACzB,aAAO,QAAQ;AAAA,IACjB;AAEA,QAAI,KAAK,IAAI,eAAe,WAAAA,QAAU,MAAM;AAC1C,WAAK,GAAG,KAAK,KAAK,UAAU,MAAM,CAAC;AACnC,MAAAF,KAAI,KAAK,+BAA+B,QAAQ,GAAG,SAAS,eAAe,oBAAoB,EAAE,EAAE;AAAA,IACrG;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,eAAe,CAAC;AACrB,SAAK,cAAc;AAEnB,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AAEA,IAAAA,KAAI,MAAM,2BAA2B;AAAA,EACvC;AAAA,EAEQ,UAAgB;AAEtB,UAAM,MAAM,KAAK,UAAU,QAAQ,OAAO,EAAE,IAAI;AAChD,UAAM,QAAQ,IAAI,QAAQ,YAAY,QAAQ,EAAE,QAAQ,WAAW,OAAO;AAE1E,IAAAA,KAAI,MAAM,+BAA+B,KAAK,EAAE;AAEhD,SAAK,KAAK,IAAI,WAAAE,QAAU,KAAK;AAE7B,SAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,MAAAF,KAAI,KAAK,kCAAkC;AAG3C,YAAM,SAAiC;AAAA,QACrC,MAAM;AAAA,QACN,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,MACpB;AACA,UAAI,KAAK,cAAc;AACrB,eAAO,QAAQ;AAAA,MACjB;AACA,WAAK,GAAI,KAAK,KAAK,UAAU,MAAM,CAAC;AAAA,IACtC,CAAC;AAED,SAAK,GAAG,GAAG,WAAW,CAAC,MAAM,aAAa;AACxC,UAAI,SAAU;AACd,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,aAAK,cAAc,GAAG;AAAA,MACxB,SAAS,KAAK;AACZ,QAAAA,KAAI,MAAM,yCAAyC,GAAG;AAAA,MACxD;AAAA,IACF,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,QAAQ;AAC3B,MAAAA,KAAI,MAAM,iCAAiC,GAAG;AAC9C,WAAK,KAAK,SAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,IACxE,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AACpC,MAAAA,KAAI,MAAM,kCAAkC,IAAI,IAAI,OAAO,SAAS,CAAC,EAAE;AACvE,WAAK,SAAS;AACd,WAAK,cAAc;AAGnB,UAAI,CAAC,KAAK,SAAS;AACjB,QAAAA,KAAI,KAAK,qDAAqD;AAC9D,mBAAW,MAAM;AACf,cAAI,CAAC,KAAK,QAAS,MAAK,QAAQ;AAAA,QAClC,GAAG,GAAI;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,cAAc,KAAoC;AACxD,UAAM,OAAO,IAAI;AAEjB,QAAI,SAAS,SAAS;AACpB,WAAK,YAAY,GAAG;AAAA,IACtB,WAAW,SAAS,iBAAiB;AACnC,WAAK,oBAAoB,GAAG;AAAA,IAC9B,WAAW,SAAS,aAAa;AAC/B,WAAK,eAAe,GAAG;AAAA,IACzB,WAAW,SAAS,QAAQ;AAAA,IAE5B,WAAW,SAAS,SAAS;AAC3B,YAAM,UAAU,IAAI;AACpB,YAAM,WAAY,IAAI,YAChB,OAAO,YAAY,WAAW,UAAU,KAAK,UAAU,OAAO,MAC/D;AACL,MAAAA,KAAI,MAAM,uBAAuB,QAAQ,EAAE;AAC3C,WAAK,KAAK,SAAS,IAAI,MAAM,QAAQ,CAAC;AAAA,IACxC;AAAA,EACF;AAAA,EAEQ,YAAY,KAAoC;AACtD,UAAM,WAAW,IAAI;AACrB,UAAM,OAAO,IAAI;AACjB,IAAAA,KAAI,KAAK,iCAAiC,QAAQ,cAAc,IAAI,EAAE;AAEtE,SAAK,SAAS;AAGd,eAAW,OAAO,KAAK,cAAc;AACnC,UAAI,KAAK,IAAI,eAAe,WAAAE,QAAU,MAAM;AAC1C,aAAK,GAAG,KAAK,GAAG;AAAA,MAClB;AAAA,IACF;AACA,SAAK,eAAe,CAAC;AAErB,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,oBAAoB,KAAoC;AAC9D,UAAM,OAAQ,IAAI,QAAmB;AACrC,UAAM,UAAW,IAAI,YAAwB;AAC7C,UAAM,WAAW,IAAI;AACrB,UAAM,YAAY,IAAI;AAEtB,QAAI,CAAC,KAAM;AAEX,QAAI,WAAW,cAAc,QAAW;AACtC,MAAAF,KAAI,KAAK,cAAc,UAAU,QAAQ,CAAC,CAAC,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG;AAAA,IACxE;AAEA,SAAK,KAAK,iBAAiB;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,UAAU,YAAY;AAAA,IACrC,CAA+B;AAAA,EACjC;AAAA,EAEQ,eAAe,KAAoC;AACzD,UAAM,QAAQ,IAAI;AAClB,IAAAA,KAAI,MAAM,cAAc,KAAK,EAAE;AAE/B,QAAI,UAAU,gBAAgB;AAI5B,WAAK,KAAK,iBAAiB;AAAA,QACzB,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAA+B;AAAA,IACjC;AAAA,EACF;AAAA,EAEQ,iBAAuB;AAC7B,SAAK,cAAc;AACnB,SAAK,iBAAiB,YAAY,MAAM;AACtC,UAAI,KAAK,IAAI,eAAe,WAAAE,QAAU,MAAM;AAC1C,aAAK,GAAG,KAAK,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC;AAAA,MAC/C;AAAA,IACF,GAAGD,sBAAqB;AAAA,EAC1B;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AACF;;;ACrPA,IAAAE,aAAsB;AAKtB,IAAMC,OAAM,aAAa,aAAa;AA6B/B,IAAM,cAAN,MAAuC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,KAAuB;AAAA,EACvB,iBAAuC;AAAA,EACvC,aAAgC;AAAA;AAAA,EAEhC,UAAyB,QAAQ,QAAQ;AAAA,EACzC;AAAA;AAAA,EAGR,IAAI,kBAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAY,SAA6B;AACvC,QAAI,CAAC,QAAQ,WAAW;AACtB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,QAAI,CAAC,QAAQ,YAAY;AACvB,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,QAAI,CAAC,QAAQ,UAAU,OAAO,KAAK,QAAQ,MAAM,EAAE,WAAW,GAAG;AAC/D,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAEA,SAAK,YAAY,QAAQ;AACzB,SAAK,aAAa,QAAQ;AAC1B,SAAK,SAAS,EAAE,GAAG,QAAQ,OAAO;AAClC,SAAK,cAAc,QAAQ,mBAAmB,OAAO,KAAK,KAAK,MAAM,EAAE,CAAC;AACxE,SAAK,QAAQ,QAAQ,SAAS;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,SAAwB;AAC5B,IAAAA,KAAI,KAAK,8BAA8B;AACvC,UAAM,QAAQ,YAAY,IAAI;AAC9B,QAAI;AACF,YAAM,KAAK,iBAAiB;AAC5B,MAAAA,KAAI,KAAK,2BAA2B,YAAY,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,IAAI;AAAA,IAC/E,SAAS,KAAK;AACZ,MAAAA,KAAI,KAAK,kCAAkC,GAAG;AAAA,IAChD;AAAA,EACF;AAAA;AAAA,EAGA,QAAc;AACZ,QAAI,KAAK,IAAI;AACX,MAAAA,KAAI,MAAM,uBAAuB;AACjC,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AACA,SAAK,iBAAiB;AACtB,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGA,UAAU,MAAsB;AAC9B,WAAO,kBAAkB,MAAM,KAAK,WAAW,EAC5C,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,GAAG,EACR,QAAQ,QAAQ,GAAG,EACnB,KAAK;AAAA,EACV;AAAA,EAEA,OAAO,WAAW,MAAc,QAA8C;AAC5E,QAAI,QAAQ,QAAS;AAErB,UAAM,cAAc,kBAAkB,MAAM,KAAK,WAAW;AAK5D,UAAM,WAA+B,CAAC;AACtC,eAAW,OAAO,aAAa;AAC7B,UAAI,CAAC,IAAI,KAAK,KAAK,EAAG;AACtB,UAAI,CAAC,SAAS,KAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG;AACnD,iBAAS,SAAS,SAAS,CAAC,EAAE,QAAQ,IAAI;AAAA,MAC5C,OAAO;AACL,iBAAS,KAAK,GAAG;AAAA,MACnB;AAAA,IACF;AAIA,UAAM,eAAe,KAAK,MAAM,OAAQ,GAAG,IAAI;AAC/C,UAAM,UAAU,OAAO,MAAM,YAAY;AAEzC,QAAI,WAA0B;AAC9B,eAAW,WAAW,UAAU;AAC9B,UAAI,QAAQ,QAAS;AACrB,YAAM,OAAO,KAAK,OAAO,QAAQ,IAAI,IAAI,QAAQ,OAAO,KAAK;AAG7D,UAAI,aAAa,QAAQ,SAAS,UAAU;AAC1C,cAAM;AAAA,MACR;AACA,iBAAW;AAEX,aAAO,KAAK,kBAAkB,MAAM,QAAQ,MAAM,MAAM;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,OAAe,kBACb,MACA,MACA,QACwB;AACxB,QAAI,QAAQ,QAAS;AAKrB,UAAM,KAAK;AACX,QAAI,QAAQ,QAAS;AAGrB,SAAK,UAAU,IAAI,QAAQ,CAAC,MAAM;AAAE,WAAK,iBAAiB;AAAA,IAAG,CAAC;AAE9D,UAAM,KAAK,iBAAiB;AAE5B,UAAM,KAAK,KAAK;AAChB,QAAI,CAAC,MAAM,GAAG,eAAe,WAAAC,QAAU,MAAM;AAC3C,WAAK,iBAAiB;AACtB,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AAEA,UAAM,QAAoB,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,SAAS,OAAO,OAAO,MAAM,MAAM,KAAK;AAC7F,SAAK,aAAa;AAGlB,UAAM,UAAU,MAAM;AACpB,YAAM,OAAO;AACb,YAAM,OAAO;AACb,UAAI,GAAG,eAAe,WAAAA,QAAU,MAAM;AACpC,YAAI;AACF,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC;AAAA,QAC3C,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAGzD,UAAM,cAAc,KAAK,OAAO,IAAI;AACpC,UAAM,MAA+B,EAAE,KAAK;AAC5C,QAAI,aAAa;AACf,UAAI,QAAQ,YAAY;AACxB,UAAI,YAAY,YAAY;AAC5B,UAAI,QAAQ,KAAK;AAAA,IACnB;AACA,IAAAD,KAAI,KAAK,aAAa,IAAI,YAAY,aAAa,SAAS,SAAS,cAAc,aAAa,YAAY,SAAS,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG;AAC9I,OAAG,KAAK,KAAK,UAAU,GAAG,CAAC;AAE3B,QAAI;AACF,aAAO,MAAM;AACX,YAAI,QAAQ,QAAS;AACrB,YAAI,MAAM,MAAO,OAAM,MAAM;AAE7B,YAAI,MAAM,OAAO,SAAS,GAAG;AAC3B,gBAAM,MAAM,OAAO,MAAM;AACzB;AAAA,QACF;AAEA,YAAI,MAAM,KAAM;AAGhB,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,gBAAM,OAAO;AAAA,QACf,CAAC;AACD,cAAM,OAAO;AAAA,MACf;AAGA,aAAO,MAAM,OAAO,SAAS,GAAG;AAC9B,cAAM,MAAM,OAAO,MAAM;AAAA,MAC3B;AAAA,IACF,UAAE;AACA,cAAQ,oBAAoB,SAAS,OAAO;AAE5C,UAAI,KAAK,eAAe,OAAO;AAC7B,aAAK,aAAa;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,mBAAkC;AACxC,QAAI,KAAK,MAAM,KAAK,GAAG,eAAe,WAAAC,QAAU,MAAM;AACpD,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAGA,QAAI,KAAK,eAAgB,QAAO,KAAK;AAErC,SAAK,iBAAiB,IAAI,QAAc,CAAC,SAAS,WAAW;AAE3D,YAAM,MAAM,KAAK,UAAU,QAAQ,OAAO,EAAE,IAAI;AAChD,YAAM,QAAQ,IAAI,QAAQ,YAAY,QAAQ,EAAE,QAAQ,WAAW,OAAO;AAE1E,MAAAD,KAAI,MAAM,+BAA+B,KAAK,EAAE;AAEhD,YAAM,KAAK,IAAI,WAAAC,QAAU,KAAK;AAE9B,SAAG,GAAG,QAAQ,MAAM;AAClB,aAAK,KAAK;AACV,aAAK,iBAAiB;AAGtB,cAAM,eAAe,KAAK,OAAO,KAAK,WAAW;AACjD,cAAM,MAA+B;AAAA,UACnC,aAAa,KAAK;AAAA,QACpB;AACA,YAAI,cAAc;AAChB,cAAI,SAAS;AAAA,YACX,OAAO,aAAa;AAAA,YACpB,WAAW,aAAa;AAAA,YACxB,OAAO,KAAK;AAAA,UACd;AAAA,QACF;AACA,WAAG,KAAK,KAAK,UAAU,GAAG,CAAC;AAE3B,QAAAD,KAAI,KAAK,kCAAkC;AAC3C,gBAAQ;AAAA,MACV,CAAC;AAED,SAAG,GAAG,WAAW,CAAC,MAAM,aAAa;AACnC,cAAM,QAAQ,KAAK;AACnB,YAAI,CAAC,MAAO;AAEZ,YAAI,UAAU;AAEZ,gBAAM,MAAM,OAAO,SAAS,IAAI,IAAI,OAAO,OAAO,KAAK,IAAmB;AAC1E,gBAAM,OAAO,KAAK,GAAG;AACrB,gBAAM,OAAO;AAAA,QACf,OAAO;AAEL,cAAI;AACF,kBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,gBAAI,IAAI,SAAS,QAAQ;AACvB,oBAAM,OAAO;AACb,oBAAM,OAAO;AACb,mBAAK,iBAAiB;AAAA,YACxB,WAAW,IAAI,SAAS,WAAW;AACjC,oBAAM,UAAU;AAChB,oBAAM,OAAO;AACb,oBAAM,OAAO;AACb,mBAAK,iBAAiB;AAAA,YACxB,WAAW,IAAI,SAAS,cAAc;AACpC,cAAAA,KAAI,MAAM,oBAAqB,IAAI,MAAiB,MAAM,GAAG,EAAE,CAAC,GAAG;AAAA,YACrE,WAAW,IAAI,SAAS,SAAS;AAC/B,oBAAM,WAAY,IAAI,WAAsB;AAC5C,cAAAA,KAAI,MAAM,uBAAuB,QAAQ,EAAE;AAC3C,oBAAM,QAAQ,IAAI,MAAM,QAAQ;AAChC,oBAAM,OAAO;AAAA,YACf;AAAA,UACF,QAAQ;AACN,YAAAA,KAAI,KAAK,sCAAsC;AAAA,UACjD;AAAA,QACF;AAAA,MACF,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,QAAAA,KAAI,MAAM,iCAAiC,KAAK;AAChD,cAAM,QAAQ,KAAK;AACnB,YAAI,OAAO;AACT,gBAAM,QAAQ;AACd,gBAAM,OAAO;AAAA,QACf;AACA,aAAK,KAAK;AACV,aAAK,iBAAiB;AACtB,aAAK,iBAAiB;AACtB,eAAO,KAAK;AAAA,MACd,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AAC/B,QAAAA,KAAI,MAAM,kCAAkC,IAAI,IAAI,OAAO,SAAS,CAAC,EAAE;AACvE,aAAK,KAAK;AACV,aAAK,iBAAiB;AACtB,aAAK,iBAAiB;AACtB,cAAM,QAAQ,KAAK;AACnB,YAAI,OAAO;AACT,gBAAM,OAAO;AACb,gBAAM,OAAO;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AACF;","names":["WebSocket","log","log","import_ws","log","log","WebSocket","import_ws","log","DEFAULT_SAMPLE_RATE","innerText","WebSocket","import_ws","log","KEEPALIVE_INTERVAL_MS","WebSocket","import_ws","log","WebSocket"]}
@@ -1427,7 +1427,6 @@ var DtelecomTTS = class {
1427
1427
  if (this.flushState === state) {
1428
1428
  this.flushState = null;
1429
1429
  }
1430
- this._resolveWsDone?.();
1431
1430
  }
1432
1431
  }
1433
1432
  /** Ensure a WebSocket connection exists and is open. */
@@ -1501,12 +1500,14 @@ var DtelecomTTS = class {
1501
1500
  }
1502
1501
  this.ws = null;
1503
1502
  this.connectPromise = null;
1503
+ this._resolveWsDone?.();
1504
1504
  reject(error);
1505
1505
  });
1506
1506
  ws.on("close", (code, reason) => {
1507
1507
  log7.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);
1508
1508
  this.ws = null;
1509
1509
  this.connectPromise = null;
1510
+ this._resolveWsDone?.();
1510
1511
  const state = this.flushState;
1511
1512
  if (state) {
1512
1513
  state.done = true;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/deepgram-stt.ts","../../src/providers/openrouter-llm.ts","../../src/providers/openai-llm.ts","../../src/providers/cartesia-tts.ts","../../src/providers/deepgram-tts.ts","../../src/providers/dtelecom-stt.ts","../../src/providers/dtelecom-tts.ts"],"sourcesContent":["/**\n * DeepgramSTT — real-time streaming STT via Deepgram WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/listen?... with config as query params\n * - Auth via Authorization header: \"Token <apiKey>\"\n * - Send audio as binary WebSocket frames (PCM16 16kHz mono)\n * - Receive JSON: { type: \"Results\", channel: { alternatives: [{ transcript }] }, is_final, speech_final }\n * - Send KeepAlive every 5s when no audio is being sent\n * - Send CloseStream to gracefully shut down\n *\n * End-of-utterance strategy:\n * Buffer all is_final=true transcripts. Emit the buffered utterance as a\n * single final TranscriptionResult when speech_final=true OR UtteranceEnd\n * arrives. Interim results (is_final=false) are emitted immediately for\n * real-time feedback.\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramSTT');\n\nconst DEEPGRAM_WS_URL = 'wss://api.deepgram.com/v1/listen';\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DeepgramSTTOptions {\n apiKey: string;\n /** Deepgram model (default: 'nova-3') */\n model?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Enable interim results (default: true) */\n interimResults?: boolean;\n /** Enable punctuation (default: true) */\n punctuate?: boolean;\n /** Endpointing in ms (default: 300). Set to false to disable. */\n endpointing?: number | false;\n /** Keywords to boost recognition (e.g. ['dTelecom:5', 'WebRTC:3']) */\n keywords?: string[];\n /** Enable smart formatting (default: false) */\n smartFormat?: boolean;\n /** Utterance end timeout in ms (default: 1000). Requires interimResults. */\n utteranceEndMs?: number;\n}\n\nexport class DeepgramSTT implements STTPlugin {\n private readonly options: Required<Pick<DeepgramSTTOptions, 'apiKey'>> & DeepgramSTTOptions;\n\n constructor(options: DeepgramSTTOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramSTT requires an apiKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'en';\n return new DeepgramSTTStream(this.options, language);\n }\n}\n\nclass DeepgramSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly apiKey: string;\n private readonly wsUrl: string;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private lastAudioSentAt = 0;\n /** Buffer of is_final=true transcripts for the current utterance */\n private utteranceBuffer: string[] = [];\n /** Timestamp of the last non-empty interim result (approximates end of speech) */\n private lastInterimAt = 0;\n\n constructor(options: DeepgramSTTOptions, language: string) {\n super();\n this.apiKey = options.apiKey;\n this.wsUrl = buildWsUrl(options, language);\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n this.lastAudioSentAt = performance.now();\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n // Graceful shutdown — ask server to flush remaining audio\n try {\n this.ws.send(JSON.stringify({ type: 'CloseStream' }));\n } catch {\n // Ignore send errors during shutdown\n }\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DeepgramSTT stream closed');\n }\n\n private connect(): void {\n log.debug(`Connecting to Deepgram: ${this.wsUrl.replace(/token=[^&]+/, 'token=***')}`);\n\n this.ws = new WebSocket(this.wsUrl, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n this.ws.on('open', () => {\n log.info('Deepgram WebSocket connected');\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Deepgram message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('Deepgram WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('Deepgram connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'Results') {\n this.handleResults(msg);\n } else if (type === 'UtteranceEnd') {\n this.flushUtterance();\n } else if (type === 'Metadata') {\n log.debug('Deepgram session metadata received');\n } else if (type === 'SpeechStarted') {\n log.debug('Speech started detected');\n }\n }\n\n private handleResults(msg: Record<string, unknown>): void {\n const channel = msg.channel as { alternatives?: Array<{ transcript?: string; confidence?: number }> } | undefined;\n const transcript = channel?.alternatives?.[0]?.transcript ?? '';\n const confidence = channel?.alternatives?.[0]?.confidence;\n const isFinal = msg.is_final as boolean ?? false;\n const speechFinal = msg.speech_final as boolean ?? false;\n\n if (!transcript) return;\n\n if (!isFinal) {\n // Interim result — emit immediately for real-time feedback.\n // Include any buffered finals as prefix so the UI shows the full utterance.\n this.lastInterimAt = performance.now();\n const fullInterim = this.utteranceBuffer.length > 0\n ? this.utteranceBuffer.join(' ') + ' ' + transcript\n : transcript;\n this.emit('transcription', {\n text: fullInterim,\n isFinal: false,\n confidence: confidence ?? undefined,\n } satisfies TranscriptionResult);\n return;\n }\n\n // is_final=true — buffer this segment\n this.utteranceBuffer.push(transcript);\n\n if (speechFinal) {\n // End of utterance — emit the complete buffered transcript\n this.flushUtterance();\n }\n }\n\n /** Emit the buffered utterance as a single final transcription result. */\n private flushUtterance(): void {\n if (this.utteranceBuffer.length === 0) return;\n\n const now = performance.now();\n const fullText = this.utteranceBuffer.join(' ');\n this.utteranceBuffer = [];\n\n // sttDuration = time from last interim (≈ end of speech) to now (final result)\n // This includes endpointing delay + STT processing + network\n const sttDuration = this.lastInterimAt > 0 ? now - this.lastInterimAt : undefined;\n\n if (sttDuration !== undefined) {\n log.info(`stt_final: ${sttDuration.toFixed(0)}ms \"${fullText.slice(0, 50)}\"`);\n }\n\n this.lastInterimAt = 0;\n\n this.emit('transcription', {\n text: fullText,\n isFinal: true,\n sttDuration,\n } satisfies TranscriptionResult);\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'KeepAlive' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n\n/** Build the Deepgram WebSocket URL with query parameters. */\nfunction buildWsUrl(options: DeepgramSTTOptions, language: string): string {\n const params = new URLSearchParams();\n\n params.set('model', options.model ?? 'nova-3');\n params.set('language', language);\n params.set('encoding', 'linear16');\n params.set('sample_rate', '16000');\n params.set('channels', '1');\n params.set('interim_results', String(options.interimResults ?? true));\n params.set('punctuate', String(options.punctuate ?? true));\n\n if (options.endpointing === false) {\n params.set('endpointing', 'false');\n } else {\n params.set('endpointing', String(options.endpointing ?? 300));\n }\n\n if (options.smartFormat) {\n params.set('smart_format', 'true');\n }\n\n if (options.utteranceEndMs !== undefined) {\n params.set('utterance_end_ms', String(options.utteranceEndMs));\n } else if (options.interimResults !== false) {\n // Default utterance_end_ms when interim results are enabled\n params.set('utterance_end_ms', '1000');\n }\n\n if (options.keywords?.length) {\n for (const kw of options.keywords) {\n params.append('keywords', kw);\n }\n }\n\n return `${DEEPGRAM_WS_URL}?${params.toString()}`;\n}\n","/**\n * OpenRouterLLM — streaming LLM via OpenRouter (OpenAI-compatible API).\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenRouterLLM');\n\nconst OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\nexport interface OpenRouterLLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'openai/gpt-4o', 'anthropic/claude-sonnet-4') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** OpenRouter provider routing preferences */\n providerRouting?: {\n /** Sort providers by metric (e.g. 'latency') */\n sort?: string;\n /** Pin to specific providers in order */\n order?: string[];\n /** Allow fallback to other providers if pinned ones fail */\n allowFallbacks?: boolean;\n };\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenRouterLLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly provider?: { sort?: string; order?: string[]; allow_fallbacks?: boolean };\n private readonly responseFormat?: OpenRouterLLMOptions['responseFormat'];\n\n constructor(options: OpenRouterLLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenRouterLLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n\n if (options.providerRouting) {\n this.provider = {\n sort: options.providerRouting.sort,\n order: options.providerRouting.order,\n allow_fallbacks: options.providerRouting.allowFallbacks,\n };\n }\n\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n };\n if (this.provider) {\n body.provider = { ...this.provider };\n }\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n // Ensure the provider enforces structured output parameters\n const prov = (body.provider ?? {}) as Record<string, unknown>;\n prov.require_parameters = true;\n body.provider = prov;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENROUTER_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenRouter API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenRouter response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n // Tracks brace depth to detect complete {...} objects inside the \"segments\" array,\n // then parses each with JSON.parse() — correct and streaming.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false; // true after we see \"segments\" : [\n let objectStart = -1; // index where current { started\n let braceDepth = 0; // nested brace depth inside current object\n let scanIndex = 0; // how far we've scanned in jsonBuffer\n let inString = false; // inside a JSON string literal\n let escaped = false; // previous char was backslash\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n // Detect start of segments array: look for [ after \"segments\"\n if (!inSegmentsArray) {\n if (ch === '[') {\n // Check if \"segments\" precedes this bracket\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n // Inside segments array — track objects\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n // Complete segment object\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n // End of segments array\n inSegmentsArray = false;\n }\n }\n\n // Always advance — state vars (braceDepth, inString, etc.) already\n // track parsing state, so rescanning would double-count characters.\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n // Check abort before blocking on read — prevents hanging when signal\n // was fired while we were yielding tokens to the pipeline\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) continue;\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n // Try to extract any newly completed segments\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n // Usage stats in the final chunk\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * OpenAILLM — streaming LLM via OpenAI API.\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenAILLM');\n\nconst OPENAI_URL = 'https://api.openai.com/v1/chat/completions';\n\nexport interface OpenAILLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'gpt-4o', 'gpt-4o-mini') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenAILLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly responseFormat?: OpenAILLMOptions['responseFormat'];\n\n constructor(options: OpenAILLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenAILLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n stream_options: { include_usage: true },\n };\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENAI_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenAI response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false;\n let objectStart = -1;\n let braceDepth = 0;\n let scanIndex = 0;\n let inString = false;\n let escaped = false;\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n if (!inSegmentsArray) {\n if (ch === '[') {\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n inSegmentsArray = false;\n }\n }\n\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) {\n // Final chunk may have usage without choices\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n continue;\n }\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * CartesiaTTS — real-time streaming TTS via Cartesia WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.cartesia.ai/tts/websocket?api_key=...&cartesia_version=...\n * - Send JSON: { model_id, transcript, voice: { mode: \"id\", id }, output_format, context_id }\n * - Receive JSON: { type: \"chunk\", data: \"<base64 PCM>\" } — audio data\n * - Receive JSON: { type: \"done\", context_id } — synthesis complete\n * - Audio is base64-encoded PCM16 LE at the requested sample rate\n *\n * Uses a persistent WebSocket connection to avoid per-sentence handshake overhead.\n * Each synthesize() call uses a unique context_id for multiplexing.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('CartesiaTTS');\n\nconst CARTESIA_WS_BASE = 'wss://api.cartesia.ai/tts/websocket';\nconst DEFAULT_API_VERSION = '2024-06-10';\nconst DEFAULT_MODEL = 'sonic-3';\n/** Pipeline operates at 48kHz — matches Opus/WebRTC native rate, no resampling */\nconst DEFAULT_SAMPLE_RATE = 48000;\n/** Reconnect after idle timeout (Cartesia closes after 5 min idle) */\nconst RECONNECT_DELAY_MS = 1000;\n\nexport interface CartesiaTTSOptions {\n apiKey: string;\n /** Cartesia voice ID */\n voiceId: string;\n /** Model ID (default: 'sonic-3') */\n modelId?: string;\n /** Output sample rate in Hz (default: 16000) */\n sampleRate?: number;\n /** API version (default: '2024-06-10') */\n apiVersion?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Speech speed multiplier, 0.6-1.5 (default: 1.0). Sonic-3 only. */\n speed?: number;\n /** Emotion string (e.g. 'friendly', 'calm'). Sonic-3 only. */\n emotion?: string;\n}\n\n/** Per-context state for tracking an in-flight synthesis. */\ninterface ContextState {\n chunks: Buffer[];\n done: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class CartesiaTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly voiceId: string;\n private readonly modelId: string;\n private readonly sampleRate: number;\n private readonly apiVersion: string;\n private readonly language?: string;\n private readonly speed: number | undefined;\n private readonly emotion: string | undefined;\n\n private ws: WebSocket | null = null;\n private _connected = false;\n private connectPromise: Promise<void> | null = null;\n /** Active contexts keyed by context_id */\n private contexts = new Map<string, ContextState>();\n private contextCounter = 0;\n\n constructor(options: CartesiaTTSOptions) {\n if (!options.apiKey) {\n throw new Error('CartesiaTTS requires an apiKey');\n }\n if (!options.voiceId) {\n throw new Error('CartesiaTTS requires a voiceId');\n }\n this.apiKey = options.apiKey;\n this.voiceId = options.voiceId;\n this.modelId = options.modelId ?? DEFAULT_MODEL;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n this.apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;\n this.language = options.language;\n this.speed = options.speed;\n this.emotion = options.emotion;\n }\n\n /** Close the WebSocket connection to allow clean process exit. */\n close(): void {\n if (this.ws) {\n log.debug('Closing Cartesia WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this._connected = false;\n this.connectPromise = null;\n }\n\n /** Pre-connect the WebSocket so first synthesize() doesn't pay connection cost. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection();\n\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Cartesia WebSocket not connected');\n }\n\n const contextId = `ctx-${++this.contextCounter}-${Date.now()}`;\n const ctx: ContextState = { chunks: [], done: false, error: null, wake: null };\n this.contexts.set(contextId, ctx);\n\n // Build request\n const request: Record<string, unknown> = {\n model_id: this.modelId,\n transcript: text,\n voice: { mode: 'id', id: this.voiceId },\n output_format: {\n container: 'raw',\n encoding: 'pcm_s16le',\n sample_rate: this.sampleRate,\n },\n context_id: contextId,\n continue: false,\n };\n\n if (this.language) {\n request.language = this.language;\n }\n\n // Sonic-3 generation config\n if (this.speed !== undefined || this.emotion !== undefined) {\n const genConfig: Record<string, unknown> = {};\n if (this.speed !== undefined) genConfig.speed = this.speed;\n if (this.emotion !== undefined) genConfig.emotion = this.emotion;\n request.generation_config = genConfig;\n }\n\n // Handle abort — cancel the context on the server\n const onAbort = () => {\n ctx.done = true;\n ctx.wake?.();\n // Send cancel to server so it stops generating\n if (this.ws?.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify({ context_id: contextId, cancel: true }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send synthesis request\n this.ws.send(JSON.stringify(request));\n\n // Yield audio chunks as they arrive\n try {\n while (true) {\n if (signal?.aborted) break;\n if (ctx.error) throw ctx.error;\n\n if (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n continue;\n }\n\n if (ctx.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n ctx.wake = resolve;\n });\n ctx.wake = null;\n }\n\n // Drain remaining chunks\n while (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.contexts.delete(contextId);\n }\n }\n\n /** Ensure the persistent WebSocket is connected. */\n private ensureConnection(): Promise<void> {\n if (this._connected && this.ws?.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n const url = `${CARTESIA_WS_BASE}?api_key=${this.apiKey}&cartesia_version=${this.apiVersion}`;\n log.debug('Connecting to Cartesia...');\n\n this.ws = new WebSocket(url);\n\n this.ws.on('open', () => {\n this._connected = true;\n this.connectPromise = null;\n log.info('Cartesia WebSocket connected');\n resolve();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Cartesia message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('Cartesia WebSocket error:', error);\n // Propagate error to all active contexts\n for (const ctx of this.contexts.values()) {\n ctx.error = error;\n ctx.wake?.();\n }\n this._connected = false;\n this.connectPromise = null;\n reject(error);\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Cartesia WebSocket closed: ${code} ${reason.toString()}`);\n this._connected = false;\n this.connectPromise = null;\n // Mark all active contexts as done\n for (const ctx of this.contexts.values()) {\n ctx.done = true;\n ctx.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const contextId = msg.context_id as string | undefined;\n if (!contextId) return;\n\n const ctx = this.contexts.get(contextId);\n if (!ctx) return; // Stale context — already cleaned up\n\n const type = msg.type as string;\n\n if (type === 'chunk') {\n const b64 = msg.data as string;\n if (b64) {\n const pcm = Buffer.from(b64, 'base64');\n ctx.chunks.push(pcm);\n ctx.wake?.();\n }\n } else if (type === 'done') {\n log.debug(`Cartesia synthesis done for ${contextId} (${ctx.chunks.length} chunks pending)`);\n ctx.done = true;\n ctx.wake?.();\n } else if (type === 'error') {\n const errorMsg = msg.error as string ?? 'Unknown Cartesia error';\n log.error(`Cartesia error for ${contextId}: ${errorMsg}`);\n ctx.error = new Error(`Cartesia TTS error: ${errorMsg}`);\n ctx.wake?.();\n }\n }\n}\n","/**\n * DeepgramTTS — real-time streaming TTS via Deepgram Aura-2 WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/speak?model={model}&encoding=linear16&sample_rate={rate}\n * - Auth: Authorization: Token <key> header\n * - Send: {\"type\":\"Speak\",\"text\":\"...\"} then {\"type\":\"Flush\"}\n * - Receive: binary frames (raw PCM16) until {\"type\":\"Flushed\"} JSON\n * - Cancel: {\"type\":\"Clear\"} then {\"type\":\"Flush\"}, wait for Flushed\n *\n * Supports multi-language via connection pool (one WS per language).\n * Uses SSML <lang> tags to route text segments to the correct voice.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramTTS');\n\nconst DEEPGRAM_WS_BASE = 'wss://api.deepgram.com/v1/speak';\nconst DEFAULT_SAMPLE_RATE = 48000;\n\nexport interface DeepgramTTSOptions {\n apiKey: string;\n /** Single model string OR language->model map for multi-language */\n model: string | Record<string, string>;\n /** Default language for untagged text (default: 'en' or first key) */\n defaultLanguage?: string;\n /** Sample rate (default: 48000 — matches pipeline) */\n sampleRate?: number;\n}\n\ninterface LangSegment {\n lang: string;\n text: string;\n}\n\n/** Per-connection state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n flushed: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\n/**\n * Parse SSML <lang> tags into segments.\n *\n * Input: `Great job! <lang xml:lang=\"es\">Ahora repite: buenos días.</lang>`\n * Output: [{lang:'en', text:'Great job!'}, {lang:'es', text:'Ahora repite: buenos días.'}]\n *\n * Text outside tags uses defaultLang. Handles no tags, adjacent tags, nested text.\n * Malformed input is treated as default language.\n */\nexport function parseLangSegments(text: string, defaultLang: string): LangSegment[] {\n const segments: LangSegment[] = [];\n let i = 0;\n let currentText = '';\n\n while (i < text.length) {\n // Look for opening <lang tag\n if (text[i] === '<' && text.startsWith('<lang ', i)) {\n // Flush accumulated default-language text\n if (currentText) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n currentText = '';\n }\n\n // Find xml:lang=\"...\"\n const xmlLangStart = text.indexOf('xml:lang=\"', i);\n if (xmlLangStart === -1) {\n // Malformed — treat rest as default\n currentText += text[i];\n i++;\n continue;\n }\n\n const langStart = xmlLangStart + 'xml:lang=\"'.length;\n const langEnd = text.indexOf('\"', langStart);\n if (langEnd === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n const lang = text.substring(langStart, langEnd);\n\n // Find end of opening tag \">\"\n const tagClose = text.indexOf('>', langEnd);\n if (tagClose === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n // Find closing </lang>\n const closingTag = '</lang>';\n const closingStart = text.indexOf(closingTag, tagClose + 1);\n if (closingStart === -1) {\n // No closing tag — treat the rest after opening tag as this language\n const innerText = text.substring(tagClose + 1).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n i = text.length;\n continue;\n }\n\n const innerText = text.substring(tagClose + 1, closingStart).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n\n i = closingStart + closingTag.length;\n continue;\n }\n\n currentText += text[i];\n i++;\n }\n\n // Flush remaining default-language text\n if (currentText.trim()) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n }\n\n return segments;\n}\n\nexport class DeepgramTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly models: Record<string, string>;\n private readonly defaultLang: string;\n private readonly sampleRate: number;\n private readonly multiLanguage: boolean;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n /** Connection pool: one WebSocket per language code */\n private connections = new Map<string, WebSocket>();\n private connectPromises = new Map<string, Promise<void>>();\n /** Per-connection flush state */\n private flushStates = new Map<string, FlushState>();\n\n constructor(options: DeepgramTTSOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramTTS requires an apiKey');\n }\n\n this.apiKey = options.apiKey;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n\n if (typeof options.model === 'string') {\n // Single-language mode\n this.multiLanguage = false;\n const lang = options.defaultLanguage ?? 'en';\n this.models = { [lang]: options.model };\n this.defaultLang = lang;\n } else {\n // Multi-language mode\n this.multiLanguage = true;\n this.models = { ...options.model };\n const keys = Object.keys(this.models);\n if (keys.length === 0) {\n throw new Error('DeepgramTTS model map must have at least one entry');\n }\n this.defaultLang = options.defaultLanguage ?? keys[0];\n }\n }\n\n /** Close all WebSocket connections to allow clean process exit. */\n close(): void {\n for (const [lang, ws] of this.connections) {\n log.debug(`Closing WebSocket for [${lang}]`);\n ws.close();\n }\n this.connections.clear();\n this.connectPromises.clear();\n this.flushStates.clear();\n }\n\n /** Pre-connect all language WebSocket connections. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connections...');\n const start = performance.now();\n try {\n await Promise.all(Object.keys(this.models).map((lang) => this.ensureConnection(lang)));\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = this.multiLanguage\n ? parseLangSegments(text, this.defaultLang)\n : [{ lang: this.defaultLang, text }];\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 mono: sampleRate * 0.2 * 2 bytes per sample\n const silenceBytes = Math.round(this.sampleRate * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.models[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing [${lang}]: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection(lang);\n\n const ws = this.connections.get(lang);\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n throw new Error(`Deepgram WebSocket not connected for language \"${lang}\"`);\n }\n\n const state: FlushState = { chunks: [], flushed: false, error: null, wake: null };\n this.flushStates.set(lang, state);\n\n // Handle abort — send Clear + Flush to cancel\n const onAbort = () => {\n state.flushed = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'Clear' }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send Speak + Flush\n ws.send(JSON.stringify({ type: 'Speak', text }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.flushed) break;\n\n // Wait for next chunk or Flushed signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.flushStates.delete(lang);\n }\n }\n\n /** Ensure a WebSocket connection exists for the given language. */\n private ensureConnection(lang: string): Promise<void> {\n const existing = this.connections.get(lang);\n if (existing && existing.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n const pending = this.connectPromises.get(lang);\n if (pending) return pending;\n\n const model = this.models[lang];\n if (!model) {\n return Promise.reject(new Error(`No Deepgram model configured for language \"${lang}\"`));\n }\n\n const promise = new Promise<void>((resolve, reject) => {\n const url = `${DEEPGRAM_WS_BASE}?model=${encodeURIComponent(model)}&encoding=linear16&sample_rate=${this.sampleRate}`;\n log.debug(`Connecting to Deepgram for [${lang}]: ${model}`);\n\n const ws = new WebSocket(url, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n ws.on('open', () => {\n this.connections.set(lang, ws);\n this.connectPromises.delete(lang);\n log.info(`Deepgram WebSocket connected for [${lang}] (${model})`);\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushStates.get(lang);\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 audio\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'Flushed') {\n state.flushed = true;\n state.wake?.();\n } else if (msg.type === 'Warning' || msg.type === 'Error') {\n log.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);\n }\n } catch {\n log.warn(`Failed to parse Deepgram message for [${lang}]`);\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error(`Deepgram WebSocket error [${lang}]:`, error);\n const state = this.flushStates.get(lang);\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed [${lang}]: ${code} ${reason.toString()}`);\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n const state = this.flushStates.get(lang);\n if (state) {\n state.flushed = true;\n state.wake?.();\n }\n });\n });\n\n this.connectPromises.set(lang, promise);\n return promise;\n }\n}\n","/**\n * DtelecomSTT — real-time streaming STT via dTelecom STT server (realtime-stt-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"type\":\"config\",\"language\":\"en\"} (or \"auto\" for Parakeet auto-detect)\n * - Wait for ready: {\"type\":\"ready\",\"client_id\":\"...\",\"language\":\"en\"}\n * - Send audio as binary PCM16 16kHz mono frames\n * - Receive transcriptions: {\"type\":\"transcription\",\"text\":\"...\",\"is_final\":true,\"latency_ms\":N}\n * - Receive VAD events: {\"type\":\"vad_event\",\"event\":\"speech_start\"|\"speech_end\"}\n * - Keepalive via {\"type\":\"ping\"} / {\"type\":\"pong\"}\n * - Mid-session reconfigure: send {\"type\":\"config\",\"language\":\"es\",\"model\":\"whisper\"} at any time\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomSTT');\n\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DtelecomSTTOptions {\n /** Server base URL, e.g. \"https://stt-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Initial language (default: \"auto\" for Parakeet auto-detect) */\n language?: string;\n /** Force Whisper model even if Parakeet supports the language */\n forceWhisper?: boolean;\n}\n\nexport class DtelecomSTT implements STTPlugin {\n private readonly options: DtelecomSTTOptions;\n\n constructor(options: DtelecomSTTOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomSTT requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomSTT requires a sessionKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'auto';\n return new DtelecomSTTStream(this.options, language);\n }\n}\n\nclass DtelecomSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly forceWhisper: boolean;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private language: string;\n\n constructor(options: DtelecomSTTOptions, language: string) {\n super();\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.language = language;\n this.forceWhisper = options.forceWhisper ?? false;\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n }\n }\n\n /**\n * Switch language mid-session.\n * Sends a reconfigure message to the server; clears buffers and updates model routing.\n */\n setLanguage(language: string, options?: { forceWhisper?: boolean }): void {\n if (this._closed) return;\n this.language = language;\n\n const config: Record<string, string> = { type: 'config', language };\n if (options?.forceWhisper) {\n config.model = 'whisper';\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(config));\n log.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ', model=whisper' : ''}`);\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DtelecomSTT stream closed');\n }\n\n private connect(): void {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom STT: ${wsUrl}`);\n\n this.ws = new WebSocket(wsUrl);\n\n this.ws.on('open', () => {\n log.info('dTelecom STT WebSocket connected');\n\n // Send initial config with session_key\n const config: Record<string, string> = {\n type: 'config',\n language: this.language,\n session_key: this.sessionKey,\n };\n if (this.forceWhisper) {\n config.model = 'whisper';\n }\n this.ws!.send(JSON.stringify(config));\n });\n\n this.ws.on('message', (data, isBinary) => {\n if (isBinary) return; // Server only sends JSON text messages\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse dTelecom STT message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('dTelecom STT WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('dTelecom STT connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'ready') {\n this.handleReady(msg);\n } else if (type === 'transcription') {\n this.handleTranscription(msg);\n } else if (type === 'vad_event') {\n this.handleVadEvent(msg);\n } else if (type === 'pong') {\n // Keepalive response — no action needed\n } else if (type === 'error') {\n const errData = msg.error;\n const errorMsg = (msg.message as string)\n || (typeof errData === 'string' ? errData : JSON.stringify(errData))\n || 'Unknown STT error';\n log.error(`dTelecom STT error: ${errorMsg}`);\n this.emit('error', new Error(errorMsg));\n }\n }\n\n private handleReady(msg: Record<string, unknown>): void {\n const clientId = msg.client_id as string | undefined;\n const lang = msg.language as string | undefined;\n log.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);\n\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n }\n\n private handleTranscription(msg: Record<string, unknown>): void {\n const text = (msg.text as string) ?? '';\n const isFinal = (msg.is_final as boolean) ?? false;\n const language = msg.language as string | undefined;\n const latencyMs = msg.latency_ms as number | undefined;\n\n if (!text) return;\n\n if (isFinal && latencyMs !== undefined) {\n log.info(`stt_final: ${latencyMs.toFixed(0)}ms \"${text.slice(0, 50)}\"`);\n }\n\n this.emit('transcription', {\n text,\n isFinal,\n language,\n sttDuration: isFinal ? latencyMs : undefined,\n } satisfies TranscriptionResult);\n }\n\n private handleVadEvent(msg: Record<string, unknown>): void {\n const event = msg.event as string;\n log.debug(`VAD event: ${event}`);\n\n if (event === 'speech_start') {\n // Emit empty non-final transcription to signal speech detected.\n // Note: barge-in in the pipeline requires non-empty text, so this won't\n // trigger it directly. Actual interim transcriptions that follow will.\n this.emit('transcription', {\n text: '',\n isFinal: false,\n } satisfies TranscriptionResult);\n }\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n","/**\n * DtelecomTTS — real-time streaming TTS via dTelecom TTS server (realtime-tts-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"config\":{\"voice\":\"af_heart\",\"lang_code\":\"a\",\"speed\":1.0}}\n * - Send text: {\"text\":\"Hello world\"} — uses config defaults\n * - Send text with per-message override: {\"text\":\"Hola\",\"voice\":\"ef_dora\",\"lang_code\":\"e\",\"speed\":1.0}\n * - Receive: {\"type\":\"generating\",\"text\":\"...\"} then binary PCM16 48kHz chunks, then {\"type\":\"done\"}\n * - Cancel: {\"type\":\"clear\"} → {\"type\":\"cleared\"}\n *\n * Key differences from DeepgramTTS:\n * - Single WebSocket connection (not per-language pool)\n * - Per-message voice/language switching instead of separate connections\n * - Server outputs 48kHz PCM16 (resampled from Kokoro's native 24kHz)\n * - Uses SSML <lang> tags to route text segments to correct voice (same as DeepgramTTS)\n */\n\nimport WebSocket from 'ws';\nimport { parseLangSegments } from './deepgram-tts';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomTTS');\n\nexport interface VoiceConfig {\n voice: string;\n langCode: string;\n}\n\nexport interface DtelecomTTSOptions {\n /** Server base URL, e.g. \"https://tts-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Voice config per language: { en: { voice: \"af_heart\", langCode: \"a\" }, es: { voice: \"bf_emma\", langCode: \"b\" } } */\n voices: Record<string, VoiceConfig>;\n /** Default language code (default: \"en\") */\n defaultLanguage?: string;\n /** Speech speed multiplier (default: 1.0) */\n speed?: number;\n}\n\n/** Per-request state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n done: boolean;\n cleared: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class DtelecomTTS implements TTSPlugin {\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly voices: Record<string, VoiceConfig>;\n private readonly defaultLang: string;\n private readonly speed: number;\n\n private ws: WebSocket | null = null;\n private connectPromise: Promise<void> | null = null;\n private flushState: FlushState | null = null;\n /** Resolves when the current WS conversation receives \"done\"/\"cleared\". */\n private _wsDone: Promise<void> = Promise.resolve();\n private _resolveWsDone?: () => void;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n constructor(options: DtelecomTTSOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomTTS requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomTTS requires a sessionKey');\n }\n if (!options.voices || Object.keys(options.voices).length === 0) {\n throw new Error('DtelecomTTS requires at least one voice config');\n }\n\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.voices = { ...options.voices };\n this.defaultLang = options.defaultLanguage ?? Object.keys(this.voices)[0];\n this.speed = options.speed ?? 1.0;\n }\n\n /** Pre-connect WebSocket to TTS server. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Close WebSocket connection. */\n close(): void {\n if (this.ws) {\n log.debug('Closing TTS WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this.connectPromise = null;\n this.flushState = null;\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = parseLangSegments(text, this.defaultLang);\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 48kHz mono: 48000 * 0.2 * 2 = 19200 bytes\n const silenceBytes = Math.round(48000 * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.voices[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n // Wait for previous WS conversation to finish (\"done\"/\"cleared\" received).\n // This lets pipeline prefetch start TTS for the next sentence as soon as\n // the current one finishes generating — while audio is still playing.\n await this._wsDone;\n if (signal?.aborted) return;\n\n // Set up new _wsDone for this conversation\n this._wsDone = new Promise((r) => { this._resolveWsDone = r; });\n\n await this.ensureConnection();\n\n const ws = this.ws;\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n this._resolveWsDone?.();\n throw new Error('dTelecom TTS WebSocket not connected');\n }\n\n const state: FlushState = { chunks: [], done: false, cleared: false, error: null, wake: null };\n this.flushState = state;\n\n // Handle abort — send clear to cancel\n const onAbort = () => {\n state.done = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'clear' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send text with per-message voice/language override\n const voiceConfig = this.voices[lang];\n const msg: Record<string, unknown> = { text };\n if (voiceConfig) {\n msg.voice = voiceConfig.voice;\n msg.lang_code = voiceConfig.langCode;\n msg.speed = this.speed;\n }\n log.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? 'default'} lang_code=${voiceConfig?.langCode ?? 'default'} \"${text.slice(0, 60)}\"`)\n ws.send(JSON.stringify(msg));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n // Only clear flushState if it hasn't been replaced by the next synthesis\n if (this.flushState === state) {\n this.flushState = null;\n }\n this._resolveWsDone?.();\n }\n }\n\n /** Ensure a WebSocket connection exists and is open. */\n private ensureConnection(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom TTS: ${wsUrl}`);\n\n const ws = new WebSocket(wsUrl);\n\n ws.on('open', () => {\n this.ws = ws;\n this.connectPromise = null;\n\n // Send initial config with session_key and default voice\n const defaultVoice = this.voices[this.defaultLang];\n const msg: Record<string, unknown> = {\n session_key: this.sessionKey,\n };\n if (defaultVoice) {\n msg.config = {\n voice: defaultVoice.voice,\n lang_code: defaultVoice.langCode,\n speed: this.speed,\n };\n }\n ws.send(JSON.stringify(msg));\n\n log.info('dTelecom TTS WebSocket connected');\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushState;\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 48kHz audio (server resamples from Kokoro's 24kHz)\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'done') {\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'cleared') {\n state.cleared = true;\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'generating') {\n log.debug(`TTS generating: \"${(msg.text as string)?.slice(0, 40)}\"`);\n } else if (msg.type === 'error') {\n const errorMsg = (msg.message as string) || 'Unknown TTS error';\n log.error(`dTelecom TTS error: ${errorMsg}`);\n state.error = new Error(errorMsg);\n state.wake?.();\n }\n } catch {\n log.warn('Failed to parse dTelecom TTS message');\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('dTelecom TTS WebSocket error:', error);\n const state = this.flushState;\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.ws = null;\n this.connectPromise = null;\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);\n this.ws = null;\n this.connectPromise = null;\n const state = this.flushState;\n if (state) {\n state.done = true;\n state.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n}\n"],"mappings":"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","names":["log","log","WebSocket","log","log","WebSocket","WebSocket","log","DEFAULT_SAMPLE_RATE","innerText","WebSocket","WebSocket","log","KEEPALIVE_INTERVAL_MS","WebSocket","WebSocket","log","WebSocket"]}
1
+ {"version":3,"sources":["../../src/providers/deepgram-stt.ts","../../src/providers/openrouter-llm.ts","../../src/providers/openai-llm.ts","../../src/providers/cartesia-tts.ts","../../src/providers/deepgram-tts.ts","../../src/providers/dtelecom-stt.ts","../../src/providers/dtelecom-tts.ts"],"sourcesContent":["/**\n * DeepgramSTT — real-time streaming STT via Deepgram WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/listen?... with config as query params\n * - Auth via Authorization header: \"Token <apiKey>\"\n * - Send audio as binary WebSocket frames (PCM16 16kHz mono)\n * - Receive JSON: { type: \"Results\", channel: { alternatives: [{ transcript }] }, is_final, speech_final }\n * - Send KeepAlive every 5s when no audio is being sent\n * - Send CloseStream to gracefully shut down\n *\n * End-of-utterance strategy:\n * Buffer all is_final=true transcripts. Emit the buffered utterance as a\n * single final TranscriptionResult when speech_final=true OR UtteranceEnd\n * arrives. Interim results (is_final=false) are emitted immediately for\n * real-time feedback.\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramSTT');\n\nconst DEEPGRAM_WS_URL = 'wss://api.deepgram.com/v1/listen';\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DeepgramSTTOptions {\n apiKey: string;\n /** Deepgram model (default: 'nova-3') */\n model?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Enable interim results (default: true) */\n interimResults?: boolean;\n /** Enable punctuation (default: true) */\n punctuate?: boolean;\n /** Endpointing in ms (default: 300). Set to false to disable. */\n endpointing?: number | false;\n /** Keywords to boost recognition (e.g. ['dTelecom:5', 'WebRTC:3']) */\n keywords?: string[];\n /** Enable smart formatting (default: false) */\n smartFormat?: boolean;\n /** Utterance end timeout in ms (default: 1000). Requires interimResults. */\n utteranceEndMs?: number;\n}\n\nexport class DeepgramSTT implements STTPlugin {\n private readonly options: Required<Pick<DeepgramSTTOptions, 'apiKey'>> & DeepgramSTTOptions;\n\n constructor(options: DeepgramSTTOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramSTT requires an apiKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'en';\n return new DeepgramSTTStream(this.options, language);\n }\n}\n\nclass DeepgramSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly apiKey: string;\n private readonly wsUrl: string;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private lastAudioSentAt = 0;\n /** Buffer of is_final=true transcripts for the current utterance */\n private utteranceBuffer: string[] = [];\n /** Timestamp of the last non-empty interim result (approximates end of speech) */\n private lastInterimAt = 0;\n\n constructor(options: DeepgramSTTOptions, language: string) {\n super();\n this.apiKey = options.apiKey;\n this.wsUrl = buildWsUrl(options, language);\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n this.lastAudioSentAt = performance.now();\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n // Graceful shutdown — ask server to flush remaining audio\n try {\n this.ws.send(JSON.stringify({ type: 'CloseStream' }));\n } catch {\n // Ignore send errors during shutdown\n }\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DeepgramSTT stream closed');\n }\n\n private connect(): void {\n log.debug(`Connecting to Deepgram: ${this.wsUrl.replace(/token=[^&]+/, 'token=***')}`);\n\n this.ws = new WebSocket(this.wsUrl, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n this.ws.on('open', () => {\n log.info('Deepgram WebSocket connected');\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Deepgram message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('Deepgram WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('Deepgram connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'Results') {\n this.handleResults(msg);\n } else if (type === 'UtteranceEnd') {\n this.flushUtterance();\n } else if (type === 'Metadata') {\n log.debug('Deepgram session metadata received');\n } else if (type === 'SpeechStarted') {\n log.debug('Speech started detected');\n }\n }\n\n private handleResults(msg: Record<string, unknown>): void {\n const channel = msg.channel as { alternatives?: Array<{ transcript?: string; confidence?: number }> } | undefined;\n const transcript = channel?.alternatives?.[0]?.transcript ?? '';\n const confidence = channel?.alternatives?.[0]?.confidence;\n const isFinal = msg.is_final as boolean ?? false;\n const speechFinal = msg.speech_final as boolean ?? false;\n\n if (!transcript) return;\n\n if (!isFinal) {\n // Interim result — emit immediately for real-time feedback.\n // Include any buffered finals as prefix so the UI shows the full utterance.\n this.lastInterimAt = performance.now();\n const fullInterim = this.utteranceBuffer.length > 0\n ? this.utteranceBuffer.join(' ') + ' ' + transcript\n : transcript;\n this.emit('transcription', {\n text: fullInterim,\n isFinal: false,\n confidence: confidence ?? undefined,\n } satisfies TranscriptionResult);\n return;\n }\n\n // is_final=true — buffer this segment\n this.utteranceBuffer.push(transcript);\n\n if (speechFinal) {\n // End of utterance — emit the complete buffered transcript\n this.flushUtterance();\n }\n }\n\n /** Emit the buffered utterance as a single final transcription result. */\n private flushUtterance(): void {\n if (this.utteranceBuffer.length === 0) return;\n\n const now = performance.now();\n const fullText = this.utteranceBuffer.join(' ');\n this.utteranceBuffer = [];\n\n // sttDuration = time from last interim (≈ end of speech) to now (final result)\n // This includes endpointing delay + STT processing + network\n const sttDuration = this.lastInterimAt > 0 ? now - this.lastInterimAt : undefined;\n\n if (sttDuration !== undefined) {\n log.info(`stt_final: ${sttDuration.toFixed(0)}ms \"${fullText.slice(0, 50)}\"`);\n }\n\n this.lastInterimAt = 0;\n\n this.emit('transcription', {\n text: fullText,\n isFinal: true,\n sttDuration,\n } satisfies TranscriptionResult);\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'KeepAlive' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n\n/** Build the Deepgram WebSocket URL with query parameters. */\nfunction buildWsUrl(options: DeepgramSTTOptions, language: string): string {\n const params = new URLSearchParams();\n\n params.set('model', options.model ?? 'nova-3');\n params.set('language', language);\n params.set('encoding', 'linear16');\n params.set('sample_rate', '16000');\n params.set('channels', '1');\n params.set('interim_results', String(options.interimResults ?? true));\n params.set('punctuate', String(options.punctuate ?? true));\n\n if (options.endpointing === false) {\n params.set('endpointing', 'false');\n } else {\n params.set('endpointing', String(options.endpointing ?? 300));\n }\n\n if (options.smartFormat) {\n params.set('smart_format', 'true');\n }\n\n if (options.utteranceEndMs !== undefined) {\n params.set('utterance_end_ms', String(options.utteranceEndMs));\n } else if (options.interimResults !== false) {\n // Default utterance_end_ms when interim results are enabled\n params.set('utterance_end_ms', '1000');\n }\n\n if (options.keywords?.length) {\n for (const kw of options.keywords) {\n params.append('keywords', kw);\n }\n }\n\n return `${DEEPGRAM_WS_URL}?${params.toString()}`;\n}\n","/**\n * OpenRouterLLM — streaming LLM via OpenRouter (OpenAI-compatible API).\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenRouterLLM');\n\nconst OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\nexport interface OpenRouterLLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'openai/gpt-4o', 'anthropic/claude-sonnet-4') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** OpenRouter provider routing preferences */\n providerRouting?: {\n /** Sort providers by metric (e.g. 'latency') */\n sort?: string;\n /** Pin to specific providers in order */\n order?: string[];\n /** Allow fallback to other providers if pinned ones fail */\n allowFallbacks?: boolean;\n };\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenRouterLLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly provider?: { sort?: string; order?: string[]; allow_fallbacks?: boolean };\n private readonly responseFormat?: OpenRouterLLMOptions['responseFormat'];\n\n constructor(options: OpenRouterLLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenRouterLLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n\n if (options.providerRouting) {\n this.provider = {\n sort: options.providerRouting.sort,\n order: options.providerRouting.order,\n allow_fallbacks: options.providerRouting.allowFallbacks,\n };\n }\n\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n };\n if (this.provider) {\n body.provider = { ...this.provider };\n }\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n // Ensure the provider enforces structured output parameters\n const prov = (body.provider ?? {}) as Record<string, unknown>;\n prov.require_parameters = true;\n body.provider = prov;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENROUTER_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenRouter API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenRouter response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n // Tracks brace depth to detect complete {...} objects inside the \"segments\" array,\n // then parses each with JSON.parse() — correct and streaming.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false; // true after we see \"segments\" : [\n let objectStart = -1; // index where current { started\n let braceDepth = 0; // nested brace depth inside current object\n let scanIndex = 0; // how far we've scanned in jsonBuffer\n let inString = false; // inside a JSON string literal\n let escaped = false; // previous char was backslash\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n // Detect start of segments array: look for [ after \"segments\"\n if (!inSegmentsArray) {\n if (ch === '[') {\n // Check if \"segments\" precedes this bracket\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n // Inside segments array — track objects\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n // Complete segment object\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n // End of segments array\n inSegmentsArray = false;\n }\n }\n\n // Always advance — state vars (braceDepth, inString, etc.) already\n // track parsing state, so rescanning would double-count characters.\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n // Check abort before blocking on read — prevents hanging when signal\n // was fired while we were yielding tokens to the pipeline\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) continue;\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n // Try to extract any newly completed segments\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n // Usage stats in the final chunk\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * OpenAILLM — streaming LLM via OpenAI API.\n *\n * Uses native fetch() with SSE parsing for streaming responses.\n * No SDK dependency — just HTTP.\n */\n\nimport type { LLMPlugin, LLMChunk, LLMChatOptions, Message } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('OpenAILLM');\n\nconst OPENAI_URL = 'https://api.openai.com/v1/chat/completions';\n\nexport interface OpenAILLMOptions {\n apiKey: string;\n /** Model identifier (e.g. 'gpt-4o', 'gpt-4o-mini') */\n model: string;\n /** Max tokens in response (default: 512) */\n maxTokens?: number;\n /** Sampling temperature 0-2 (default: 0.7) */\n temperature?: number;\n /** Structured output via constrained decoding (e.g. for multi-language segment routing) */\n responseFormat?: {\n type: 'json_schema';\n json_schema: { name: string; strict: boolean; schema: Record<string, unknown> };\n };\n}\n\nexport class OpenAILLM implements LLMPlugin {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxTokens: number;\n private readonly temperature: number;\n private readonly responseFormat?: OpenAILLMOptions['responseFormat'];\n\n constructor(options: OpenAILLMOptions) {\n if (!options.apiKey) {\n throw new Error('OpenAILLM requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.maxTokens = options.maxTokens ?? 512;\n this.temperature = options.temperature ?? 0.7;\n this.responseFormat = options.responseFormat;\n }\n\n /**\n * Warm up the LLM by sending the system prompt and a short message.\n * Primes the HTTP/TLS connection and model loading on the provider side.\n */\n async warmup(systemPrompt: string): Promise<void> {\n log.info('Warming up LLM connection...');\n const start = performance.now();\n\n const messages: Message[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: 'Hello' },\n ];\n\n try {\n const gen = this.chat(messages);\n for await (const chunk of gen) {\n if (chunk.type === 'done') break;\n }\n log.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('LLM warmup failed (non-fatal):', err);\n }\n }\n\n async *chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk> {\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n stream: true,\n stream_options: { include_usage: true },\n };\n if (this.responseFormat && !options?.plainText) {\n body.response_format = this.responseFormat;\n }\n if (options?.tools?.length) {\n body.tools = options.tools;\n }\n\n log.debug(`LLM request: model=${this.model}, messages=${messages.length}`);\n\n const response = await fetch(OPENAI_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error ${response.status}: ${errorText}`);\n }\n\n if (!response.body) {\n throw new Error('OpenAI response has no body');\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // Structured output: stream segment objects as they complete in the JSON buffer.\n const structured = !!this.responseFormat && !options?.plainText;\n let jsonBuffer = '';\n let segmentsYielded = false;\n let lastUsage: { promptTokens: number; completionTokens: number } | undefined;\n const pendingToolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n\n // Streaming segment extraction state\n let inSegmentsArray = false;\n let objectStart = -1;\n let braceDepth = 0;\n let scanIndex = 0;\n let inString = false;\n let escaped = false;\n\n function extractSegments(buf: string): Array<{ lang: string; text: string }> {\n const results: Array<{ lang: string; text: string }> = [];\n\n for (let i = scanIndex; i < buf.length; i++) {\n const ch = buf[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\' && inString) {\n escaped = true;\n continue;\n }\n\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n\n if (inString) continue;\n\n if (!inSegmentsArray) {\n if (ch === '[') {\n const before = buf.slice(0, i).trimEnd();\n if (before.endsWith(':') && buf.slice(0, i).includes('\"segments\"')) {\n inSegmentsArray = true;\n }\n }\n continue;\n }\n\n if (ch === '{') {\n if (braceDepth === 0) objectStart = i;\n braceDepth++;\n } else if (ch === '}') {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n const objStr = buf.slice(objectStart, i + 1);\n try {\n const seg = JSON.parse(objStr);\n if (seg.lang && seg.text) {\n results.push({ lang: seg.lang, text: seg.text });\n }\n } catch {\n // Incomplete or malformed — skip\n }\n objectStart = -1;\n }\n } else if (ch === ']' && braceDepth === 0) {\n inSegmentsArray = false;\n }\n }\n\n scanIndex = buf.length;\n return results;\n }\n\n try {\n while (true) {\n if (signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6);\n if (data === '[DONE]') break;\n\n try {\n const parsed = JSON.parse(data);\n const choice = parsed.choices?.[0];\n if (!choice) {\n // Final chunk may have usage without choices\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n continue;\n }\n\n const delta = choice.delta;\n if (delta?.content) {\n if (structured) {\n jsonBuffer += delta.content;\n\n const segments = extractSegments(jsonBuffer);\n for (const seg of segments) {\n yield { type: 'segment', segment: seg };\n segmentsYielded = true;\n }\n } else {\n yield { type: 'token', token: delta.content };\n }\n }\n\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls as Array<{ index: number; id?: string; function?: { name?: string; arguments?: string } }>) {\n const idx = tc.index;\n if (tc.id) {\n pendingToolCalls.set(idx, { id: tc.id, name: tc.function?.name ?? '', arguments: tc.function?.arguments ?? '' });\n } else {\n const existing = pendingToolCalls.get(idx);\n if (existing) {\n if (tc.function?.name) existing.name += tc.function.name;\n if (tc.function?.arguments) existing.arguments += tc.function.arguments;\n }\n }\n }\n }\n\n if (parsed.usage) {\n lastUsage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n if (structured && !segmentsYielded && jsonBuffer.length > 0) {\n log.warn(`LLM returned no segments. Raw JSON: \"${jsonBuffer.slice(0, 300)}\"`);\n }\n\n for (const [, tc] of pendingToolCalls) {\n yield { type: 'tool_call', toolCall: tc };\n }\n\n yield { type: 'done', ...(lastUsage ? { usage: lastUsage } : {}) };\n }\n}\n","/**\n * CartesiaTTS — real-time streaming TTS via Cartesia WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.cartesia.ai/tts/websocket?api_key=...&cartesia_version=...\n * - Send JSON: { model_id, transcript, voice: { mode: \"id\", id }, output_format, context_id }\n * - Receive JSON: { type: \"chunk\", data: \"<base64 PCM>\" } — audio data\n * - Receive JSON: { type: \"done\", context_id } — synthesis complete\n * - Audio is base64-encoded PCM16 LE at the requested sample rate\n *\n * Uses a persistent WebSocket connection to avoid per-sentence handshake overhead.\n * Each synthesize() call uses a unique context_id for multiplexing.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('CartesiaTTS');\n\nconst CARTESIA_WS_BASE = 'wss://api.cartesia.ai/tts/websocket';\nconst DEFAULT_API_VERSION = '2024-06-10';\nconst DEFAULT_MODEL = 'sonic-3';\n/** Pipeline operates at 48kHz — matches Opus/WebRTC native rate, no resampling */\nconst DEFAULT_SAMPLE_RATE = 48000;\n/** Reconnect after idle timeout (Cartesia closes after 5 min idle) */\nconst RECONNECT_DELAY_MS = 1000;\n\nexport interface CartesiaTTSOptions {\n apiKey: string;\n /** Cartesia voice ID */\n voiceId: string;\n /** Model ID (default: 'sonic-3') */\n modelId?: string;\n /** Output sample rate in Hz (default: 16000) */\n sampleRate?: number;\n /** API version (default: '2024-06-10') */\n apiVersion?: string;\n /** Language code (default: 'en') */\n language?: string;\n /** Speech speed multiplier, 0.6-1.5 (default: 1.0). Sonic-3 only. */\n speed?: number;\n /** Emotion string (e.g. 'friendly', 'calm'). Sonic-3 only. */\n emotion?: string;\n}\n\n/** Per-context state for tracking an in-flight synthesis. */\ninterface ContextState {\n chunks: Buffer[];\n done: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class CartesiaTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly voiceId: string;\n private readonly modelId: string;\n private readonly sampleRate: number;\n private readonly apiVersion: string;\n private readonly language?: string;\n private readonly speed: number | undefined;\n private readonly emotion: string | undefined;\n\n private ws: WebSocket | null = null;\n private _connected = false;\n private connectPromise: Promise<void> | null = null;\n /** Active contexts keyed by context_id */\n private contexts = new Map<string, ContextState>();\n private contextCounter = 0;\n\n constructor(options: CartesiaTTSOptions) {\n if (!options.apiKey) {\n throw new Error('CartesiaTTS requires an apiKey');\n }\n if (!options.voiceId) {\n throw new Error('CartesiaTTS requires a voiceId');\n }\n this.apiKey = options.apiKey;\n this.voiceId = options.voiceId;\n this.modelId = options.modelId ?? DEFAULT_MODEL;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n this.apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;\n this.language = options.language;\n this.speed = options.speed;\n this.emotion = options.emotion;\n }\n\n /** Close the WebSocket connection to allow clean process exit. */\n close(): void {\n if (this.ws) {\n log.debug('Closing Cartesia WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this._connected = false;\n this.connectPromise = null;\n }\n\n /** Pre-connect the WebSocket so first synthesize() doesn't pay connection cost. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection();\n\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Cartesia WebSocket not connected');\n }\n\n const contextId = `ctx-${++this.contextCounter}-${Date.now()}`;\n const ctx: ContextState = { chunks: [], done: false, error: null, wake: null };\n this.contexts.set(contextId, ctx);\n\n // Build request\n const request: Record<string, unknown> = {\n model_id: this.modelId,\n transcript: text,\n voice: { mode: 'id', id: this.voiceId },\n output_format: {\n container: 'raw',\n encoding: 'pcm_s16le',\n sample_rate: this.sampleRate,\n },\n context_id: contextId,\n continue: false,\n };\n\n if (this.language) {\n request.language = this.language;\n }\n\n // Sonic-3 generation config\n if (this.speed !== undefined || this.emotion !== undefined) {\n const genConfig: Record<string, unknown> = {};\n if (this.speed !== undefined) genConfig.speed = this.speed;\n if (this.emotion !== undefined) genConfig.emotion = this.emotion;\n request.generation_config = genConfig;\n }\n\n // Handle abort — cancel the context on the server\n const onAbort = () => {\n ctx.done = true;\n ctx.wake?.();\n // Send cancel to server so it stops generating\n if (this.ws?.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify({ context_id: contextId, cancel: true }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send synthesis request\n this.ws.send(JSON.stringify(request));\n\n // Yield audio chunks as they arrive\n try {\n while (true) {\n if (signal?.aborted) break;\n if (ctx.error) throw ctx.error;\n\n if (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n continue;\n }\n\n if (ctx.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n ctx.wake = resolve;\n });\n ctx.wake = null;\n }\n\n // Drain remaining chunks\n while (ctx.chunks.length > 0) {\n yield ctx.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.contexts.delete(contextId);\n }\n }\n\n /** Ensure the persistent WebSocket is connected. */\n private ensureConnection(): Promise<void> {\n if (this._connected && this.ws?.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n const url = `${CARTESIA_WS_BASE}?api_key=${this.apiKey}&cartesia_version=${this.apiVersion}`;\n log.debug('Connecting to Cartesia...');\n\n this.ws = new WebSocket(url);\n\n this.ws.on('open', () => {\n this._connected = true;\n this.connectPromise = null;\n log.info('Cartesia WebSocket connected');\n resolve();\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse Cartesia message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('Cartesia WebSocket error:', error);\n // Propagate error to all active contexts\n for (const ctx of this.contexts.values()) {\n ctx.error = error;\n ctx.wake?.();\n }\n this._connected = false;\n this.connectPromise = null;\n reject(error);\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`Cartesia WebSocket closed: ${code} ${reason.toString()}`);\n this._connected = false;\n this.connectPromise = null;\n // Mark all active contexts as done\n for (const ctx of this.contexts.values()) {\n ctx.done = true;\n ctx.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const contextId = msg.context_id as string | undefined;\n if (!contextId) return;\n\n const ctx = this.contexts.get(contextId);\n if (!ctx) return; // Stale context — already cleaned up\n\n const type = msg.type as string;\n\n if (type === 'chunk') {\n const b64 = msg.data as string;\n if (b64) {\n const pcm = Buffer.from(b64, 'base64');\n ctx.chunks.push(pcm);\n ctx.wake?.();\n }\n } else if (type === 'done') {\n log.debug(`Cartesia synthesis done for ${contextId} (${ctx.chunks.length} chunks pending)`);\n ctx.done = true;\n ctx.wake?.();\n } else if (type === 'error') {\n const errorMsg = msg.error as string ?? 'Unknown Cartesia error';\n log.error(`Cartesia error for ${contextId}: ${errorMsg}`);\n ctx.error = new Error(`Cartesia TTS error: ${errorMsg}`);\n ctx.wake?.();\n }\n }\n}\n","/**\n * DeepgramTTS — real-time streaming TTS via Deepgram Aura-2 WebSocket API.\n *\n * Protocol:\n * - Connect to wss://api.deepgram.com/v1/speak?model={model}&encoding=linear16&sample_rate={rate}\n * - Auth: Authorization: Token <key> header\n * - Send: {\"type\":\"Speak\",\"text\":\"...\"} then {\"type\":\"Flush\"}\n * - Receive: binary frames (raw PCM16) until {\"type\":\"Flushed\"} JSON\n * - Cancel: {\"type\":\"Clear\"} then {\"type\":\"Flush\"}, wait for Flushed\n *\n * Supports multi-language via connection pool (one WS per language).\n * Uses SSML <lang> tags to route text segments to the correct voice.\n */\n\nimport WebSocket from 'ws';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DeepgramTTS');\n\nconst DEEPGRAM_WS_BASE = 'wss://api.deepgram.com/v1/speak';\nconst DEFAULT_SAMPLE_RATE = 48000;\n\nexport interface DeepgramTTSOptions {\n apiKey: string;\n /** Single model string OR language->model map for multi-language */\n model: string | Record<string, string>;\n /** Default language for untagged text (default: 'en' or first key) */\n defaultLanguage?: string;\n /** Sample rate (default: 48000 — matches pipeline) */\n sampleRate?: number;\n}\n\ninterface LangSegment {\n lang: string;\n text: string;\n}\n\n/** Per-connection state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n flushed: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\n/**\n * Parse SSML <lang> tags into segments.\n *\n * Input: `Great job! <lang xml:lang=\"es\">Ahora repite: buenos días.</lang>`\n * Output: [{lang:'en', text:'Great job!'}, {lang:'es', text:'Ahora repite: buenos días.'}]\n *\n * Text outside tags uses defaultLang. Handles no tags, adjacent tags, nested text.\n * Malformed input is treated as default language.\n */\nexport function parseLangSegments(text: string, defaultLang: string): LangSegment[] {\n const segments: LangSegment[] = [];\n let i = 0;\n let currentText = '';\n\n while (i < text.length) {\n // Look for opening <lang tag\n if (text[i] === '<' && text.startsWith('<lang ', i)) {\n // Flush accumulated default-language text\n if (currentText) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n currentText = '';\n }\n\n // Find xml:lang=\"...\"\n const xmlLangStart = text.indexOf('xml:lang=\"', i);\n if (xmlLangStart === -1) {\n // Malformed — treat rest as default\n currentText += text[i];\n i++;\n continue;\n }\n\n const langStart = xmlLangStart + 'xml:lang=\"'.length;\n const langEnd = text.indexOf('\"', langStart);\n if (langEnd === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n const lang = text.substring(langStart, langEnd);\n\n // Find end of opening tag \">\"\n const tagClose = text.indexOf('>', langEnd);\n if (tagClose === -1) {\n currentText += text[i];\n i++;\n continue;\n }\n\n // Find closing </lang>\n const closingTag = '</lang>';\n const closingStart = text.indexOf(closingTag, tagClose + 1);\n if (closingStart === -1) {\n // No closing tag — treat the rest after opening tag as this language\n const innerText = text.substring(tagClose + 1).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n i = text.length;\n continue;\n }\n\n const innerText = text.substring(tagClose + 1, closingStart).trim();\n if (innerText) {\n segments.push({ lang, text: innerText });\n }\n\n i = closingStart + closingTag.length;\n continue;\n }\n\n currentText += text[i];\n i++;\n }\n\n // Flush remaining default-language text\n if (currentText.trim()) {\n segments.push({ lang: defaultLang, text: currentText.trim() });\n }\n\n return segments;\n}\n\nexport class DeepgramTTS implements TTSPlugin {\n private readonly apiKey: string;\n private readonly models: Record<string, string>;\n private readonly defaultLang: string;\n private readonly sampleRate: number;\n private readonly multiLanguage: boolean;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n /** Connection pool: one WebSocket per language code */\n private connections = new Map<string, WebSocket>();\n private connectPromises = new Map<string, Promise<void>>();\n /** Per-connection flush state */\n private flushStates = new Map<string, FlushState>();\n\n constructor(options: DeepgramTTSOptions) {\n if (!options.apiKey) {\n throw new Error('DeepgramTTS requires an apiKey');\n }\n\n this.apiKey = options.apiKey;\n this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n\n if (typeof options.model === 'string') {\n // Single-language mode\n this.multiLanguage = false;\n const lang = options.defaultLanguage ?? 'en';\n this.models = { [lang]: options.model };\n this.defaultLang = lang;\n } else {\n // Multi-language mode\n this.multiLanguage = true;\n this.models = { ...options.model };\n const keys = Object.keys(this.models);\n if (keys.length === 0) {\n throw new Error('DeepgramTTS model map must have at least one entry');\n }\n this.defaultLang = options.defaultLanguage ?? keys[0];\n }\n }\n\n /** Close all WebSocket connections to allow clean process exit. */\n close(): void {\n for (const [lang, ws] of this.connections) {\n log.debug(`Closing WebSocket for [${lang}]`);\n ws.close();\n }\n this.connections.clear();\n this.connectPromises.clear();\n this.flushStates.clear();\n }\n\n /** Pre-connect all language WebSocket connections. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connections...');\n const start = performance.now();\n try {\n await Promise.all(Object.keys(this.models).map((lang) => this.ensureConnection(lang)));\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = this.multiLanguage\n ? parseLangSegments(text, this.defaultLang)\n : [{ lang: this.defaultLang, text }];\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 mono: sampleRate * 0.2 * 2 bytes per sample\n const silenceBytes = Math.round(this.sampleRate * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.models[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n log.debug(`Synthesizing [${lang}]: \"${text.slice(0, 60)}\"`);\n\n await this.ensureConnection(lang);\n\n const ws = this.connections.get(lang);\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n throw new Error(`Deepgram WebSocket not connected for language \"${lang}\"`);\n }\n\n const state: FlushState = { chunks: [], flushed: false, error: null, wake: null };\n this.flushStates.set(lang, state);\n\n // Handle abort — send Clear + Flush to cancel\n const onAbort = () => {\n state.flushed = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'Clear' }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send Speak + Flush\n ws.send(JSON.stringify({ type: 'Speak', text }));\n ws.send(JSON.stringify({ type: 'Flush' }));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.flushed) break;\n\n // Wait for next chunk or Flushed signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n this.flushStates.delete(lang);\n }\n }\n\n /** Ensure a WebSocket connection exists for the given language. */\n private ensureConnection(lang: string): Promise<void> {\n const existing = this.connections.get(lang);\n if (existing && existing.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n const pending = this.connectPromises.get(lang);\n if (pending) return pending;\n\n const model = this.models[lang];\n if (!model) {\n return Promise.reject(new Error(`No Deepgram model configured for language \"${lang}\"`));\n }\n\n const promise = new Promise<void>((resolve, reject) => {\n const url = `${DEEPGRAM_WS_BASE}?model=${encodeURIComponent(model)}&encoding=linear16&sample_rate=${this.sampleRate}`;\n log.debug(`Connecting to Deepgram for [${lang}]: ${model}`);\n\n const ws = new WebSocket(url, {\n headers: {\n Authorization: `Token ${this.apiKey}`,\n },\n });\n\n ws.on('open', () => {\n this.connections.set(lang, ws);\n this.connectPromises.delete(lang);\n log.info(`Deepgram WebSocket connected for [${lang}] (${model})`);\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushStates.get(lang);\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 audio\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'Flushed') {\n state.flushed = true;\n state.wake?.();\n } else if (msg.type === 'Warning' || msg.type === 'Error') {\n log.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);\n }\n } catch {\n log.warn(`Failed to parse Deepgram message for [${lang}]`);\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error(`Deepgram WebSocket error [${lang}]:`, error);\n const state = this.flushStates.get(lang);\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`Deepgram WebSocket closed [${lang}]: ${code} ${reason.toString()}`);\n this.connections.delete(lang);\n this.connectPromises.delete(lang);\n const state = this.flushStates.get(lang);\n if (state) {\n state.flushed = true;\n state.wake?.();\n }\n });\n });\n\n this.connectPromises.set(lang, promise);\n return promise;\n }\n}\n","/**\n * DtelecomSTT — real-time streaming STT via dTelecom STT server (realtime-stt-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"type\":\"config\",\"language\":\"en\"} (or \"auto\" for Parakeet auto-detect)\n * - Wait for ready: {\"type\":\"ready\",\"client_id\":\"...\",\"language\":\"en\"}\n * - Send audio as binary PCM16 16kHz mono frames\n * - Receive transcriptions: {\"type\":\"transcription\",\"text\":\"...\",\"is_final\":true,\"latency_ms\":N}\n * - Receive VAD events: {\"type\":\"vad_event\",\"event\":\"speech_start\"|\"speech_end\"}\n * - Keepalive via {\"type\":\"ping\"} / {\"type\":\"pong\"}\n * - Mid-session reconfigure: send {\"type\":\"config\",\"language\":\"es\",\"model\":\"whisper\"} at any time\n */\n\nimport WebSocket from 'ws';\nimport { BaseSTTStream } from '../core/base-stt-stream';\nimport type { STTPlugin, STTStream, STTStreamOptions, TranscriptionResult } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomSTT');\n\nconst KEEPALIVE_INTERVAL_MS = 5_000;\n\nexport interface DtelecomSTTOptions {\n /** Server base URL, e.g. \"https://stt-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Initial language (default: \"auto\" for Parakeet auto-detect) */\n language?: string;\n /** Force Whisper model even if Parakeet supports the language */\n forceWhisper?: boolean;\n}\n\nexport class DtelecomSTT implements STTPlugin {\n private readonly options: DtelecomSTTOptions;\n\n constructor(options: DtelecomSTTOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomSTT requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomSTT requires a sessionKey');\n }\n this.options = options;\n }\n\n createStream(options?: STTStreamOptions): STTStream {\n const language = options?.language ?? this.options.language ?? 'auto';\n return new DtelecomSTTStream(this.options, language);\n }\n}\n\nclass DtelecomSTTStream extends BaseSTTStream {\n private ws: WebSocket | null = null;\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly forceWhisper: boolean;\n private _ready = false;\n private _closed = false;\n private pendingAudio: Buffer[] = [];\n private keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n private language: string;\n\n constructor(options: DtelecomSTTOptions, language: string) {\n super();\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.language = language;\n this.forceWhisper = options.forceWhisper ?? false;\n this.connect();\n }\n\n sendAudio(pcm16: Buffer): void {\n if (this._closed) return;\n\n if (!this._ready) {\n this.pendingAudio.push(pcm16);\n return;\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(pcm16);\n }\n }\n\n /**\n * Switch language mid-session.\n * Sends a reconfigure message to the server; clears buffers and updates model routing.\n */\n setLanguage(language: string, options?: { forceWhisper?: boolean }): void {\n if (this._closed) return;\n this.language = language;\n\n const config: Record<string, string> = { type: 'config', language };\n if (options?.forceWhisper) {\n config.model = 'whisper';\n }\n\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(config));\n log.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ', model=whisper' : ''}`);\n }\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._ready = false;\n this.pendingAudio = [];\n this.stopKeepAlive();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n log.debug('DtelecomSTT stream closed');\n }\n\n private connect(): void {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom STT: ${wsUrl}`);\n\n this.ws = new WebSocket(wsUrl);\n\n this.ws.on('open', () => {\n log.info('dTelecom STT WebSocket connected');\n\n // Send initial config with session_key\n const config: Record<string, string> = {\n type: 'config',\n language: this.language,\n session_key: this.sessionKey,\n };\n if (this.forceWhisper) {\n config.model = 'whisper';\n }\n this.ws!.send(JSON.stringify(config));\n });\n\n this.ws.on('message', (data, isBinary) => {\n if (isBinary) return; // Server only sends JSON text messages\n try {\n const msg = JSON.parse(data.toString());\n this.handleMessage(msg);\n } catch (err) {\n log.error('Failed to parse dTelecom STT message:', err);\n }\n });\n\n this.ws.on('error', (err) => {\n log.error('dTelecom STT WebSocket error:', err);\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n\n this.ws.on('close', (code, reason) => {\n log.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);\n this._ready = false;\n this.stopKeepAlive();\n\n // Reconnect if not intentionally closed\n if (!this._closed) {\n log.info('dTelecom STT connection lost, reconnecting in 1s...');\n setTimeout(() => {\n if (!this._closed) this.connect();\n }, 1000);\n }\n });\n }\n\n private handleMessage(msg: Record<string, unknown>): void {\n const type = msg.type as string;\n\n if (type === 'ready') {\n this.handleReady(msg);\n } else if (type === 'transcription') {\n this.handleTranscription(msg);\n } else if (type === 'vad_event') {\n this.handleVadEvent(msg);\n } else if (type === 'pong') {\n // Keepalive response — no action needed\n } else if (type === 'error') {\n const errData = msg.error;\n const errorMsg = (msg.message as string)\n || (typeof errData === 'string' ? errData : JSON.stringify(errData))\n || 'Unknown STT error';\n log.error(`dTelecom STT error: ${errorMsg}`);\n this.emit('error', new Error(errorMsg));\n }\n }\n\n private handleReady(msg: Record<string, unknown>): void {\n const clientId = msg.client_id as string | undefined;\n const lang = msg.language as string | undefined;\n log.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);\n\n this._ready = true;\n\n // Flush pending audio\n for (const buf of this.pendingAudio) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(buf);\n }\n }\n this.pendingAudio = [];\n\n this.startKeepAlive();\n }\n\n private handleTranscription(msg: Record<string, unknown>): void {\n const text = (msg.text as string) ?? '';\n const isFinal = (msg.is_final as boolean) ?? false;\n const language = msg.language as string | undefined;\n const latencyMs = msg.latency_ms as number | undefined;\n\n if (!text) return;\n\n if (isFinal && latencyMs !== undefined) {\n log.info(`stt_final: ${latencyMs.toFixed(0)}ms \"${text.slice(0, 50)}\"`);\n }\n\n this.emit('transcription', {\n text,\n isFinal,\n language,\n sttDuration: isFinal ? latencyMs : undefined,\n } satisfies TranscriptionResult);\n }\n\n private handleVadEvent(msg: Record<string, unknown>): void {\n const event = msg.event as string;\n log.debug(`VAD event: ${event}`);\n\n if (event === 'speech_start') {\n // Emit empty non-final transcription to signal speech detected.\n // Note: barge-in in the pipeline requires non-empty text, so this won't\n // trigger it directly. Actual interim transcriptions that follow will.\n this.emit('transcription', {\n text: '',\n isFinal: false,\n } satisfies TranscriptionResult);\n }\n }\n\n private startKeepAlive(): void {\n this.stopKeepAlive();\n this.keepAliveTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = null;\n }\n }\n}\n","/**\n * DtelecomTTS — real-time streaming TTS via dTelecom TTS server (realtime-tts-m2).\n *\n * Protocol:\n * - Connect to ws://<server>:<port> (address from options, no API key)\n * - Send config: {\"config\":{\"voice\":\"af_heart\",\"lang_code\":\"a\",\"speed\":1.0}}\n * - Send text: {\"text\":\"Hello world\"} — uses config defaults\n * - Send text with per-message override: {\"text\":\"Hola\",\"voice\":\"ef_dora\",\"lang_code\":\"e\",\"speed\":1.0}\n * - Receive: {\"type\":\"generating\",\"text\":\"...\"} then binary PCM16 48kHz chunks, then {\"type\":\"done\"}\n * - Cancel: {\"type\":\"clear\"} → {\"type\":\"cleared\"}\n *\n * Key differences from DeepgramTTS:\n * - Single WebSocket connection (not per-language pool)\n * - Per-message voice/language switching instead of separate connections\n * - Server outputs 48kHz PCM16 (resampled from Kokoro's native 24kHz)\n * - Uses SSML <lang> tags to route text segments to correct voice (same as DeepgramTTS)\n */\n\nimport WebSocket from 'ws';\nimport { parseLangSegments } from './deepgram-tts';\nimport type { TTSPlugin } from '../core/types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('DtelecomTTS');\n\nexport interface VoiceConfig {\n voice: string;\n langCode: string;\n}\n\nexport interface DtelecomTTSOptions {\n /** Server base URL, e.g. \"https://tts-xxx.dtel.network\" */\n serverUrl: string;\n /** JWT session key from x402 gateway */\n sessionKey: string;\n /** Voice config per language: { en: { voice: \"af_heart\", langCode: \"a\" }, es: { voice: \"bf_emma\", langCode: \"b\" } } */\n voices: Record<string, VoiceConfig>;\n /** Default language code (default: \"en\") */\n defaultLanguage?: string;\n /** Speech speed multiplier (default: 1.0) */\n speed?: number;\n}\n\n/** Per-request state for tracking an in-flight synthesis. */\ninterface FlushState {\n chunks: Buffer[];\n done: boolean;\n cleared: boolean;\n error: Error | null;\n wake: (() => void) | null;\n}\n\nexport class DtelecomTTS implements TTSPlugin {\n private readonly serverUrl: string;\n private readonly sessionKey: string;\n private readonly voices: Record<string, VoiceConfig>;\n private readonly defaultLang: string;\n private readonly speed: number;\n\n private ws: WebSocket | null = null;\n private connectPromise: Promise<void> | null = null;\n private flushState: FlushState | null = null;\n /** Resolves when the current WS conversation receives \"done\"/\"cleared\". */\n private _wsDone: Promise<void> = Promise.resolve();\n private _resolveWsDone?: () => void;\n\n /** Default language code for untagged text (e.g. 'en'). */\n get defaultLanguage(): string {\n return this.defaultLang;\n }\n\n constructor(options: DtelecomTTSOptions) {\n if (!options.serverUrl) {\n throw new Error('DtelecomTTS requires a serverUrl');\n }\n if (!options.sessionKey) {\n throw new Error('DtelecomTTS requires a sessionKey');\n }\n if (!options.voices || Object.keys(options.voices).length === 0) {\n throw new Error('DtelecomTTS requires at least one voice config');\n }\n\n this.serverUrl = options.serverUrl;\n this.sessionKey = options.sessionKey;\n this.voices = { ...options.voices };\n this.defaultLang = options.defaultLanguage ?? Object.keys(this.voices)[0];\n this.speed = options.speed ?? 1.0;\n }\n\n /** Pre-connect WebSocket to TTS server. */\n async warmup(): Promise<void> {\n log.info('Warming up TTS connection...');\n const start = performance.now();\n try {\n await this.ensureConnection();\n log.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);\n } catch (err) {\n log.warn('TTS warmup failed (non-fatal):', err);\n }\n }\n\n /** Close WebSocket connection. */\n close(): void {\n if (this.ws) {\n log.debug('Closing TTS WebSocket');\n this.ws.close();\n this.ws = null;\n }\n this.connectPromise = null;\n this.flushState = null;\n }\n\n /** Strip SSML lang tags from text for display/events. */\n cleanText(text: string): string {\n return parseLangSegments(text, this.defaultLang)\n .map((s) => s.text)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async *synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n const rawSegments = parseLangSegments(text, this.defaultLang);\n\n // Merge punctuation-only segments into the previous segment.\n // After <lang> tag splitting, trailing punctuation (e.g. \"?\" or \".\") can end up\n // in its own segment. It belongs to the previous phrase for correct intonation.\n const segments: typeof rawSegments = [];\n for (const seg of rawSegments) {\n if (!seg.text.trim()) continue;\n if (!/\\p{L}/u.test(seg.text) && segments.length > 0) {\n segments[segments.length - 1].text += seg.text;\n } else {\n segments.push(seg);\n }\n }\n\n // 200ms silence buffer for gaps between language switches\n // PCM16 48kHz mono: 48000 * 0.2 * 2 = 19200 bytes\n const silenceBytes = Math.round(48000 * 0.2) * 2;\n const silence = Buffer.alloc(silenceBytes);\n\n let prevLang: string | null = null;\n for (const segment of segments) {\n if (signal?.aborted) break;\n const lang = this.voices[segment.lang] ? segment.lang : this.defaultLang;\n\n // Insert silence when switching between languages\n if (prevLang !== null && lang !== prevLang) {\n yield silence;\n }\n prevLang = lang;\n\n yield* this.synthesizeSegment(lang, segment.text, signal);\n }\n }\n\n private async *synthesizeSegment(\n lang: string,\n text: string,\n signal?: AbortSignal,\n ): AsyncGenerator<Buffer> {\n if (signal?.aborted) return;\n\n // Wait for previous WS conversation to finish (\"done\"/\"cleared\" received).\n // This lets pipeline prefetch start TTS for the next sentence as soon as\n // the current one finishes generating — while audio is still playing.\n await this._wsDone;\n if (signal?.aborted) return;\n\n // Set up new _wsDone for this conversation\n this._wsDone = new Promise((r) => { this._resolveWsDone = r; });\n\n await this.ensureConnection();\n\n const ws = this.ws;\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n this._resolveWsDone?.();\n throw new Error('dTelecom TTS WebSocket not connected');\n }\n\n const state: FlushState = { chunks: [], done: false, cleared: false, error: null, wake: null };\n this.flushState = state;\n\n // Handle abort — send clear to cancel\n const onAbort = () => {\n state.done = true;\n state.wake?.();\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(JSON.stringify({ type: 'clear' }));\n } catch {\n // Ignore send errors during cancellation\n }\n }\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Send text with per-message voice/language override\n const voiceConfig = this.voices[lang];\n const msg: Record<string, unknown> = { text };\n if (voiceConfig) {\n msg.voice = voiceConfig.voice;\n msg.lang_code = voiceConfig.langCode;\n msg.speed = this.speed;\n }\n log.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? 'default'} lang_code=${voiceConfig?.langCode ?? 'default'} \"${text.slice(0, 60)}\"`)\n ws.send(JSON.stringify(msg));\n\n try {\n while (true) {\n if (signal?.aborted) break;\n if (state.error) throw state.error;\n\n if (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n continue;\n }\n\n if (state.done) break;\n\n // Wait for next chunk or done signal\n await new Promise<void>((resolve) => {\n state.wake = resolve;\n });\n state.wake = null;\n }\n\n // Drain remaining chunks\n while (state.chunks.length > 0) {\n yield state.chunks.shift()!;\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n // Only clear flushState if it hasn't been replaced by the next synthesis\n if (this.flushState === state) {\n this.flushState = null;\n }\n }\n }\n\n /** Ensure a WebSocket connection exists and is open. */\n private ensureConnection(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n return Promise.resolve();\n }\n\n // Deduplicate concurrent connection attempts\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n // Use /v1/stream auth path, convert https:// to wss://\n const url = this.serverUrl.replace(/\\/$/, '') + '/v1/stream';\n const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://');\n\n log.debug(`Connecting to dTelecom TTS: ${wsUrl}`);\n\n const ws = new WebSocket(wsUrl);\n\n ws.on('open', () => {\n this.ws = ws;\n this.connectPromise = null;\n\n // Send initial config with session_key and default voice\n const defaultVoice = this.voices[this.defaultLang];\n const msg: Record<string, unknown> = {\n session_key: this.sessionKey,\n };\n if (defaultVoice) {\n msg.config = {\n voice: defaultVoice.voice,\n lang_code: defaultVoice.langCode,\n speed: this.speed,\n };\n }\n ws.send(JSON.stringify(msg));\n\n log.info('dTelecom TTS WebSocket connected');\n resolve();\n });\n\n ws.on('message', (data, isBinary) => {\n const state = this.flushState;\n if (!state) return;\n\n if (isBinary) {\n // Binary frame = raw PCM16 48kHz audio (server resamples from Kokoro's 24kHz)\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n state.chunks.push(buf);\n state.wake?.();\n } else {\n // Text frame = JSON control message\n try {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'done') {\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'cleared') {\n state.cleared = true;\n state.done = true;\n state.wake?.();\n this._resolveWsDone?.();\n } else if (msg.type === 'generating') {\n log.debug(`TTS generating: \"${(msg.text as string)?.slice(0, 40)}\"`);\n } else if (msg.type === 'error') {\n const errorMsg = (msg.message as string) || 'Unknown TTS error';\n log.error(`dTelecom TTS error: ${errorMsg}`);\n state.error = new Error(errorMsg);\n state.wake?.();\n }\n } catch {\n log.warn('Failed to parse dTelecom TTS message');\n }\n }\n });\n\n ws.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error('dTelecom TTS WebSocket error:', error);\n const state = this.flushState;\n if (state) {\n state.error = error;\n state.wake?.();\n }\n this.ws = null;\n this.connectPromise = null;\n this._resolveWsDone?.();\n reject(error);\n });\n\n ws.on('close', (code, reason) => {\n log.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);\n this.ws = null;\n this.connectPromise = null;\n this._resolveWsDone?.();\n const state = this.flushState;\n if (state) {\n state.done = true;\n state.wake?.();\n }\n });\n });\n\n return this.connectPromise;\n }\n}\n"],"mappings":"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","names":["log","log","WebSocket","log","log","WebSocket","WebSocket","log","DEFAULT_SAMPLE_RATE","innerText","WebSocket","WebSocket","log","KEEPALIVE_INTERVAL_MS","WebSocket","WebSocket","log","WebSocket"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtelecom/agents-js",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "AI voice agent framework for dTelecom rooms",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",