@happyvertical/speech 0.101.2 → 0.102.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +36 -0
  2. package/dist/browser.d.ts +8 -0
  3. package/dist/browser.d.ts.map +1 -0
  4. package/dist/browser.js +159 -0
  5. package/dist/browser.js.map +1 -0
  6. package/dist/chunks/{errors-CQBX3OPS.js → errors-Cs0vX-mV.js} +25 -2
  7. package/dist/chunks/errors-Cs0vX-mV.js.map +1 -0
  8. package/dist/chunks/{registry-C_5BakmM.js → registry-DFSF3WoM.js} +2 -2
  9. package/dist/chunks/{registry-C_5BakmM.js.map → registry-DFSF3WoM.js.map} +1 -1
  10. package/dist/chunks/wav-B0Djpj2r.js +232 -0
  11. package/dist/chunks/wav-B0Djpj2r.js.map +1 -0
  12. package/dist/chunks/{websocket-CR9uXAuS.js → websocket-YbAfY6YI.js} +2 -2
  13. package/dist/chunks/{websocket-CR9uXAuS.js.map → websocket-YbAfY6YI.js.map} +1 -1
  14. package/dist/conversation-server.js +2 -2
  15. package/dist/conversation.js +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +35 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/local.js +2 -2
  21. package/dist/pcm.d.ts +8 -0
  22. package/dist/pcm.d.ts.map +1 -0
  23. package/dist/pcm.js +3 -0
  24. package/dist/shared/errors.d.ts +18 -0
  25. package/dist/shared/errors.d.ts.map +1 -1
  26. package/dist/shared/input-format.d.ts +20 -0
  27. package/dist/shared/input-format.d.ts.map +1 -0
  28. package/dist/shared/pcm-capture.d.ts +38 -0
  29. package/dist/shared/pcm-capture.d.ts.map +1 -0
  30. package/dist/shared/resample.d.ts +15 -0
  31. package/dist/shared/resample.d.ts.map +1 -0
  32. package/dist/shared/wav.d.ts +52 -0
  33. package/dist/shared/wav.d.ts.map +1 -0
  34. package/package.json +9 -1
  35. package/dist/chunks/errors-CQBX3OPS.js.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/shared/retry.ts","../src/shared/http.ts","../src/shared/realtime-auth.ts","../src/shared/pcm.ts","../src/shared/turn-limit.ts","../src/shared/realtime-session.ts","../src/adapters/openai-realtime.ts","../src/adapters/voxtral-realtime.ts","../src/adapters/openai-compatible.ts","../src/adapters/openai-compatible-transcriber.ts","../src/adapters/qwen3.ts","../src/adapters/studio-server.ts","../src/shared/streaming-factory.ts","../src/shared/factory.ts","../src/shared/streaming-client-secret.ts"],"sourcesContent":["/**\n * Retry with exponential backoff for speech provider calls. Retries only\n * rate-limit (429) and server (5xx) responses, honours `Retry-After`, and\n * stops immediately when the caller aborts.\n */\n\nimport { SpeechConfigurationError, SpeechProviderError } from './errors.js';\n\nexport interface SpeechRetryOptions {\n /** Retries after the first attempt. Default `2`. `0` disables retries. */\n maxRetries?: number;\n /** First backoff delay in milliseconds. Default `500`. */\n initialDelayMs?: number;\n /**\n * Upper bound for any single wait in milliseconds. Default `30000`. A\n * `Retry-After` longer than this ends retrying rather than retrying early.\n */\n maxDelayMs?: number;\n}\n\nexport const DEFAULT_SPEECH_RETRY: Required<SpeechRetryOptions> = {\n maxRetries: 2,\n initialDelayMs: 500,\n maxDelayMs: 30_000,\n};\n\nexport function resolveRetryOptions(\n retry: SpeechRetryOptions | false | undefined,\n defaults: Required<SpeechRetryOptions> | false = DEFAULT_SPEECH_RETRY,\n): Required<SpeechRetryOptions> | false {\n if (retry === false) {\n return false;\n }\n\n if (retry === undefined) {\n return defaults;\n }\n\n const base = defaults || DEFAULT_SPEECH_RETRY;\n return {\n maxRetries: Math.floor(\n finiteRetryValue('maxRetries', retry.maxRetries, base.maxRetries),\n ),\n initialDelayMs: finiteRetryValue(\n 'initialDelayMs',\n retry.initialDelayMs,\n base.initialDelayMs,\n ),\n maxDelayMs: finiteRetryValue(\n 'maxDelayMs',\n retry.maxDelayMs,\n base.maxDelayMs,\n ),\n };\n}\n\n/**\n * Rejects non-finite values (`NaN`, `Infinity`): they would make the retry\n * budget or delay cap unbounded. Negative values clamp to `0`.\n */\nfunction finiteRetryValue(\n name: keyof SpeechRetryOptions,\n value: number | undefined,\n fallback: number,\n): number {\n if (value === undefined) {\n return Math.max(0, fallback);\n }\n\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new SpeechConfigurationError(\n `Speech retry option ${name} must be a finite number`,\n );\n }\n\n return Math.max(0, value);\n}\n\nexport function isRetryableStatus(status: number | undefined): boolean {\n return status === 429 || (status !== undefined && status >= 500);\n}\n\n/**\n * Parses a `Retry-After` header (delta-seconds or HTTP-date) into\n * milliseconds. Returns `undefined` when absent or unparseable.\n */\nexport function parseRetryAfter(\n value: string | null | undefined,\n now: number = Date.now(),\n): number | undefined {\n if (!value?.trim()) {\n return undefined;\n }\n\n const trimmed = value.trim();\n if (/^\\d+(\\.\\d+)?$/.test(trimmed)) {\n return Math.round(Number(trimmed) * 1000);\n }\n\n const date = Date.parse(trimmed);\n return Number.isNaN(date) ? undefined : Math.max(0, date - now);\n}\n\n/**\n * Runs `attempt` until it succeeds, throws a non-retryable error, or the\n * retry budget is exhausted. Retryable errors are {@link SpeechProviderError}\n * instances whose `status` is 429 or 5xx.\n */\nexport async function withSpeechRetry<T>(\n attempt: (attemptNumber: number) => Promise<T>,\n retry: Required<SpeechRetryOptions> | false,\n signal?: AbortSignal,\n): Promise<T> {\n const maxRetries = retry ? retry.maxRetries : 0;\n\n for (let attemptNumber = 0; ; attemptNumber += 1) {\n try {\n return await attempt(attemptNumber);\n } catch (error) {\n if (\n !retry ||\n attemptNumber >= maxRetries ||\n signal?.aborted ||\n !(error instanceof SpeechProviderError) ||\n !isRetryableStatus(error.status)\n ) {\n throw error;\n }\n\n const backoff = Math.min(\n retry.maxDelayMs,\n retry.initialDelayMs * 2 ** attemptNumber,\n );\n const retryAfterMs = error.retryAfterMs;\n if (retryAfterMs !== undefined && retryAfterMs > retry.maxDelayMs) {\n throw error;\n }\n\n await sleep(Math.max(backoff, retryAfterMs ?? 0), signal);\n }\n }\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","import { SpeechConfigurationError, SpeechProviderError } from './errors.js';\nimport {\n parseRetryAfter,\n type SpeechRetryOptions,\n withSpeechRetry,\n} from './retry.js';\nimport type {\n HttpSpeechOptions,\n SpeechFetch,\n SynthesisRequest,\n SynthesizedSpeech,\n TranscriptResult,\n TranscriptSegment,\n WordTiming,\n} from './types.js';\n\n/**\n * Parses a base URL, dropping any fragment and keeping any query string.\n * Throws {@link SpeechConfigurationError} for a blank or unparseable value.\n * The value is not echoed in the error because it may carry credentials.\n */\nfunction parseBaseUrl(baseUrl: string): URL {\n const trimmed = baseUrl.trim();\n if (!trimmed) {\n throw new SpeechConfigurationError('Speech adapter baseUrl is required');\n }\n\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n throw new SpeechConfigurationError(\n 'Speech adapter baseUrl is not a valid absolute URL',\n );\n }\n\n url.hash = '';\n return url;\n}\n\n/**\n * Returns `baseUrl` with a trailing `/` on its path so relative paths resolve\n * beneath it. The query string is preserved and any fragment is dropped.\n */\nexport function normalizeBaseUrl(baseUrl: string): string {\n const url = parseBaseUrl(baseUrl);\n if (!url.pathname.endsWith('/')) {\n url.pathname = `${url.pathname}/`;\n }\n\n return url.toString();\n}\n\n/**\n * Resolves `path` against `baseUrl`. A relative path without its own query\n * inherits the base URL's query (for example a gateway `api-version`); an\n * absolute URL is returned unchanged.\n */\nexport function resolveSpeechUrl(baseUrl: string, path: string): string {\n const base = new URL(normalizeBaseUrl(baseUrl));\n const isAbsolute = /^[a-z][a-z\\d+.-]*:/i.test(path);\n const resolved = new URL(isAbsolute ? path : path.replace(/^\\//, ''), base);\n if (!isAbsolute && !resolved.search && base.search) {\n resolved.search = base.search;\n }\n\n return resolved.toString();\n}\n\n/**\n * Resolves an OpenAI-compatible endpoint from a base URL. Accepts a server\n * root (`https://api.openai.com` → `/v1/<resource>`), an API root ending in a\n * version segment (`http://gateway/stt/v1` → `/<resource>`), or the full\n * endpoint URL (used as-is). Only the path is rewritten: the query string is\n * preserved and any fragment is dropped.\n */\nexport function resolveOpenAICompatibleUrl(\n baseUrl: string,\n resource: string,\n): string {\n const trimmedResource = resource.replace(/^\\/+|\\/+$/g, '');\n const url = parseBaseUrl(baseUrl);\n const pathname = url.pathname.replace(/\\/+$/, '');\n\n if (pathname.endsWith(`/${trimmedResource}`)) {\n url.pathname = pathname;\n } else if (/\\/v\\d+$/.test(pathname)) {\n url.pathname = `${pathname}/${trimmedResource}`;\n } else {\n url.pathname = `${pathname}/v1/${trimmedResource}`;\n }\n\n return url.toString();\n}\n\nexport function resolveFetch(fetchOverride?: SpeechFetch): SpeechFetch {\n if (fetchOverride) {\n return fetchOverride;\n }\n\n const globalFetch = globalThis.fetch;\n if (!globalFetch) {\n throw new SpeechConfigurationError(\n 'No fetch implementation available for speech adapter',\n );\n }\n\n return globalFetch.bind(globalThis);\n}\n\nexport function mergeHeaders(\n options: Pick<HttpSpeechOptions, 'apiKey' | 'headers'>,\n extra?: HeadersInit,\n): Headers {\n const headers = new Headers(options.headers);\n\n if (options.apiKey && !headers.has('authorization')) {\n headers.set('authorization', `Bearer ${options.apiKey}`);\n }\n\n if (extra) {\n new Headers(extra).forEach((value, key) => {\n headers.set(key, value);\n });\n }\n\n return headers;\n}\n\nexport abstract class HttpSpeechAdapter {\n protected readonly baseUrl: string;\n protected readonly fetchImpl: SpeechFetch;\n protected readonly apiKey?: string;\n protected readonly headers?: HeadersInit;\n protected readonly timeoutMs?: number;\n\n constructor(options: HttpSpeechOptions) {\n this.baseUrl = options.baseUrl;\n this.fetchImpl = resolveFetch(options.fetch);\n this.apiKey = options.apiKey;\n this.headers = options.headers;\n this.timeoutMs = options.timeoutMs;\n }\n\n /**\n * POSTs to `path` (relative to `baseUrl`, or an absolute URL). The optional\n * `retry` policy retries 429/5xx responses; `timeoutMs` applies per attempt.\n * The request body must be re-sendable (string, Blob, FormData) when\n * retries are enabled.\n */\n protected async post<T>(\n adapterName: string,\n path: string,\n init: Omit<RequestInit, 'method'>,\n readResponse: (response: Response) => Promise<T>,\n retry: Required<SpeechRetryOptions> | false = false,\n ): Promise<T> {\n return withSpeechRetry(\n () => this.postOnce(adapterName, path, init, readResponse),\n retry,\n init.signal ?? undefined,\n );\n }\n\n private async postOnce<T>(\n adapterName: string,\n path: string,\n init: Omit<RequestInit, 'method'>,\n readResponse: (response: Response) => Promise<T>,\n ): Promise<T> {\n const timeoutController =\n this.timeoutMs && this.timeoutMs > 0 ? new AbortController() : undefined;\n const timeout =\n timeoutController && this.timeoutMs\n ? setTimeout(() => timeoutController.abort(), this.timeoutMs)\n : undefined;\n const requestSignal = composeAbortSignals(\n init.signal,\n timeoutController?.signal,\n );\n\n try {\n // Fail fast when the caller aborted before the request was sent.\n requestSignal.signal?.throwIfAborted();\n const response = await this.fetchImpl(\n resolveSpeechUrl(this.baseUrl, path),\n {\n ...init,\n method: 'POST',\n headers: mergeHeaders(\n { apiKey: this.apiKey, headers: this.headers },\n init.headers,\n ),\n signal: requestSignal.signal,\n },\n );\n await assertOk(response, adapterName, this.apiKey);\n return await readResponse(response);\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n requestSignal.cleanup();\n }\n }\n}\n\nfunction composeAbortSignals(\n ...signals: Array<AbortSignal | undefined | null>\n): { signal?: AbortSignal; cleanup: () => void } {\n const activeSignals = signals.filter((signal): signal is AbortSignal =>\n Boolean(signal),\n );\n\n if (activeSignals.length === 0) {\n return { cleanup: () => undefined };\n }\n\n if (activeSignals.length === 1) {\n return { signal: activeSignals[0], cleanup: () => undefined };\n }\n\n const controller = new AbortController();\n let cleanup = () => undefined;\n\n const abortFrom = (signal: AbortSignal) => {\n if (!controller.signal.aborted) {\n controller.abort(signal.reason);\n }\n cleanup();\n };\n\n const onAbort = (event: Event) => {\n abortFrom(event.target as AbortSignal);\n };\n\n cleanup = () => {\n for (const signal of activeSignals) {\n signal.removeEventListener('abort', onAbort);\n }\n };\n\n for (const signal of activeSignals) {\n if (signal.aborted) {\n abortFrom(signal);\n return { signal: controller.signal, cleanup: () => undefined };\n }\n }\n\n for (const signal of activeSignals) {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n return { signal: controller.signal, cleanup };\n}\n\nasync function assertOk(\n response: Response,\n adapterName: string,\n apiKey?: string,\n): Promise<void> {\n if (response.ok) {\n return;\n }\n\n const responseBody = await response.text().catch(() => undefined);\n throw new SpeechProviderError(\n adapterName,\n `${adapterName} speech request failed with HTTP ${response.status}`,\n {\n status: response.status,\n responseBody: redactSecret(responseBody, apiKey),\n retryAfterMs: parseRetryAfter(response.headers.get('retry-after')),\n },\n );\n}\n\n/** Removes an API key from provider-supplied text before it is surfaced. */\nexport function redactSecret(\n text: string | undefined,\n secret: string | undefined,\n): string | undefined {\n if (!text || !secret) {\n return text;\n }\n\n return text.split(secret).join('[REDACTED]');\n}\n\nexport function appendOptionalFormValue(\n form: FormData,\n name: string,\n value: unknown,\n): void {\n if (value === undefined || value === null) {\n return;\n }\n\n form.append(name, String(value));\n}\n\nexport function createHappyVerticalSynthesisForm(\n request: SynthesisRequest,\n defaultVoice?: string,\n): FormData {\n const form = new FormData();\n const voice =\n request.voice && typeof request.voice !== 'string'\n ? request.voice\n : undefined;\n form.set('text', request.text);\n appendOptionalFormValue(\n form,\n 'language',\n request.language ?? voice?.language,\n );\n appendOptionalFormValue(\n form,\n 'speaker',\n voiceToString(request.voice, defaultVoice),\n );\n appendOptionalFormValue(form, 'speed', request.speed);\n\n appendOptionalFormValue(form, 'voice_prompt', voice?.prompt);\n\n return form;\n}\n\nexport function voiceToString(\n voice:\n | string\n | { id?: string; name?: string; speakerId?: string }\n | undefined,\n fallback?: string,\n): string | undefined {\n if (!voice) {\n return fallback;\n }\n\n if (typeof voice === 'string') {\n return voice;\n }\n\n return voice.id ?? voice.name ?? voice.speakerId ?? fallback;\n}\n\nexport function compactJson(\n value: Record<string, unknown>,\n): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(value).filter(([, entryValue]) => entryValue !== undefined),\n );\n}\n\nexport function contentTypeToFormat(contentType: string): string | undefined {\n const match = /^audio\\/([^;]+)/.exec(contentType);\n return match?.[1];\n}\n\nexport function arrayBufferFromBytes(bytes: Uint8Array): ArrayBuffer {\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n return copy.buffer;\n}\n\nexport function decodeBase64(base64: string): ArrayBuffer {\n if (typeof Buffer !== 'undefined') {\n return arrayBufferFromBytes(Buffer.from(base64, 'base64'));\n }\n\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes.buffer;\n}\n\nfunction getNumber(\n value: Record<string, unknown>,\n ...keys: string[]\n): number | undefined {\n for (const key of keys) {\n const candidate = value[key];\n if (typeof candidate === 'number') {\n return candidate;\n }\n }\n\n return undefined;\n}\n\nfunction getString(\n value: Record<string, unknown>,\n ...keys: string[]\n): string | undefined {\n for (const key of keys) {\n const candidate = value[key];\n if (typeof candidate === 'string') {\n return candidate;\n }\n }\n\n return undefined;\n}\n\nexport function normalizeWordTimings(value: unknown): WordTiming[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n return value.flatMap((entry): WordTiming[] => {\n if (!entry || typeof entry !== 'object') {\n return [];\n }\n\n const record = entry as Record<string, unknown>;\n const word = getString(record, 'word', 'text', 'token');\n const startSeconds = getNumber(\n record,\n 'startSeconds',\n 'start',\n 'start_time',\n );\n const endSeconds = getNumber(record, 'endSeconds', 'end', 'end_time');\n\n if (!word || startSeconds === undefined || endSeconds === undefined) {\n return [];\n }\n\n return [\n {\n word,\n startSeconds,\n endSeconds,\n confidence: getNumber(record, 'confidence'),\n speakerId: getString(record, 'speakerId', 'speaker', 'speaker_id'),\n },\n ];\n });\n}\n\nexport function normalizeTranscriptSegments(\n value: unknown,\n): TranscriptSegment[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n return value.flatMap((entry): TranscriptSegment[] => {\n if (!entry || typeof entry !== 'object') {\n return [];\n }\n\n const record = entry as Record<string, unknown>;\n const text = getString(record, 'text', 'transcript');\n if (!text) {\n return [];\n }\n\n return [\n {\n text,\n startSeconds: getNumber(record, 'startSeconds', 'start', 'start_time'),\n endSeconds: getNumber(record, 'endSeconds', 'end', 'end_time'),\n confidence: getNumber(record, 'confidence'),\n speakerId: getString(record, 'speakerId', 'speaker', 'speaker_id'),\n },\n ];\n });\n}\n\nexport async function readTranscriptResponse(\n response: Response,\n adapterName: string,\n): Promise<TranscriptResult> {\n const contentType = response.headers.get('content-type') ?? '';\n\n if (!contentType.includes('application/json')) {\n const text = await response.text();\n return {\n text,\n provider: adapterName,\n };\n }\n\n const json = (await response.json()) as Record<string, unknown>;\n const text = getString(json, 'text', 'transcript', 'transcription') ?? '';\n\n return {\n text,\n language: getString(json, 'language', 'lang'),\n durationSeconds: getNumber(json, 'durationSeconds', 'duration'),\n words: normalizeWordTimings(\n json.words ?? json.wordTimings ?? json.word_timings,\n ),\n segments: normalizeTranscriptSegments(json.segments),\n provider: getString(json, 'provider') ?? adapterName,\n model: getString(json, 'model'),\n raw: json,\n };\n}\n\nexport async function readSynthesizedSpeechResponse(\n response: Response,\n adapterName: string,\n): Promise<SynthesizedSpeech> {\n const responseContentType =\n response.headers.get('content-type') ?? 'application/octet-stream';\n\n if (!responseContentType.includes('application/json')) {\n const audio = await response.arrayBuffer();\n const sampleRate = parseHeaderNumber(response, 'x-sample-rate');\n return {\n audio,\n contentType: responseContentType,\n format: contentTypeToFormat(responseContentType),\n sampleRate,\n provider: adapterName,\n };\n }\n\n const json = (await response.json()) as Record<string, unknown>;\n const contentType =\n getString(json, 'contentType', 'content_type', 'mimeType', 'mime_type') ??\n 'application/octet-stream';\n const encodedAudio = getString(\n json,\n 'audio',\n 'audioContent',\n 'audio_content',\n );\n\n if (!encodedAudio) {\n throw new SpeechProviderError(\n adapterName,\n `${adapterName} JSON speech response did not include base64 audio`,\n );\n }\n\n return {\n audio: decodeBase64(encodedAudio),\n contentType,\n format:\n getString(json, 'format', 'response_format') ??\n contentTypeToFormat(contentType),\n sampleRate: getNumber(json, 'sampleRate', 'sample_rate'),\n channels: getNumber(json, 'channels'),\n durationSeconds: getNumber(json, 'durationSeconds', 'duration'),\n words: normalizeWordTimings(\n json.words ?? json.wordTimings ?? json.word_timings,\n ),\n provider: getString(json, 'provider') ?? adapterName,\n model: getString(json, 'model'),\n raw: json,\n };\n}\n\nfunction parseHeaderNumber(\n response: Response,\n headerName: string,\n): number | undefined {\n const value = response.headers.get(headerName);\n if (!value) {\n return undefined;\n }\n\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n","/**\n * Credential handling shared by realtime (WebSocket) adapters.\n *\n * Node callers authenticate with a long-lived `apiKey` in the\n * `Authorization` header. Browsers cannot set WebSocket headers and must never\n * hold a long-lived key, so they pass a short-lived `clientSecret`, which is\n * sent as a WebSocket subprotocol.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\nimport type { RealtimeConnection } from './realtime-session.js';\nimport type { StreamingClientSecret } from './streaming-types.js';\nimport { isBrowserRuntime } from './websocket.js';\n\n/** Subprotocol prefix that carries a client secret (OpenAI's convention). */\nexport const CLIENT_SECRET_SUBPROTOCOL_PREFIX = 'openai-insecure-api-key.';\n\n/**\n * Long-lived OpenAI-style keys (`sk-…`, `sk-proj-…`, `sk-svcacct-…`). A\n * browser `clientSecret` that looks like one is refused: browsers accept only\n * short-lived tokens such as OpenAI's `ek_…` client secrets.\n */\nconst LONG_LIVED_KEY = /^sk-/;\n\n/** Throws when an adapter is given a long-lived `apiKey` in a browser. */\nexport function assertNoBrowserApiKey(\n adapter: string,\n apiKey: string | undefined,\n): void {\n if (apiKey && isBrowserRuntime()) {\n throw new SpeechConfigurationError(\n `${adapter} refuses apiKey in a browser; mint a short-lived clientSecret on your server instead`,\n adapter,\n );\n }\n}\n\n/** Resolves a static or per-session client secret and validates its shape. */\nexport async function resolveClientSecret(\n adapter: string,\n secret: StreamingClientSecret | undefined,\n): Promise<string | undefined> {\n const value = typeof secret === 'function' ? await secret() : secret;\n const trimmed = value?.trim();\n if (trimmed === undefined) {\n return undefined;\n }\n if (!/^[\\x21-\\x7e]+$/.test(trimmed)) {\n throw new SpeechConfigurationError(\n 'clientSecret must be a non-empty token without spaces',\n adapter,\n );\n }\n if (isBrowserRuntime() && LONG_LIVED_KEY.test(trimmed)) {\n throw new SpeechConfigurationError(\n 'clientSecret looks like a long-lived API key; browsers accept only short-lived tokens minted by your server (see createStreamingClientSecret)',\n adapter,\n );\n }\n return trimmed;\n}\n\nexport interface RealtimeConnectorOptions {\n adapter: string;\n url: string;\n apiKey?: string;\n headers: Record<string, string>;\n clientSecret?: StreamingClientSecret;\n /** Subprotocols to offer, given the resolved client secret (if any). */\n protocols: (secret: string | undefined) => string[];\n}\n\n/**\n * Builds the per-session `connect` callback: refuses handshake headers in a\n * browser, resolves the client secret, and sets `Authorization` from\n * `apiKey` unless a header already supplies it.\n */\nexport function realtimeConnector(\n options: RealtimeConnectorOptions,\n): () => Promise<RealtimeConnection> {\n if (isBrowserRuntime() && Object.keys(options.headers).length > 0) {\n throw new SpeechConfigurationError(\n 'Browsers cannot send WebSocket headers; remove `headers` or connect from a server',\n options.adapter,\n );\n }\n\n return async () => {\n const secret = await resolveClientSecret(\n options.adapter,\n options.clientSecret,\n );\n const headers = { ...options.headers };\n // Gateway credentials (e.g. `x-bf-vk`, an explicit Authorization) are\n // redacted from surfaced provider text just like the API key.\n const secrets: string[] = headerSecrets(headers);\n if (secret) {\n secrets.push(secret);\n }\n if (options.apiKey) {\n secrets.push(options.apiKey);\n if (!hasHeader(headers, 'authorization')) {\n headers.authorization = `Bearer ${options.apiKey}`;\n }\n }\n return {\n url: options.url,\n protocols: options.protocols(secret),\n headers,\n secrets,\n };\n };\n}\n\n/** Values of other headers shorter than this are not treated as credentials. */\nconst MIN_HEADER_SECRET_LENGTH = 8;\n\n/**\n * Header names that carry credentials (`authorization`, `x-api-key`,\n * `x-bf-vk`, `*-token`, `*-secret`, …). Their values are redacted at any\n * length.\n */\nconst CREDENTIAL_HEADER = /auth|key|token|secret|password|credential|-vk$/i;\n\n/**\n * Values of caller-supplied headers to redact from provider error text.\n * Credential-named headers are redacted whatever their length; for\n * `Authorization`, the credential after the scheme is included too. Other\n * headers may also carry tenant secrets, so their values are redacted when\n * they are at least 8 characters long, which keeps short values such as\n * `1` from mangling messages.\n */\nexport function headerSecrets(headers: HeadersInit | undefined): string[] {\n const secrets: string[] = [];\n for (const [key, value] of Object.entries(mergeHeaderRecords(headers))) {\n const credential = CREDENTIAL_HEADER.test(key);\n const candidates = [value];\n if (key.toLowerCase() === 'authorization') {\n candidates.push(value.replace(/^\\S+\\s+/, ''));\n }\n for (const candidate of candidates) {\n const trimmed = candidate.trim();\n const long = trimmed.length >= MIN_HEADER_SECRET_LENGTH;\n if (trimmed && (credential || long) && !secrets.includes(trimmed)) {\n secrets.push(trimmed);\n }\n }\n }\n // Longest first, so a full `Bearer <token>` is replaced before its token.\n return secrets.sort((left, right) => right.length - left.length);\n}\n\nexport function mergeHeaderRecords(\n ...sets: Array<HeadersInit | undefined>\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const set of sets) {\n if (set) {\n new Headers(set).forEach((value, key) => {\n merged[key] = value;\n });\n }\n }\n return merged;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n return Object.keys(headers).some((key) => key.toLowerCase() === name);\n}\n","/**\n * Raw-audio helpers for streaming adapters: byte views, base64 encoding,\n * duration from byte counts, and WAV/`audio/L16` unwrapping for the\n * record-then-send wrapper. Nothing here decodes compressed audio or resamples.\n */\n\nimport { normalizeMimeType } from './audio.js';\nimport { SpeechConfigurationError } from './errors.js';\nimport type {\n StreamingAudioChunk,\n StreamingAudioEncoding,\n StreamingAudioFormat,\n} from './streaming-types.js';\n\nconst ENCODING_BYTES_PER_SAMPLE: Record<StreamingAudioEncoding, number> = {\n pcm16: 2,\n g711_ulaw: 1,\n g711_alaw: 1,\n};\n\n/** Returns a byte view over a chunk without copying. */\nexport function chunkToBytes(\n chunk: StreamingAudioChunk,\n adapter?: string,\n): Uint8Array {\n if (chunk instanceof Uint8Array) {\n return chunk;\n }\n if (chunk instanceof ArrayBuffer) {\n return new Uint8Array(chunk);\n }\n if (ArrayBuffer.isView(chunk)) {\n return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n }\n throw new SpeechConfigurationError(\n 'Streaming audio chunks must be Uint8Array, ArrayBuffer, or an ArrayBuffer view',\n adapter,\n );\n}\n\nexport function encodeBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(\n bytes.buffer,\n bytes.byteOffset,\n bytes.byteLength,\n ).toString('base64');\n }\n\n let binary = '';\n const step = 0x8000;\n for (let index = 0; index < bytes.length; index += step) {\n binary += String.fromCharCode(...bytes.subarray(index, index + step));\n }\n return btoa(binary);\n}\n\n/** Bytes per second of audio in `format`. */\nexport function bytesPerSecond(format: StreamingAudioFormat): number {\n return (\n format.sampleRate *\n format.channels *\n ENCODING_BYTES_PER_SAMPLE[format.encoding]\n );\n}\n\n/** Audio duration in seconds for `bytes` of `format`, rounded to milliseconds. */\nexport function audioSecondsForBytes(\n bytes: number,\n format: StreamingAudioFormat,\n): number {\n return Math.round((bytes / bytesPerSecond(format)) * 1000) / 1000;\n}\n\nexport interface RawAudio {\n bytes: Uint8Array;\n /** Format fields the input declared (WAV header, MIME parameters, or `AudioInput`). */\n format: Partial<StreamingAudioFormat>;\n}\n\nconst WAV_TYPES = new Set([\n 'audio/wav',\n 'audio/wave',\n 'audio/x-wav',\n 'audio/vnd.wave',\n]);\nconst RAW_PCM_TYPES = new Set([\n 'audio/pcm',\n 'audio/l16',\n 'audio/raw',\n 'audio/x-raw',\n]);\n/** Untyped bytes: the streaming transcriber's default format applies. */\nconst UNTYPED = 'application/octet-stream';\nconst MULAW_TYPES = new Set(['audio/pcmu', 'audio/basic', 'audio/x-mulaw']);\nconst ALAW_TYPES = new Set(['audio/pcma', 'audio/x-alaw']);\n\n/**\n * Interprets a finished recording as raw audio for a streaming session.\n * Accepts PCM/G.711 WAV files, `audio/pcm`/`audio/L16` (with optional `rate`\n * and `channels` parameters), `audio/pcmu`/`audio/pcma`, and untyped bytes.\n * Compressed containers are rejected with a `SpeechConfigurationError`.\n */\nexport function unwrapRawAudio(\n bytes: Uint8Array,\n mimeType: string,\n adapter: string,\n): RawAudio {\n const essence = normalizeMimeType(mimeType) ?? 'application/octet-stream';\n const params = parseMimeParameters(mimeType);\n const rate = parsePositiveInt(params.rate);\n const channels = parsePositiveInt(params.channels);\n\n if (WAV_TYPES.has(essence) || isRiffWave(bytes)) {\n return parseWav(bytes, adapter);\n }\n if (essence === UNTYPED) {\n // No encoding: `start()` keeps the adapter's `audioFormat` (e.g. G.711).\n return { bytes, format: compact({ sampleRate: rate, channels }) };\n }\n if (RAW_PCM_TYPES.has(essence)) {\n if (essence === 'audio/l16' && bytes.byteLength % 2 !== 0) {\n throw new SpeechConfigurationError(\n `audio/L16 payload has an odd byte length (${bytes.byteLength}); 16-bit samples need an even number of bytes`,\n adapter,\n );\n }\n return {\n // RFC 2586: audio/L16 samples are big-endian; pcm16 is little-endian.\n bytes: essence === 'audio/l16' ? swapBytePairs(bytes) : bytes,\n format: compact({ encoding: 'pcm16', sampleRate: rate, channels }),\n };\n }\n if (MULAW_TYPES.has(essence)) {\n return {\n bytes,\n format: compact({ encoding: 'g711_ulaw', sampleRate: rate, channels }),\n };\n }\n if (ALAW_TYPES.has(essence)) {\n return {\n bytes,\n format: compact({ encoding: 'g711_alaw', sampleRate: rate, channels }),\n };\n }\n\n throw new SpeechConfigurationError(\n `${adapter} streams raw audio and cannot decode ${essence}; send PCM WAV or raw PCM, or use an HTTP transcriber for compressed recordings`,\n adapter,\n );\n}\n\n/** Returns a copy with each 16-bit sample's bytes swapped. */\nfunction swapBytePairs(bytes: Uint8Array): Uint8Array {\n const swapped = new Uint8Array(bytes.byteLength);\n for (let index = 0; index + 1 < bytes.byteLength; index += 2) {\n swapped[index] = bytes[index + 1] ?? 0;\n swapped[index + 1] = bytes[index] ?? 0;\n }\n return swapped;\n}\n\nfunction isRiffWave(bytes: Uint8Array): boolean {\n return (\n bytes.byteLength >= 12 &&\n ascii(bytes, 0, 4) === 'RIFF' &&\n ascii(bytes, 8, 4) === 'WAVE'\n );\n}\n\nfunction parseWav(bytes: Uint8Array, adapter: string): RawAudio {\n if (!isRiffWave(bytes)) {\n throw new SpeechConfigurationError('Invalid WAV header', adapter);\n }\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let offset = 12;\n let format: Partial<StreamingAudioFormat> | undefined;\n\n while (offset + 8 <= bytes.byteLength) {\n const id = ascii(bytes, offset, 4);\n const size = view.getUint32(offset + 4, true);\n const body = offset + 8;\n\n if (id === 'fmt ' && body + 16 <= bytes.byteLength) {\n const audioFormat = view.getUint16(body, true);\n const channels = view.getUint16(body + 2, true);\n const sampleRate = view.getUint32(body + 4, true);\n const bitsPerSample = view.getUint16(body + 14, true);\n format = {\n encoding: wavEncoding(audioFormat, bitsPerSample, adapter),\n sampleRate,\n channels,\n };\n } else if (id === 'data') {\n if (!format) {\n throw new SpeechConfigurationError(\n 'WAV data chunk precedes its fmt chunk',\n adapter,\n );\n }\n const end = Math.min(bytes.byteLength, body + size);\n return { bytes: bytes.subarray(body, end), format };\n }\n\n // Chunks are padded to an even size.\n offset = body + size + (size % 2);\n }\n\n throw new SpeechConfigurationError('WAV file has no data chunk', adapter);\n}\n\nfunction wavEncoding(\n audioFormat: number,\n bitsPerSample: number,\n adapter: string,\n): StreamingAudioEncoding {\n // 0xfffe is WAVE_FORMAT_EXTENSIBLE; its common PCM case is treated as PCM.\n if ((audioFormat === 1 || audioFormat === 0xfffe) && bitsPerSample === 16) {\n return 'pcm16';\n }\n if (audioFormat === 7 && bitsPerSample === 8) {\n return 'g711_ulaw';\n }\n if (audioFormat === 6 && bitsPerSample === 8) {\n return 'g711_alaw';\n }\n throw new SpeechConfigurationError(\n `Unsupported WAV encoding (format ${audioFormat}, ${bitsPerSample}-bit); expected 16-bit PCM or 8-bit G.711`,\n adapter,\n );\n}\n\nfunction ascii(bytes: Uint8Array, offset: number, length: number): string {\n return String.fromCharCode(...bytes.subarray(offset, offset + length));\n}\n\nfunction parseMimeParameters(mimeType: string): Record<string, string> {\n const params: Record<string, string> = {};\n for (const part of mimeType.split(';').slice(1)) {\n const [key, value] = part.split('=');\n if (key && value) {\n params[key.trim().toLowerCase()] = value.trim();\n }\n }\n return params;\n}\n\nfunction parsePositiveInt(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nfunction compact(\n format: Partial<StreamingAudioFormat>,\n): Partial<StreamingAudioFormat> {\n return Object.fromEntries(\n Object.entries(format).filter(([, value]) => value !== undefined),\n ) as Partial<StreamingAudioFormat>;\n}\n","/**\n * Per-turn audio limits for realtime sessions.\n *\n * Some providers cap the audio of one turn rather than of a session: vLLM's\n * realtime endpoint keeps a turn's audio and generated tokens in one model\n * context (`max_model_len`) and clears it after each final commit. A\n * {@link TurnSplitter} measures the audio of the current turn from byte counts\n * and the negotiated format, and picks where to roll over to a new turn: at\n * the first quiet stretch inside a window before the limit, or at the limit\n * itself when no quiet stretch turns up. The silence check is a plain RMS\n * energy test, not a voice-activity model.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\nimport { bytesPerSecond } from './pcm.js';\nimport type {\n StreamingAudioEncoding,\n StreamingAudioFormat,\n StreamingRolloverOptions,\n StreamingSessionSettings,\n StreamingTurnLimit,\n} from './streaming-types.js';\n\n/** Default per-turn audio cap for providers that need one (`voxtral-realtime`). */\nexport const DEFAULT_MAX_TURN_SECONDS = 270;\n/** How long before the cap the rollover starts looking for silence. */\nexport const DEFAULT_ROLLOVER_WINDOW_SECONDS = 15;\n/** RMS level (0-1 of full scale, about -40 dBFS) below which audio is quiet. */\nexport const DEFAULT_ROLLOVER_SILENCE_THRESHOLD = 0.01;\n/** Quiet audio needed before the rollover commits. */\nexport const DEFAULT_ROLLOVER_MIN_SILENCE_MS = 300;\n/** Energy is measured over frames of this length. */\nconst ANALYSIS_FRAME_MS = 20;\n\nconst BYTES_PER_SAMPLE: Record<StreamingAudioEncoding, number> = {\n pcm16: 2,\n g711_ulaw: 1,\n g711_alaw: 1,\n};\n\n/** A resolved per-turn limit; `undefined` means turns are unbounded. */\nexport interface TurnLimit {\n maxTurnSeconds: number;\n /** `false`: writes past the cap reject with `SPEECH_TURN_TOO_LONG`. */\n rollover: boolean;\n windowSeconds: number;\n silenceThreshold: number;\n minSilenceMs: number;\n}\n\n/**\n * Resolves the turn limit from settings, highest priority first (session\n * options, then adapter options, which already carry environment values),\n * falling back to `defaultMaxTurnSeconds`. Returns `undefined` when no limit\n * applies or `maxTurnSeconds` is `Infinity`.\n */\nexport function resolveTurnLimit(\n adapter: string,\n defaultMaxTurnSeconds: number | undefined,\n ...settings: Array<\n Pick<StreamingSessionSettings, 'maxTurnSeconds' | 'rollover'> | undefined\n >\n): TurnLimit | undefined {\n const maxTurnSeconds =\n settings.find((entry) => entry?.maxTurnSeconds !== undefined)\n ?.maxTurnSeconds ?? defaultMaxTurnSeconds;\n if (maxTurnSeconds === undefined) {\n return undefined;\n }\n if (\n typeof maxTurnSeconds !== 'number' ||\n Number.isNaN(maxTurnSeconds) ||\n maxTurnSeconds <= 0\n ) {\n throw new SpeechConfigurationError(\n `maxTurnSeconds must be a positive number of seconds or Infinity (got ${String(maxTurnSeconds)})`,\n adapter,\n );\n }\n if (maxTurnSeconds === Number.POSITIVE_INFINITY) {\n return undefined;\n }\n\n // On/off comes from the first layer that sets `rollover`; each tuning\n // field from the first layer whose `rollover` object sets that field, so a\n // `rollover: true` session keeps the adapter's tuning.\n const rollover = settings.find(\n (entry) => entry?.rollover !== undefined,\n )?.rollover;\n const tuning = <K extends keyof StreamingRolloverOptions>(key: K) =>\n settings\n .map((entry) => entry?.rollover)\n .find(\n (value): value is StreamingRolloverOptions =>\n typeof value === 'object' &&\n value !== null &&\n value[key] !== undefined,\n )?.[key];\n const limit: TurnLimit = {\n maxTurnSeconds,\n rollover: rollover !== false,\n windowSeconds: tuning('windowSeconds') ?? DEFAULT_ROLLOVER_WINDOW_SECONDS,\n silenceThreshold:\n tuning('silenceThreshold') ?? DEFAULT_ROLLOVER_SILENCE_THRESHOLD,\n minSilenceMs: tuning('minSilenceMs') ?? DEFAULT_ROLLOVER_MIN_SILENCE_MS,\n };\n assertNonNegative(adapter, 'rollover.windowSeconds', limit.windowSeconds);\n assertNonNegative(adapter, 'rollover.minSilenceMs', limit.minSilenceMs);\n assertNonNegative(\n adapter,\n 'rollover.silenceThreshold',\n limit.silenceThreshold,\n );\n if (limit.silenceThreshold > 1) {\n throw new SpeechConfigurationError(\n `rollover.silenceThreshold is an RMS level from 0 to 1 (got ${limit.silenceThreshold})`,\n adapter,\n );\n }\n return limit;\n}\n\nfunction assertNonNegative(adapter: string, name: string, value: unknown) {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n throw new SpeechConfigurationError(\n `${name} must be a non-negative finite number (got ${String(value)})`,\n adapter,\n );\n }\n}\n\n/** Bytes of one sample frame (one sample per channel). */\nexport function blockAlign(format: StreamingAudioFormat): number {\n return BYTES_PER_SAMPLE[format.encoding] * format.channels;\n}\n\n/** Whole sample frames in `seconds` of audio, as bytes (rounded down). */\nexport function bytesForSeconds(\n seconds: number,\n format: StreamingAudioFormat,\n): number {\n const align = blockAlign(format);\n // The epsilon keeps decimal seconds such as 0.3 from losing a frame.\n return Math.floor((seconds * bytesPerSecond(format)) / align + 1e-9) * align;\n}\n\n/** Digital silence in `format`: zero samples, encoded. */\nexport function silence(\n bytes: number,\n format: StreamingAudioFormat,\n): Uint8Array {\n const fill =\n format.encoding === 'g711_ulaw'\n ? 0xff\n : format.encoding === 'g711_alaw'\n ? 0xd5\n : 0;\n return new Uint8Array(bytes).fill(fill);\n}\n\n/** Bytes of audio one turn may hold: whole sample frames, at least one. */\nexport function turnCapBytes(\n limit: TurnLimit,\n format: StreamingAudioFormat,\n): number {\n return Math.max(\n blockAlign(format),\n bytesForSeconds(limit.maxTurnSeconds, format),\n );\n}\n\n/** The public view of a resolved limit for a session format. */\nexport function describeTurnLimit(\n limit: TurnLimit | undefined,\n format: StreamingAudioFormat,\n): StreamingTurnLimit | undefined {\n return (\n limit && {\n maxTurnSeconds: limit.maxTurnSeconds,\n rollover: limit.rollover,\n maxTurnBytes: turnCapBytes(limit, format),\n }\n );\n}\n\n/**\n * Tracks the audio of the current turn and finds rollover points.\n *\n * Feed every chunk in write order to {@link split} (rollover on) or\n * {@link add} (rollover off), and call {@link reset} whenever the turn ends\n * for another reason (a manual commit).\n */\nexport class TurnSplitter {\n /** Bytes of the current turn so far. */\n turnBytes = 0;\n readonly capBytes: number;\n private readonly windowStartBytes: number;\n private readonly frameBytes: number;\n private readonly minSilenceBytes: number;\n private readonly threshold: number;\n private readonly encoding: StreamingAudioEncoding;\n private readonly sampleBytes: number;\n // Energy of the analysis frame being filled.\n private frameFill = 0;\n private frameSumSquares = 0;\n private frameSamples = 0;\n /** Trailing bytes of a sample split across chunks. */\n private carry: number[] = [];\n /** Consecutive quiet bytes ending at the last complete frame. */\n private quietRun = 0;\n\n constructor(limit: TurnLimit, format: StreamingAudioFormat) {\n const align = blockAlign(format);\n this.capBytes = turnCapBytes(limit, format);\n this.windowStartBytes = Math.max(\n 0,\n this.capBytes - bytesForSeconds(limit.windowSeconds, format),\n );\n this.frameBytes = Math.max(\n align,\n bytesForSeconds(ANALYSIS_FRAME_MS / 1000, format),\n );\n this.minSilenceBytes = Math.max(\n this.frameBytes,\n bytesForSeconds(limit.minSilenceMs / 1000, format),\n );\n this.threshold = limit.silenceThreshold;\n this.encoding = format.encoding;\n this.sampleBytes = BYTES_PER_SAMPLE[format.encoding];\n }\n\n /** Ends the current turn: the next byte starts a new one. */\n reset(): void {\n this.turnBytes = 0;\n this.frameFill = 0;\n this.frameSumSquares = 0;\n this.frameSamples = 0;\n this.carry = [];\n this.quietRun = 0;\n }\n\n /** Whether `bytes` more would push the current turn past the cap. */\n wouldOverflow(bytes: number): boolean {\n return this.turnBytes + bytes > this.capBytes;\n }\n\n /** Counts `bytes` toward the current turn without analysing them. */\n add(bytes: number): void {\n this.turnBytes += bytes;\n }\n\n /**\n * Feeds one chunk and returns the offsets inside it where the turn should\n * end (each in `1..chunk.byteLength`, ascending). The splitter resets at\n * each offset, so bytes after it count toward the next turn.\n */\n split(chunk: Uint8Array): number[] {\n const cuts: number[] = [];\n let offset = 0;\n while (offset < chunk.byteLength) {\n if (this.turnBytes < this.windowStartBytes) {\n // Before the window only the length matters.\n const skip = Math.min(\n chunk.byteLength - offset,\n this.windowStartBytes - this.turnBytes,\n );\n offset += skip;\n this.turnBytes += skip;\n // With no window (`windowSeconds: 0`) the window starts at the cap.\n if (this.turnBytes >= this.capBytes) {\n cuts.push(offset);\n this.reset();\n }\n continue;\n }\n\n const take = Math.min(\n chunk.byteLength - offset,\n this.frameBytes - this.frameFill,\n this.capBytes - this.turnBytes,\n );\n this.measure(chunk, offset, take);\n offset += take;\n this.turnBytes += take;\n this.frameFill += take;\n\n let cut = false;\n if (this.frameFill >= this.frameBytes) {\n const quiet = this.frameIsQuiet();\n this.quietRun = quiet ? this.quietRun + this.frameFill : 0;\n this.frameFill = 0;\n this.frameSumSquares = 0;\n this.frameSamples = 0;\n cut = this.quietRun >= this.minSilenceBytes;\n }\n if (cut || this.turnBytes >= this.capBytes) {\n cuts.push(offset);\n this.reset();\n }\n }\n return cuts;\n }\n\n private frameIsQuiet(): boolean {\n if (this.frameSamples === 0) {\n return false;\n }\n const rms = Math.sqrt(this.frameSumSquares / this.frameSamples) / 32_768;\n return rms < this.threshold;\n }\n\n private measure(chunk: Uint8Array, start: number, length: number): void {\n const end = start + length;\n let index = start;\n // Complete a sample left over from the previous chunk.\n while (this.carry.length > 0 && index < end) {\n this.carry.push(chunk[index] ?? 0);\n index += 1;\n if (this.carry.length === this.sampleBytes) {\n this.addSample(this.carry);\n this.carry = [];\n }\n }\n for (; index + this.sampleBytes <= end; index += this.sampleBytes) {\n this.addSampleAt(chunk, index);\n }\n for (; index < end; index += 1) {\n this.carry.push(chunk[index] ?? 0);\n }\n }\n\n private addSample(bytes: number[]): void {\n this.addLinear(\n this.encoding === 'pcm16'\n ? pcm16Sample(bytes[0] ?? 0, bytes[1] ?? 0)\n : this.decodeG711(bytes[0] ?? 0),\n );\n }\n\n private addSampleAt(chunk: Uint8Array, index: number): void {\n this.addLinear(\n this.encoding === 'pcm16'\n ? pcm16Sample(chunk[index] ?? 0, chunk[index + 1] ?? 0)\n : this.decodeG711(chunk[index] ?? 0),\n );\n }\n\n private addLinear(sample: number): void {\n this.frameSumSquares += sample * sample;\n this.frameSamples += 1;\n }\n\n private decodeG711(byte: number): number {\n return this.encoding === 'g711_ulaw' ? decodeMuLaw(byte) : decodeALaw(byte);\n }\n}\n\n/** Signed 16-bit little-endian sample. */\nfunction pcm16Sample(low: number, high: number): number {\n const value = low | (high << 8);\n return value >= 0x8000 ? value - 0x10000 : value;\n}\n\n/** G.711 mu-law byte to a 16-bit linear sample (ITU-T G.711). */\nexport function decodeMuLaw(byte: number): number {\n const value = ~byte & 0xff;\n const exponent = (value >> 4) & 0x07;\n const mantissa = value & 0x0f;\n const magnitude = (((mantissa << 3) + 0x84) << exponent) - 0x84;\n return value & 0x80 ? -magnitude : magnitude;\n}\n\n/** G.711 A-law byte to a 16-bit linear sample (ITU-T G.711). */\nexport function decodeALaw(byte: number): number {\n const value = byte ^ 0x55;\n const exponent = (value >> 4) & 0x07;\n const mantissa = value & 0x0f;\n const magnitude =\n exponent === 0\n ? (mantissa << 4) + 8\n : ((mantissa << 4) + 0x108) << (exponent - 1);\n return value & 0x80 ? magnitude : -magnitude;\n}\n","/**\n * Protocol-agnostic realtime transcription session over a WebSocket.\n *\n * The session owns the socket lifecycle, write queue and backpressure,\n * timeouts, turn bookkeeping, events, and the final `TranscriptResult`. A\n * {@link RealtimeProtocol} supplies the wire format, so a sibling adapter\n * (for example a vLLM-served Voxtral realtime endpoint) only has to map its\n * own messages.\n */\n\nimport {\n SpeechConfigurationError,\n SpeechError,\n SpeechProviderError,\n} from './errors.js';\nimport { redactSecret } from './http.js';\nimport {\n audioSecondsForBytes,\n bytesPerSecond,\n chunkToBytes,\n encodeBase64,\n} from './pcm.js';\nimport type {\n StreamingAudioChunk,\n StreamingAudioFormat,\n StreamingSession,\n StreamingSessionEventName,\n StreamingSessionEvents,\n StreamingSessionListener,\n StreamingSessionState,\n StreamingTurnDetection,\n} from './streaming-types.js';\nimport {\n blockAlign,\n silence,\n type TurnLimit,\n TurnSplitter,\n} from './turn-limit.js';\nimport type { TranscriptResult, TranscriptSegment } from './types.js';\nimport {\n audioSecondsFromProviderUsage,\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from './usage.js';\nimport {\n messageDataToText,\n type SpeechWebSocket,\n type SpeechWebSocketCloseEvent,\n type SpeechWebSocketFactory,\n} from './websocket.js';\n\nexport const DEFAULT_STREAMING_TIMEOUT_MS = 30_000;\nexport const DEFAULT_STREAMING_CONNECT_TIMEOUT_MS = 10_000;\nexport const DEFAULT_STREAMING_HIGH_WATER_MARK = 1024 * 1024;\nexport const DEFAULT_STREAMING_MAX_BUFFERED_BYTES = 16 * 1024 * 1024;\nconst DRAIN_POLL_MS = 10;\n\n/** Normalised events a protocol parser returns for one server message. */\nexport type RealtimeProtocolEvent =\n | { type: 'session'; sessionId?: string }\n /** The provider committed a turn (from a client commit or server VAD). */\n | { type: 'committed'; itemId?: string }\n /** The provider rejected a commit because its buffer was empty. Not fatal. */\n | { type: 'commit_empty' }\n | { type: 'partial'; itemId?: string; delta: string }\n | {\n type: 'final';\n itemId?: string;\n text: string;\n providerUsage?: Record<string, unknown>;\n raw?: unknown;\n /**\n * `false` when the provider reports that the turn consumed no audio\n * (vLLM: `usage.prompt_tokens <= 1`). A turn that was sent audio but\n * consumed none means the provider's turn state is out of sync, and the\n * session fails instead of silently dropping that audio.\n */\n audioConsumed?: boolean;\n }\n | { type: 'speech_started'; itemId?: string; audioMs?: number }\n | { type: 'speech_stopped'; itemId?: string; audioMs?: number }\n /** A fatal provider error. */\n | { type: 'error'; message: string; code?: string; raw?: unknown };\n\n/** Resolved per-session settings handed to the protocol. */\nexport interface RealtimeSessionConfig {\n model: string;\n format: StreamingAudioFormat;\n turnDetection: StreamingTurnDetection;\n language?: string;\n prompt?: string;\n}\n\n/** Wire format of one realtime provider. */\nexport interface RealtimeProtocol {\n /** Adapter name used for errors and usage, e.g. `openai-realtime`. */\n readonly provider: string;\n /**\n * How a client commit is acknowledged: by a `committed` event (OpenAI), or\n * only by the turn's `final` event (servers without a commit ack).\n */\n readonly commitAck: 'committed' | 'final';\n /**\n * Whether `end()` commits only when uncommitted audio remains\n * (`if-audio`), or always sends a final commit (`always`, for servers that\n * need an explicit end-of-stream marker).\n */\n readonly endCommit: 'if-audio' | 'always';\n /**\n * Default per-turn audio cap in seconds, for providers whose model context\n * covers one turn (vLLM resets it after each final commit). Adapters pass\n * it to `resolveTurnLimit()`; a session with a turn limit rolls over to a\n * new turn before the cap, or rejects writes past it. Unset: no default.\n */\n readonly maxTurnSeconds?: number;\n /**\n * Shortest audio, in seconds, the provider accepts in one commit (OpenAI:\n * 100 ms; shorter commits are rejected as empty). A session with a turn\n * limit pads a shorter, non-empty turn with silence before committing it,\n * so the tail left after a rollover is transcribed instead of dropped.\n */\n readonly minCommitSeconds?: number;\n /** Messages sent once the socket opens (session configuration). */\n sessionMessages(config: RealtimeSessionConfig): unknown[];\n /** One audio chunk, already base64-encoded. */\n appendMessage(base64Audio: string): unknown;\n /** Ends the current turn. `final` is true for the commit sent by `end()`. */\n commitMessage(options: { final: boolean }): unknown;\n /**\n * Opens a turn before its first audio, for servers that only start\n * generating after an explicit start signal (vLLM's non-final\n * `input_audio_buffer.commit`). When set, the session sends it before the\n * first append of every turn, and holds audio written after a commit until\n * every outstanding turn has been acknowledged, so a server that resets its\n * buffer between turns never drops audio.\n */\n turnStartMessage?(): unknown;\n /** Maps one parsed JSON server message to zero or more events. */\n parse(message: unknown): RealtimeProtocolEvent[];\n}\n\n/** Where and how to open the socket; resolved per session. */\nexport interface RealtimeConnection {\n url: string;\n protocols: string[];\n headers: Record<string, string>;\n /** Credentials to redact from surfaced provider text. */\n secrets: string[];\n}\n\nexport interface RealtimeSessionInit {\n protocol: RealtimeProtocol;\n config: RealtimeSessionConfig;\n /** Resolves the connection (may mint a short-lived token). */\n connect: () => Promise<RealtimeConnection>;\n createWebSocket: SpeechWebSocketFactory;\n timeoutMs: number;\n connectTimeoutMs: number;\n highWaterMark: number;\n maxBufferedBytes: number;\n signal?: AbortSignal;\n /** Called in order with the aggregated usage when `end()` succeeds. */\n onUsage: Array<SpeechUsageCallback | undefined>;\n /** Per-turn audio limit; unset means turns are unbounded. */\n turnLimit?: TurnLimit;\n}\n\ntype QueueItem =\n | {\n kind: 'audio';\n base64: string;\n bytes: number;\n resolve: () => void;\n reject: (error: Error) => void;\n }\n | { kind: 'commit'; final: boolean };\n\ninterface FinalRecord {\n itemId?: string;\n text: string;\n providerUsage?: Record<string, unknown>;\n raw?: unknown;\n arrival: number;\n}\n\nconst noop = () => undefined;\n\nexport class RealtimeTranscriptionSession implements StreamingSession {\n state: StreamingSessionState = 'connecting';\n sessionId?: string;\n readonly ready: Promise<void>;\n\n private readonly init: RealtimeSessionInit;\n private readonly provider: string;\n private resolveReady: () => void = noop;\n private rejectReady: (error: Error) => void = noop;\n private socket?: SpeechWebSocket;\n private opened = false;\n private secrets: string[] = [];\n private queue: QueueItem[] = [];\n private queued = 0;\n private pumping = false;\n private totalBytes = 0;\n private uncommittedBytes = 0;\n private outstandingCommits = 0;\n /** Per outstanding commit, in order: whether it carried audio. */\n private readonly commitHadAudio: boolean[] = [];\n private turnOpen = false;\n /** `Date.now()` of the last server message; bounds inactivity waits. */\n private lastServerMessageAt = 0;\n private readonly itemOrder: string[] = [];\n private readonly pendingItems = new Set<string>();\n private readonly vadStoppedItems = new Set<string>();\n private readonly partials = new Map<string, string>();\n private readonly finals: FinalRecord[] = [];\n private readonly listeners = new Map<\n StreamingSessionEventName,\n Set<(event: never) => void>\n >();\n private error?: Error;\n private endPromise?: Promise<TranscriptResult>;\n private endWaiter?: { resolve: () => void; reject: (error: Error) => void };\n private connectTimer?: ReturnType<typeof setTimeout>;\n private endTimer?: ReturnType<typeof setTimeout>;\n private closeEmitted = false;\n /** Measures the current turn in write order when a turn limit applies. */\n private readonly splitter?: TurnSplitter;\n private readonly onAbort = () => {\n this.fail(toError(this.init.signal?.reason, this.provider));\n };\n\n constructor(init: RealtimeSessionInit) {\n this.init = init;\n this.provider = init.protocol.provider;\n assertTurnLimitFitsProtocol(init.protocol, init.turnLimit);\n if (init.turnLimit) {\n this.splitter = new TurnSplitter(init.turnLimit, init.config.format);\n }\n this.ready = new Promise<void>((resolve, reject) => {\n this.resolveReady = resolve;\n this.rejectReady = reject;\n });\n this.ready.catch(noop);\n\n if (init.signal?.aborted) {\n this.fail(toError(init.signal.reason, this.provider));\n return;\n }\n init.signal?.addEventListener('abort', this.onAbort, { once: true });\n void this.connect();\n }\n\n get queuedBytes(): number {\n return this.queued;\n }\n\n write(chunk: StreamingAudioChunk): Promise<void> {\n const promise = this.enqueueAudio(chunk);\n promise.catch(noop);\n return promise;\n }\n\n commit(): void {\n if (this.isTerminal() || this.endPromise) {\n return;\n }\n this.queue.push({ kind: 'commit', final: false });\n this.splitter?.reset();\n void this.pump();\n }\n\n end(): Promise<TranscriptResult> {\n if (this.endPromise) {\n return this.endPromise;\n }\n\n if (this.error) {\n this.endPromise = Promise.reject(this.error);\n return this.endPromise;\n }\n\n this.state = 'ending';\n this.queue.push({ kind: 'commit', final: true });\n const settled = new Promise<void>((resolve, reject) => {\n this.endWaiter = { resolve, reject };\n });\n this.armEndTimer();\n this.endPromise = settled.then(() => this.finish());\n void this.pump();\n this.checkEnded();\n return this.endPromise;\n }\n\n /**\n * (Re)starts the end timeout. It measures provider inactivity: every server\n * message restarts it, so a long recording that is still being transcribed\n * is not cut off, while a silent provider still fails after `timeoutMs`.\n */\n private armEndTimer(): void {\n clearTimeout(this.endTimer);\n this.endTimer = setTimeout(() => {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} timed out after ${this.init.timeoutMs} ms waiting for the final transcript`,\n ),\n );\n }, this.init.timeoutMs);\n }\n\n abort(reason?: unknown): void {\n this.fail(toError(reason, this.provider));\n }\n\n on<E extends StreamingSessionEventName>(\n event: E,\n listener: StreamingSessionListener<E>,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as (event: never) => void);\n return () => this.off(event, listener);\n }\n\n off<E extends StreamingSessionEventName>(\n event: E,\n listener: StreamingSessionListener<E>,\n ): void {\n this.listeners.get(event)?.delete(listener as (event: never) => void);\n }\n\n private enqueueAudio(chunk: StreamingAudioChunk): Promise<void> {\n if (this.error) {\n return Promise.reject(this.error);\n }\n if (this.endPromise || this.isTerminal()) {\n return Promise.reject(\n new SpeechError(\n 'Cannot write to a streaming session after end()',\n 'SPEECH_SESSION_ENDED',\n this.provider,\n ),\n );\n }\n\n let bytes: Uint8Array;\n try {\n bytes = chunkToBytes(chunk, this.provider);\n } catch (error) {\n return Promise.reject(error);\n }\n if (bytes.byteLength === 0) {\n return Promise.resolve();\n }\n if (this.queued + bytes.byteLength > this.init.maxBufferedBytes) {\n return Promise.reject(\n new SpeechError(\n `Streaming write would queue more than maxBufferedBytes (${this.init.maxBufferedBytes}); await write() before sending more audio`,\n 'SPEECH_BACKPRESSURE',\n this.provider,\n ),\n );\n }\n\n const splitter = this.splitter;\n let cuts: number[] = [];\n if (splitter && !this.init.turnLimit?.rollover) {\n if (splitter.wouldOverflow(bytes.byteLength)) {\n return Promise.reject(\n new SpeechError(\n `Streaming write would push the current turn past maxTurnSeconds (${this.init.turnLimit?.maxTurnSeconds} s); call commit() before writing more audio`,\n 'SPEECH_TURN_TOO_LONG',\n this.provider,\n ),\n );\n }\n splitter.add(bytes.byteLength);\n } else if (splitter) {\n cuts = splitter.split(bytes);\n }\n\n return new Promise<void>((resolve, reject) => {\n // A rollover splits the chunk: each part before a cut ends with an\n // automatic commit, and the write settles with its last part.\n const pushPart = (start: number, end: number) => {\n const part = bytes.subarray(start, end);\n this.queue.push({\n kind: 'audio',\n // Encode now so callers may reuse their buffer once write() returns.\n base64: encodeBase64(part),\n bytes: part.byteLength,\n resolve: end === bytes.byteLength ? resolve : noop,\n reject,\n });\n this.queued += part.byteLength;\n };\n // Cuts are ascending offsets in 1..byteLength, so no part is empty.\n let start = 0;\n for (const cut of cuts) {\n pushPart(start, cut);\n this.queue.push({ kind: 'commit', final: false });\n start = cut;\n }\n if (start < bytes.byteLength) {\n pushPart(start, bytes.byteLength);\n }\n void this.pump();\n });\n }\n\n private async connect(): Promise<void> {\n this.connectTimer = setTimeout(() => {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} connection timed out after ${this.init.connectTimeoutMs} ms`,\n ),\n );\n }, this.init.connectTimeoutMs);\n\n try {\n const connection = await this.init.connect();\n if (this.isTerminal()) {\n return;\n }\n this.secrets = connection.secrets;\n const socket = this.init.createWebSocket(connection.url, {\n protocols: connection.protocols,\n headers: connection.headers,\n });\n this.socket = socket;\n try {\n socket.binaryType = 'arraybuffer';\n } catch {\n // Some implementations expose a read-only binaryType.\n }\n socket.addEventListener('open', () => this.onOpen());\n socket.addEventListener('message', (event) => this.onMessage(event.data));\n socket.addEventListener('error', () => {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} WebSocket error`,\n ),\n );\n });\n socket.addEventListener('close', (event) => this.onClose(event));\n } catch (error) {\n this.fail(toError(error, this.provider));\n }\n }\n\n private onOpen(): void {\n clearTimeout(this.connectTimer);\n if (this.isTerminal()) {\n this.closeSocket();\n return;\n }\n\n this.opened = true;\n for (const message of this.init.protocol.sessionMessages(\n this.init.config,\n )) {\n if (!this.send(message)) {\n return;\n }\n }\n if (this.state === 'connecting') {\n this.state = 'open';\n }\n this.resolveReady();\n this.emit('open', { sessionId: this.sessionId });\n void this.pump();\n }\n\n private onMessage(data: unknown): void {\n if (this.isTerminal()) {\n return;\n }\n\n this.lastServerMessageAt = Date.now();\n if (this.endWaiter) {\n this.armEndTimer();\n }\n\n const text = messageDataToText(data);\n if (text === undefined) {\n return;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return;\n }\n\n let events: RealtimeProtocolEvent[];\n try {\n events = this.init.protocol.parse(parsed);\n } catch (error) {\n this.fail(toError(error, this.provider));\n return;\n }\n\n for (const event of events) {\n if (this.isTerminal()) {\n return;\n }\n this.handle(event);\n }\n this.checkEnded();\n }\n\n private handle(event: RealtimeProtocolEvent): void {\n switch (event.type) {\n case 'session':\n this.sessionId = event.sessionId ?? this.sessionId;\n return;\n case 'committed':\n if (event.itemId && !this.pendingItems.has(event.itemId)) {\n this.pendingItems.add(event.itemId);\n this.itemOrder.push(event.itemId);\n }\n // A turn whose speech_stopped we saw was committed by server VAD, so\n // it does not acknowledge one of our commits.\n if (\n this.init.protocol.commitAck === 'committed' &&\n !(event.itemId && this.vadStoppedItems.has(event.itemId))\n ) {\n this.ackCommit();\n }\n return;\n case 'commit_empty':\n this.ackCommit();\n return;\n case 'partial': {\n const key = event.itemId ?? '';\n const text = (this.partials.get(key) ?? '') + event.delta;\n this.partials.set(key, text);\n this.emit('partial', {\n delta: event.delta,\n text,\n itemId: event.itemId,\n });\n return;\n }\n case 'final': {\n let serverEnded = false;\n if (this.init.protocol.commitAck === 'final') {\n if (this.outstandingCommits === 0) {\n // A final we did not ask for: the server ended the open turn by\n // itself (e.g. a server-side generation limit).\n serverEnded = true;\n } else if (this.ackCommit() && event.audioConsumed === false) {\n // Our turn carried audio but the server consumed none. vLLM does\n // this when a stale end-of-turn marker (left by a commit that\n // crossed a server-ended turn) ends the turn at once.\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} finished a turn without transcribing its audio (the server's turn state is out of sync, e.g. after a commit crossed a server-ended turn); start a new session`,\n ),\n );\n return;\n }\n }\n if (event.itemId) {\n this.pendingItems.delete(event.itemId);\n }\n this.partials.delete(event.itemId ?? '');\n this.finals.push({\n itemId: event.itemId,\n text: event.text,\n providerUsage: event.providerUsage,\n raw: event.raw,\n arrival: this.finals.length,\n });\n this.emit('final', {\n text: event.text,\n itemId: event.itemId,\n providerUsage: event.providerUsage,\n raw: event.raw,\n });\n if (serverEnded) {\n // Fail closed: audio sent after the cut-off was never transcribed,\n // and the server may now be out of step with our turns.\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} ended a turn before it was committed (for example at a server generation limit); audio sent after that point was not transcribed. Call commit() more often and start a new session`,\n ),\n );\n }\n return;\n }\n case 'speech_started':\n this.emit(event.type, { itemId: event.itemId, audioMs: event.audioMs });\n return;\n case 'speech_stopped':\n if (event.itemId) {\n this.vadStoppedItems.add(event.itemId);\n }\n this.emit(event.type, { itemId: event.itemId, audioMs: event.audioMs });\n return;\n case 'error':\n this.fail(\n new SpeechProviderError(\n this.provider,\n this.redact(\n `${this.provider} error${event.code ? ` (${event.code})` : ''}: ${event.message}`,\n ) ?? `${this.provider} error`,\n {\n responseBody:\n event.raw === undefined\n ? undefined\n : this.redact(JSON.stringify(event.raw)),\n },\n ),\n );\n return;\n }\n }\n\n /** Acknowledges the oldest outstanding commit; returns whether it carried audio. */\n private ackCommit(): boolean {\n this.outstandingCommits = Math.max(0, this.outstandingCommits - 1);\n return this.commitHadAudio.shift() ?? false;\n }\n\n private async pump(): Promise<void> {\n if (this.pumping || !this.opened) {\n return;\n }\n\n this.pumping = true;\n try {\n while (this.queue.length > 0 && !this.isTerminal()) {\n await this.waitForDrain();\n if (!(await this.openTurnIfNeeded())) {\n break;\n }\n const item = this.queue.shift();\n if (!item || this.isTerminal()) {\n if (item?.kind === 'audio') {\n item.reject(this.error ?? new Error('Session closed'));\n }\n break;\n }\n\n if (item.kind === 'commit') {\n this.sendCommit(item.final);\n continue;\n }\n\n this.queued -= item.bytes;\n if (!this.send(this.init.protocol.appendMessage(item.base64))) {\n item.reject(this.error ?? new Error('Session closed'));\n break;\n }\n this.totalBytes += item.bytes;\n this.uncommittedBytes += item.bytes;\n await this.waitForDrain();\n if (this.error) {\n item.reject(this.error);\n } else {\n item.resolve();\n }\n }\n } finally {\n this.pumping = false;\n }\n this.checkEnded();\n }\n\n private sendCommit(final: boolean): void {\n const needed =\n this.uncommittedBytes > 0 ||\n (final && this.init.protocol.endCommit === 'always');\n if (!needed || !this.padShortTurn()) {\n return;\n }\n if (this.send(this.init.protocol.commitMessage({ final }))) {\n this.commitHadAudio.push(this.uncommittedBytes > 0);\n this.uncommittedBytes = 0;\n this.outstandingCommits += 1;\n this.turnOpen = false;\n }\n }\n\n /**\n * In a session with a turn limit, tops up a non-empty turn shorter than the\n * protocol's `minCommitSeconds` with silence (not counted as streamed\n * audio). Rollover can leave such a tail, which the provider would\n * otherwise reject as empty and drop. Returns false when the send failed.\n */\n private padShortTurn(): boolean {\n const minSeconds = this.init.protocol.minCommitSeconds;\n if (!this.init.turnLimit || !minSeconds || this.uncommittedBytes === 0) {\n return true;\n }\n const format = this.init.config.format;\n const align = blockAlign(format);\n // Round up to whole sample frames so the turn reaches the minimum.\n const minBytes =\n Math.ceil((minSeconds * bytesPerSecond(format)) / align - 1e-9) * align;\n const missing = minBytes - this.uncommittedBytes;\n if (missing <= 0) {\n return true;\n }\n if (\n !this.send(\n this.init.protocol.appendMessage(\n encodeBase64(silence(missing, format)),\n ),\n )\n ) {\n return false;\n }\n this.uncommittedBytes += missing;\n return true;\n }\n\n /**\n * For protocols with an explicit turn start: before the next audio, waits\n * for every outstanding turn to be acknowledged, then opens a new turn. Like\n * the end timeout, the wait is bounded by provider inactivity: it fails only\n * after `timeoutMs` without any server message. Returns false when the\n * session has failed.\n */\n private async openTurnIfNeeded(): Promise<boolean> {\n const start = this.init.protocol.turnStartMessage;\n if (!start || this.turnOpen || this.queue[0]?.kind !== 'audio') {\n return true;\n }\n\n const waitStartedAt = Date.now();\n while (!this.isTerminal() && this.outstandingCommits > 0) {\n const lastActivity = Math.max(waitStartedAt, this.lastServerMessageAt);\n if (Date.now() - lastActivity >= this.init.timeoutMs) {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} timed out after ${this.init.timeoutMs} ms without a server message while waiting for the previous turn to finish`,\n ),\n );\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, DRAIN_POLL_MS));\n }\n if (this.isTerminal() || !this.send(start.call(this.init.protocol))) {\n return false;\n }\n this.turnOpen = true;\n return true;\n }\n\n /**\n * Waits until the socket's send buffer is at or below `highWaterMark`. A\n * socket that stays congested for `timeoutMs` fails the session, so a\n * stalled connection can never leave `write()` pending forever.\n */\n private async waitForDrain(): Promise<void> {\n const deadline = Date.now() + this.init.timeoutMs;\n while (\n !this.isTerminal() &&\n this.socket &&\n this.socket.bufferedAmount > this.init.highWaterMark\n ) {\n if (Date.now() >= deadline) {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} socket did not drain below highWaterMark within ${this.init.timeoutMs} ms`,\n ),\n );\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, DRAIN_POLL_MS));\n }\n }\n\n private send(message: unknown): boolean {\n try {\n this.socket?.send(JSON.stringify(message));\n return true;\n } catch (error) {\n this.fail(toError(error, this.provider));\n return false;\n }\n }\n\n private checkEnded(): void {\n if (\n !this.endWaiter ||\n this.isTerminal() ||\n !this.opened ||\n this.pumping ||\n this.queue.length > 0 ||\n this.outstandingCommits > 0 ||\n this.pendingItems.size > 0\n ) {\n return;\n }\n\n clearTimeout(this.endTimer);\n const waiter = this.endWaiter;\n this.endWaiter = undefined;\n waiter.resolve();\n }\n\n private async finish(): Promise<TranscriptResult> {\n this.state = 'closed';\n this.cleanup();\n this.closeSocket();\n\n const ordered = [...this.finals].sort(\n (left, right) => this.rank(left) - this.rank(right),\n );\n const segments: TranscriptSegment[] = ordered\n .map((final) => final.text.trim())\n .filter(Boolean)\n .map((text) => ({ text }));\n const providerUsage = sumUsage(ordered.map((final) => final.providerUsage));\n const durationSeconds = audioSecondsForBytes(\n this.totalBytes,\n this.init.config.format,\n );\n const usage: SpeechUsage = {\n operation: 'transcription',\n provider: this.provider,\n model: this.init.config.model,\n audioSeconds:\n audioSecondsFromProviderUsage(providerUsage) ?? durationSeconds,\n bytes: this.totalBytes,\n providerUsage,\n };\n const result: TranscriptResult = {\n text: segments.map((segment) => segment.text).join(' '),\n language: this.init.config.language,\n durationSeconds,\n segments,\n provider: this.provider,\n model: this.init.config.model,\n raw: {\n sessionId: this.sessionId,\n finals: ordered.map((final) => final.raw),\n },\n usage,\n };\n\n await reportSpeechUsage(usage, ...this.init.onUsage);\n return result;\n }\n\n private rank(final: FinalRecord): number {\n const index = final.itemId ? this.itemOrder.indexOf(final.itemId) : -1;\n return index >= 0 ? index : this.itemOrder.length + final.arrival;\n }\n\n private onClose(event: SpeechWebSocketCloseEvent): void {\n clearTimeout(this.connectTimer);\n if (!this.isTerminal()) {\n const code = event.code ?? 1005;\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} socket closed unexpectedly (code ${code}${event.reason ? `: ${this.redact(event.reason)}` : ''})`,\n ),\n );\n }\n if (!this.closeEmitted) {\n this.closeEmitted = true;\n this.emit('close', {\n code: event.code ?? 1005,\n reason: this.redact(event.reason ?? '') ?? '',\n });\n }\n }\n\n private fail(error: Error): void {\n if (this.isTerminal()) {\n return;\n }\n\n this.error = error;\n this.state = 'failed';\n this.cleanup();\n const queue = this.queue;\n this.queue = [];\n this.queued = 0;\n for (const item of queue) {\n if (item.kind === 'audio') {\n item.reject(error);\n }\n }\n this.rejectReady(error);\n const waiter = this.endWaiter;\n this.endWaiter = undefined;\n waiter?.reject(error);\n this.emit('error', error);\n this.closeSocket();\n }\n\n private cleanup(): void {\n clearTimeout(this.connectTimer);\n clearTimeout(this.endTimer);\n this.init.signal?.removeEventListener('abort', this.onAbort);\n }\n\n private closeSocket(): void {\n try {\n this.socket?.close(1000);\n } catch {\n // Already closing.\n }\n }\n\n private emit<E extends StreamingSessionEventName>(\n event: E,\n payload: StreamingSessionEvents[E],\n ): void {\n for (const listener of [...(this.listeners.get(event) ?? [])]) {\n try {\n (listener as StreamingSessionListener<E>)(payload);\n } catch (error) {\n // A throwing listener fails the session so the bug surfaces via end().\n if (event !== 'error' && event !== 'close') {\n this.fail(toError(error, this.provider));\n }\n }\n }\n }\n\n private isTerminal(): boolean {\n return this.state === 'closed' || this.state === 'failed';\n }\n\n private redact(text: string | undefined): string | undefined {\n let result = text;\n for (const secret of this.secrets) {\n result = redactSecret(result, secret);\n }\n return result;\n }\n}\n\n/**\n * Throws when a turn cap is below the provider's minimum commit length:\n * padding a short turn up to that minimum would then exceed the cap.\n */\nexport function assertTurnLimitFitsProtocol(\n protocol: Pick<RealtimeProtocol, 'provider' | 'minCommitSeconds'>,\n limit: Pick<TurnLimit, 'maxTurnSeconds'> | undefined,\n): void {\n const minimum = protocol.minCommitSeconds;\n if (limit && minimum && limit.maxTurnSeconds < minimum) {\n throw new SpeechConfigurationError(\n `maxTurnSeconds (${limit.maxTurnSeconds}) is below ${protocol.provider}'s minimum commit of ${minimum} s`,\n protocol.provider,\n );\n }\n}\n\nfunction toError(value: unknown, provider: string): Error {\n if (value instanceof Error) {\n return value;\n }\n return new SpeechError(\n value === undefined ? 'Streaming session aborted' : String(value),\n 'SPEECH_ABORTED',\n provider,\n );\n}\n\n/**\n * Sums numeric fields of per-turn provider usage blocks (recursively), keeping\n * the first value of non-numeric fields such as `type`.\n */\nexport function sumUsage(\n blocks: Array<Record<string, unknown> | undefined>,\n): Record<string, unknown> | undefined {\n const present = blocks.filter(\n (block): block is Record<string, unknown> =>\n Boolean(block) && typeof block === 'object',\n );\n if (present.length === 0) {\n return undefined;\n }\n\n const total: Record<string, unknown> = {};\n for (const block of present) {\n for (const [key, value] of Object.entries(block)) {\n const current = total[key];\n if (typeof value === 'number') {\n total[key] = (typeof current === 'number' ? current : 0) + value;\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n total[key] = sumUsage([\n current as Record<string, unknown> | undefined,\n value as Record<string, unknown>,\n ]);\n } else if (current === undefined) {\n total[key] = value;\n }\n }\n }\n return total;\n}\n","/**\n * OpenAI Realtime transcription adapter (`openai-realtime`).\n *\n * Targets the GA Realtime API transcription session (no `OpenAI-Beta`\n * header): connect to `wss://api.openai.com/v1/realtime?intent=transcription`,\n * send `session.update` with `session.type: \"transcription\"`, stream\n * `input_audio_buffer.append`, and read\n * `conversation.item.input_audio_transcription.delta` / `.completed`.\n * Reference: https://developers.openai.com/api/docs/guides/realtime-transcription\n * and https://developers.openai.com/api/reference/resources/realtime/client-events\n */\n\nimport { SpeechConfigurationError } from '../shared/errors.js';\nimport { compactJson, resolveOpenAICompatibleUrl } from '../shared/http.js';\nimport {\n assertNoBrowserApiKey,\n CLIENT_SECRET_SUBPROTOCOL_PREFIX,\n mergeHeaderRecords,\n realtimeConnector,\n} from '../shared/realtime-auth.js';\nimport {\n assertTurnLimitFitsProtocol,\n DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n DEFAULT_STREAMING_HIGH_WATER_MARK,\n DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n DEFAULT_STREAMING_TIMEOUT_MS,\n type RealtimeProtocol,\n type RealtimeProtocolEvent,\n type RealtimeSessionConfig,\n RealtimeTranscriptionSession,\n} from '../shared/realtime-session.js';\nimport type {\n OpenAIRealtimeTranscriberOptions,\n StreamingAudioFormat,\n StreamingSession,\n StreamingSessionOptions,\n StreamingTranscriber,\n StreamingTurnDetection,\n StreamingTurnLimit,\n} from '../shared/streaming-types.js';\nimport {\n describeTurnLimit,\n resolveTurnLimit,\n type TurnLimit,\n} from '../shared/turn-limit.js';\nimport {\n resolveWebSocketFactory,\n type SpeechWebSocketFactory,\n toWebSocketUrl,\n} from '../shared/websocket.js';\n\nconst ADAPTER = 'openai-realtime';\nexport const OPENAI_REALTIME_DEFAULT_URL = 'wss://api.openai.com/v1/realtime';\nexport const OPENAI_REALTIME_DEFAULT_MODEL = 'gpt-4o-transcribe';\n\n/** GA realtime input formats: 24 kHz mono PCM16, or 8 kHz mono G.711. */\nconst WIRE_FORMATS: Record<\n StreamingAudioFormat['encoding'],\n { type: string; sampleRate: number }\n> = {\n pcm16: { type: 'audio/pcm', sampleRate: 24_000 },\n g711_ulaw: { type: 'audio/pcmu', sampleRate: 8_000 },\n g711_alaw: { type: 'audio/pcma', sampleRate: 8_000 },\n};\n\n/** The OpenAI Realtime transcription wire protocol. */\nexport const openAIRealtimeProtocol = (\n extras: Pick<\n OpenAIRealtimeTranscriberOptions,\n 'noiseReduction' | 'include' | 'transcriptionOptions'\n > = {},\n): RealtimeProtocol => ({\n provider: ADAPTER,\n commitAck: 'committed',\n endCommit: 'if-audio',\n // No per-turn cap by default: server VAD ends turns. Manual sessions may opt\n // in with `maxTurnSeconds`.\n // OpenAI rejects a commit with under 100 ms of audio as empty.\n minCommitSeconds: 0.1,\n sessionMessages(config: RealtimeSessionConfig) {\n return [\n {\n type: 'session.update',\n session: openAIRealtimeTranscriptionSession(config, extras),\n },\n ];\n },\n appendMessage(base64Audio: string) {\n return { type: 'input_audio_buffer.append', audio: base64Audio };\n },\n commitMessage() {\n return { type: 'input_audio_buffer.commit' };\n },\n parse: parseOpenAIRealtimeEvent,\n});\n\n/**\n * The GA transcription session object (`session.type: \"transcription\"`),\n * shared by `session.update` and the client-secret mint request.\n */\nexport function openAIRealtimeTranscriptionSession(\n config: RealtimeSessionConfig,\n extras: Pick<\n OpenAIRealtimeTranscriberOptions,\n 'noiseReduction' | 'include' | 'transcriptionOptions'\n > = {},\n): Record<string, unknown> {\n const input: Record<string, unknown> = {\n format:\n config.format.encoding === 'pcm16'\n ? { type: 'audio/pcm', rate: config.format.sampleRate }\n : { type: WIRE_FORMATS[config.format.encoding].type },\n // Extension fields first: the typed settings must win, so the model\n // sent (or bound to a minted client secret) always matches the model\n // reported in usage.\n transcription: {\n ...compactJson(extras.transcriptionOptions ?? {}),\n ...compactJson({\n model: config.model,\n language: config.language,\n prompt: config.prompt,\n }),\n },\n turn_detection: turnDetectionToWire(config.turnDetection),\n };\n if (extras.noiseReduction) {\n input.noise_reduction = { type: extras.noiseReduction };\n }\n\n return compactJson({\n type: 'transcription',\n audio: { input },\n include: extras.include,\n });\n}\n\n/** Maps one OpenAI Realtime server event to normalised session events. */\nexport function parseOpenAIRealtimeEvent(\n message: unknown,\n): RealtimeProtocolEvent[] {\n if (!message || typeof message !== 'object') {\n return [];\n }\n\n const event = message as Record<string, unknown>;\n const itemId = stringField(event, 'item_id');\n\n switch (event.type) {\n case 'session.created':\n case 'session.updated':\n case 'transcription_session.created':\n case 'transcription_session.updated': {\n const session = event.session as Record<string, unknown> | undefined;\n return [\n { type: 'session', sessionId: session && stringField(session, 'id') },\n ];\n }\n case 'input_audio_buffer.committed':\n return [{ type: 'committed', itemId }];\n case 'input_audio_buffer.speech_started':\n return [\n {\n type: 'speech_started',\n itemId,\n audioMs: numberField(event, 'audio_start_ms'),\n },\n ];\n case 'input_audio_buffer.speech_stopped':\n return [\n {\n type: 'speech_stopped',\n itemId,\n audioMs: numberField(event, 'audio_end_ms'),\n },\n ];\n case 'conversation.item.input_audio_transcription.delta': {\n const delta = stringField(event, 'delta');\n return delta ? [{ type: 'partial', itemId, delta }] : [];\n }\n case 'conversation.item.input_audio_transcription.completed': {\n const usage = event.usage;\n return [\n {\n type: 'final',\n itemId,\n text: stringField(event, 'transcript') ?? '',\n providerUsage:\n usage && typeof usage === 'object' && !Array.isArray(usage)\n ? (usage as Record<string, unknown>)\n : undefined,\n raw: event,\n },\n ];\n }\n case 'conversation.item.input_audio_transcription.failed': {\n const error = (event.error ?? {}) as Record<string, unknown>;\n return [\n {\n type: 'error',\n message: stringField(error, 'message') ?? 'transcription failed',\n code: stringField(error, 'code'),\n raw: event,\n },\n ];\n }\n case 'error': {\n const error = (event.error ?? {}) as Record<string, unknown>;\n const code = stringField(error, 'code');\n if (code === 'input_audio_buffer_commit_empty') {\n return [{ type: 'commit_empty' }];\n }\n return [\n {\n type: 'error',\n message: stringField(error, 'message') ?? 'unknown error',\n code,\n raw: event,\n },\n ];\n }\n default:\n return [];\n }\n}\n\n/**\n * Realtime transcriber for OpenAI and OpenAI-Realtime-compatible servers.\n *\n * Auth: `apiKey` is sent as `Authorization: Bearer` (Node only). In browsers,\n * pass `clientSecret` (an ephemeral client secret minted by your server); it\n * is sent as the `openai-insecure-api-key.<secret>` subprotocol. Supplying\n * `apiKey` or `headers` in a browser throws.\n */\nexport class OpenAIRealtimeTranscriber implements StreamingTranscriber {\n readonly type = ADAPTER;\n readonly audioFormat: StreamingAudioFormat;\n\n private readonly options: OpenAIRealtimeTranscriberOptions;\n private readonly url: string;\n private readonly createWebSocket: SpeechWebSocketFactory;\n private readonly protocol: RealtimeProtocol;\n\n constructor(options: OpenAIRealtimeTranscriberOptions = {}) {\n assertNoBrowserApiKey(ADAPTER, options.apiKey);\n\n this.options = options;\n this.url = resolveRealtimeUrl(\n options.baseUrl ?? OPENAI_REALTIME_DEFAULT_URL,\n );\n this.createWebSocket = resolveWebSocketFactory(ADAPTER, options);\n this.audioFormat = resolveOpenAIRealtimeFormat(options.format);\n this.protocol = openAIRealtimeProtocol(options);\n resolveTurnLimit(ADAPTER, undefined, options);\n }\n\n /**\n * A turn limit applies only to `manual` sessions with `maxTurnSeconds` set;\n * under server or semantic VAD the provider ends turns itself.\n */\n turnLimit(\n options: StreamingSessionOptions = {},\n ): StreamingTurnLimit | undefined {\n return describeTurnLimit(\n this.resolveTurnLimit(options),\n this.sessionFormat(options),\n );\n }\n\n private sessionFormat(\n options: StreamingSessionOptions,\n ): StreamingAudioFormat {\n // A different encoding starts from that encoding's defaults.\n return resolveOpenAIRealtimeFormat(\n options.format?.encoding &&\n options.format.encoding !== this.audioFormat.encoding\n ? options.format\n : { ...this.audioFormat, ...options.format },\n );\n }\n\n private resolveTurnLimit(\n options: StreamingSessionOptions,\n ): TurnLimit | undefined {\n const limit = resolveTurnLimit(\n ADAPTER,\n this.protocol.maxTurnSeconds,\n options,\n this.options,\n );\n if (this.turnDetection(options).type !== 'manual') {\n return undefined;\n }\n assertTurnLimitFitsProtocol(this.protocol, limit);\n return limit;\n }\n\n private turnDetection(\n options: StreamingSessionOptions,\n ): StreamingTurnDetection {\n return (\n options.turnDetection ??\n this.options.turnDetection ?? { type: 'server_vad' }\n );\n }\n\n start(options: StreamingSessionOptions = {}): StreamingSession {\n const format = this.sessionFormat(options);\n const connect = realtimeConnector({\n adapter: ADAPTER,\n url: this.url,\n apiKey: this.options.apiKey,\n headers: mergeHeaderRecords(this.options.headers, options.headers),\n clientSecret: options.clientSecret ?? this.options.clientSecret,\n protocols: (secret) =>\n secret\n ? ['realtime', `${CLIENT_SECRET_SUBPROTOCOL_PREFIX}${secret}`]\n : ['realtime'],\n });\n\n return new RealtimeTranscriptionSession({\n protocol: this.protocol,\n config: {\n model:\n options.model?.trim() ||\n this.options.model?.trim() ||\n OPENAI_REALTIME_DEFAULT_MODEL,\n format,\n turnDetection: this.turnDetection(options),\n language: options.language ?? this.options.language,\n prompt: options.prompt ?? this.options.prompt,\n },\n connect,\n createWebSocket: this.createWebSocket,\n timeoutMs: this.options.timeoutMs ?? DEFAULT_STREAMING_TIMEOUT_MS,\n connectTimeoutMs:\n this.options.connectTimeoutMs ?? DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n highWaterMark:\n this.options.highWaterMark ?? DEFAULT_STREAMING_HIGH_WATER_MARK,\n maxBufferedBytes:\n this.options.maxBufferedBytes ?? DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n signal: options.signal,\n onUsage: [this.options.onUsage, options.onUsage],\n turnLimit: this.resolveTurnLimit(options),\n });\n }\n}\n\n/**\n * Resolves a realtime WebSocket URL: `http(s)` becomes `ws(s)`, a server root\n * gets `/v1/realtime`, a versioned API root gets `/realtime`, and\n * `intent=transcription` is added unless an `intent` is already present. No\n * `model` query parameter is added: the model travels in `session.update`.\n */\nexport function resolveRealtimeUrl(baseUrl: string): string {\n const url = new URL(\n toWebSocketUrl(resolveOpenAICompatibleUrl(baseUrl, 'realtime'), ADAPTER),\n );\n if (!url.searchParams.has('intent')) {\n url.searchParams.set('intent', 'transcription');\n }\n return url.toString();\n}\n\nexport function resolveOpenAIRealtimeFormat(\n format: Partial<StreamingAudioFormat> | undefined,\n): StreamingAudioFormat {\n const encoding = format?.encoding ?? 'pcm16';\n const wire = WIRE_FORMATS[encoding];\n if (!wire) {\n throw new SpeechConfigurationError(\n `openai-realtime does not support encoding ${String(encoding)}`,\n ADAPTER,\n );\n }\n\n const resolved: StreamingAudioFormat = {\n encoding,\n sampleRate: format?.sampleRate ?? wire.sampleRate,\n channels: format?.channels ?? 1,\n };\n if (resolved.sampleRate !== wire.sampleRate || resolved.channels !== 1) {\n throw new SpeechConfigurationError(\n `openai-realtime requires ${encoding} at ${wire.sampleRate} Hz mono (got ${resolved.sampleRate} Hz, ${resolved.channels} channel(s)); resample before streaming`,\n ADAPTER,\n );\n }\n return resolved;\n}\n\nfunction turnDetectionToWire(\n turnDetection: StreamingTurnDetection,\n): Record<string, unknown> | null {\n switch (turnDetection.type) {\n case 'manual':\n return null;\n case 'server_vad':\n return compactJson({\n type: 'server_vad',\n threshold: turnDetection.threshold,\n prefix_padding_ms: turnDetection.prefixPaddingMs,\n silence_duration_ms: turnDetection.silenceDurationMs,\n });\n case 'semantic_vad':\n return compactJson({\n type: 'semantic_vad',\n eagerness: turnDetection.eagerness,\n });\n default:\n throw new SpeechConfigurationError(\n `Unknown turn detection type: ${String((turnDetection as { type?: unknown }).type)}`,\n ADAPTER,\n );\n }\n}\n\nfunction stringField(\n record: Record<string, unknown>,\n key: string,\n): string | undefined {\n const value = record[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction numberField(\n record: Record<string, unknown>,\n key: string,\n): number | undefined {\n const value = record[key];\n return typeof value === 'number' ? value : undefined;\n}\n","/**\n * vLLM realtime transcription adapter (`voxtral-realtime`), for Mistral's\n * Voxtral Realtime models (e.g. `mistralai/Voxtral-Mini-4B-Realtime-2602`)\n * served by vLLM at `/v1/realtime`.\n *\n * vLLM's protocol borrows OpenAI event names but is not OpenAI-compatible.\n * Verified against vLLM's `entrypoints/speech_to_text/realtime` source and a\n * live server:\n *\n * 1. The server sends `session.created` (`{ id, created }`) on connect.\n * 2. The client sends `session.update` with a top-level `model` (no nested\n * `session` object, no acknowledgement; an unknown model yields an\n * `error` with code `model_not_found`).\n * 3. A non-final `input_audio_buffer.commit` starts generation for a turn.\n * 4. `input_audio_buffer.append` carries base64 PCM16 at 16 kHz mono. Audio\n * appended before the start commit is kept.\n * 5. The server streams `transcription.delta` (`{ delta }`, often empty) while\n * audio arrives, at roughly real-time speed.\n * 6. `input_audio_buffer.commit` with `final: true` ends the turn; the server\n * replies `transcription.done` (`{ text, usage }`, token usage only) and\n * clears its buffer. A further start commit opens the next turn on the same\n * socket.\n * 7. Errors arrive as `{ type: 'error', error: string, code }` and leave the\n * socket open; this adapter treats every error as fatal.\n * 8. A turn whose audio and tokens exceed `max_model_len` (~12.5 tokens per\n * audio second plus a 39-token prompt) ends in `error` `processing_error`\n * and can take the engine down. The limit is per turn: after a final\n * commit vLLM clears the turn's context. The session therefore rolls over\n * to a new turn before `maxTurnSeconds` (default 270) of audio.\n * 9. A stray final commit leaves a stale end marker: the next turn ends at once\n * with `prompt_tokens: 1` (any audio costs 39) and its audio is dropped.\n * The session fails closed on that, and on any `done` for a turn it did\n * not commit, rather than losing audio silently.\n */\n\nimport { SpeechConfigurationError } from '../shared/errors.js';\nimport { resolveOpenAICompatibleUrl } from '../shared/http.js';\nimport {\n assertNoBrowserApiKey,\n CLIENT_SECRET_SUBPROTOCOL_PREFIX,\n mergeHeaderRecords,\n realtimeConnector,\n} from '../shared/realtime-auth.js';\nimport {\n DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n DEFAULT_STREAMING_HIGH_WATER_MARK,\n DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n DEFAULT_STREAMING_TIMEOUT_MS,\n type RealtimeProtocol,\n type RealtimeProtocolEvent,\n RealtimeTranscriptionSession,\n} from '../shared/realtime-session.js';\nimport type {\n StreamingAudioFormat,\n StreamingSession,\n StreamingSessionOptions,\n StreamingTranscriber,\n StreamingTurnDetection,\n StreamingTurnLimit,\n VoxtralRealtimeTranscriberOptions,\n} from '../shared/streaming-types.js';\nimport {\n DEFAULT_MAX_TURN_SECONDS,\n describeTurnLimit,\n resolveTurnLimit,\n type TurnLimit,\n} from '../shared/turn-limit.js';\nimport {\n resolveWebSocketFactory,\n type SpeechWebSocketFactory,\n toWebSocketUrl,\n} from '../shared/websocket.js';\n\nconst ADAPTER = 'voxtral-realtime';\n\n/** vLLM's default served model name for Voxtral Mini Realtime. */\nexport const VOXTRAL_REALTIME_DEFAULT_MODEL =\n 'mistralai/Voxtral-Mini-4B-Realtime-2602';\n\n/** The only input format vLLM's realtime endpoint accepts. */\nexport const VOXTRAL_REALTIME_AUDIO_FORMAT: Readonly<StreamingAudioFormat> =\n Object.freeze({ encoding: 'pcm16', sampleRate: 16_000, channels: 1 });\n\n/** The vLLM realtime wire protocol. */\nexport const voxtralRealtimeProtocol = (): RealtimeProtocol => ({\n provider: ADAPTER,\n // vLLM never acknowledges a commit; `transcription.done` closes the turn.\n commitAck: 'final',\n // The start commit is sent lazily before a turn's first audio, so a session\n // with no uncommitted audio has no open turn and needs no end commit.\n endCommit: 'if-audio',\n // vLLM's context (`max_model_len`) covers one turn; roll over before it.\n maxTurnSeconds: DEFAULT_MAX_TURN_SECONDS,\n sessionMessages(config) {\n return [{ type: 'session.update', model: config.model }];\n },\n turnStartMessage() {\n return { type: 'input_audio_buffer.commit' };\n },\n appendMessage(base64Audio) {\n return { type: 'input_audio_buffer.append', audio: base64Audio };\n },\n commitMessage() {\n // Every turn end is a final commit; the next turn gets a new start commit.\n return { type: 'input_audio_buffer.commit', final: true };\n },\n parse: parseVoxtralRealtimeEvent,\n});\n\n/** Maps one vLLM realtime server event to normalised session events. */\nexport function parseVoxtralRealtimeEvent(\n message: unknown,\n): RealtimeProtocolEvent[] {\n if (!message || typeof message !== 'object') {\n return [];\n }\n\n const event = message as Record<string, unknown>;\n switch (event.type) {\n case 'session.created':\n return [\n {\n type: 'session',\n sessionId: typeof event.id === 'string' ? event.id : undefined,\n },\n ];\n case 'transcription.delta': {\n const delta = typeof event.delta === 'string' ? event.delta : '';\n return delta ? [{ type: 'partial', delta }] : [];\n }\n case 'transcription.done': {\n const usage =\n event.usage &&\n typeof event.usage === 'object' &&\n !Array.isArray(event.usage)\n ? (event.usage as Record<string, unknown>)\n : undefined;\n // Any audio, even one sample, costs the turn's full audio prompt\n // (39 tokens on Voxtral Mini Realtime); a turn that consumed no audio\n // reports `prompt_tokens: 1`.\n const promptTokens = usage?.prompt_tokens;\n return [\n {\n type: 'final',\n text: typeof event.text === 'string' ? event.text.trim() : '',\n providerUsage: usage,\n raw: event,\n audioConsumed:\n typeof promptTokens === 'number' ? promptTokens > 1 : undefined,\n },\n ];\n }\n case 'error':\n return [\n {\n type: 'error',\n message:\n typeof event.error === 'string' ? event.error : 'unknown error',\n code: typeof event.code === 'string' ? event.code : undefined,\n raw: event,\n },\n ];\n default:\n return [];\n }\n}\n\n/**\n * Realtime transcriber for Voxtral Realtime served by vLLM.\n *\n * - Audio: PCM16, 16 kHz, mono only; write whole samples (even byte counts).\n * - Turns: manual only. vLLM has no VAD; it streams partials continuously and\n * produces one final per `commit()` / `end()`, plus one per automatic\n * rollover when a turn reaches `maxTurnSeconds` (default 270).\n * - `language` and `prompt` are not sent: vLLM's realtime session accepts\n * only `model`.\n * - Auth: `apiKey` → `Authorization: Bearer` (Node only). vLLM has no\n * ephemeral tokens, so browsers must connect through the consumer's own\n * proxy or token gateway, passing that gateway's short-lived `clientSecret`\n * (sent as the `openai-insecure-api-key.<secret>` subprotocol next to\n * `realtime`; the gateway must echo `realtime`).\n */\nexport class VoxtralRealtimeTranscriber implements StreamingTranscriber {\n readonly type = ADAPTER;\n readonly audioFormat: StreamingAudioFormat = {\n ...VOXTRAL_REALTIME_AUDIO_FORMAT,\n };\n\n private readonly options: VoxtralRealtimeTranscriberOptions;\n private readonly url: string;\n private readonly createWebSocket: SpeechWebSocketFactory;\n private readonly protocol = voxtralRealtimeProtocol();\n\n constructor(options: VoxtralRealtimeTranscriberOptions = {}) {\n assertNoBrowserApiKey(ADAPTER, options.apiKey);\n if (!options.baseUrl?.trim()) {\n throw new SpeechConfigurationError(\n 'voxtral-realtime requires baseUrl (the vLLM server, e.g. http://localhost:8000 or its /v1 root)',\n ADAPTER,\n );\n }\n\n this.options = options;\n this.url = resolveVoxtralRealtimeUrl(options.baseUrl);\n this.createWebSocket = resolveWebSocketFactory(ADAPTER, options);\n assertFormat(options.format);\n assertTurnDetection(options.turnDetection);\n this.resolveTurnLimit();\n }\n\n turnLimit(\n options: StreamingSessionOptions = {},\n ): StreamingTurnLimit | undefined {\n assertFormat(options.format);\n return describeTurnLimit(\n this.resolveTurnLimit(options),\n VOXTRAL_REALTIME_AUDIO_FORMAT,\n );\n }\n\n private resolveTurnLimit(\n options: StreamingSessionOptions = {},\n ): TurnLimit | undefined {\n return resolveTurnLimit(\n ADAPTER,\n this.protocol.maxTurnSeconds,\n options,\n this.options,\n );\n }\n\n start(options: StreamingSessionOptions = {}): StreamingSession {\n assertFormat(options.format);\n assertTurnDetection(options.turnDetection);\n const turnLimit = this.resolveTurnLimit(options);\n const connect = realtimeConnector({\n adapter: ADAPTER,\n url: this.url,\n apiKey: this.options.apiKey,\n headers: mergeHeaderRecords(this.options.headers, options.headers),\n clientSecret: options.clientSecret ?? this.options.clientSecret,\n // A plain vLLM server selects no subprotocol, so offer none unless a\n // gateway token must travel in one.\n protocols: (secret) =>\n secret\n ? ['realtime', `${CLIENT_SECRET_SUBPROTOCOL_PREFIX}${secret}`]\n : [],\n });\n\n return new RealtimeTranscriptionSession({\n protocol: this.protocol,\n config: {\n model:\n options.model?.trim() ||\n this.options.model?.trim() ||\n VOXTRAL_REALTIME_DEFAULT_MODEL,\n format: { ...VOXTRAL_REALTIME_AUDIO_FORMAT },\n turnDetection: { type: 'manual' },\n language: options.language ?? this.options.language,\n },\n connect,\n createWebSocket: this.createWebSocket,\n timeoutMs: this.options.timeoutMs ?? DEFAULT_STREAMING_TIMEOUT_MS,\n connectTimeoutMs:\n this.options.connectTimeoutMs ?? DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n highWaterMark:\n this.options.highWaterMark ?? DEFAULT_STREAMING_HIGH_WATER_MARK,\n maxBufferedBytes:\n this.options.maxBufferedBytes ?? DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n signal: options.signal,\n onUsage: [this.options.onUsage, options.onUsage],\n turnLimit,\n });\n }\n}\n\n/**\n * Resolves vLLM's realtime WebSocket URL: `http(s)` becomes `ws(s)`, a server\n * root gets `/v1/realtime`, a versioned API root (e.g. a gateway's\n * `/stt/v1`) gets `/realtime`, and a URL ending in `/realtime` is kept.\n */\nexport function resolveVoxtralRealtimeUrl(baseUrl: string): string {\n return toWebSocketUrl(\n resolveOpenAICompatibleUrl(baseUrl, 'realtime'),\n ADAPTER,\n );\n}\n\nfunction assertFormat(format: Partial<StreamingAudioFormat> | undefined): void {\n const expected = VOXTRAL_REALTIME_AUDIO_FORMAT;\n if (\n format &&\n ((format.encoding !== undefined && format.encoding !== expected.encoding) ||\n (format.sampleRate !== undefined &&\n format.sampleRate !== expected.sampleRate) ||\n (format.channels !== undefined && format.channels !== expected.channels))\n ) {\n throw new SpeechConfigurationError(\n `voxtral-realtime requires pcm16 at 16000 Hz mono (got ${format.encoding ?? 'pcm16'} at ${format.sampleRate ?? 16_000} Hz, ${format.channels ?? 1} channel(s)); resample before streaming`,\n ADAPTER,\n );\n }\n}\n\nfunction assertTurnDetection(\n turnDetection: StreamingTurnDetection | undefined,\n): void {\n if (turnDetection && turnDetection.type !== 'manual') {\n throw new SpeechConfigurationError(\n `voxtral-realtime has no server turn detection (got ${turnDetection.type}); use manual turns with commit()/end()`,\n ADAPTER,\n );\n }\n}\n","import {\n compactJson,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\n resolveOpenAICompatibleUrl,\n voiceToString,\n} from '../shared/http.js';\nimport type {\n OpenAICompatibleSpeechSynthesizerOptions,\n SpeechSynthesizer,\n SynthesisRequest,\n SynthesizedSpeech,\n} from '../shared/types.js';\n\nexport class OpenAICompatibleSpeechSynthesizer\n extends HttpSpeechAdapter\n implements SpeechSynthesizer\n{\n readonly type = 'openai-compatible' as const;\n\n /** Explicit override, resolved relative to `baseUrl` (or absolute). */\n private readonly speechPath?: string;\n private readonly defaultModel: string;\n private readonly defaultVoice: string;\n\n constructor(options: OpenAICompatibleSpeechSynthesizerOptions) {\n super(options);\n this.speechPath = options.speechPath;\n this.defaultModel = options.defaultModel ?? 'tts-1';\n this.defaultVoice = options.defaultVoice ?? 'alloy';\n }\n\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n const outputFormat = request.outputFormat ?? 'mp3';\n const payload = compactJson({\n model: request.model ?? this.defaultModel,\n input: request.text,\n voice: voiceToString(request.voice, this.defaultVoice),\n // biome-ignore lint/style/useNamingConvention: OpenAI-compatible API uses snake_case.\n response_format: outputFormat,\n speed: request.speed,\n });\n\n // Without an override, resolve the endpoint the way the transcriber does:\n // a server root gets `/v1/audio/speech`, a `/vN` API root gets\n // `/audio/speech`, and a full endpoint URL is used as-is. Resolved per\n // call so an invalid baseUrl still fails at synthesize time.\n const endpoint =\n this.speechPath ??\n resolveOpenAICompatibleUrl(this.baseUrl, 'audio/speech');\n\n const speech = await this.post(\n this.type,\n endpoint,\n {\n headers: {\n 'content-type': 'application/json',\n },\n body: JSON.stringify(payload),\n signal: request.signal,\n },\n (response) => readSynthesizedSpeechResponse(response, this.type),\n );\n\n return {\n ...speech,\n format: speech.format ?? outputFormat,\n model: speech.model ?? String(payload.model),\n };\n }\n}\n","import {\n DEFAULT_MAX_AUDIO_BYTES,\n normalizeAudioInput,\n} from '../shared/audio.js';\nimport { SpeechProviderError } from '../shared/errors.js';\nimport {\n appendOptionalFormValue,\n HttpSpeechAdapter,\n readTranscriptResponse,\n resolveOpenAICompatibleUrl,\n} from '../shared/http.js';\nimport {\n resolveRetryOptions,\n type SpeechRetryOptions,\n} from '../shared/retry.js';\nimport type {\n OpenAICompatibleTranscriberOptions,\n TimestampGranularity,\n Transcriber,\n TranscriptionRequest,\n TranscriptionResponseFormat,\n TranscriptResult,\n} from '../shared/types.js';\nimport {\n audioSecondsFromProviderUsage,\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from '../shared/usage.js';\n\nconst DEFAULT_MODEL = 'whisper-1';\n\n/**\n * Models that only accept `json`/`text` and reject `verbose_json` and\n * `timestamp_granularities[]` (e.g. `gpt-4o-transcribe`,\n * `gpt-4o-mini-transcribe`, optionally gateway-prefixed like\n * `openai/gpt-4o-transcribe`).\n */\nconst JSON_ONLY_MODEL = /(^|\\/)gpt-4o[\\w.-]*-transcribe/i;\n\n/** Provider 400/422 bodies that indicate an unsupported format option. */\nconst UNSUPPORTED_FORMAT_ERROR =\n /verbose_json|response_format|timestamp_granularit/i;\n\nexport function isJsonOnlyTranscriptionModel(model: string): boolean {\n return JSON_ONLY_MODEL.test(model);\n}\n\ninterface TranscriptionWireOptions {\n model: string;\n responseFormat: TranscriptionResponseFormat;\n timestampGranularities?: TimestampGranularity[];\n}\n\n/**\n * OpenAI-compatible speech-to-text adapter (`POST <base>/audio/transcriptions`).\n * Works with OpenAI, Groq, Fireworks, LiteLLM/Bifrost gateways, vLLM,\n * speaches/faster-whisper-server, and whisper.cpp server.\n *\n * Server-side only: the adapter holds the API key.\n */\nexport class OpenAICompatibleTranscriber\n extends HttpSpeechAdapter\n implements Transcriber\n{\n readonly type = 'openai-compatible' as const;\n\n private readonly endpoint: string;\n private readonly model: string;\n private readonly responseFormat?: TranscriptionResponseFormat;\n private readonly maxBytes: number;\n private readonly retry: Required<SpeechRetryOptions> | false;\n private readonly onUsage?: SpeechUsageCallback;\n\n constructor(options: OpenAICompatibleTranscriberOptions) {\n super(options);\n this.endpoint = resolveOpenAICompatibleUrl(\n options.baseUrl,\n 'audio/transcriptions',\n );\n this.model = options.model?.trim() || DEFAULT_MODEL;\n this.responseFormat = options.responseFormat;\n this.maxBytes = options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES;\n this.retry = resolveRetryOptions(options.retry);\n this.onUsage = options.onUsage;\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes: request.maxBytes ?? this.maxBytes,\n adapter: this.type,\n signal: request.signal,\n });\n const wire = this.resolveWireOptions(request);\n\n const send = (options: TranscriptionWireOptions) => {\n const form = new FormData();\n form.append('file', audio.blob, audio.filename);\n form.set('model', options.model);\n appendOptionalFormValue(form, 'language', request.language);\n appendOptionalFormValue(form, 'prompt', request.prompt);\n appendOptionalFormValue(form, 'temperature', request.temperature);\n form.set('response_format', options.responseFormat);\n for (const granularity of options.timestampGranularities ?? []) {\n form.append('timestamp_granularities[]', granularity);\n }\n\n return this.post(\n this.type,\n this.endpoint,\n {\n body: form,\n headers: request.headers,\n signal: request.signal,\n },\n (response) => readTranscriptResponse(response, this.type),\n this.retry,\n );\n };\n\n let transcript: TranscriptResult;\n try {\n transcript = await send(wire);\n } catch (error) {\n if (!isUnsupportedFormatError(error, wire)) {\n throw error;\n }\n // The server rejected verbose_json/timestamps: fall back to plain JSON.\n transcript = await send({\n model: wire.model,\n responseFormat: 'json',\n });\n }\n\n const providerUsage = extractProviderUsage(transcript.raw);\n const usage: SpeechUsage = {\n operation: 'transcription',\n provider: this.type,\n model: transcript.model ?? wire.model,\n audioSeconds:\n transcript.durationSeconds ??\n audioSecondsFromProviderUsage(providerUsage) ??\n audio.durationSeconds,\n bytes: audio.bytes,\n providerUsage,\n };\n const result: TranscriptResult = {\n ...transcript,\n // Plain `text` responses end with a newline; JSON text is kept verbatim.\n text:\n transcript.raw === undefined ? transcript.text.trim() : transcript.text,\n provider: this.type,\n model: transcript.model ?? wire.model,\n usage,\n };\n\n await reportSpeechUsage(usage, this.onUsage, request.onUsage);\n return result;\n }\n\n private resolveWireOptions(\n request: TranscriptionRequest,\n ): TranscriptionWireOptions {\n const model = request.model?.trim() || this.model;\n const jsonOnly = isJsonOnlyTranscriptionModel(model);\n let responseFormat =\n request.responseFormat ??\n this.responseFormat ??\n (jsonOnly ? 'json' : 'verbose_json');\n\n if (jsonOnly && responseFormat === 'verbose_json') {\n responseFormat = 'json';\n }\n\n const timestampGranularities =\n responseFormat === 'verbose_json' &&\n request.timestampGranularities?.length\n ? [...new Set(request.timestampGranularities)]\n : undefined;\n\n return { model, responseFormat, timestampGranularities };\n }\n}\n\nfunction isUnsupportedFormatError(\n error: unknown,\n wire: TranscriptionWireOptions,\n): boolean {\n return (\n error instanceof SpeechProviderError &&\n (error.status === 400 || error.status === 422) &&\n (wire.responseFormat === 'verbose_json' ||\n Boolean(wire.timestampGranularities?.length)) &&\n UNSUPPORTED_FORMAT_ERROR.test(error.responseBody ?? '')\n );\n}\n\nfunction extractProviderUsage(\n raw: unknown,\n): Record<string, unknown> | undefined {\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n const usage = (raw as Record<string, unknown>).usage;\n return usage && typeof usage === 'object' && !Array.isArray(usage)\n ? (usage as Record<string, unknown>)\n : undefined;\n}\n","import {\n createHappyVerticalSynthesisForm,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\n} from '../shared/http.js';\nimport type {\n Qwen3SpeechSynthesizerOptions,\n SpeechSynthesizer,\n SynthesisRequest,\n SynthesizedSpeech,\n} from '../shared/types.js';\n\nexport class Qwen3SpeechSynthesizer\n extends HttpSpeechAdapter\n implements SpeechSynthesizer\n{\n readonly type = 'qwen3-tts' as const;\n\n private readonly speechPath: string;\n private readonly defaultModel: string;\n private readonly defaultVoice?: string;\n\n constructor(options: Qwen3SpeechSynthesizerOptions) {\n super(options);\n this.speechPath = options.speechPath ?? '/v1/audio/speech';\n this.defaultModel = options.defaultModel ?? 'qwen3-tts';\n this.defaultVoice = options.defaultVoice;\n }\n\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n const form = createHappyVerticalSynthesisForm(request, this.defaultVoice);\n const speech = await this.post(\n this.type,\n this.speechPath,\n {\n body: form,\n signal: request.signal,\n },\n (response) => readSynthesizedSpeechResponse(response, this.type),\n );\n\n return {\n ...speech,\n format: speech.format ?? request.outputFormat,\n model: speech.model ?? request.model ?? this.defaultModel,\n };\n }\n}\n","import { normalizeAudioInput } from '../shared/audio.js';\nimport {\n appendOptionalFormValue,\n createHappyVerticalSynthesisForm,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\n readTranscriptResponse,\n} from '../shared/http.js';\nimport type {\n SpeechSynthesizer,\n StudioServerSpeechSynthesizerOptions,\n StudioServerTranscriberOptions,\n SynthesisRequest,\n SynthesizedSpeech,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from '../shared/types.js';\n\nexport class StudioServerTranscriber\n extends HttpSpeechAdapter\n implements Transcriber\n{\n readonly type = 'studio-server' as const;\n private readonly transcribePath: string;\n\n constructor(options: StudioServerTranscriberOptions) {\n super(options);\n this.transcribePath = options.transcribePath ?? '/v1/transcribe';\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes: request.maxBytes,\n adapter: this.type,\n signal: request.signal,\n deriveExtension: false,\n });\n const form = new FormData();\n form.append('audio', audio.blob, audio.filename);\n appendOptionalFormValue(form, 'language', request.language);\n\n return this.post(\n this.type,\n this.transcribePath,\n {\n body: form,\n headers: request.headers,\n signal: request.signal,\n },\n (response) => readTranscriptResponse(response, this.type),\n );\n }\n}\n\nexport class StudioServerSpeechSynthesizer\n extends HttpSpeechAdapter\n implements SpeechSynthesizer\n{\n readonly type = 'studio-server' as const;\n private readonly synthesizePath: string;\n private readonly defaultVoice?: string;\n\n constructor(options: StudioServerSpeechSynthesizerOptions) {\n super(options);\n this.synthesizePath = options.synthesizePath ?? '/v1/tts/synthesize';\n this.defaultVoice = options.defaultVoice;\n }\n\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n const form = createHappyVerticalSynthesisForm(request, this.defaultVoice);\n\n return this.post(\n this.type,\n this.synthesizePath,\n {\n body: form,\n signal: request.signal,\n },\n (response) => readSynthesizedSpeechResponse(response, this.type),\n );\n }\n}\n","/**\n * Streaming transcriber factory, environment configuration, and the\n * record-then-send `Transcriber` wrapper.\n */\n\nimport { OpenAIRealtimeTranscriber } from '../adapters/openai-realtime.js';\nimport { VoxtralRealtimeTranscriber } from '../adapters/voxtral-realtime.js';\nimport { DEFAULT_MAX_AUDIO_BYTES, normalizeAudioInput } from './audio.js';\nimport {\n defaultEnv,\n mergeHeaderInits,\n parseHeadersEnv,\n parseOptionalInteger,\n readEnv,\n type SpeechEnv,\n} from './env.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n SpeechError,\n} from './errors.js';\nimport { unwrapRawAudio } from './pcm.js';\nimport type {\n GetStreamingTranscriberOptions,\n StreamingSessionOptions,\n StreamingTranscriber,\n StreamingTranscriberType,\n StreamingTurnDetection,\n} from './streaming-types.js';\nimport type {\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from './types.js';\n\n/** Streaming adapters `getStreamingTranscriber()` can build. */\nexport const STREAMING_TRANSCRIBER_TYPES: readonly StreamingTranscriberType[] =\n ['openai-realtime', 'voxtral-realtime'];\n\n/** Environment variable names read for each streaming setting, in priority order. */\nexport const STREAMING_TRANSCRIBER_ENV_KEYS = {\n type: ['HAVE_SPEECH_STREAMING_TYPE'],\n baseUrl: ['HAVE_SPEECH_STREAMING_BASE_URL'],\n model: ['HAVE_SPEECH_STREAMING_MODEL'],\n apiKey: ['HAVE_SPEECH_STREAMING_API_KEY'],\n language: ['HAVE_SPEECH_STREAMING_LANGUAGE'],\n turnDetection: ['HAVE_SPEECH_STREAMING_TURN_DETECTION'],\n timeoutMs: [\n 'HAVE_SPEECH_STREAMING_TIMEOUT',\n 'HAVE_SPEECH_STREAMING_TIMEOUT_MS',\n ],\n connectTimeoutMs: ['HAVE_SPEECH_STREAMING_CONNECT_TIMEOUT_MS'],\n headers: ['HAVE_SPEECH_STREAMING_HEADERS'],\n maxTurnSeconds: ['HAVE_SPEECH_STREAMING_MAX_TURN_SECONDS'],\n} as const;\n\nexport interface StreamingFactoryContext {\n env?: SpeechEnv;\n /** Headers merged under explicit `headers` (env → context → options). */\n headers?: HeadersInit;\n}\n\nexport function isStreamingTranscriberType(\n type: string | undefined,\n): type is StreamingTranscriberType {\n return (\n type !== undefined &&\n (STREAMING_TRANSCRIBER_TYPES as readonly string[]).includes(type)\n );\n}\n\n/**\n * Creates a realtime streaming transcriber. Explicit options win over\n * `HAVE_SPEECH_STREAMING_*` environment variables. The type defaults to\n * `openai-realtime`.\n */\nexport function getStreamingTranscriber(\n options: GetStreamingTranscriberOptions = {},\n context: StreamingFactoryContext = {},\n): StreamingTranscriber {\n const env = context.env ?? defaultEnv();\n const keys = STREAMING_TRANSCRIBER_ENV_KEYS;\n const type = options.type ?? readEnv(env, ...keys.type) ?? 'openai-realtime';\n\n if (!isStreamingTranscriberType(type)) {\n throw new InvalidSpeechAdapterError(type, 'streaming STT');\n }\n\n const headers = mergeHeaderInits(\n parseHeadersEnv(env, keys.headers),\n context.headers,\n options.headers,\n );\n const resolved = {\n ...options,\n type,\n baseUrl: (options.baseUrl ?? readEnv(env, ...keys.baseUrl))?.trim(),\n model: options.model ?? readEnv(env, ...keys.model),\n apiKey: options.apiKey ?? readEnv(env, ...keys.apiKey),\n language: options.language ?? readEnv(env, ...keys.language),\n turnDetection:\n options.turnDetection ??\n parseTurnDetection(readEnv(env, ...keys.turnDetection)),\n timeoutMs:\n options.timeoutMs ??\n parseOptionalInteger(readEnv(env, ...keys.timeoutMs)),\n connectTimeoutMs:\n options.connectTimeoutMs ??\n parseOptionalInteger(readEnv(env, ...keys.connectTimeoutMs)),\n maxTurnSeconds:\n options.maxTurnSeconds ??\n parseMaxTurnSeconds(readEnv(env, ...keys.maxTurnSeconds)),\n headers,\n };\n\n switch (type) {\n case 'openai-realtime':\n return new OpenAIRealtimeTranscriber({ ...resolved, type });\n case 'voxtral-realtime':\n return new VoxtralRealtimeTranscriber({ ...resolved, type });\n default:\n throw new InvalidSpeechAdapterError(type, 'streaming STT');\n }\n}\n\nexport function parseTurnDetection(\n value: string | undefined,\n): StreamingTurnDetection | undefined {\n if (!value) {\n return undefined;\n }\n const normalized = value.toLowerCase();\n if (normalized === 'server_vad' || normalized === 'semantic_vad') {\n return { type: normalized };\n }\n if (normalized === 'manual' || normalized === 'none') {\n return { type: 'manual' };\n }\n throw new SpeechConfigurationError(\n 'HAVE_SPEECH_STREAMING_TURN_DETECTION must be server_vad, semantic_vad, or manual',\n );\n}\n\n/**\n * Parses `HAVE_SPEECH_STREAMING_MAX_TURN_SECONDS`: a positive number of\n * seconds, or `Infinity` for no limit.\n */\nexport function parseMaxTurnSeconds(\n value: string | undefined,\n): number | undefined {\n if (!value) {\n return undefined;\n }\n const parsed = Number(value);\n if (Number.isNaN(parsed) || parsed <= 0) {\n throw new SpeechConfigurationError(\n 'HAVE_SPEECH_STREAMING_MAX_TURN_SECONDS must be a positive number of seconds or Infinity',\n );\n }\n return parsed;\n}\n\nexport interface StreamingTranscriberWrapperOptions {\n /** Bytes per `write()`. Default 64 KiB. */\n chunkBytes?: number;\n /** Upload limit in bytes. Default 25 MB; `Infinity` disables it. */\n maxBytes?: number;\n /**\n * Turn detection for wrapped sessions. Default `manual`: the whole clip is\n * one turn, committed by `end()`.\n */\n turnDetection?: StreamingTurnDetection;\n}\n\n/**\n * Adapts a streaming transcriber to the request/response `Transcriber`\n * interface for record-then-send callers. Each `transcribe()` opens one\n * session, streams the recording, and returns `session.end()`.\n *\n * Input must be raw audio: a PCM16/G.711 WAV file, `audio/pcm` or\n * `audio/L16` (optionally with `rate`/`channels` parameters), `audio/pcmu`,\n * `audio/pcma`, or untyped bytes in the adapter's default format. WAV headers\n * and MIME parameters set the session format; compressed recordings are\n * rejected, so use an HTTP transcriber for WebM/MP4/Ogg.\n *\n * A clip longer than the adapter's per-turn limit (`maxTurnSeconds`) is\n * never sent as one turn: with rollover (the default) the session splits it\n * into several turns at quiet boundaries, and with `rollover: false` the clip\n * is rejected with `SPEECH_TURN_TOO_LONG` before connecting.\n */\nexport function wrapStreamingTranscriber(\n streaming: StreamingTranscriber,\n options: StreamingTranscriberWrapperOptions = {},\n): Transcriber {\n const chunkBytes = options.chunkBytes ?? 64 * 1024;\n if (!Number.isInteger(chunkBytes) || chunkBytes <= 0) {\n throw new SpeechConfigurationError(\n 'chunkBytes must be a positive integer',\n streaming.type,\n );\n }\n\n return {\n type: streaming.type,\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes:\n request.maxBytes ?? options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES,\n adapter: streaming.type,\n signal: request.signal,\n deriveExtension: false,\n });\n const bytes = new Uint8Array(await audio.blob.arrayBuffer());\n const raw = unwrapRawAudio(bytes, audio.mimeType, streaming.type);\n const declared =\n request.audio &&\n typeof request.audio === 'object' &&\n 'data' in request.audio\n ? request.audio\n : undefined;\n const format = {\n ...raw.format,\n sampleRate: raw.format.sampleRate ?? declared?.sampleRate,\n channels: raw.format.channels ?? declared?.channels,\n };\n\n const sessionOptions: StreamingSessionOptions = {\n format: Object.fromEntries(\n Object.entries(format).filter(([, value]) => value !== undefined),\n ),\n language: request.language,\n model: request.model,\n prompt: request.prompt,\n headers: request.headers,\n signal: request.signal,\n onUsage: request.onUsage,\n metadata: request.metadata,\n turnDetection: options.turnDetection ?? { type: 'manual' },\n };\n const limit = streaming.turnLimit?.(sessionOptions);\n if (\n limit &&\n !limit.rollover &&\n raw.bytes.byteLength > limit.maxTurnBytes\n ) {\n throw new SpeechError(\n `The recording is longer than one turn may be (maxTurnSeconds ${limit.maxTurnSeconds} s) and rollover is disabled; enable rollover or split the recording`,\n 'SPEECH_TURN_TOO_LONG',\n streaming.type,\n );\n }\n\n const session = streaming.start(sessionOptions);\n\n try {\n for (\n let offset = 0;\n offset < raw.bytes.byteLength;\n offset += chunkBytes\n ) {\n await session.write(raw.bytes.subarray(offset, offset + chunkBytes));\n }\n } catch (error) {\n session.abort(error);\n throw error;\n }\n return session.end();\n },\n };\n}\n","import { OpenAICompatibleSpeechSynthesizer } from '../adapters/openai-compatible.js';\nimport { OpenAICompatibleTranscriber } from '../adapters/openai-compatible-transcriber.js';\nimport { Qwen3SpeechSynthesizer } from '../adapters/qwen3.js';\nimport {\n StudioServerSpeechSynthesizer,\n StudioServerTranscriber,\n} from '../adapters/studio-server.js';\nimport {\n defaultEnv,\n hasAnyEnv,\n hasTranscriberEnv,\n parseOptionalInteger,\n readEnv,\n resolveTranscriberConfig,\n type SpeechEnv,\n TRANSCRIBER_ENV_KEYS,\n} from './env.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n} from './errors.js';\nimport { compactJson } from './http.js';\nimport {\n getStreamingTranscriber,\n isStreamingTranscriberType,\n STREAMING_TRANSCRIBER_TYPES,\n wrapStreamingTranscriber,\n} from './streaming-factory.js';\nimport type {\n GetStreamingTranscriberOptions,\n StreamingTranscriberType,\n} from './streaming-types.js';\nimport {\n getOptionalTranscriberFactory,\n optionalTranscriberEntry,\n registeredOptionalTranscribers,\n} from './registry.js';\nimport type {\n GetSpeechOptions,\n GetSpeechSynthesizerOptions,\n GetTranscriberOptions,\n OpenAICompatibleSpeechSynthesizerOptions,\n OpenAICompatibleTranscriberOptions,\n Qwen3SpeechSynthesizerOptions,\n Speech,\n SpeechAdapterAvailability,\n SpeechFetch,\n SpeechSynthesizer,\n StudioServerSpeechSynthesizerOptions,\n StudioServerTranscriberOptions,\n SynthesisRequest,\n SynthesizedSpeech,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from './types.js';\n\nexport interface SpeechFactoryContext {\n env?: SpeechEnv;\n fetch?: SpeechFetch;\n headers?: HeadersInit;\n}\n\n/**\n * Creates a speech service from explicit options and/or environment variables.\n *\n * Explicit options win over environment defaults. If no STT or TTS config is\n * available, the returned service is still usable for dependency injection, but\n * calling the missing operation throws a configuration error.\n */\nexport async function getSpeech(\n options: GetSpeechOptions = {},\n context: SpeechFactoryContext = {},\n): Promise<Speech> {\n const transcriber =\n options.transcriber === false\n ? undefined\n : await getOptionalTranscriber(options.transcriber, context);\n const synthesizer =\n options.synthesizer === false\n ? undefined\n : await getOptionalSpeechSynthesizer(options.synthesizer, context);\n\n return {\n transcriber,\n synthesizer,\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n if (!transcriber) {\n throw new SpeechConfigurationError('No STT provider configured');\n }\n return transcriber.transcribe(request);\n },\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n if (!synthesizer) {\n throw new SpeechConfigurationError('No TTS provider configured');\n }\n return synthesizer.synthesize(request);\n },\n };\n}\n\nexport async function getTranscriber(\n options: GetTranscriberOptions = {},\n context: SpeechFactoryContext = {},\n): Promise<Transcriber> {\n const streamingType =\n options.type ??\n readEnv(context.env ?? defaultEnv(), ...TRANSCRIBER_ENV_KEYS.type);\n if (isStreamingTranscriberType(streamingType)) {\n return getWrappedStreamingTranscriber(streamingType, options, context);\n }\n\n const optional = await getRegisteredTranscriber(options, context);\n if (optional) {\n return optional;\n }\n\n const resolved = normalizeTranscriberOptions(options, context);\n\n switch (resolved.type) {\n case 'studio-server':\n return new StudioServerTranscriber(resolved);\n case 'openai-compatible':\n return new OpenAICompatibleTranscriber(resolved);\n default:\n throw new InvalidSpeechAdapterError(\n (resolved as { type?: string }).type ?? 'unknown',\n 'STT',\n );\n }\n}\n\nexport async function getSpeechSynthesizer(\n options?: GetSpeechSynthesizerOptions,\n context: SpeechFactoryContext = {},\n): Promise<SpeechSynthesizer> {\n const resolved = normalizeSpeechSynthesizerOptions(options, context);\n\n switch (resolved.type) {\n case 'studio-server':\n return new StudioServerSpeechSynthesizer(resolved);\n case 'qwen3-tts':\n return new Qwen3SpeechSynthesizer(resolved);\n case 'openai-compatible':\n return new OpenAICompatibleSpeechSynthesizer(resolved);\n default:\n throw new InvalidSpeechAdapterError(\n (resolved as { type?: string }).type ?? 'unknown',\n 'TTS',\n );\n }\n}\n\nexport function getAvailableSpeechAdapters(): SpeechAdapterAvailability {\n return {\n transcribers: [\n 'studio-server',\n 'openai-compatible',\n ...registeredOptionalTranscribers(),\n ...STREAMING_TRANSCRIBER_TYPES,\n ],\n streamingTranscribers: [...STREAMING_TRANSCRIBER_TYPES],\n synthesizers: ['studio-server', 'qwen3-tts', 'openai-compatible'],\n };\n}\n\n/**\n * Builds a streaming adapter from `getTranscriber()` options and wraps it as\n * a record-then-send `Transcriber`. Settings come from explicit options, then\n * `options.streaming`, then `HAVE_SPEECH_STREAMING_*`.\n */\nfunction getWrappedStreamingTranscriber(\n type: StreamingTranscriberType,\n options: GetTranscriberOptions,\n context: SpeechFactoryContext,\n): Transcriber {\n const streaming = getStreamingTranscriber(\n {\n ...options.streaming,\n ...(compactJson({\n type,\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n model: options.model,\n headers: options.headers,\n timeoutMs: options.timeoutMs,\n onUsage: options.onUsage,\n }) as GetStreamingTranscriberOptions),\n },\n { env: context.env, headers: context.headers },\n );\n return wrapStreamingTranscriber(streaming, { maxBytes: options.maxBytes });\n}\n\n/**\n * Builds opt-in adapters (e.g. `local`) registered by their subpath entry.\n * Returns `undefined` for built-in types.\n */\nasync function getRegisteredTranscriber(\n options: GetTranscriberOptions,\n context: SpeechFactoryContext,\n): Promise<Transcriber | undefined> {\n const type = resolveTranscriberConfig(options, context).type;\n const entry = type ? optionalTranscriberEntry(type) : undefined;\n if (!type || !entry) {\n return undefined;\n }\n\n const factory = getOptionalTranscriberFactory(type);\n if (!factory) {\n throw new SpeechConfigurationError(\n `The '${type}' transcriber is opt-in: import '${entry}' before calling getTranscriber()`,\n type,\n );\n }\n return factory({ ...options, type: type as Transcriber['type'] }, context);\n}\n\nasync function getOptionalTranscriber(\n options: GetTranscriberOptions | undefined,\n context: SpeechFactoryContext,\n): Promise<Transcriber | undefined> {\n if (!options && !hasTranscriberEnv(context.env ?? defaultEnv())) {\n return undefined;\n }\n\n return getTranscriber(options, context);\n}\n\nasync function getOptionalSpeechSynthesizer(\n options: GetSpeechSynthesizerOptions | undefined,\n context: SpeechFactoryContext,\n): Promise<SpeechSynthesizer | undefined> {\n if (!options && !hasSynthesizerEnv(context.env ?? defaultEnv())) {\n return undefined;\n }\n\n return getSpeechSynthesizer(options, context);\n}\n\nfunction normalizeTranscriberOptions(\n options: GetTranscriberOptions = {},\n context: SpeechFactoryContext,\n): StudioServerTranscriberOptions | OpenAICompatibleTranscriberOptions {\n const resolved = resolveTranscriberConfig(options, context);\n const type = resolved.type ?? 'studio-server';\n\n if (type !== 'studio-server' && type !== 'openai-compatible') {\n throw new InvalidSpeechAdapterError(type, 'STT');\n }\n\n if (!resolved.baseUrl) {\n throw new SpeechConfigurationError('STT baseUrl is required', type);\n }\n\n const shared = {\n baseUrl: resolved.baseUrl,\n fetch: resolved.fetch,\n headers: resolved.headers,\n apiKey: resolved.apiKey,\n timeoutMs: resolved.timeoutMs,\n };\n\n if (type === 'openai-compatible') {\n return {\n ...shared,\n type,\n model: resolved.model,\n responseFormat: options.responseFormat,\n maxBytes: resolved.maxBytes,\n retry: options.retry,\n onUsage: options.onUsage,\n };\n }\n\n return {\n ...shared,\n type,\n transcribePath: options.transcribePath ?? resolved.path,\n };\n}\n\nfunction normalizeSpeechSynthesizerOptions(\n options: GetSpeechSynthesizerOptions | undefined,\n context: SpeechFactoryContext,\n):\n | StudioServerSpeechSynthesizerOptions\n | Qwen3SpeechSynthesizerOptions\n | OpenAICompatibleSpeechSynthesizerOptions {\n const env = context.env ?? defaultEnv();\n const type =\n options?.type ??\n (readEnv(\n env,\n 'HAVE_SPEECH_TTS_TYPE',\n 'HAVE_SPEECH_TTS_ADAPTER',\n 'TTS_ADAPTER',\n ) as GetSpeechSynthesizerOptions['type'] | undefined);\n const baseUrl =\n options?.baseUrl ??\n readEnv(env, 'HAVE_SPEECH_TTS_BASE_URL', 'TTS_BASE_URL');\n\n if (!type) {\n throw new SpeechConfigurationError('TTS provider type is required');\n }\n\n if (\n type !== 'studio-server' &&\n type !== 'qwen3-tts' &&\n type !== 'openai-compatible'\n ) {\n throw new InvalidSpeechAdapterError(type, 'TTS');\n }\n\n if (!baseUrl?.trim()) {\n throw new SpeechConfigurationError('TTS baseUrl is required', type);\n }\n\n const shared = {\n type,\n baseUrl: baseUrl.trim(),\n fetch: options?.fetch ?? context.fetch,\n headers: options?.headers ?? context.headers,\n apiKey:\n options?.apiKey ??\n readEnv(env, 'HAVE_SPEECH_TTS_API_KEY', 'TTS_API_KEY', 'SPEECH_API_KEY'),\n timeoutMs:\n options?.timeoutMs ??\n parseOptionalInteger(\n readEnv(env, 'HAVE_SPEECH_TTS_TIMEOUT_MS', 'TTS_TIMEOUT_MS'),\n ),\n };\n\n if (type === 'studio-server') {\n return {\n ...shared,\n type,\n synthesizePath:\n options?.synthesizePath ??\n readEnv(env, 'HAVE_SPEECH_TTS_PATH', 'TTS_PATH'),\n defaultVoice:\n options?.defaultVoice ??\n readEnv(env, 'HAVE_SPEECH_TTS_VOICE', 'TTS_VOICE'),\n };\n }\n\n if (type === 'qwen3-tts') {\n return {\n ...shared,\n type,\n speechPath:\n options?.speechPath ?? readEnv(env, 'HAVE_SPEECH_TTS_PATH', 'TTS_PATH'),\n defaultModel:\n options?.defaultModel ??\n readEnv(env, 'HAVE_SPEECH_TTS_MODEL', 'TTS_MODEL'),\n defaultVoice:\n options?.defaultVoice ??\n readEnv(env, 'HAVE_SPEECH_TTS_VOICE', 'TTS_VOICE'),\n };\n }\n\n if (type === 'openai-compatible') {\n return {\n ...shared,\n type,\n speechPath:\n options?.speechPath ?? readEnv(env, 'HAVE_SPEECH_TTS_PATH', 'TTS_PATH'),\n defaultModel:\n options?.defaultModel ??\n readEnv(env, 'HAVE_SPEECH_TTS_MODEL', 'TTS_MODEL'),\n defaultVoice:\n options?.defaultVoice ??\n readEnv(env, 'HAVE_SPEECH_TTS_VOICE', 'TTS_VOICE'),\n };\n }\n\n throw new InvalidSpeechAdapterError(type, 'TTS');\n}\n\nfunction hasSynthesizerEnv(env: SpeechEnv): boolean {\n return hasAnyEnv(\n env,\n 'HAVE_SPEECH_TTS_TYPE',\n 'HAVE_SPEECH_TTS_ADAPTER',\n 'HAVE_SPEECH_TTS_BASE_URL',\n 'TTS_ADAPTER',\n 'TTS_BASE_URL',\n );\n}\n","/**\n * Server-side minting of short-lived browser credentials for realtime\n * transcription.\n *\n * A browser must never hold the long-lived provider key. Your server calls\n * {@link createStreamingClientSecret} once per tenant session (after its own\n * auth check), records the returned tenant/session attribution, and hands the\n * browser only `value` and `expiresAt`. The browser passes `value` as the\n * streaming adapter's `clientSecret`.\n *\n * - `openai-realtime`: `POST /v1/realtime/client_secrets` with\n * `expires_after: { anchor: 'created_at', seconds }` and a\n * `session.type: \"transcription\"` config, returning an `ek_…` secret\n * (https://developers.openai.com/api/reference/resources/realtime/subresources/client_secrets/methods/create).\n * - `voxtral-realtime`: vLLM has no ephemeral-token mechanism, so this throws\n * `SpeechConfigurationError`. Browser Voxtral needs your own proxy or token\n * gateway in front of vLLM.\n */\n\nimport {\n OPENAI_REALTIME_DEFAULT_MODEL,\n OPENAI_REALTIME_DEFAULT_URL,\n openAIRealtimeTranscriptionSession,\n resolveOpenAIRealtimeFormat,\n} from '../adapters/openai-realtime.js';\nimport {\n defaultEnv,\n mergeHeaderInits,\n parseHeadersEnv,\n parseOptionalInteger,\n readEnv,\n type SpeechEnv,\n} from './env.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n SpeechProviderError,\n} from './errors.js';\nimport {\n HttpSpeechAdapter,\n redactSecret,\n resolveOpenAICompatibleUrl,\n} from './http.js';\nimport { headerSecrets } from './realtime-auth.js';\nimport {\n isStreamingTranscriberType,\n parseTurnDetection,\n STREAMING_TRANSCRIBER_ENV_KEYS,\n} from './streaming-factory.js';\nimport type {\n OpenAIRealtimeTranscriberOptions,\n StreamingAudioFormat,\n StreamingTranscriberType,\n StreamingTurnDetection,\n} from './streaming-types.js';\nimport type { SpeechFetch } from './types.js';\nimport { isBrowserRuntime } from './websocket.js';\n\n/** Default client-secret lifetime: long enough to connect, short if leaked. */\nexport const DEFAULT_CLIENT_SECRET_TTL_SECONDS = 60;\n/** OpenAI's accepted `expires_after.seconds` range. */\nexport const CLIENT_SECRET_TTL_RANGE = { min: 10, max: 7200 } as const;\n\nexport interface CreateStreamingClientSecretOptions {\n /** Streaming adapter. Default `HAVE_SPEECH_STREAMING_TYPE`, then `openai-realtime`. */\n type?: StreamingTranscriberType;\n /** Long-lived server key. Default `HAVE_SPEECH_STREAMING_API_KEY`. */\n apiKey?: string;\n /**\n * API base. Accepts the same values as the streaming adapter\n * (`https://api.openai.com`, `…/v1`, or `wss://…/v1/realtime`). Default\n * `HAVE_SPEECH_STREAMING_BASE_URL`, then `https://api.openai.com/v1`.\n */\n baseUrl?: string;\n /** Secret lifetime in seconds, 10–7200. Default 60. */\n ttlSeconds?: number;\n /**\n * Tenant the secret is minted for. Echoed in the result for your usage\n * ledger; OpenAI's transcription client secrets carry no attribution field.\n */\n tenantId?: string;\n /** Your session id (e.g. the dictation session), echoed in the result. */\n sessionId?: string;\n /** Extra attribution echoed in the result. Never sent to the provider. */\n metadata?: Record<string, unknown>;\n /** Transcription model the secret is bound to. Default `HAVE_SPEECH_STREAMING_MODEL`, then `gpt-4o-transcribe`. */\n model?: string;\n language?: string;\n prompt?: string;\n /** Default `HAVE_SPEECH_STREAMING_TURN_DETECTION`, then `server_vad`. */\n turnDetection?: StreamingTurnDetection;\n /** Default PCM16 24 kHz mono. */\n format?: Partial<StreamingAudioFormat>;\n noiseReduction?: OpenAIRealtimeTranscriberOptions['noiseReduction'];\n include?: string[];\n /**\n * Extra `transcription` fields. The typed `model`, `language`, and `prompt`\n * take precedence, so the secret is bound to the model the result reports.\n */\n transcriptionOptions?: Record<string, unknown>;\n /**\n * Headers for the mint request, e.g. a per-tenant gateway virtual key\n * (`x-bf-vk`) for provider-side attribution. Merged over\n * `HAVE_SPEECH_STREAMING_HEADERS`.\n */\n headers?: HeadersInit;\n fetch?: SpeechFetch;\n signal?: AbortSignal;\n /** Request timeout. Default `HAVE_SPEECH_STREAMING_TIMEOUT[_MS]`, then 10000. */\n timeoutMs?: number;\n}\n\n/** A minted browser credential plus the attribution it was minted for. */\nexport interface StreamingClientSecretResult {\n type: StreamingTranscriberType;\n /** The short-lived secret. Send it to the browser as `clientSecret`. */\n value: string;\n /** Expiry as epoch milliseconds. */\n expiresAt: number;\n ttlSeconds: number;\n model: string;\n tenantId?: string;\n sessionId?: string;\n metadata?: Record<string, unknown>;\n /** The provider's echoed session config. */\n session?: Record<string, unknown>;\n}\n\nexport interface StreamingClientSecretContext {\n env?: SpeechEnv;\n}\n\nconst OPENAI_CLIENT_SECRET_ENDPOINT = 'realtime/client_secrets';\n\n/**\n * Mints a short-lived, session-scoped client secret for browser streaming.\n * Server-side only: throws in a browser runtime, and for adapters without an\n * ephemeral-token mechanism (`voxtral-realtime`).\n */\nexport async function createStreamingClientSecret(\n options: CreateStreamingClientSecretOptions = {},\n context: StreamingClientSecretContext = {},\n): Promise<StreamingClientSecretResult> {\n const env = context.env ?? defaultEnv();\n const keys = STREAMING_TRANSCRIBER_ENV_KEYS;\n const type = options.type ?? readEnv(env, ...keys.type) ?? 'openai-realtime';\n if (!isStreamingTranscriberType(type)) {\n throw new InvalidSpeechAdapterError(type, 'streaming STT');\n }\n\n if (isBrowserRuntime()) {\n throw new SpeechConfigurationError(\n 'createStreamingClientSecret runs on your server: it needs the long-lived API key, which must never reach a browser',\n type,\n );\n }\n\n if (type === 'voxtral-realtime') {\n throw new SpeechConfigurationError(\n 'voxtral-realtime cannot mint browser credentials: vLLM has no ephemeral-token mechanism. Put your own proxy or token gateway in front of vLLM, mint its short-lived tokens yourself, and never send the vLLM API key to a browser',\n type,\n );\n }\n\n const ttlSeconds = options.ttlSeconds ?? DEFAULT_CLIENT_SECRET_TTL_SECONDS;\n if (\n !Number.isInteger(ttlSeconds) ||\n ttlSeconds < CLIENT_SECRET_TTL_RANGE.min ||\n ttlSeconds > CLIENT_SECRET_TTL_RANGE.max\n ) {\n throw new SpeechConfigurationError(\n `ttlSeconds must be an integer from ${CLIENT_SECRET_TTL_RANGE.min} to ${CLIENT_SECRET_TTL_RANGE.max}`,\n type,\n );\n }\n\n const apiKey = options.apiKey ?? readEnv(env, ...keys.apiKey);\n if (!apiKey) {\n throw new SpeechConfigurationError(\n 'createStreamingClientSecret requires apiKey (or HAVE_SPEECH_STREAMING_API_KEY)',\n type,\n );\n }\n\n const model =\n options.model?.trim() ||\n readEnv(env, ...keys.model)?.trim() ||\n OPENAI_REALTIME_DEFAULT_MODEL;\n const session = openAIRealtimeTranscriptionSession(\n {\n model,\n format: resolveOpenAIRealtimeFormat(options.format),\n turnDetection: options.turnDetection ??\n parseTurnDetection(readEnv(env, ...keys.turnDetection)) ?? {\n type: 'server_vad',\n },\n language: options.language ?? readEnv(env, ...keys.language),\n prompt: options.prompt,\n },\n options,\n );\n\n const endpoint = resolveClientSecretUrl(\n options.baseUrl?.trim() ||\n readEnv(env, ...keys.baseUrl)?.trim() ||\n OPENAI_REALTIME_DEFAULT_URL,\n );\n const headers = mergeHeaderInits(\n parseHeadersEnv(env, keys.headers),\n options.headers,\n );\n const client = new ClientSecretClient({\n baseUrl: endpoint,\n apiKey,\n fetch: options.fetch,\n headers,\n timeoutMs:\n options.timeoutMs ??\n parseOptionalInteger(readEnv(env, ...keys.timeoutMs)) ??\n 10_000,\n });\n\n const body = await client\n .mint(\n endpoint,\n {\n expires_after: { anchor: 'created_at', seconds: ttlSeconds },\n session,\n },\n options.signal,\n )\n .catch((error: unknown) => {\n throw redactProviderError(error, headerSecrets(headers));\n });\n\n const value = body.value;\n if (typeof value !== 'string' || !value) {\n throw new SpeechProviderError(\n type,\n 'openai-realtime client secret response did not include a value',\n );\n }\n const expiresAtSeconds = body.expires_at;\n\n return {\n type,\n value,\n expiresAt:\n typeof expiresAtSeconds === 'number' && Number.isFinite(expiresAtSeconds)\n ? expiresAtSeconds * 1000\n : Date.now() + ttlSeconds * 1000,\n ttlSeconds,\n model,\n tenantId: options.tenantId,\n sessionId: options.sessionId,\n metadata: options.metadata,\n session:\n body.session && typeof body.session === 'object'\n ? (body.session as Record<string, unknown>)\n : undefined,\n };\n}\n\n/**\n * Resolves the client-secret endpoint from any accepted realtime base:\n * `ws(s)` becomes `http(s)`, a trailing `/realtime` and query are dropped,\n * then `/v1/realtime/client_secrets` (or `/realtime/client_secrets` under a\n * versioned root) is appended.\n */\nexport function resolveClientSecretUrl(baseUrl: string): string {\n const url = new URL(baseUrl);\n if (url.protocol === 'ws:') {\n url.protocol = 'http:';\n } else if (url.protocol === 'wss:') {\n url.protocol = 'https:';\n }\n url.search = '';\n url.hash = '';\n url.pathname = url.pathname.replace(/\\/+$/, '').replace(/\\/realtime$/, '');\n return resolveOpenAICompatibleUrl(\n url.toString(),\n OPENAI_CLIENT_SECRET_ENDPOINT,\n );\n}\n\n/**\n * The HTTP layer redacts the API key from provider error bodies; this also\n * removes gateway credentials sent in `headers` (e.g. `x-bf-vk`).\n */\nfunction redactProviderError(error: unknown, secrets: string[]): unknown {\n if (!(error instanceof SpeechProviderError) || secrets.length === 0) {\n return error;\n }\n let responseBody = error.responseBody;\n let message = error.message;\n for (const secret of secrets) {\n responseBody = redactSecret(responseBody, secret);\n message = redactSecret(message, secret) ?? message;\n }\n if (responseBody === error.responseBody && message === error.message) {\n return error;\n }\n return new SpeechProviderError(error.adapter ?? 'openai-realtime', message, {\n status: error.status,\n responseBody,\n retryAfterMs: error.retryAfterMs,\n });\n}\n\nclass ClientSecretClient extends HttpSpeechAdapter {\n mint(\n endpoint: string,\n payload: Record<string, unknown>,\n signal: AbortSignal | undefined,\n ): Promise<Record<string, unknown>> {\n return this.post(\n 'openai-realtime',\n endpoint,\n {\n body: JSON.stringify(payload),\n headers: { 'content-type': 'application/json' },\n signal,\n },\n async (response) => {\n const json = (await response.json().catch(() => undefined)) as unknown;\n if (!json || typeof json !== 'object' || Array.isArray(json)) {\n throw new SpeechProviderError(\n 'openai-realtime',\n 'openai-realtime client secret response was not a JSON object',\n );\n }\n return json as Record<string, unknown>;\n },\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/shared/retry.ts","../src/shared/http.ts","../src/shared/realtime-auth.ts","../src/shared/pcm.ts","../src/shared/turn-limit.ts","../src/shared/realtime-session.ts","../src/adapters/openai-realtime.ts","../src/adapters/voxtral-realtime.ts","../src/adapters/openai-compatible.ts","../src/adapters/openai-compatible-transcriber.ts","../src/adapters/qwen3.ts","../src/adapters/studio-server.ts","../src/shared/streaming-factory.ts","../src/shared/factory.ts","../src/shared/input-format.ts","../src/shared/streaming-client-secret.ts"],"sourcesContent":["/**\n * Retry with exponential backoff for speech provider calls. Retries only\n * rate-limit (429) and server (5xx) responses, honours `Retry-After`, and\n * stops immediately when the caller aborts.\n */\n\nimport { SpeechConfigurationError, SpeechProviderError } from './errors.js';\n\nexport interface SpeechRetryOptions {\n /** Retries after the first attempt. Default `2`. `0` disables retries. */\n maxRetries?: number;\n /** First backoff delay in milliseconds. Default `500`. */\n initialDelayMs?: number;\n /**\n * Upper bound for any single wait in milliseconds. Default `30000`. A\n * `Retry-After` longer than this ends retrying rather than retrying early.\n */\n maxDelayMs?: number;\n}\n\nexport const DEFAULT_SPEECH_RETRY: Required<SpeechRetryOptions> = {\n maxRetries: 2,\n initialDelayMs: 500,\n maxDelayMs: 30_000,\n};\n\nexport function resolveRetryOptions(\n retry: SpeechRetryOptions | false | undefined,\n defaults: Required<SpeechRetryOptions> | false = DEFAULT_SPEECH_RETRY,\n): Required<SpeechRetryOptions> | false {\n if (retry === false) {\n return false;\n }\n\n if (retry === undefined) {\n return defaults;\n }\n\n const base = defaults || DEFAULT_SPEECH_RETRY;\n return {\n maxRetries: Math.floor(\n finiteRetryValue('maxRetries', retry.maxRetries, base.maxRetries),\n ),\n initialDelayMs: finiteRetryValue(\n 'initialDelayMs',\n retry.initialDelayMs,\n base.initialDelayMs,\n ),\n maxDelayMs: finiteRetryValue(\n 'maxDelayMs',\n retry.maxDelayMs,\n base.maxDelayMs,\n ),\n };\n}\n\n/**\n * Rejects non-finite values (`NaN`, `Infinity`): they would make the retry\n * budget or delay cap unbounded. Negative values clamp to `0`.\n */\nfunction finiteRetryValue(\n name: keyof SpeechRetryOptions,\n value: number | undefined,\n fallback: number,\n): number {\n if (value === undefined) {\n return Math.max(0, fallback);\n }\n\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new SpeechConfigurationError(\n `Speech retry option ${name} must be a finite number`,\n );\n }\n\n return Math.max(0, value);\n}\n\nexport function isRetryableStatus(status: number | undefined): boolean {\n return status === 429 || (status !== undefined && status >= 500);\n}\n\n/**\n * Parses a `Retry-After` header (delta-seconds or HTTP-date) into\n * milliseconds. Returns `undefined` when absent or unparseable.\n */\nexport function parseRetryAfter(\n value: string | null | undefined,\n now: number = Date.now(),\n): number | undefined {\n if (!value?.trim()) {\n return undefined;\n }\n\n const trimmed = value.trim();\n if (/^\\d+(\\.\\d+)?$/.test(trimmed)) {\n return Math.round(Number(trimmed) * 1000);\n }\n\n const date = Date.parse(trimmed);\n return Number.isNaN(date) ? undefined : Math.max(0, date - now);\n}\n\n/**\n * Runs `attempt` until it succeeds, throws a non-retryable error, or the\n * retry budget is exhausted. Retryable errors are {@link SpeechProviderError}\n * instances whose `status` is 429 or 5xx.\n */\nexport async function withSpeechRetry<T>(\n attempt: (attemptNumber: number) => Promise<T>,\n retry: Required<SpeechRetryOptions> | false,\n signal?: AbortSignal,\n): Promise<T> {\n const maxRetries = retry ? retry.maxRetries : 0;\n\n for (let attemptNumber = 0; ; attemptNumber += 1) {\n try {\n return await attempt(attemptNumber);\n } catch (error) {\n if (\n !retry ||\n attemptNumber >= maxRetries ||\n signal?.aborted ||\n !(error instanceof SpeechProviderError) ||\n !isRetryableStatus(error.status)\n ) {\n throw error;\n }\n\n const backoff = Math.min(\n retry.maxDelayMs,\n retry.initialDelayMs * 2 ** attemptNumber,\n );\n const retryAfterMs = error.retryAfterMs;\n if (retryAfterMs !== undefined && retryAfterMs > retry.maxDelayMs) {\n throw error;\n }\n\n await sleep(Math.max(backoff, retryAfterMs ?? 0), signal);\n }\n }\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","import { SpeechConfigurationError, SpeechProviderError } from './errors.js';\nimport {\n parseRetryAfter,\n type SpeechRetryOptions,\n withSpeechRetry,\n} from './retry.js';\nimport type {\n HttpSpeechOptions,\n SpeechFetch,\n SynthesisRequest,\n SynthesizedSpeech,\n TranscriptResult,\n TranscriptSegment,\n WordTiming,\n} from './types.js';\n\n/**\n * Parses a base URL, dropping any fragment and keeping any query string.\n * Throws {@link SpeechConfigurationError} for a blank or unparseable value.\n * The value is not echoed in the error because it may carry credentials.\n */\nfunction parseBaseUrl(baseUrl: string): URL {\n const trimmed = baseUrl.trim();\n if (!trimmed) {\n throw new SpeechConfigurationError('Speech adapter baseUrl is required');\n }\n\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n throw new SpeechConfigurationError(\n 'Speech adapter baseUrl is not a valid absolute URL',\n );\n }\n\n url.hash = '';\n return url;\n}\n\n/**\n * Returns `baseUrl` with a trailing `/` on its path so relative paths resolve\n * beneath it. The query string is preserved and any fragment is dropped.\n */\nexport function normalizeBaseUrl(baseUrl: string): string {\n const url = parseBaseUrl(baseUrl);\n if (!url.pathname.endsWith('/')) {\n url.pathname = `${url.pathname}/`;\n }\n\n return url.toString();\n}\n\n/**\n * Resolves `path` against `baseUrl`. A relative path without its own query\n * inherits the base URL's query (for example a gateway `api-version`); an\n * absolute URL is returned unchanged.\n */\nexport function resolveSpeechUrl(baseUrl: string, path: string): string {\n const base = new URL(normalizeBaseUrl(baseUrl));\n const isAbsolute = /^[a-z][a-z\\d+.-]*:/i.test(path);\n const resolved = new URL(isAbsolute ? path : path.replace(/^\\//, ''), base);\n if (!isAbsolute && !resolved.search && base.search) {\n resolved.search = base.search;\n }\n\n return resolved.toString();\n}\n\n/**\n * Resolves an OpenAI-compatible endpoint from a base URL. Accepts a server\n * root (`https://api.openai.com` → `/v1/<resource>`), an API root ending in a\n * version segment (`http://gateway/stt/v1` → `/<resource>`), or the full\n * endpoint URL (used as-is). Only the path is rewritten: the query string is\n * preserved and any fragment is dropped.\n */\nexport function resolveOpenAICompatibleUrl(\n baseUrl: string,\n resource: string,\n): string {\n const trimmedResource = resource.replace(/^\\/+|\\/+$/g, '');\n const url = parseBaseUrl(baseUrl);\n const pathname = url.pathname.replace(/\\/+$/, '');\n\n if (pathname.endsWith(`/${trimmedResource}`)) {\n url.pathname = pathname;\n } else if (/\\/v\\d+$/.test(pathname)) {\n url.pathname = `${pathname}/${trimmedResource}`;\n } else {\n url.pathname = `${pathname}/v1/${trimmedResource}`;\n }\n\n return url.toString();\n}\n\nexport function resolveFetch(fetchOverride?: SpeechFetch): SpeechFetch {\n if (fetchOverride) {\n return fetchOverride;\n }\n\n const globalFetch = globalThis.fetch;\n if (!globalFetch) {\n throw new SpeechConfigurationError(\n 'No fetch implementation available for speech adapter',\n );\n }\n\n return globalFetch.bind(globalThis);\n}\n\nexport function mergeHeaders(\n options: Pick<HttpSpeechOptions, 'apiKey' | 'headers'>,\n extra?: HeadersInit,\n): Headers {\n const headers = new Headers(options.headers);\n\n if (options.apiKey && !headers.has('authorization')) {\n headers.set('authorization', `Bearer ${options.apiKey}`);\n }\n\n if (extra) {\n new Headers(extra).forEach((value, key) => {\n headers.set(key, value);\n });\n }\n\n return headers;\n}\n\nexport abstract class HttpSpeechAdapter {\n protected readonly baseUrl: string;\n protected readonly fetchImpl: SpeechFetch;\n protected readonly apiKey?: string;\n protected readonly headers?: HeadersInit;\n protected readonly timeoutMs?: number;\n\n constructor(options: HttpSpeechOptions) {\n this.baseUrl = options.baseUrl;\n this.fetchImpl = resolveFetch(options.fetch);\n this.apiKey = options.apiKey;\n this.headers = options.headers;\n this.timeoutMs = options.timeoutMs;\n }\n\n /**\n * POSTs to `path` (relative to `baseUrl`, or an absolute URL). The optional\n * `retry` policy retries 429/5xx responses; `timeoutMs` applies per attempt.\n * The request body must be re-sendable (string, Blob, FormData) when\n * retries are enabled.\n */\n protected async post<T>(\n adapterName: string,\n path: string,\n init: Omit<RequestInit, 'method'>,\n readResponse: (response: Response) => Promise<T>,\n retry: Required<SpeechRetryOptions> | false = false,\n ): Promise<T> {\n return withSpeechRetry(\n () => this.postOnce(adapterName, path, init, readResponse),\n retry,\n init.signal ?? undefined,\n );\n }\n\n private async postOnce<T>(\n adapterName: string,\n path: string,\n init: Omit<RequestInit, 'method'>,\n readResponse: (response: Response) => Promise<T>,\n ): Promise<T> {\n const timeoutController =\n this.timeoutMs && this.timeoutMs > 0 ? new AbortController() : undefined;\n const timeout =\n timeoutController && this.timeoutMs\n ? setTimeout(() => timeoutController.abort(), this.timeoutMs)\n : undefined;\n const requestSignal = composeAbortSignals(\n init.signal,\n timeoutController?.signal,\n );\n\n try {\n // Fail fast when the caller aborted before the request was sent.\n requestSignal.signal?.throwIfAborted();\n const response = await this.fetchImpl(\n resolveSpeechUrl(this.baseUrl, path),\n {\n ...init,\n method: 'POST',\n headers: mergeHeaders(\n { apiKey: this.apiKey, headers: this.headers },\n init.headers,\n ),\n signal: requestSignal.signal,\n },\n );\n await assertOk(response, adapterName, this.apiKey);\n return await readResponse(response);\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n requestSignal.cleanup();\n }\n }\n}\n\nfunction composeAbortSignals(\n ...signals: Array<AbortSignal | undefined | null>\n): { signal?: AbortSignal; cleanup: () => void } {\n const activeSignals = signals.filter((signal): signal is AbortSignal =>\n Boolean(signal),\n );\n\n if (activeSignals.length === 0) {\n return { cleanup: () => undefined };\n }\n\n if (activeSignals.length === 1) {\n return { signal: activeSignals[0], cleanup: () => undefined };\n }\n\n const controller = new AbortController();\n let cleanup = () => undefined;\n\n const abortFrom = (signal: AbortSignal) => {\n if (!controller.signal.aborted) {\n controller.abort(signal.reason);\n }\n cleanup();\n };\n\n const onAbort = (event: Event) => {\n abortFrom(event.target as AbortSignal);\n };\n\n cleanup = () => {\n for (const signal of activeSignals) {\n signal.removeEventListener('abort', onAbort);\n }\n };\n\n for (const signal of activeSignals) {\n if (signal.aborted) {\n abortFrom(signal);\n return { signal: controller.signal, cleanup: () => undefined };\n }\n }\n\n for (const signal of activeSignals) {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n return { signal: controller.signal, cleanup };\n}\n\nasync function assertOk(\n response: Response,\n adapterName: string,\n apiKey?: string,\n): Promise<void> {\n if (response.ok) {\n return;\n }\n\n const responseBody = await response.text().catch(() => undefined);\n throw new SpeechProviderError(\n adapterName,\n `${adapterName} speech request failed with HTTP ${response.status}`,\n {\n status: response.status,\n responseBody: redactSecret(responseBody, apiKey),\n retryAfterMs: parseRetryAfter(response.headers.get('retry-after')),\n },\n );\n}\n\n/** Removes an API key from provider-supplied text before it is surfaced. */\nexport function redactSecret(\n text: string | undefined,\n secret: string | undefined,\n): string | undefined {\n if (!text || !secret) {\n return text;\n }\n\n return text.split(secret).join('[REDACTED]');\n}\n\nexport function appendOptionalFormValue(\n form: FormData,\n name: string,\n value: unknown,\n): void {\n if (value === undefined || value === null) {\n return;\n }\n\n form.append(name, String(value));\n}\n\nexport function createHappyVerticalSynthesisForm(\n request: SynthesisRequest,\n defaultVoice?: string,\n): FormData {\n const form = new FormData();\n const voice =\n request.voice && typeof request.voice !== 'string'\n ? request.voice\n : undefined;\n form.set('text', request.text);\n appendOptionalFormValue(\n form,\n 'language',\n request.language ?? voice?.language,\n );\n appendOptionalFormValue(\n form,\n 'speaker',\n voiceToString(request.voice, defaultVoice),\n );\n appendOptionalFormValue(form, 'speed', request.speed);\n\n appendOptionalFormValue(form, 'voice_prompt', voice?.prompt);\n\n return form;\n}\n\nexport function voiceToString(\n voice:\n | string\n | { id?: string; name?: string; speakerId?: string }\n | undefined,\n fallback?: string,\n): string | undefined {\n if (!voice) {\n return fallback;\n }\n\n if (typeof voice === 'string') {\n return voice;\n }\n\n return voice.id ?? voice.name ?? voice.speakerId ?? fallback;\n}\n\nexport function compactJson(\n value: Record<string, unknown>,\n): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(value).filter(([, entryValue]) => entryValue !== undefined),\n );\n}\n\nexport function contentTypeToFormat(contentType: string): string | undefined {\n const match = /^audio\\/([^;]+)/.exec(contentType);\n return match?.[1];\n}\n\nexport function arrayBufferFromBytes(bytes: Uint8Array): ArrayBuffer {\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n return copy.buffer;\n}\n\nexport function decodeBase64(base64: string): ArrayBuffer {\n if (typeof Buffer !== 'undefined') {\n return arrayBufferFromBytes(Buffer.from(base64, 'base64'));\n }\n\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes.buffer;\n}\n\nfunction getNumber(\n value: Record<string, unknown>,\n ...keys: string[]\n): number | undefined {\n for (const key of keys) {\n const candidate = value[key];\n if (typeof candidate === 'number') {\n return candidate;\n }\n }\n\n return undefined;\n}\n\nfunction getString(\n value: Record<string, unknown>,\n ...keys: string[]\n): string | undefined {\n for (const key of keys) {\n const candidate = value[key];\n if (typeof candidate === 'string') {\n return candidate;\n }\n }\n\n return undefined;\n}\n\nexport function normalizeWordTimings(value: unknown): WordTiming[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n return value.flatMap((entry): WordTiming[] => {\n if (!entry || typeof entry !== 'object') {\n return [];\n }\n\n const record = entry as Record<string, unknown>;\n const word = getString(record, 'word', 'text', 'token');\n const startSeconds = getNumber(\n record,\n 'startSeconds',\n 'start',\n 'start_time',\n );\n const endSeconds = getNumber(record, 'endSeconds', 'end', 'end_time');\n\n if (!word || startSeconds === undefined || endSeconds === undefined) {\n return [];\n }\n\n return [\n {\n word,\n startSeconds,\n endSeconds,\n confidence: getNumber(record, 'confidence'),\n speakerId: getString(record, 'speakerId', 'speaker', 'speaker_id'),\n },\n ];\n });\n}\n\nexport function normalizeTranscriptSegments(\n value: unknown,\n): TranscriptSegment[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n return value.flatMap((entry): TranscriptSegment[] => {\n if (!entry || typeof entry !== 'object') {\n return [];\n }\n\n const record = entry as Record<string, unknown>;\n const text = getString(record, 'text', 'transcript');\n if (!text) {\n return [];\n }\n\n return [\n {\n text,\n startSeconds: getNumber(record, 'startSeconds', 'start', 'start_time'),\n endSeconds: getNumber(record, 'endSeconds', 'end', 'end_time'),\n confidence: getNumber(record, 'confidence'),\n speakerId: getString(record, 'speakerId', 'speaker', 'speaker_id'),\n },\n ];\n });\n}\n\nexport async function readTranscriptResponse(\n response: Response,\n adapterName: string,\n): Promise<TranscriptResult> {\n const contentType = response.headers.get('content-type') ?? '';\n\n if (!contentType.includes('application/json')) {\n const text = await response.text();\n return {\n text,\n provider: adapterName,\n };\n }\n\n const json = (await response.json()) as Record<string, unknown>;\n const text = getString(json, 'text', 'transcript', 'transcription') ?? '';\n\n return {\n text,\n language: getString(json, 'language', 'lang'),\n durationSeconds: getNumber(json, 'durationSeconds', 'duration'),\n words: normalizeWordTimings(\n json.words ?? json.wordTimings ?? json.word_timings,\n ),\n segments: normalizeTranscriptSegments(json.segments),\n provider: getString(json, 'provider') ?? adapterName,\n model: getString(json, 'model'),\n raw: json,\n };\n}\n\nexport async function readSynthesizedSpeechResponse(\n response: Response,\n adapterName: string,\n): Promise<SynthesizedSpeech> {\n const responseContentType =\n response.headers.get('content-type') ?? 'application/octet-stream';\n\n if (!responseContentType.includes('application/json')) {\n const audio = await response.arrayBuffer();\n const sampleRate = parseHeaderNumber(response, 'x-sample-rate');\n return {\n audio,\n contentType: responseContentType,\n format: contentTypeToFormat(responseContentType),\n sampleRate,\n provider: adapterName,\n };\n }\n\n const json = (await response.json()) as Record<string, unknown>;\n const contentType =\n getString(json, 'contentType', 'content_type', 'mimeType', 'mime_type') ??\n 'application/octet-stream';\n const encodedAudio = getString(\n json,\n 'audio',\n 'audioContent',\n 'audio_content',\n );\n\n if (!encodedAudio) {\n throw new SpeechProviderError(\n adapterName,\n `${adapterName} JSON speech response did not include base64 audio`,\n );\n }\n\n return {\n audio: decodeBase64(encodedAudio),\n contentType,\n format:\n getString(json, 'format', 'response_format') ??\n contentTypeToFormat(contentType),\n sampleRate: getNumber(json, 'sampleRate', 'sample_rate'),\n channels: getNumber(json, 'channels'),\n durationSeconds: getNumber(json, 'durationSeconds', 'duration'),\n words: normalizeWordTimings(\n json.words ?? json.wordTimings ?? json.word_timings,\n ),\n provider: getString(json, 'provider') ?? adapterName,\n model: getString(json, 'model'),\n raw: json,\n };\n}\n\nfunction parseHeaderNumber(\n response: Response,\n headerName: string,\n): number | undefined {\n const value = response.headers.get(headerName);\n if (!value) {\n return undefined;\n }\n\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n","/**\n * Credential handling shared by realtime (WebSocket) adapters.\n *\n * Node callers authenticate with a long-lived `apiKey` in the\n * `Authorization` header. Browsers cannot set WebSocket headers and must never\n * hold a long-lived key, so they pass a short-lived `clientSecret`, which is\n * sent as a WebSocket subprotocol.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\nimport type { RealtimeConnection } from './realtime-session.js';\nimport type { StreamingClientSecret } from './streaming-types.js';\nimport { isBrowserRuntime } from './websocket.js';\n\n/** Subprotocol prefix that carries a client secret (OpenAI's convention). */\nexport const CLIENT_SECRET_SUBPROTOCOL_PREFIX = 'openai-insecure-api-key.';\n\n/**\n * Long-lived OpenAI-style keys (`sk-…`, `sk-proj-…`, `sk-svcacct-…`). A\n * browser `clientSecret` that looks like one is refused: browsers accept only\n * short-lived tokens such as OpenAI's `ek_…` client secrets.\n */\nconst LONG_LIVED_KEY = /^sk-/;\n\n/** Throws when an adapter is given a long-lived `apiKey` in a browser. */\nexport function assertNoBrowserApiKey(\n adapter: string,\n apiKey: string | undefined,\n): void {\n if (apiKey && isBrowserRuntime()) {\n throw new SpeechConfigurationError(\n `${adapter} refuses apiKey in a browser; mint a short-lived clientSecret on your server instead`,\n adapter,\n );\n }\n}\n\n/** Resolves a static or per-session client secret and validates its shape. */\nexport async function resolveClientSecret(\n adapter: string,\n secret: StreamingClientSecret | undefined,\n): Promise<string | undefined> {\n const value = typeof secret === 'function' ? await secret() : secret;\n const trimmed = value?.trim();\n if (trimmed === undefined) {\n return undefined;\n }\n if (!/^[\\x21-\\x7e]+$/.test(trimmed)) {\n throw new SpeechConfigurationError(\n 'clientSecret must be a non-empty token without spaces',\n adapter,\n );\n }\n if (isBrowserRuntime() && LONG_LIVED_KEY.test(trimmed)) {\n throw new SpeechConfigurationError(\n 'clientSecret looks like a long-lived API key; browsers accept only short-lived tokens minted by your server (see createStreamingClientSecret)',\n adapter,\n );\n }\n return trimmed;\n}\n\nexport interface RealtimeConnectorOptions {\n adapter: string;\n url: string;\n apiKey?: string;\n headers: Record<string, string>;\n clientSecret?: StreamingClientSecret;\n /** Subprotocols to offer, given the resolved client secret (if any). */\n protocols: (secret: string | undefined) => string[];\n}\n\n/**\n * Builds the per-session `connect` callback: refuses handshake headers in a\n * browser, resolves the client secret, and sets `Authorization` from\n * `apiKey` unless a header already supplies it.\n */\nexport function realtimeConnector(\n options: RealtimeConnectorOptions,\n): () => Promise<RealtimeConnection> {\n if (isBrowserRuntime() && Object.keys(options.headers).length > 0) {\n throw new SpeechConfigurationError(\n 'Browsers cannot send WebSocket headers; remove `headers` or connect from a server',\n options.adapter,\n );\n }\n\n return async () => {\n const secret = await resolveClientSecret(\n options.adapter,\n options.clientSecret,\n );\n const headers = { ...options.headers };\n // Gateway credentials (e.g. `x-bf-vk`, an explicit Authorization) are\n // redacted from surfaced provider text just like the API key.\n const secrets: string[] = headerSecrets(headers);\n if (secret) {\n secrets.push(secret);\n }\n if (options.apiKey) {\n secrets.push(options.apiKey);\n if (!hasHeader(headers, 'authorization')) {\n headers.authorization = `Bearer ${options.apiKey}`;\n }\n }\n return {\n url: options.url,\n protocols: options.protocols(secret),\n headers,\n secrets,\n };\n };\n}\n\n/** Values of other headers shorter than this are not treated as credentials. */\nconst MIN_HEADER_SECRET_LENGTH = 8;\n\n/**\n * Header names that carry credentials (`authorization`, `x-api-key`,\n * `x-bf-vk`, `*-token`, `*-secret`, …). Their values are redacted at any\n * length.\n */\nconst CREDENTIAL_HEADER = /auth|key|token|secret|password|credential|-vk$/i;\n\n/**\n * Values of caller-supplied headers to redact from provider error text.\n * Credential-named headers are redacted whatever their length; for\n * `Authorization`, the credential after the scheme is included too. Other\n * headers may also carry tenant secrets, so their values are redacted when\n * they are at least 8 characters long, which keeps short values such as\n * `1` from mangling messages.\n */\nexport function headerSecrets(headers: HeadersInit | undefined): string[] {\n const secrets: string[] = [];\n for (const [key, value] of Object.entries(mergeHeaderRecords(headers))) {\n const credential = CREDENTIAL_HEADER.test(key);\n const candidates = [value];\n if (key.toLowerCase() === 'authorization') {\n candidates.push(value.replace(/^\\S+\\s+/, ''));\n }\n for (const candidate of candidates) {\n const trimmed = candidate.trim();\n const long = trimmed.length >= MIN_HEADER_SECRET_LENGTH;\n if (trimmed && (credential || long) && !secrets.includes(trimmed)) {\n secrets.push(trimmed);\n }\n }\n }\n // Longest first, so a full `Bearer <token>` is replaced before its token.\n return secrets.sort((left, right) => right.length - left.length);\n}\n\nexport function mergeHeaderRecords(\n ...sets: Array<HeadersInit | undefined>\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const set of sets) {\n if (set) {\n new Headers(set).forEach((value, key) => {\n merged[key] = value;\n });\n }\n }\n return merged;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n return Object.keys(headers).some((key) => key.toLowerCase() === name);\n}\n","/**\n * Raw-audio helpers for streaming adapters: byte views, base64 encoding,\n * duration from byte counts, and WAV/`audio/L16` unwrapping for the\n * record-then-send wrapper. Nothing here decodes compressed audio or resamples.\n */\n\nimport { normalizeMimeType } from './audio.js';\nimport { SpeechConfigurationError } from './errors.js';\nimport type {\n StreamingAudioChunk,\n StreamingAudioEncoding,\n StreamingAudioFormat,\n} from './streaming-types.js';\n\nconst ENCODING_BYTES_PER_SAMPLE: Record<StreamingAudioEncoding, number> = {\n pcm16: 2,\n g711_ulaw: 1,\n g711_alaw: 1,\n};\n\n/** Returns a byte view over a chunk without copying. */\nexport function chunkToBytes(\n chunk: StreamingAudioChunk,\n adapter?: string,\n): Uint8Array {\n if (chunk instanceof Uint8Array) {\n return chunk;\n }\n if (chunk instanceof ArrayBuffer) {\n return new Uint8Array(chunk);\n }\n if (ArrayBuffer.isView(chunk)) {\n return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n }\n throw new SpeechConfigurationError(\n 'Streaming audio chunks must be Uint8Array, ArrayBuffer, or an ArrayBuffer view',\n adapter,\n );\n}\n\nexport function encodeBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(\n bytes.buffer,\n bytes.byteOffset,\n bytes.byteLength,\n ).toString('base64');\n }\n\n let binary = '';\n const step = 0x8000;\n for (let index = 0; index < bytes.length; index += step) {\n binary += String.fromCharCode(...bytes.subarray(index, index + step));\n }\n return btoa(binary);\n}\n\n/** Bytes per second of audio in `format`. */\nexport function bytesPerSecond(format: StreamingAudioFormat): number {\n return (\n format.sampleRate *\n format.channels *\n ENCODING_BYTES_PER_SAMPLE[format.encoding]\n );\n}\n\n/** Audio duration in seconds for `bytes` of `format`, rounded to milliseconds. */\nexport function audioSecondsForBytes(\n bytes: number,\n format: StreamingAudioFormat,\n): number {\n return Math.round((bytes / bytesPerSecond(format)) * 1000) / 1000;\n}\n\nexport interface RawAudio {\n bytes: Uint8Array;\n /** Format fields the input declared (WAV header, MIME parameters, or `AudioInput`). */\n format: Partial<StreamingAudioFormat>;\n}\n\nconst WAV_TYPES = new Set([\n 'audio/wav',\n 'audio/wave',\n 'audio/x-wav',\n 'audio/vnd.wave',\n]);\nconst RAW_PCM_TYPES = new Set([\n 'audio/pcm',\n 'audio/l16',\n 'audio/raw',\n 'audio/x-raw',\n]);\n/** Untyped bytes: the streaming transcriber's default format applies. */\nconst UNTYPED = 'application/octet-stream';\nconst MULAW_TYPES = new Set(['audio/pcmu', 'audio/basic', 'audio/x-mulaw']);\nconst ALAW_TYPES = new Set(['audio/pcma', 'audio/x-alaw']);\n\n/**\n * Interprets a finished recording as raw audio for a streaming session.\n * Accepts PCM/G.711 WAV files, `audio/pcm`/`audio/L16` (with optional `rate`\n * and `channels` parameters), `audio/pcmu`/`audio/pcma`, and untyped bytes.\n * Compressed containers are rejected with a `SpeechConfigurationError`.\n */\nexport function unwrapRawAudio(\n bytes: Uint8Array,\n mimeType: string,\n adapter: string,\n): RawAudio {\n const essence = normalizeMimeType(mimeType) ?? 'application/octet-stream';\n const params = parseMimeParameters(mimeType);\n const rate = parsePositiveInt(params.rate);\n const channels = parsePositiveInt(params.channels);\n\n if (WAV_TYPES.has(essence) || isRiffWave(bytes)) {\n return parseWav(bytes, adapter);\n }\n if (essence === UNTYPED) {\n // No encoding: `start()` keeps the adapter's `audioFormat` (e.g. G.711).\n return { bytes, format: compact({ sampleRate: rate, channels }) };\n }\n if (RAW_PCM_TYPES.has(essence)) {\n if (essence === 'audio/l16' && bytes.byteLength % 2 !== 0) {\n throw new SpeechConfigurationError(\n `audio/L16 payload has an odd byte length (${bytes.byteLength}); 16-bit samples need an even number of bytes`,\n adapter,\n );\n }\n return {\n // RFC 2586: audio/L16 samples are big-endian; pcm16 is little-endian.\n bytes: essence === 'audio/l16' ? swapBytePairs(bytes) : bytes,\n format: compact({ encoding: 'pcm16', sampleRate: rate, channels }),\n };\n }\n if (MULAW_TYPES.has(essence)) {\n return {\n bytes,\n format: compact({ encoding: 'g711_ulaw', sampleRate: rate, channels }),\n };\n }\n if (ALAW_TYPES.has(essence)) {\n return {\n bytes,\n format: compact({ encoding: 'g711_alaw', sampleRate: rate, channels }),\n };\n }\n\n throw new SpeechConfigurationError(\n `${adapter} streams raw audio and cannot decode ${essence}; send PCM WAV or raw PCM, or use an HTTP transcriber for compressed recordings`,\n adapter,\n );\n}\n\n/** Returns a copy with each 16-bit sample's bytes swapped. */\nfunction swapBytePairs(bytes: Uint8Array): Uint8Array {\n const swapped = new Uint8Array(bytes.byteLength);\n for (let index = 0; index + 1 < bytes.byteLength; index += 2) {\n swapped[index] = bytes[index + 1] ?? 0;\n swapped[index + 1] = bytes[index] ?? 0;\n }\n return swapped;\n}\n\nfunction isRiffWave(bytes: Uint8Array): boolean {\n return (\n bytes.byteLength >= 12 &&\n ascii(bytes, 0, 4) === 'RIFF' &&\n ascii(bytes, 8, 4) === 'WAVE'\n );\n}\n\nfunction parseWav(bytes: Uint8Array, adapter: string): RawAudio {\n if (!isRiffWave(bytes)) {\n throw new SpeechConfigurationError('Invalid WAV header', adapter);\n }\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let offset = 12;\n let format: Partial<StreamingAudioFormat> | undefined;\n\n while (offset + 8 <= bytes.byteLength) {\n const id = ascii(bytes, offset, 4);\n const size = view.getUint32(offset + 4, true);\n const body = offset + 8;\n\n if (id === 'fmt ' && body + 16 <= bytes.byteLength) {\n const audioFormat = view.getUint16(body, true);\n const channels = view.getUint16(body + 2, true);\n const sampleRate = view.getUint32(body + 4, true);\n const bitsPerSample = view.getUint16(body + 14, true);\n format = {\n encoding: wavEncoding(audioFormat, bitsPerSample, adapter),\n sampleRate,\n channels,\n };\n } else if (id === 'data') {\n if (!format) {\n throw new SpeechConfigurationError(\n 'WAV data chunk precedes its fmt chunk',\n adapter,\n );\n }\n const end = Math.min(bytes.byteLength, body + size);\n return { bytes: bytes.subarray(body, end), format };\n }\n\n // Chunks are padded to an even size.\n offset = body + size + (size % 2);\n }\n\n throw new SpeechConfigurationError('WAV file has no data chunk', adapter);\n}\n\nfunction wavEncoding(\n audioFormat: number,\n bitsPerSample: number,\n adapter: string,\n): StreamingAudioEncoding {\n // 0xfffe is WAVE_FORMAT_EXTENSIBLE; its common PCM case is treated as PCM.\n if ((audioFormat === 1 || audioFormat === 0xfffe) && bitsPerSample === 16) {\n return 'pcm16';\n }\n if (audioFormat === 7 && bitsPerSample === 8) {\n return 'g711_ulaw';\n }\n if (audioFormat === 6 && bitsPerSample === 8) {\n return 'g711_alaw';\n }\n throw new SpeechConfigurationError(\n `Unsupported WAV encoding (format ${audioFormat}, ${bitsPerSample}-bit); expected 16-bit PCM or 8-bit G.711`,\n adapter,\n );\n}\n\nfunction ascii(bytes: Uint8Array, offset: number, length: number): string {\n return String.fromCharCode(...bytes.subarray(offset, offset + length));\n}\n\nfunction parseMimeParameters(mimeType: string): Record<string, string> {\n const params: Record<string, string> = {};\n for (const part of mimeType.split(';').slice(1)) {\n const [key, value] = part.split('=');\n if (key && value) {\n params[key.trim().toLowerCase()] = value.trim();\n }\n }\n return params;\n}\n\nfunction parsePositiveInt(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nfunction compact(\n format: Partial<StreamingAudioFormat>,\n): Partial<StreamingAudioFormat> {\n return Object.fromEntries(\n Object.entries(format).filter(([, value]) => value !== undefined),\n ) as Partial<StreamingAudioFormat>;\n}\n","/**\n * Per-turn audio limits for realtime sessions.\n *\n * Some providers cap the audio of one turn rather than of a session: vLLM's\n * realtime endpoint keeps a turn's audio and generated tokens in one model\n * context (`max_model_len`) and clears it after each final commit. A\n * {@link TurnSplitter} measures the audio of the current turn from byte counts\n * and the negotiated format, and picks where to roll over to a new turn: at\n * the first quiet stretch inside a window before the limit, or at the limit\n * itself when no quiet stretch turns up. The silence check is a plain RMS\n * energy test, not a voice-activity model.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\nimport { bytesPerSecond } from './pcm.js';\nimport type {\n StreamingAudioEncoding,\n StreamingAudioFormat,\n StreamingRolloverOptions,\n StreamingSessionSettings,\n StreamingTurnLimit,\n} from './streaming-types.js';\n\n/** Default per-turn audio cap for providers that need one (`voxtral-realtime`). */\nexport const DEFAULT_MAX_TURN_SECONDS = 270;\n/** How long before the cap the rollover starts looking for silence. */\nexport const DEFAULT_ROLLOVER_WINDOW_SECONDS = 15;\n/** RMS level (0-1 of full scale, about -40 dBFS) below which audio is quiet. */\nexport const DEFAULT_ROLLOVER_SILENCE_THRESHOLD = 0.01;\n/** Quiet audio needed before the rollover commits. */\nexport const DEFAULT_ROLLOVER_MIN_SILENCE_MS = 300;\n/** Energy is measured over frames of this length. */\nconst ANALYSIS_FRAME_MS = 20;\n\nconst BYTES_PER_SAMPLE: Record<StreamingAudioEncoding, number> = {\n pcm16: 2,\n g711_ulaw: 1,\n g711_alaw: 1,\n};\n\n/** A resolved per-turn limit; `undefined` means turns are unbounded. */\nexport interface TurnLimit {\n maxTurnSeconds: number;\n /** `false`: writes past the cap reject with `SPEECH_TURN_TOO_LONG`. */\n rollover: boolean;\n windowSeconds: number;\n silenceThreshold: number;\n minSilenceMs: number;\n}\n\n/**\n * Resolves the turn limit from settings, highest priority first (session\n * options, then adapter options, which already carry environment values),\n * falling back to `defaultMaxTurnSeconds`. Returns `undefined` when no limit\n * applies or `maxTurnSeconds` is `Infinity`.\n */\nexport function resolveTurnLimit(\n adapter: string,\n defaultMaxTurnSeconds: number | undefined,\n ...settings: Array<\n Pick<StreamingSessionSettings, 'maxTurnSeconds' | 'rollover'> | undefined\n >\n): TurnLimit | undefined {\n const maxTurnSeconds =\n settings.find((entry) => entry?.maxTurnSeconds !== undefined)\n ?.maxTurnSeconds ?? defaultMaxTurnSeconds;\n if (maxTurnSeconds === undefined) {\n return undefined;\n }\n if (\n typeof maxTurnSeconds !== 'number' ||\n Number.isNaN(maxTurnSeconds) ||\n maxTurnSeconds <= 0\n ) {\n throw new SpeechConfigurationError(\n `maxTurnSeconds must be a positive number of seconds or Infinity (got ${String(maxTurnSeconds)})`,\n adapter,\n );\n }\n if (maxTurnSeconds === Number.POSITIVE_INFINITY) {\n return undefined;\n }\n\n // On/off comes from the first layer that sets `rollover`; each tuning\n // field from the first layer whose `rollover` object sets that field, so a\n // `rollover: true` session keeps the adapter's tuning.\n const rollover = settings.find(\n (entry) => entry?.rollover !== undefined,\n )?.rollover;\n const tuning = <K extends keyof StreamingRolloverOptions>(key: K) =>\n settings\n .map((entry) => entry?.rollover)\n .find(\n (value): value is StreamingRolloverOptions =>\n typeof value === 'object' &&\n value !== null &&\n value[key] !== undefined,\n )?.[key];\n const limit: TurnLimit = {\n maxTurnSeconds,\n rollover: rollover !== false,\n windowSeconds: tuning('windowSeconds') ?? DEFAULT_ROLLOVER_WINDOW_SECONDS,\n silenceThreshold:\n tuning('silenceThreshold') ?? DEFAULT_ROLLOVER_SILENCE_THRESHOLD,\n minSilenceMs: tuning('minSilenceMs') ?? DEFAULT_ROLLOVER_MIN_SILENCE_MS,\n };\n assertNonNegative(adapter, 'rollover.windowSeconds', limit.windowSeconds);\n assertNonNegative(adapter, 'rollover.minSilenceMs', limit.minSilenceMs);\n assertNonNegative(\n adapter,\n 'rollover.silenceThreshold',\n limit.silenceThreshold,\n );\n if (limit.silenceThreshold > 1) {\n throw new SpeechConfigurationError(\n `rollover.silenceThreshold is an RMS level from 0 to 1 (got ${limit.silenceThreshold})`,\n adapter,\n );\n }\n return limit;\n}\n\nfunction assertNonNegative(adapter: string, name: string, value: unknown) {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n throw new SpeechConfigurationError(\n `${name} must be a non-negative finite number (got ${String(value)})`,\n adapter,\n );\n }\n}\n\n/** Bytes of one sample frame (one sample per channel). */\nexport function blockAlign(format: StreamingAudioFormat): number {\n return BYTES_PER_SAMPLE[format.encoding] * format.channels;\n}\n\n/** Whole sample frames in `seconds` of audio, as bytes (rounded down). */\nexport function bytesForSeconds(\n seconds: number,\n format: StreamingAudioFormat,\n): number {\n const align = blockAlign(format);\n // The epsilon keeps decimal seconds such as 0.3 from losing a frame.\n return Math.floor((seconds * bytesPerSecond(format)) / align + 1e-9) * align;\n}\n\n/** Digital silence in `format`: zero samples, encoded. */\nexport function silence(\n bytes: number,\n format: StreamingAudioFormat,\n): Uint8Array {\n const fill =\n format.encoding === 'g711_ulaw'\n ? 0xff\n : format.encoding === 'g711_alaw'\n ? 0xd5\n : 0;\n return new Uint8Array(bytes).fill(fill);\n}\n\n/** Bytes of audio one turn may hold: whole sample frames, at least one. */\nexport function turnCapBytes(\n limit: TurnLimit,\n format: StreamingAudioFormat,\n): number {\n return Math.max(\n blockAlign(format),\n bytesForSeconds(limit.maxTurnSeconds, format),\n );\n}\n\n/** The public view of a resolved limit for a session format. */\nexport function describeTurnLimit(\n limit: TurnLimit | undefined,\n format: StreamingAudioFormat,\n): StreamingTurnLimit | undefined {\n return (\n limit && {\n maxTurnSeconds: limit.maxTurnSeconds,\n rollover: limit.rollover,\n maxTurnBytes: turnCapBytes(limit, format),\n }\n );\n}\n\n/**\n * Tracks the audio of the current turn and finds rollover points.\n *\n * Feed every chunk in write order to {@link split} (rollover on) or\n * {@link add} (rollover off), and call {@link reset} whenever the turn ends\n * for another reason (a manual commit).\n */\nexport class TurnSplitter {\n /** Bytes of the current turn so far. */\n turnBytes = 0;\n readonly capBytes: number;\n private readonly windowStartBytes: number;\n private readonly frameBytes: number;\n private readonly minSilenceBytes: number;\n private readonly threshold: number;\n private readonly encoding: StreamingAudioEncoding;\n private readonly sampleBytes: number;\n // Energy of the analysis frame being filled.\n private frameFill = 0;\n private frameSumSquares = 0;\n private frameSamples = 0;\n /** Trailing bytes of a sample split across chunks. */\n private carry: number[] = [];\n /** Consecutive quiet bytes ending at the last complete frame. */\n private quietRun = 0;\n\n constructor(limit: TurnLimit, format: StreamingAudioFormat) {\n const align = blockAlign(format);\n this.capBytes = turnCapBytes(limit, format);\n this.windowStartBytes = Math.max(\n 0,\n this.capBytes - bytesForSeconds(limit.windowSeconds, format),\n );\n this.frameBytes = Math.max(\n align,\n bytesForSeconds(ANALYSIS_FRAME_MS / 1000, format),\n );\n this.minSilenceBytes = Math.max(\n this.frameBytes,\n bytesForSeconds(limit.minSilenceMs / 1000, format),\n );\n this.threshold = limit.silenceThreshold;\n this.encoding = format.encoding;\n this.sampleBytes = BYTES_PER_SAMPLE[format.encoding];\n }\n\n /** Ends the current turn: the next byte starts a new one. */\n reset(): void {\n this.turnBytes = 0;\n this.frameFill = 0;\n this.frameSumSquares = 0;\n this.frameSamples = 0;\n this.carry = [];\n this.quietRun = 0;\n }\n\n /** Whether `bytes` more would push the current turn past the cap. */\n wouldOverflow(bytes: number): boolean {\n return this.turnBytes + bytes > this.capBytes;\n }\n\n /** Counts `bytes` toward the current turn without analysing them. */\n add(bytes: number): void {\n this.turnBytes += bytes;\n }\n\n /**\n * Feeds one chunk and returns the offsets inside it where the turn should\n * end (each in `1..chunk.byteLength`, ascending). The splitter resets at\n * each offset, so bytes after it count toward the next turn.\n */\n split(chunk: Uint8Array): number[] {\n const cuts: number[] = [];\n let offset = 0;\n while (offset < chunk.byteLength) {\n if (this.turnBytes < this.windowStartBytes) {\n // Before the window only the length matters.\n const skip = Math.min(\n chunk.byteLength - offset,\n this.windowStartBytes - this.turnBytes,\n );\n offset += skip;\n this.turnBytes += skip;\n // With no window (`windowSeconds: 0`) the window starts at the cap.\n if (this.turnBytes >= this.capBytes) {\n cuts.push(offset);\n this.reset();\n }\n continue;\n }\n\n const take = Math.min(\n chunk.byteLength - offset,\n this.frameBytes - this.frameFill,\n this.capBytes - this.turnBytes,\n );\n this.measure(chunk, offset, take);\n offset += take;\n this.turnBytes += take;\n this.frameFill += take;\n\n let cut = false;\n if (this.frameFill >= this.frameBytes) {\n const quiet = this.frameIsQuiet();\n this.quietRun = quiet ? this.quietRun + this.frameFill : 0;\n this.frameFill = 0;\n this.frameSumSquares = 0;\n this.frameSamples = 0;\n cut = this.quietRun >= this.minSilenceBytes;\n }\n if (cut || this.turnBytes >= this.capBytes) {\n cuts.push(offset);\n this.reset();\n }\n }\n return cuts;\n }\n\n private frameIsQuiet(): boolean {\n if (this.frameSamples === 0) {\n return false;\n }\n const rms = Math.sqrt(this.frameSumSquares / this.frameSamples) / 32_768;\n return rms < this.threshold;\n }\n\n private measure(chunk: Uint8Array, start: number, length: number): void {\n const end = start + length;\n let index = start;\n // Complete a sample left over from the previous chunk.\n while (this.carry.length > 0 && index < end) {\n this.carry.push(chunk[index] ?? 0);\n index += 1;\n if (this.carry.length === this.sampleBytes) {\n this.addSample(this.carry);\n this.carry = [];\n }\n }\n for (; index + this.sampleBytes <= end; index += this.sampleBytes) {\n this.addSampleAt(chunk, index);\n }\n for (; index < end; index += 1) {\n this.carry.push(chunk[index] ?? 0);\n }\n }\n\n private addSample(bytes: number[]): void {\n this.addLinear(\n this.encoding === 'pcm16'\n ? pcm16Sample(bytes[0] ?? 0, bytes[1] ?? 0)\n : this.decodeG711(bytes[0] ?? 0),\n );\n }\n\n private addSampleAt(chunk: Uint8Array, index: number): void {\n this.addLinear(\n this.encoding === 'pcm16'\n ? pcm16Sample(chunk[index] ?? 0, chunk[index + 1] ?? 0)\n : this.decodeG711(chunk[index] ?? 0),\n );\n }\n\n private addLinear(sample: number): void {\n this.frameSumSquares += sample * sample;\n this.frameSamples += 1;\n }\n\n private decodeG711(byte: number): number {\n return this.encoding === 'g711_ulaw' ? decodeMuLaw(byte) : decodeALaw(byte);\n }\n}\n\n/** Signed 16-bit little-endian sample. */\nfunction pcm16Sample(low: number, high: number): number {\n const value = low | (high << 8);\n return value >= 0x8000 ? value - 0x10000 : value;\n}\n\n/** G.711 mu-law byte to a 16-bit linear sample (ITU-T G.711). */\nexport function decodeMuLaw(byte: number): number {\n const value = ~byte & 0xff;\n const exponent = (value >> 4) & 0x07;\n const mantissa = value & 0x0f;\n const magnitude = (((mantissa << 3) + 0x84) << exponent) - 0x84;\n return value & 0x80 ? -magnitude : magnitude;\n}\n\n/** G.711 A-law byte to a 16-bit linear sample (ITU-T G.711). */\nexport function decodeALaw(byte: number): number {\n const value = byte ^ 0x55;\n const exponent = (value >> 4) & 0x07;\n const mantissa = value & 0x0f;\n const magnitude =\n exponent === 0\n ? (mantissa << 4) + 8\n : ((mantissa << 4) + 0x108) << (exponent - 1);\n return value & 0x80 ? magnitude : -magnitude;\n}\n","/**\n * Protocol-agnostic realtime transcription session over a WebSocket.\n *\n * The session owns the socket lifecycle, write queue and backpressure,\n * timeouts, turn bookkeeping, events, and the final `TranscriptResult`. A\n * {@link RealtimeProtocol} supplies the wire format, so a sibling adapter\n * (for example a vLLM-served Voxtral realtime endpoint) only has to map its\n * own messages.\n */\n\nimport {\n SpeechConfigurationError,\n SpeechError,\n SpeechProviderError,\n} from './errors.js';\nimport { redactSecret } from './http.js';\nimport {\n audioSecondsForBytes,\n bytesPerSecond,\n chunkToBytes,\n encodeBase64,\n} from './pcm.js';\nimport type {\n StreamingAudioChunk,\n StreamingAudioFormat,\n StreamingSession,\n StreamingSessionEventName,\n StreamingSessionEvents,\n StreamingSessionListener,\n StreamingSessionState,\n StreamingTurnDetection,\n} from './streaming-types.js';\nimport {\n blockAlign,\n silence,\n type TurnLimit,\n TurnSplitter,\n} from './turn-limit.js';\nimport type { TranscriptResult, TranscriptSegment } from './types.js';\nimport {\n audioSecondsFromProviderUsage,\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from './usage.js';\nimport {\n messageDataToText,\n type SpeechWebSocket,\n type SpeechWebSocketCloseEvent,\n type SpeechWebSocketFactory,\n} from './websocket.js';\n\nexport const DEFAULT_STREAMING_TIMEOUT_MS = 30_000;\nexport const DEFAULT_STREAMING_CONNECT_TIMEOUT_MS = 10_000;\nexport const DEFAULT_STREAMING_HIGH_WATER_MARK = 1024 * 1024;\nexport const DEFAULT_STREAMING_MAX_BUFFERED_BYTES = 16 * 1024 * 1024;\nconst DRAIN_POLL_MS = 10;\n\n/** Normalised events a protocol parser returns for one server message. */\nexport type RealtimeProtocolEvent =\n | { type: 'session'; sessionId?: string }\n /** The provider committed a turn (from a client commit or server VAD). */\n | { type: 'committed'; itemId?: string }\n /** The provider rejected a commit because its buffer was empty. Not fatal. */\n | { type: 'commit_empty' }\n | { type: 'partial'; itemId?: string; delta: string }\n | {\n type: 'final';\n itemId?: string;\n text: string;\n providerUsage?: Record<string, unknown>;\n raw?: unknown;\n /**\n * `false` when the provider reports that the turn consumed no audio\n * (vLLM: `usage.prompt_tokens <= 1`). A turn that was sent audio but\n * consumed none means the provider's turn state is out of sync, and the\n * session fails instead of silently dropping that audio.\n */\n audioConsumed?: boolean;\n }\n | { type: 'speech_started'; itemId?: string; audioMs?: number }\n | { type: 'speech_stopped'; itemId?: string; audioMs?: number }\n /** A fatal provider error. */\n | { type: 'error'; message: string; code?: string; raw?: unknown };\n\n/** Resolved per-session settings handed to the protocol. */\nexport interface RealtimeSessionConfig {\n model: string;\n format: StreamingAudioFormat;\n turnDetection: StreamingTurnDetection;\n language?: string;\n prompt?: string;\n}\n\n/** Wire format of one realtime provider. */\nexport interface RealtimeProtocol {\n /** Adapter name used for errors and usage, e.g. `openai-realtime`. */\n readonly provider: string;\n /**\n * How a client commit is acknowledged: by a `committed` event (OpenAI), or\n * only by the turn's `final` event (servers without a commit ack).\n */\n readonly commitAck: 'committed' | 'final';\n /**\n * Whether `end()` commits only when uncommitted audio remains\n * (`if-audio`), or always sends a final commit (`always`, for servers that\n * need an explicit end-of-stream marker).\n */\n readonly endCommit: 'if-audio' | 'always';\n /**\n * Default per-turn audio cap in seconds, for providers whose model context\n * covers one turn (vLLM resets it after each final commit). Adapters pass\n * it to `resolveTurnLimit()`; a session with a turn limit rolls over to a\n * new turn before the cap, or rejects writes past it. Unset: no default.\n */\n readonly maxTurnSeconds?: number;\n /**\n * Shortest audio, in seconds, the provider accepts in one commit (OpenAI:\n * 100 ms; shorter commits are rejected as empty). A session with a turn\n * limit pads a shorter, non-empty turn with silence before committing it,\n * so the tail left after a rollover is transcribed instead of dropped.\n */\n readonly minCommitSeconds?: number;\n /** Messages sent once the socket opens (session configuration). */\n sessionMessages(config: RealtimeSessionConfig): unknown[];\n /** One audio chunk, already base64-encoded. */\n appendMessage(base64Audio: string): unknown;\n /** Ends the current turn. `final` is true for the commit sent by `end()`. */\n commitMessage(options: { final: boolean }): unknown;\n /**\n * Opens a turn before its first audio, for servers that only start\n * generating after an explicit start signal (vLLM's non-final\n * `input_audio_buffer.commit`). When set, the session sends it before the\n * first append of every turn, and holds audio written after a commit until\n * every outstanding turn has been acknowledged, so a server that resets its\n * buffer between turns never drops audio.\n */\n turnStartMessage?(): unknown;\n /** Maps one parsed JSON server message to zero or more events. */\n parse(message: unknown): RealtimeProtocolEvent[];\n}\n\n/** Where and how to open the socket; resolved per session. */\nexport interface RealtimeConnection {\n url: string;\n protocols: string[];\n headers: Record<string, string>;\n /** Credentials to redact from surfaced provider text. */\n secrets: string[];\n}\n\nexport interface RealtimeSessionInit {\n protocol: RealtimeProtocol;\n config: RealtimeSessionConfig;\n /** Resolves the connection (may mint a short-lived token). */\n connect: () => Promise<RealtimeConnection>;\n createWebSocket: SpeechWebSocketFactory;\n timeoutMs: number;\n connectTimeoutMs: number;\n highWaterMark: number;\n maxBufferedBytes: number;\n signal?: AbortSignal;\n /** Called in order with the aggregated usage when `end()` succeeds. */\n onUsage: Array<SpeechUsageCallback | undefined>;\n /** Per-turn audio limit; unset means turns are unbounded. */\n turnLimit?: TurnLimit;\n}\n\ntype QueueItem =\n | {\n kind: 'audio';\n base64: string;\n bytes: number;\n resolve: () => void;\n reject: (error: Error) => void;\n }\n | { kind: 'commit'; final: boolean };\n\ninterface FinalRecord {\n itemId?: string;\n text: string;\n providerUsage?: Record<string, unknown>;\n raw?: unknown;\n arrival: number;\n}\n\nconst noop = () => undefined;\n\nexport class RealtimeTranscriptionSession implements StreamingSession {\n state: StreamingSessionState = 'connecting';\n sessionId?: string;\n readonly ready: Promise<void>;\n\n private readonly init: RealtimeSessionInit;\n private readonly provider: string;\n private resolveReady: () => void = noop;\n private rejectReady: (error: Error) => void = noop;\n private socket?: SpeechWebSocket;\n private opened = false;\n private secrets: string[] = [];\n private queue: QueueItem[] = [];\n private queued = 0;\n private pumping = false;\n private totalBytes = 0;\n private uncommittedBytes = 0;\n private outstandingCommits = 0;\n /** Per outstanding commit, in order: whether it carried audio. */\n private readonly commitHadAudio: boolean[] = [];\n private turnOpen = false;\n /** `Date.now()` of the last server message; bounds inactivity waits. */\n private lastServerMessageAt = 0;\n private readonly itemOrder: string[] = [];\n private readonly pendingItems = new Set<string>();\n private readonly vadStoppedItems = new Set<string>();\n private readonly partials = new Map<string, string>();\n private readonly finals: FinalRecord[] = [];\n private readonly listeners = new Map<\n StreamingSessionEventName,\n Set<(event: never) => void>\n >();\n private error?: Error;\n private endPromise?: Promise<TranscriptResult>;\n private endWaiter?: { resolve: () => void; reject: (error: Error) => void };\n private connectTimer?: ReturnType<typeof setTimeout>;\n private endTimer?: ReturnType<typeof setTimeout>;\n private closeEmitted = false;\n /** Measures the current turn in write order when a turn limit applies. */\n private readonly splitter?: TurnSplitter;\n private readonly onAbort = () => {\n this.fail(toError(this.init.signal?.reason, this.provider));\n };\n\n constructor(init: RealtimeSessionInit) {\n this.init = init;\n this.provider = init.protocol.provider;\n assertTurnLimitFitsProtocol(init.protocol, init.turnLimit);\n if (init.turnLimit) {\n this.splitter = new TurnSplitter(init.turnLimit, init.config.format);\n }\n this.ready = new Promise<void>((resolve, reject) => {\n this.resolveReady = resolve;\n this.rejectReady = reject;\n });\n this.ready.catch(noop);\n\n if (init.signal?.aborted) {\n this.fail(toError(init.signal.reason, this.provider));\n return;\n }\n init.signal?.addEventListener('abort', this.onAbort, { once: true });\n void this.connect();\n }\n\n get queuedBytes(): number {\n return this.queued;\n }\n\n write(chunk: StreamingAudioChunk): Promise<void> {\n const promise = this.enqueueAudio(chunk);\n promise.catch(noop);\n return promise;\n }\n\n commit(): void {\n if (this.isTerminal() || this.endPromise) {\n return;\n }\n this.queue.push({ kind: 'commit', final: false });\n this.splitter?.reset();\n void this.pump();\n }\n\n end(): Promise<TranscriptResult> {\n if (this.endPromise) {\n return this.endPromise;\n }\n\n if (this.error) {\n this.endPromise = Promise.reject(this.error);\n return this.endPromise;\n }\n\n this.state = 'ending';\n this.queue.push({ kind: 'commit', final: true });\n const settled = new Promise<void>((resolve, reject) => {\n this.endWaiter = { resolve, reject };\n });\n this.armEndTimer();\n this.endPromise = settled.then(() => this.finish());\n void this.pump();\n this.checkEnded();\n return this.endPromise;\n }\n\n /**\n * (Re)starts the end timeout. It measures provider inactivity: every server\n * message restarts it, so a long recording that is still being transcribed\n * is not cut off, while a silent provider still fails after `timeoutMs`.\n */\n private armEndTimer(): void {\n clearTimeout(this.endTimer);\n this.endTimer = setTimeout(() => {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} timed out after ${this.init.timeoutMs} ms waiting for the final transcript`,\n ),\n );\n }, this.init.timeoutMs);\n }\n\n abort(reason?: unknown): void {\n this.fail(toError(reason, this.provider));\n }\n\n on<E extends StreamingSessionEventName>(\n event: E,\n listener: StreamingSessionListener<E>,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as (event: never) => void);\n return () => this.off(event, listener);\n }\n\n off<E extends StreamingSessionEventName>(\n event: E,\n listener: StreamingSessionListener<E>,\n ): void {\n this.listeners.get(event)?.delete(listener as (event: never) => void);\n }\n\n private enqueueAudio(chunk: StreamingAudioChunk): Promise<void> {\n if (this.error) {\n return Promise.reject(this.error);\n }\n if (this.endPromise || this.isTerminal()) {\n return Promise.reject(\n new SpeechError(\n 'Cannot write to a streaming session after end()',\n 'SPEECH_SESSION_ENDED',\n this.provider,\n ),\n );\n }\n\n let bytes: Uint8Array;\n try {\n bytes = chunkToBytes(chunk, this.provider);\n } catch (error) {\n return Promise.reject(error);\n }\n if (bytes.byteLength === 0) {\n return Promise.resolve();\n }\n if (this.queued + bytes.byteLength > this.init.maxBufferedBytes) {\n return Promise.reject(\n new SpeechError(\n `Streaming write would queue more than maxBufferedBytes (${this.init.maxBufferedBytes}); await write() before sending more audio`,\n 'SPEECH_BACKPRESSURE',\n this.provider,\n ),\n );\n }\n\n const splitter = this.splitter;\n let cuts: number[] = [];\n if (splitter && !this.init.turnLimit?.rollover) {\n if (splitter.wouldOverflow(bytes.byteLength)) {\n return Promise.reject(\n new SpeechError(\n `Streaming write would push the current turn past maxTurnSeconds (${this.init.turnLimit?.maxTurnSeconds} s); call commit() before writing more audio`,\n 'SPEECH_TURN_TOO_LONG',\n this.provider,\n ),\n );\n }\n splitter.add(bytes.byteLength);\n } else if (splitter) {\n cuts = splitter.split(bytes);\n }\n\n return new Promise<void>((resolve, reject) => {\n // A rollover splits the chunk: each part before a cut ends with an\n // automatic commit, and the write settles with its last part.\n const pushPart = (start: number, end: number) => {\n const part = bytes.subarray(start, end);\n this.queue.push({\n kind: 'audio',\n // Encode now so callers may reuse their buffer once write() returns.\n base64: encodeBase64(part),\n bytes: part.byteLength,\n resolve: end === bytes.byteLength ? resolve : noop,\n reject,\n });\n this.queued += part.byteLength;\n };\n // Cuts are ascending offsets in 1..byteLength, so no part is empty.\n let start = 0;\n for (const cut of cuts) {\n pushPart(start, cut);\n this.queue.push({ kind: 'commit', final: false });\n start = cut;\n }\n if (start < bytes.byteLength) {\n pushPart(start, bytes.byteLength);\n }\n void this.pump();\n });\n }\n\n private async connect(): Promise<void> {\n this.connectTimer = setTimeout(() => {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} connection timed out after ${this.init.connectTimeoutMs} ms`,\n ),\n );\n }, this.init.connectTimeoutMs);\n\n try {\n const connection = await this.init.connect();\n if (this.isTerminal()) {\n return;\n }\n this.secrets = connection.secrets;\n const socket = this.init.createWebSocket(connection.url, {\n protocols: connection.protocols,\n headers: connection.headers,\n });\n this.socket = socket;\n try {\n socket.binaryType = 'arraybuffer';\n } catch {\n // Some implementations expose a read-only binaryType.\n }\n socket.addEventListener('open', () => this.onOpen());\n socket.addEventListener('message', (event) => this.onMessage(event.data));\n socket.addEventListener('error', () => {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} WebSocket error`,\n ),\n );\n });\n socket.addEventListener('close', (event) => this.onClose(event));\n } catch (error) {\n this.fail(toError(error, this.provider));\n }\n }\n\n private onOpen(): void {\n clearTimeout(this.connectTimer);\n if (this.isTerminal()) {\n this.closeSocket();\n return;\n }\n\n this.opened = true;\n for (const message of this.init.protocol.sessionMessages(\n this.init.config,\n )) {\n if (!this.send(message)) {\n return;\n }\n }\n if (this.state === 'connecting') {\n this.state = 'open';\n }\n this.resolveReady();\n this.emit('open', { sessionId: this.sessionId });\n void this.pump();\n }\n\n private onMessage(data: unknown): void {\n if (this.isTerminal()) {\n return;\n }\n\n this.lastServerMessageAt = Date.now();\n if (this.endWaiter) {\n this.armEndTimer();\n }\n\n const text = messageDataToText(data);\n if (text === undefined) {\n return;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return;\n }\n\n let events: RealtimeProtocolEvent[];\n try {\n events = this.init.protocol.parse(parsed);\n } catch (error) {\n this.fail(toError(error, this.provider));\n return;\n }\n\n for (const event of events) {\n if (this.isTerminal()) {\n return;\n }\n this.handle(event);\n }\n this.checkEnded();\n }\n\n private handle(event: RealtimeProtocolEvent): void {\n switch (event.type) {\n case 'session':\n this.sessionId = event.sessionId ?? this.sessionId;\n return;\n case 'committed':\n if (event.itemId && !this.pendingItems.has(event.itemId)) {\n this.pendingItems.add(event.itemId);\n this.itemOrder.push(event.itemId);\n }\n // A turn whose speech_stopped we saw was committed by server VAD, so\n // it does not acknowledge one of our commits.\n if (\n this.init.protocol.commitAck === 'committed' &&\n !(event.itemId && this.vadStoppedItems.has(event.itemId))\n ) {\n this.ackCommit();\n }\n return;\n case 'commit_empty':\n this.ackCommit();\n return;\n case 'partial': {\n const key = event.itemId ?? '';\n const text = (this.partials.get(key) ?? '') + event.delta;\n this.partials.set(key, text);\n this.emit('partial', {\n delta: event.delta,\n text,\n itemId: event.itemId,\n });\n return;\n }\n case 'final': {\n let serverEnded = false;\n if (this.init.protocol.commitAck === 'final') {\n if (this.outstandingCommits === 0) {\n // A final we did not ask for: the server ended the open turn by\n // itself (e.g. a server-side generation limit).\n serverEnded = true;\n } else if (this.ackCommit() && event.audioConsumed === false) {\n // Our turn carried audio but the server consumed none. vLLM does\n // this when a stale end-of-turn marker (left by a commit that\n // crossed a server-ended turn) ends the turn at once.\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} finished a turn without transcribing its audio (the server's turn state is out of sync, e.g. after a commit crossed a server-ended turn); start a new session`,\n ),\n );\n return;\n }\n }\n if (event.itemId) {\n this.pendingItems.delete(event.itemId);\n }\n this.partials.delete(event.itemId ?? '');\n this.finals.push({\n itemId: event.itemId,\n text: event.text,\n providerUsage: event.providerUsage,\n raw: event.raw,\n arrival: this.finals.length,\n });\n this.emit('final', {\n text: event.text,\n itemId: event.itemId,\n providerUsage: event.providerUsage,\n raw: event.raw,\n });\n if (serverEnded) {\n // Fail closed: audio sent after the cut-off was never transcribed,\n // and the server may now be out of step with our turns.\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} ended a turn before it was committed (for example at a server generation limit); audio sent after that point was not transcribed. Call commit() more often and start a new session`,\n ),\n );\n }\n return;\n }\n case 'speech_started':\n this.emit(event.type, { itemId: event.itemId, audioMs: event.audioMs });\n return;\n case 'speech_stopped':\n if (event.itemId) {\n this.vadStoppedItems.add(event.itemId);\n }\n this.emit(event.type, { itemId: event.itemId, audioMs: event.audioMs });\n return;\n case 'error':\n this.fail(\n new SpeechProviderError(\n this.provider,\n this.redact(\n `${this.provider} error${event.code ? ` (${event.code})` : ''}: ${event.message}`,\n ) ?? `${this.provider} error`,\n {\n responseBody:\n event.raw === undefined\n ? undefined\n : this.redact(JSON.stringify(event.raw)),\n },\n ),\n );\n return;\n }\n }\n\n /** Acknowledges the oldest outstanding commit; returns whether it carried audio. */\n private ackCommit(): boolean {\n this.outstandingCommits = Math.max(0, this.outstandingCommits - 1);\n return this.commitHadAudio.shift() ?? false;\n }\n\n private async pump(): Promise<void> {\n if (this.pumping || !this.opened) {\n return;\n }\n\n this.pumping = true;\n try {\n while (this.queue.length > 0 && !this.isTerminal()) {\n await this.waitForDrain();\n if (!(await this.openTurnIfNeeded())) {\n break;\n }\n const item = this.queue.shift();\n if (!item || this.isTerminal()) {\n if (item?.kind === 'audio') {\n item.reject(this.error ?? new Error('Session closed'));\n }\n break;\n }\n\n if (item.kind === 'commit') {\n this.sendCommit(item.final);\n continue;\n }\n\n this.queued -= item.bytes;\n if (!this.send(this.init.protocol.appendMessage(item.base64))) {\n item.reject(this.error ?? new Error('Session closed'));\n break;\n }\n this.totalBytes += item.bytes;\n this.uncommittedBytes += item.bytes;\n await this.waitForDrain();\n if (this.error) {\n item.reject(this.error);\n } else {\n item.resolve();\n }\n }\n } finally {\n this.pumping = false;\n }\n this.checkEnded();\n }\n\n private sendCommit(final: boolean): void {\n const needed =\n this.uncommittedBytes > 0 ||\n (final && this.init.protocol.endCommit === 'always');\n if (!needed || !this.padShortTurn()) {\n return;\n }\n if (this.send(this.init.protocol.commitMessage({ final }))) {\n this.commitHadAudio.push(this.uncommittedBytes > 0);\n this.uncommittedBytes = 0;\n this.outstandingCommits += 1;\n this.turnOpen = false;\n }\n }\n\n /**\n * In a session with a turn limit, tops up a non-empty turn shorter than the\n * protocol's `minCommitSeconds` with silence (not counted as streamed\n * audio). Rollover can leave such a tail, which the provider would\n * otherwise reject as empty and drop. Returns false when the send failed.\n */\n private padShortTurn(): boolean {\n const minSeconds = this.init.protocol.minCommitSeconds;\n if (!this.init.turnLimit || !minSeconds || this.uncommittedBytes === 0) {\n return true;\n }\n const format = this.init.config.format;\n const align = blockAlign(format);\n // Round up to whole sample frames so the turn reaches the minimum.\n const minBytes =\n Math.ceil((minSeconds * bytesPerSecond(format)) / align - 1e-9) * align;\n const missing = minBytes - this.uncommittedBytes;\n if (missing <= 0) {\n return true;\n }\n if (\n !this.send(\n this.init.protocol.appendMessage(\n encodeBase64(silence(missing, format)),\n ),\n )\n ) {\n return false;\n }\n this.uncommittedBytes += missing;\n return true;\n }\n\n /**\n * For protocols with an explicit turn start: before the next audio, waits\n * for every outstanding turn to be acknowledged, then opens a new turn. Like\n * the end timeout, the wait is bounded by provider inactivity: it fails only\n * after `timeoutMs` without any server message. Returns false when the\n * session has failed.\n */\n private async openTurnIfNeeded(): Promise<boolean> {\n const start = this.init.protocol.turnStartMessage;\n if (!start || this.turnOpen || this.queue[0]?.kind !== 'audio') {\n return true;\n }\n\n const waitStartedAt = Date.now();\n while (!this.isTerminal() && this.outstandingCommits > 0) {\n const lastActivity = Math.max(waitStartedAt, this.lastServerMessageAt);\n if (Date.now() - lastActivity >= this.init.timeoutMs) {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} timed out after ${this.init.timeoutMs} ms without a server message while waiting for the previous turn to finish`,\n ),\n );\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, DRAIN_POLL_MS));\n }\n if (this.isTerminal() || !this.send(start.call(this.init.protocol))) {\n return false;\n }\n this.turnOpen = true;\n return true;\n }\n\n /**\n * Waits until the socket's send buffer is at or below `highWaterMark`. A\n * socket that stays congested for `timeoutMs` fails the session, so a\n * stalled connection can never leave `write()` pending forever.\n */\n private async waitForDrain(): Promise<void> {\n const deadline = Date.now() + this.init.timeoutMs;\n while (\n !this.isTerminal() &&\n this.socket &&\n this.socket.bufferedAmount > this.init.highWaterMark\n ) {\n if (Date.now() >= deadline) {\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} socket did not drain below highWaterMark within ${this.init.timeoutMs} ms`,\n ),\n );\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, DRAIN_POLL_MS));\n }\n }\n\n private send(message: unknown): boolean {\n try {\n this.socket?.send(JSON.stringify(message));\n return true;\n } catch (error) {\n this.fail(toError(error, this.provider));\n return false;\n }\n }\n\n private checkEnded(): void {\n if (\n !this.endWaiter ||\n this.isTerminal() ||\n !this.opened ||\n this.pumping ||\n this.queue.length > 0 ||\n this.outstandingCommits > 0 ||\n this.pendingItems.size > 0\n ) {\n return;\n }\n\n clearTimeout(this.endTimer);\n const waiter = this.endWaiter;\n this.endWaiter = undefined;\n waiter.resolve();\n }\n\n private async finish(): Promise<TranscriptResult> {\n this.state = 'closed';\n this.cleanup();\n this.closeSocket();\n\n const ordered = [...this.finals].sort(\n (left, right) => this.rank(left) - this.rank(right),\n );\n const segments: TranscriptSegment[] = ordered\n .map((final) => final.text.trim())\n .filter(Boolean)\n .map((text) => ({ text }));\n const providerUsage = sumUsage(ordered.map((final) => final.providerUsage));\n const durationSeconds = audioSecondsForBytes(\n this.totalBytes,\n this.init.config.format,\n );\n const usage: SpeechUsage = {\n operation: 'transcription',\n provider: this.provider,\n model: this.init.config.model,\n audioSeconds:\n audioSecondsFromProviderUsage(providerUsage) ?? durationSeconds,\n bytes: this.totalBytes,\n providerUsage,\n };\n const result: TranscriptResult = {\n text: segments.map((segment) => segment.text).join(' '),\n language: this.init.config.language,\n durationSeconds,\n segments,\n provider: this.provider,\n model: this.init.config.model,\n raw: {\n sessionId: this.sessionId,\n finals: ordered.map((final) => final.raw),\n },\n usage,\n };\n\n await reportSpeechUsage(usage, ...this.init.onUsage);\n return result;\n }\n\n private rank(final: FinalRecord): number {\n const index = final.itemId ? this.itemOrder.indexOf(final.itemId) : -1;\n return index >= 0 ? index : this.itemOrder.length + final.arrival;\n }\n\n private onClose(event: SpeechWebSocketCloseEvent): void {\n clearTimeout(this.connectTimer);\n if (!this.isTerminal()) {\n const code = event.code ?? 1005;\n this.fail(\n new SpeechProviderError(\n this.provider,\n `${this.provider} socket closed unexpectedly (code ${code}${event.reason ? `: ${this.redact(event.reason)}` : ''})`,\n ),\n );\n }\n if (!this.closeEmitted) {\n this.closeEmitted = true;\n this.emit('close', {\n code: event.code ?? 1005,\n reason: this.redact(event.reason ?? '') ?? '',\n });\n }\n }\n\n private fail(error: Error): void {\n if (this.isTerminal()) {\n return;\n }\n\n this.error = error;\n this.state = 'failed';\n this.cleanup();\n const queue = this.queue;\n this.queue = [];\n this.queued = 0;\n for (const item of queue) {\n if (item.kind === 'audio') {\n item.reject(error);\n }\n }\n this.rejectReady(error);\n const waiter = this.endWaiter;\n this.endWaiter = undefined;\n waiter?.reject(error);\n this.emit('error', error);\n this.closeSocket();\n }\n\n private cleanup(): void {\n clearTimeout(this.connectTimer);\n clearTimeout(this.endTimer);\n this.init.signal?.removeEventListener('abort', this.onAbort);\n }\n\n private closeSocket(): void {\n try {\n this.socket?.close(1000);\n } catch {\n // Already closing.\n }\n }\n\n private emit<E extends StreamingSessionEventName>(\n event: E,\n payload: StreamingSessionEvents[E],\n ): void {\n for (const listener of [...(this.listeners.get(event) ?? [])]) {\n try {\n (listener as StreamingSessionListener<E>)(payload);\n } catch (error) {\n // A throwing listener fails the session so the bug surfaces via end().\n if (event !== 'error' && event !== 'close') {\n this.fail(toError(error, this.provider));\n }\n }\n }\n }\n\n private isTerminal(): boolean {\n return this.state === 'closed' || this.state === 'failed';\n }\n\n private redact(text: string | undefined): string | undefined {\n let result = text;\n for (const secret of this.secrets) {\n result = redactSecret(result, secret);\n }\n return result;\n }\n}\n\n/**\n * Throws when a turn cap is below the provider's minimum commit length:\n * padding a short turn up to that minimum would then exceed the cap.\n */\nexport function assertTurnLimitFitsProtocol(\n protocol: Pick<RealtimeProtocol, 'provider' | 'minCommitSeconds'>,\n limit: Pick<TurnLimit, 'maxTurnSeconds'> | undefined,\n): void {\n const minimum = protocol.minCommitSeconds;\n if (limit && minimum && limit.maxTurnSeconds < minimum) {\n throw new SpeechConfigurationError(\n `maxTurnSeconds (${limit.maxTurnSeconds}) is below ${protocol.provider}'s minimum commit of ${minimum} s`,\n protocol.provider,\n );\n }\n}\n\nfunction toError(value: unknown, provider: string): Error {\n if (value instanceof Error) {\n return value;\n }\n return new SpeechError(\n value === undefined ? 'Streaming session aborted' : String(value),\n 'SPEECH_ABORTED',\n provider,\n );\n}\n\n/**\n * Sums numeric fields of per-turn provider usage blocks (recursively), keeping\n * the first value of non-numeric fields such as `type`.\n */\nexport function sumUsage(\n blocks: Array<Record<string, unknown> | undefined>,\n): Record<string, unknown> | undefined {\n const present = blocks.filter(\n (block): block is Record<string, unknown> =>\n Boolean(block) && typeof block === 'object',\n );\n if (present.length === 0) {\n return undefined;\n }\n\n const total: Record<string, unknown> = {};\n for (const block of present) {\n for (const [key, value] of Object.entries(block)) {\n const current = total[key];\n if (typeof value === 'number') {\n total[key] = (typeof current === 'number' ? current : 0) + value;\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n total[key] = sumUsage([\n current as Record<string, unknown> | undefined,\n value as Record<string, unknown>,\n ]);\n } else if (current === undefined) {\n total[key] = value;\n }\n }\n }\n return total;\n}\n","/**\n * OpenAI Realtime transcription adapter (`openai-realtime`).\n *\n * Targets the GA Realtime API transcription session (no `OpenAI-Beta`\n * header): connect to `wss://api.openai.com/v1/realtime?intent=transcription`,\n * send `session.update` with `session.type: \"transcription\"`, stream\n * `input_audio_buffer.append`, and read\n * `conversation.item.input_audio_transcription.delta` / `.completed`.\n * Reference: https://developers.openai.com/api/docs/guides/realtime-transcription\n * and https://developers.openai.com/api/reference/resources/realtime/client-events\n */\n\nimport { SpeechConfigurationError } from '../shared/errors.js';\nimport { compactJson, resolveOpenAICompatibleUrl } from '../shared/http.js';\nimport {\n assertNoBrowserApiKey,\n CLIENT_SECRET_SUBPROTOCOL_PREFIX,\n mergeHeaderRecords,\n realtimeConnector,\n} from '../shared/realtime-auth.js';\nimport {\n assertTurnLimitFitsProtocol,\n DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n DEFAULT_STREAMING_HIGH_WATER_MARK,\n DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n DEFAULT_STREAMING_TIMEOUT_MS,\n type RealtimeProtocol,\n type RealtimeProtocolEvent,\n type RealtimeSessionConfig,\n RealtimeTranscriptionSession,\n} from '../shared/realtime-session.js';\nimport type {\n OpenAIRealtimeTranscriberOptions,\n StreamingAudioFormat,\n StreamingSession,\n StreamingSessionOptions,\n StreamingTranscriber,\n StreamingTurnDetection,\n StreamingTurnLimit,\n} from '../shared/streaming-types.js';\nimport {\n describeTurnLimit,\n resolveTurnLimit,\n type TurnLimit,\n} from '../shared/turn-limit.js';\nimport {\n resolveWebSocketFactory,\n type SpeechWebSocketFactory,\n toWebSocketUrl,\n} from '../shared/websocket.js';\n\nconst ADAPTER = 'openai-realtime';\nexport const OPENAI_REALTIME_DEFAULT_URL = 'wss://api.openai.com/v1/realtime';\nexport const OPENAI_REALTIME_DEFAULT_MODEL = 'gpt-4o-transcribe';\n\n/** GA realtime input formats: 24 kHz mono PCM16, or 8 kHz mono G.711. */\nconst WIRE_FORMATS: Record<\n StreamingAudioFormat['encoding'],\n { type: string; sampleRate: number }\n> = {\n pcm16: { type: 'audio/pcm', sampleRate: 24_000 },\n g711_ulaw: { type: 'audio/pcmu', sampleRate: 8_000 },\n g711_alaw: { type: 'audio/pcma', sampleRate: 8_000 },\n};\n\n/** The OpenAI Realtime transcription wire protocol. */\nexport const openAIRealtimeProtocol = (\n extras: Pick<\n OpenAIRealtimeTranscriberOptions,\n 'noiseReduction' | 'include' | 'transcriptionOptions'\n > = {},\n): RealtimeProtocol => ({\n provider: ADAPTER,\n commitAck: 'committed',\n endCommit: 'if-audio',\n // No per-turn cap by default: server VAD ends turns. Manual sessions may opt\n // in with `maxTurnSeconds`.\n // OpenAI rejects a commit with under 100 ms of audio as empty.\n minCommitSeconds: 0.1,\n sessionMessages(config: RealtimeSessionConfig) {\n return [\n {\n type: 'session.update',\n session: openAIRealtimeTranscriptionSession(config, extras),\n },\n ];\n },\n appendMessage(base64Audio: string) {\n return { type: 'input_audio_buffer.append', audio: base64Audio };\n },\n commitMessage() {\n return { type: 'input_audio_buffer.commit' };\n },\n parse: parseOpenAIRealtimeEvent,\n});\n\n/**\n * The GA transcription session object (`session.type: \"transcription\"`),\n * shared by `session.update` and the client-secret mint request.\n */\nexport function openAIRealtimeTranscriptionSession(\n config: RealtimeSessionConfig,\n extras: Pick<\n OpenAIRealtimeTranscriberOptions,\n 'noiseReduction' | 'include' | 'transcriptionOptions'\n > = {},\n): Record<string, unknown> {\n const input: Record<string, unknown> = {\n format:\n config.format.encoding === 'pcm16'\n ? { type: 'audio/pcm', rate: config.format.sampleRate }\n : { type: WIRE_FORMATS[config.format.encoding].type },\n // Extension fields first: the typed settings must win, so the model\n // sent (or bound to a minted client secret) always matches the model\n // reported in usage.\n transcription: {\n ...compactJson(extras.transcriptionOptions ?? {}),\n ...compactJson({\n model: config.model,\n language: config.language,\n prompt: config.prompt,\n }),\n },\n turn_detection: turnDetectionToWire(config.turnDetection),\n };\n if (extras.noiseReduction) {\n input.noise_reduction = { type: extras.noiseReduction };\n }\n\n return compactJson({\n type: 'transcription',\n audio: { input },\n include: extras.include,\n });\n}\n\n/** Maps one OpenAI Realtime server event to normalised session events. */\nexport function parseOpenAIRealtimeEvent(\n message: unknown,\n): RealtimeProtocolEvent[] {\n if (!message || typeof message !== 'object') {\n return [];\n }\n\n const event = message as Record<string, unknown>;\n const itemId = stringField(event, 'item_id');\n\n switch (event.type) {\n case 'session.created':\n case 'session.updated':\n case 'transcription_session.created':\n case 'transcription_session.updated': {\n const session = event.session as Record<string, unknown> | undefined;\n return [\n { type: 'session', sessionId: session && stringField(session, 'id') },\n ];\n }\n case 'input_audio_buffer.committed':\n return [{ type: 'committed', itemId }];\n case 'input_audio_buffer.speech_started':\n return [\n {\n type: 'speech_started',\n itemId,\n audioMs: numberField(event, 'audio_start_ms'),\n },\n ];\n case 'input_audio_buffer.speech_stopped':\n return [\n {\n type: 'speech_stopped',\n itemId,\n audioMs: numberField(event, 'audio_end_ms'),\n },\n ];\n case 'conversation.item.input_audio_transcription.delta': {\n const delta = stringField(event, 'delta');\n return delta ? [{ type: 'partial', itemId, delta }] : [];\n }\n case 'conversation.item.input_audio_transcription.completed': {\n const usage = event.usage;\n return [\n {\n type: 'final',\n itemId,\n text: stringField(event, 'transcript') ?? '',\n providerUsage:\n usage && typeof usage === 'object' && !Array.isArray(usage)\n ? (usage as Record<string, unknown>)\n : undefined,\n raw: event,\n },\n ];\n }\n case 'conversation.item.input_audio_transcription.failed': {\n const error = (event.error ?? {}) as Record<string, unknown>;\n return [\n {\n type: 'error',\n message: stringField(error, 'message') ?? 'transcription failed',\n code: stringField(error, 'code'),\n raw: event,\n },\n ];\n }\n case 'error': {\n const error = (event.error ?? {}) as Record<string, unknown>;\n const code = stringField(error, 'code');\n if (code === 'input_audio_buffer_commit_empty') {\n return [{ type: 'commit_empty' }];\n }\n return [\n {\n type: 'error',\n message: stringField(error, 'message') ?? 'unknown error',\n code,\n raw: event,\n },\n ];\n }\n default:\n return [];\n }\n}\n\n/**\n * Realtime transcriber for OpenAI and OpenAI-Realtime-compatible servers.\n *\n * Auth: `apiKey` is sent as `Authorization: Bearer` (Node only). In browsers,\n * pass `clientSecret` (an ephemeral client secret minted by your server); it\n * is sent as the `openai-insecure-api-key.<secret>` subprotocol. Supplying\n * `apiKey` or `headers` in a browser throws.\n */\nexport class OpenAIRealtimeTranscriber implements StreamingTranscriber {\n readonly type = ADAPTER;\n readonly audioFormat: StreamingAudioFormat;\n\n private readonly options: OpenAIRealtimeTranscriberOptions;\n private readonly url: string;\n private readonly createWebSocket: SpeechWebSocketFactory;\n private readonly protocol: RealtimeProtocol;\n\n constructor(options: OpenAIRealtimeTranscriberOptions = {}) {\n assertNoBrowserApiKey(ADAPTER, options.apiKey);\n\n this.options = options;\n this.url = resolveRealtimeUrl(\n options.baseUrl ?? OPENAI_REALTIME_DEFAULT_URL,\n );\n this.createWebSocket = resolveWebSocketFactory(ADAPTER, options);\n this.audioFormat = resolveOpenAIRealtimeFormat(options.format);\n this.protocol = openAIRealtimeProtocol(options);\n resolveTurnLimit(ADAPTER, undefined, options);\n }\n\n /**\n * A turn limit applies only to `manual` sessions with `maxTurnSeconds` set;\n * under server or semantic VAD the provider ends turns itself.\n */\n turnLimit(\n options: StreamingSessionOptions = {},\n ): StreamingTurnLimit | undefined {\n return describeTurnLimit(\n this.resolveTurnLimit(options),\n this.sessionFormat(options),\n );\n }\n\n private sessionFormat(\n options: StreamingSessionOptions,\n ): StreamingAudioFormat {\n // A different encoding starts from that encoding's defaults.\n return resolveOpenAIRealtimeFormat(\n options.format?.encoding &&\n options.format.encoding !== this.audioFormat.encoding\n ? options.format\n : { ...this.audioFormat, ...options.format },\n );\n }\n\n private resolveTurnLimit(\n options: StreamingSessionOptions,\n ): TurnLimit | undefined {\n const limit = resolveTurnLimit(\n ADAPTER,\n this.protocol.maxTurnSeconds,\n options,\n this.options,\n );\n if (this.turnDetection(options).type !== 'manual') {\n return undefined;\n }\n assertTurnLimitFitsProtocol(this.protocol, limit);\n return limit;\n }\n\n private turnDetection(\n options: StreamingSessionOptions,\n ): StreamingTurnDetection {\n return (\n options.turnDetection ??\n this.options.turnDetection ?? { type: 'server_vad' }\n );\n }\n\n start(options: StreamingSessionOptions = {}): StreamingSession {\n const format = this.sessionFormat(options);\n const connect = realtimeConnector({\n adapter: ADAPTER,\n url: this.url,\n apiKey: this.options.apiKey,\n headers: mergeHeaderRecords(this.options.headers, options.headers),\n clientSecret: options.clientSecret ?? this.options.clientSecret,\n protocols: (secret) =>\n secret\n ? ['realtime', `${CLIENT_SECRET_SUBPROTOCOL_PREFIX}${secret}`]\n : ['realtime'],\n });\n\n return new RealtimeTranscriptionSession({\n protocol: this.protocol,\n config: {\n model:\n options.model?.trim() ||\n this.options.model?.trim() ||\n OPENAI_REALTIME_DEFAULT_MODEL,\n format,\n turnDetection: this.turnDetection(options),\n language: options.language ?? this.options.language,\n prompt: options.prompt ?? this.options.prompt,\n },\n connect,\n createWebSocket: this.createWebSocket,\n timeoutMs: this.options.timeoutMs ?? DEFAULT_STREAMING_TIMEOUT_MS,\n connectTimeoutMs:\n this.options.connectTimeoutMs ?? DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n highWaterMark:\n this.options.highWaterMark ?? DEFAULT_STREAMING_HIGH_WATER_MARK,\n maxBufferedBytes:\n this.options.maxBufferedBytes ?? DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n signal: options.signal,\n onUsage: [this.options.onUsage, options.onUsage],\n turnLimit: this.resolveTurnLimit(options),\n });\n }\n}\n\n/**\n * Resolves a realtime WebSocket URL: `http(s)` becomes `ws(s)`, a server root\n * gets `/v1/realtime`, a versioned API root gets `/realtime`, and\n * `intent=transcription` is added unless an `intent` is already present. No\n * `model` query parameter is added: the model travels in `session.update`.\n */\nexport function resolveRealtimeUrl(baseUrl: string): string {\n const url = new URL(\n toWebSocketUrl(resolveOpenAICompatibleUrl(baseUrl, 'realtime'), ADAPTER),\n );\n if (!url.searchParams.has('intent')) {\n url.searchParams.set('intent', 'transcription');\n }\n return url.toString();\n}\n\nexport function resolveOpenAIRealtimeFormat(\n format: Partial<StreamingAudioFormat> | undefined,\n): StreamingAudioFormat {\n const encoding = format?.encoding ?? 'pcm16';\n const wire = WIRE_FORMATS[encoding];\n if (!wire) {\n throw new SpeechConfigurationError(\n `openai-realtime does not support encoding ${String(encoding)}`,\n ADAPTER,\n );\n }\n\n const resolved: StreamingAudioFormat = {\n encoding,\n sampleRate: format?.sampleRate ?? wire.sampleRate,\n channels: format?.channels ?? 1,\n };\n if (resolved.sampleRate !== wire.sampleRate || resolved.channels !== 1) {\n throw new SpeechConfigurationError(\n `openai-realtime requires ${encoding} at ${wire.sampleRate} Hz mono (got ${resolved.sampleRate} Hz, ${resolved.channels} channel(s)); resample before streaming`,\n ADAPTER,\n );\n }\n return resolved;\n}\n\nfunction turnDetectionToWire(\n turnDetection: StreamingTurnDetection,\n): Record<string, unknown> | null {\n switch (turnDetection.type) {\n case 'manual':\n return null;\n case 'server_vad':\n return compactJson({\n type: 'server_vad',\n threshold: turnDetection.threshold,\n prefix_padding_ms: turnDetection.prefixPaddingMs,\n silence_duration_ms: turnDetection.silenceDurationMs,\n });\n case 'semantic_vad':\n return compactJson({\n type: 'semantic_vad',\n eagerness: turnDetection.eagerness,\n });\n default:\n throw new SpeechConfigurationError(\n `Unknown turn detection type: ${String((turnDetection as { type?: unknown }).type)}`,\n ADAPTER,\n );\n }\n}\n\nfunction stringField(\n record: Record<string, unknown>,\n key: string,\n): string | undefined {\n const value = record[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction numberField(\n record: Record<string, unknown>,\n key: string,\n): number | undefined {\n const value = record[key];\n return typeof value === 'number' ? value : undefined;\n}\n","/**\n * vLLM realtime transcription adapter (`voxtral-realtime`), for Mistral's\n * Voxtral Realtime models (e.g. `mistralai/Voxtral-Mini-4B-Realtime-2602`)\n * served by vLLM at `/v1/realtime`.\n *\n * vLLM's protocol borrows OpenAI event names but is not OpenAI-compatible.\n * Verified against vLLM's `entrypoints/speech_to_text/realtime` source and a\n * live server:\n *\n * 1. The server sends `session.created` (`{ id, created }`) on connect.\n * 2. The client sends `session.update` with a top-level `model` (no nested\n * `session` object, no acknowledgement; an unknown model yields an\n * `error` with code `model_not_found`).\n * 3. A non-final `input_audio_buffer.commit` starts generation for a turn.\n * 4. `input_audio_buffer.append` carries base64 PCM16 at 16 kHz mono. Audio\n * appended before the start commit is kept.\n * 5. The server streams `transcription.delta` (`{ delta }`, often empty) while\n * audio arrives, at roughly real-time speed.\n * 6. `input_audio_buffer.commit` with `final: true` ends the turn; the server\n * replies `transcription.done` (`{ text, usage }`, token usage only) and\n * clears its buffer. A further start commit opens the next turn on the same\n * socket.\n * 7. Errors arrive as `{ type: 'error', error: string, code }` and leave the\n * socket open; this adapter treats every error as fatal.\n * 8. A turn whose audio and tokens exceed `max_model_len` (~12.5 tokens per\n * audio second plus a 39-token prompt) ends in `error` `processing_error`\n * and can take the engine down. The limit is per turn: after a final\n * commit vLLM clears the turn's context. The session therefore rolls over\n * to a new turn before `maxTurnSeconds` (default 270) of audio.\n * 9. A stray final commit leaves a stale end marker: the next turn ends at once\n * with `prompt_tokens: 1` (any audio costs 39) and its audio is dropped.\n * The session fails closed on that, and on any `done` for a turn it did\n * not commit, rather than losing audio silently.\n */\n\nimport { SpeechConfigurationError } from '../shared/errors.js';\nimport { resolveOpenAICompatibleUrl } from '../shared/http.js';\nimport {\n assertNoBrowserApiKey,\n CLIENT_SECRET_SUBPROTOCOL_PREFIX,\n mergeHeaderRecords,\n realtimeConnector,\n} from '../shared/realtime-auth.js';\nimport {\n DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n DEFAULT_STREAMING_HIGH_WATER_MARK,\n DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n DEFAULT_STREAMING_TIMEOUT_MS,\n type RealtimeProtocol,\n type RealtimeProtocolEvent,\n RealtimeTranscriptionSession,\n} from '../shared/realtime-session.js';\nimport type {\n StreamingAudioFormat,\n StreamingSession,\n StreamingSessionOptions,\n StreamingTranscriber,\n StreamingTurnDetection,\n StreamingTurnLimit,\n VoxtralRealtimeTranscriberOptions,\n} from '../shared/streaming-types.js';\nimport {\n DEFAULT_MAX_TURN_SECONDS,\n describeTurnLimit,\n resolveTurnLimit,\n type TurnLimit,\n} from '../shared/turn-limit.js';\nimport {\n resolveWebSocketFactory,\n type SpeechWebSocketFactory,\n toWebSocketUrl,\n} from '../shared/websocket.js';\n\nconst ADAPTER = 'voxtral-realtime';\n\n/** vLLM's default served model name for Voxtral Mini Realtime. */\nexport const VOXTRAL_REALTIME_DEFAULT_MODEL =\n 'mistralai/Voxtral-Mini-4B-Realtime-2602';\n\n/** The only input format vLLM's realtime endpoint accepts. */\nexport const VOXTRAL_REALTIME_AUDIO_FORMAT: Readonly<StreamingAudioFormat> =\n Object.freeze({ encoding: 'pcm16', sampleRate: 16_000, channels: 1 });\n\n/** The vLLM realtime wire protocol. */\nexport const voxtralRealtimeProtocol = (): RealtimeProtocol => ({\n provider: ADAPTER,\n // vLLM never acknowledges a commit; `transcription.done` closes the turn.\n commitAck: 'final',\n // The start commit is sent lazily before a turn's first audio, so a session\n // with no uncommitted audio has no open turn and needs no end commit.\n endCommit: 'if-audio',\n // vLLM's context (`max_model_len`) covers one turn; roll over before it.\n maxTurnSeconds: DEFAULT_MAX_TURN_SECONDS,\n sessionMessages(config) {\n return [{ type: 'session.update', model: config.model }];\n },\n turnStartMessage() {\n return { type: 'input_audio_buffer.commit' };\n },\n appendMessage(base64Audio) {\n return { type: 'input_audio_buffer.append', audio: base64Audio };\n },\n commitMessage() {\n // Every turn end is a final commit; the next turn gets a new start commit.\n return { type: 'input_audio_buffer.commit', final: true };\n },\n parse: parseVoxtralRealtimeEvent,\n});\n\n/** Maps one vLLM realtime server event to normalised session events. */\nexport function parseVoxtralRealtimeEvent(\n message: unknown,\n): RealtimeProtocolEvent[] {\n if (!message || typeof message !== 'object') {\n return [];\n }\n\n const event = message as Record<string, unknown>;\n switch (event.type) {\n case 'session.created':\n return [\n {\n type: 'session',\n sessionId: typeof event.id === 'string' ? event.id : undefined,\n },\n ];\n case 'transcription.delta': {\n const delta = typeof event.delta === 'string' ? event.delta : '';\n return delta ? [{ type: 'partial', delta }] : [];\n }\n case 'transcription.done': {\n const usage =\n event.usage &&\n typeof event.usage === 'object' &&\n !Array.isArray(event.usage)\n ? (event.usage as Record<string, unknown>)\n : undefined;\n // Any audio, even one sample, costs the turn's full audio prompt\n // (39 tokens on Voxtral Mini Realtime); a turn that consumed no audio\n // reports `prompt_tokens: 1`.\n const promptTokens = usage?.prompt_tokens;\n return [\n {\n type: 'final',\n text: typeof event.text === 'string' ? event.text.trim() : '',\n providerUsage: usage,\n raw: event,\n audioConsumed:\n typeof promptTokens === 'number' ? promptTokens > 1 : undefined,\n },\n ];\n }\n case 'error':\n return [\n {\n type: 'error',\n message:\n typeof event.error === 'string' ? event.error : 'unknown error',\n code: typeof event.code === 'string' ? event.code : undefined,\n raw: event,\n },\n ];\n default:\n return [];\n }\n}\n\n/**\n * Realtime transcriber for Voxtral Realtime served by vLLM.\n *\n * - Audio: PCM16, 16 kHz, mono only; write whole samples (even byte counts).\n * - Turns: manual only. vLLM has no VAD; it streams partials continuously and\n * produces one final per `commit()` / `end()`, plus one per automatic\n * rollover when a turn reaches `maxTurnSeconds` (default 270).\n * - `language` and `prompt` are not sent: vLLM's realtime session accepts\n * only `model`.\n * - Auth: `apiKey` → `Authorization: Bearer` (Node only). vLLM has no\n * ephemeral tokens, so browsers must connect through the consumer's own\n * proxy or token gateway, passing that gateway's short-lived `clientSecret`\n * (sent as the `openai-insecure-api-key.<secret>` subprotocol next to\n * `realtime`; the gateway must echo `realtime`).\n */\nexport class VoxtralRealtimeTranscriber implements StreamingTranscriber {\n readonly type = ADAPTER;\n readonly audioFormat: StreamingAudioFormat = {\n ...VOXTRAL_REALTIME_AUDIO_FORMAT,\n };\n\n private readonly options: VoxtralRealtimeTranscriberOptions;\n private readonly url: string;\n private readonly createWebSocket: SpeechWebSocketFactory;\n private readonly protocol = voxtralRealtimeProtocol();\n\n constructor(options: VoxtralRealtimeTranscriberOptions = {}) {\n assertNoBrowserApiKey(ADAPTER, options.apiKey);\n if (!options.baseUrl?.trim()) {\n throw new SpeechConfigurationError(\n 'voxtral-realtime requires baseUrl (the vLLM server, e.g. http://localhost:8000 or its /v1 root)',\n ADAPTER,\n );\n }\n\n this.options = options;\n this.url = resolveVoxtralRealtimeUrl(options.baseUrl);\n this.createWebSocket = resolveWebSocketFactory(ADAPTER, options);\n assertFormat(options.format);\n assertTurnDetection(options.turnDetection);\n this.resolveTurnLimit();\n }\n\n turnLimit(\n options: StreamingSessionOptions = {},\n ): StreamingTurnLimit | undefined {\n assertFormat(options.format);\n return describeTurnLimit(\n this.resolveTurnLimit(options),\n VOXTRAL_REALTIME_AUDIO_FORMAT,\n );\n }\n\n private resolveTurnLimit(\n options: StreamingSessionOptions = {},\n ): TurnLimit | undefined {\n return resolveTurnLimit(\n ADAPTER,\n this.protocol.maxTurnSeconds,\n options,\n this.options,\n );\n }\n\n start(options: StreamingSessionOptions = {}): StreamingSession {\n assertFormat(options.format);\n assertTurnDetection(options.turnDetection);\n const turnLimit = this.resolveTurnLimit(options);\n const connect = realtimeConnector({\n adapter: ADAPTER,\n url: this.url,\n apiKey: this.options.apiKey,\n headers: mergeHeaderRecords(this.options.headers, options.headers),\n clientSecret: options.clientSecret ?? this.options.clientSecret,\n // A plain vLLM server selects no subprotocol, so offer none unless a\n // gateway token must travel in one.\n protocols: (secret) =>\n secret\n ? ['realtime', `${CLIENT_SECRET_SUBPROTOCOL_PREFIX}${secret}`]\n : [],\n });\n\n return new RealtimeTranscriptionSession({\n protocol: this.protocol,\n config: {\n model:\n options.model?.trim() ||\n this.options.model?.trim() ||\n VOXTRAL_REALTIME_DEFAULT_MODEL,\n format: { ...VOXTRAL_REALTIME_AUDIO_FORMAT },\n turnDetection: { type: 'manual' },\n language: options.language ?? this.options.language,\n },\n connect,\n createWebSocket: this.createWebSocket,\n timeoutMs: this.options.timeoutMs ?? DEFAULT_STREAMING_TIMEOUT_MS,\n connectTimeoutMs:\n this.options.connectTimeoutMs ?? DEFAULT_STREAMING_CONNECT_TIMEOUT_MS,\n highWaterMark:\n this.options.highWaterMark ?? DEFAULT_STREAMING_HIGH_WATER_MARK,\n maxBufferedBytes:\n this.options.maxBufferedBytes ?? DEFAULT_STREAMING_MAX_BUFFERED_BYTES,\n signal: options.signal,\n onUsage: [this.options.onUsage, options.onUsage],\n turnLimit,\n });\n }\n}\n\n/**\n * Resolves vLLM's realtime WebSocket URL: `http(s)` becomes `ws(s)`, a server\n * root gets `/v1/realtime`, a versioned API root (e.g. a gateway's\n * `/stt/v1`) gets `/realtime`, and a URL ending in `/realtime` is kept.\n */\nexport function resolveVoxtralRealtimeUrl(baseUrl: string): string {\n return toWebSocketUrl(\n resolveOpenAICompatibleUrl(baseUrl, 'realtime'),\n ADAPTER,\n );\n}\n\nfunction assertFormat(format: Partial<StreamingAudioFormat> | undefined): void {\n const expected = VOXTRAL_REALTIME_AUDIO_FORMAT;\n if (\n format &&\n ((format.encoding !== undefined && format.encoding !== expected.encoding) ||\n (format.sampleRate !== undefined &&\n format.sampleRate !== expected.sampleRate) ||\n (format.channels !== undefined && format.channels !== expected.channels))\n ) {\n throw new SpeechConfigurationError(\n `voxtral-realtime requires pcm16 at 16000 Hz mono (got ${format.encoding ?? 'pcm16'} at ${format.sampleRate ?? 16_000} Hz, ${format.channels ?? 1} channel(s)); resample before streaming`,\n ADAPTER,\n );\n }\n}\n\nfunction assertTurnDetection(\n turnDetection: StreamingTurnDetection | undefined,\n): void {\n if (turnDetection && turnDetection.type !== 'manual') {\n throw new SpeechConfigurationError(\n `voxtral-realtime has no server turn detection (got ${turnDetection.type}); use manual turns with commit()/end()`,\n ADAPTER,\n );\n }\n}\n","import {\n compactJson,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\n resolveOpenAICompatibleUrl,\n voiceToString,\n} from '../shared/http.js';\nimport type {\n OpenAICompatibleSpeechSynthesizerOptions,\n SpeechSynthesizer,\n SynthesisRequest,\n SynthesizedSpeech,\n} from '../shared/types.js';\n\nexport class OpenAICompatibleSpeechSynthesizer\n extends HttpSpeechAdapter\n implements SpeechSynthesizer\n{\n readonly type = 'openai-compatible' as const;\n\n /** Explicit override, resolved relative to `baseUrl` (or absolute). */\n private readonly speechPath?: string;\n private readonly defaultModel: string;\n private readonly defaultVoice: string;\n\n constructor(options: OpenAICompatibleSpeechSynthesizerOptions) {\n super(options);\n this.speechPath = options.speechPath;\n this.defaultModel = options.defaultModel ?? 'tts-1';\n this.defaultVoice = options.defaultVoice ?? 'alloy';\n }\n\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n const outputFormat = request.outputFormat ?? 'mp3';\n const payload = compactJson({\n model: request.model ?? this.defaultModel,\n input: request.text,\n voice: voiceToString(request.voice, this.defaultVoice),\n // biome-ignore lint/style/useNamingConvention: OpenAI-compatible API uses snake_case.\n response_format: outputFormat,\n speed: request.speed,\n });\n\n // Without an override, resolve the endpoint the way the transcriber does:\n // a server root gets `/v1/audio/speech`, a `/vN` API root gets\n // `/audio/speech`, and a full endpoint URL is used as-is. Resolved per\n // call so an invalid baseUrl still fails at synthesize time.\n const endpoint =\n this.speechPath ??\n resolveOpenAICompatibleUrl(this.baseUrl, 'audio/speech');\n\n const speech = await this.post(\n this.type,\n endpoint,\n {\n headers: {\n 'content-type': 'application/json',\n },\n body: JSON.stringify(payload),\n signal: request.signal,\n },\n (response) => readSynthesizedSpeechResponse(response, this.type),\n );\n\n return {\n ...speech,\n format: speech.format ?? outputFormat,\n model: speech.model ?? String(payload.model),\n };\n }\n}\n","import {\n DEFAULT_MAX_AUDIO_BYTES,\n normalizeAudioInput,\n} from '../shared/audio.js';\nimport { SpeechProviderError } from '../shared/errors.js';\nimport {\n appendOptionalFormValue,\n HttpSpeechAdapter,\n readTranscriptResponse,\n resolveOpenAICompatibleUrl,\n} from '../shared/http.js';\nimport {\n resolveRetryOptions,\n type SpeechRetryOptions,\n} from '../shared/retry.js';\nimport type {\n OpenAICompatibleTranscriberOptions,\n TimestampGranularity,\n Transcriber,\n TranscriptionRequest,\n TranscriptionResponseFormat,\n TranscriptResult,\n} from '../shared/types.js';\nimport {\n audioSecondsFromProviderUsage,\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from '../shared/usage.js';\n\nconst DEFAULT_MODEL = 'whisper-1';\n\n/**\n * Models that only accept `json`/`text` and reject `verbose_json` and\n * `timestamp_granularities[]` (e.g. `gpt-4o-transcribe`,\n * `gpt-4o-mini-transcribe`, optionally gateway-prefixed like\n * `openai/gpt-4o-transcribe`).\n */\nconst JSON_ONLY_MODEL = /(^|\\/)gpt-4o[\\w.-]*-transcribe/i;\n\n/** Provider 400/422 bodies that indicate an unsupported format option. */\nconst UNSUPPORTED_FORMAT_ERROR =\n /verbose_json|response_format|timestamp_granularit/i;\n\nexport function isJsonOnlyTranscriptionModel(model: string): boolean {\n return JSON_ONLY_MODEL.test(model);\n}\n\ninterface TranscriptionWireOptions {\n model: string;\n responseFormat: TranscriptionResponseFormat;\n timestampGranularities?: TimestampGranularity[];\n}\n\n/**\n * OpenAI-compatible speech-to-text adapter (`POST <base>/audio/transcriptions`).\n * Works with OpenAI, Groq, Fireworks, LiteLLM/Bifrost gateways, vLLM,\n * speaches/faster-whisper-server, and whisper.cpp server.\n *\n * Server-side only: the adapter holds the API key.\n */\nexport class OpenAICompatibleTranscriber\n extends HttpSpeechAdapter\n implements Transcriber\n{\n readonly type = 'openai-compatible' as const;\n\n private readonly endpoint: string;\n private readonly model: string;\n private readonly responseFormat?: TranscriptionResponseFormat;\n private readonly maxBytes: number;\n private readonly retry: Required<SpeechRetryOptions> | false;\n private readonly onUsage?: SpeechUsageCallback;\n\n constructor(options: OpenAICompatibleTranscriberOptions) {\n super(options);\n this.endpoint = resolveOpenAICompatibleUrl(\n options.baseUrl,\n 'audio/transcriptions',\n );\n this.model = options.model?.trim() || DEFAULT_MODEL;\n this.responseFormat = options.responseFormat;\n this.maxBytes = options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES;\n this.retry = resolveRetryOptions(options.retry);\n this.onUsage = options.onUsage;\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes: request.maxBytes ?? this.maxBytes,\n adapter: this.type,\n signal: request.signal,\n });\n const wire = this.resolveWireOptions(request);\n\n const send = (options: TranscriptionWireOptions) => {\n const form = new FormData();\n form.append('file', audio.blob, audio.filename);\n form.set('model', options.model);\n appendOptionalFormValue(form, 'language', request.language);\n appendOptionalFormValue(form, 'prompt', request.prompt);\n appendOptionalFormValue(form, 'temperature', request.temperature);\n form.set('response_format', options.responseFormat);\n for (const granularity of options.timestampGranularities ?? []) {\n form.append('timestamp_granularities[]', granularity);\n }\n\n return this.post(\n this.type,\n this.endpoint,\n {\n body: form,\n headers: request.headers,\n signal: request.signal,\n },\n (response) => readTranscriptResponse(response, this.type),\n this.retry,\n );\n };\n\n let transcript: TranscriptResult;\n try {\n transcript = await send(wire);\n } catch (error) {\n if (!isUnsupportedFormatError(error, wire)) {\n throw error;\n }\n // The server rejected verbose_json/timestamps: fall back to plain JSON.\n transcript = await send({\n model: wire.model,\n responseFormat: 'json',\n });\n }\n\n const providerUsage = extractProviderUsage(transcript.raw);\n const usage: SpeechUsage = {\n operation: 'transcription',\n provider: this.type,\n model: transcript.model ?? wire.model,\n audioSeconds:\n transcript.durationSeconds ??\n audioSecondsFromProviderUsage(providerUsage) ??\n audio.durationSeconds,\n bytes: audio.bytes,\n providerUsage,\n };\n const result: TranscriptResult = {\n ...transcript,\n // Plain `text` responses end with a newline; JSON text is kept verbatim.\n text:\n transcript.raw === undefined ? transcript.text.trim() : transcript.text,\n provider: this.type,\n model: transcript.model ?? wire.model,\n usage,\n };\n\n await reportSpeechUsage(usage, this.onUsage, request.onUsage);\n return result;\n }\n\n private resolveWireOptions(\n request: TranscriptionRequest,\n ): TranscriptionWireOptions {\n const model = request.model?.trim() || this.model;\n const jsonOnly = isJsonOnlyTranscriptionModel(model);\n let responseFormat =\n request.responseFormat ??\n this.responseFormat ??\n (jsonOnly ? 'json' : 'verbose_json');\n\n if (jsonOnly && responseFormat === 'verbose_json') {\n responseFormat = 'json';\n }\n\n const timestampGranularities =\n responseFormat === 'verbose_json' &&\n request.timestampGranularities?.length\n ? [...new Set(request.timestampGranularities)]\n : undefined;\n\n return { model, responseFormat, timestampGranularities };\n }\n}\n\nfunction isUnsupportedFormatError(\n error: unknown,\n wire: TranscriptionWireOptions,\n): boolean {\n return (\n error instanceof SpeechProviderError &&\n (error.status === 400 || error.status === 422) &&\n (wire.responseFormat === 'verbose_json' ||\n Boolean(wire.timestampGranularities?.length)) &&\n UNSUPPORTED_FORMAT_ERROR.test(error.responseBody ?? '')\n );\n}\n\nfunction extractProviderUsage(\n raw: unknown,\n): Record<string, unknown> | undefined {\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n const usage = (raw as Record<string, unknown>).usage;\n return usage && typeof usage === 'object' && !Array.isArray(usage)\n ? (usage as Record<string, unknown>)\n : undefined;\n}\n","import {\n createHappyVerticalSynthesisForm,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\n} from '../shared/http.js';\nimport type {\n Qwen3SpeechSynthesizerOptions,\n SpeechSynthesizer,\n SynthesisRequest,\n SynthesizedSpeech,\n} from '../shared/types.js';\n\nexport class Qwen3SpeechSynthesizer\n extends HttpSpeechAdapter\n implements SpeechSynthesizer\n{\n readonly type = 'qwen3-tts' as const;\n\n private readonly speechPath: string;\n private readonly defaultModel: string;\n private readonly defaultVoice?: string;\n\n constructor(options: Qwen3SpeechSynthesizerOptions) {\n super(options);\n this.speechPath = options.speechPath ?? '/v1/audio/speech';\n this.defaultModel = options.defaultModel ?? 'qwen3-tts';\n this.defaultVoice = options.defaultVoice;\n }\n\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n const form = createHappyVerticalSynthesisForm(request, this.defaultVoice);\n const speech = await this.post(\n this.type,\n this.speechPath,\n {\n body: form,\n signal: request.signal,\n },\n (response) => readSynthesizedSpeechResponse(response, this.type),\n );\n\n return {\n ...speech,\n format: speech.format ?? request.outputFormat,\n model: speech.model ?? request.model ?? this.defaultModel,\n };\n }\n}\n","import { normalizeAudioInput } from '../shared/audio.js';\nimport {\n appendOptionalFormValue,\n createHappyVerticalSynthesisForm,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\n readTranscriptResponse,\n} from '../shared/http.js';\nimport type {\n SpeechSynthesizer,\n StudioServerSpeechSynthesizerOptions,\n StudioServerTranscriberOptions,\n SynthesisRequest,\n SynthesizedSpeech,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from '../shared/types.js';\n\nexport class StudioServerTranscriber\n extends HttpSpeechAdapter\n implements Transcriber\n{\n readonly type = 'studio-server' as const;\n private readonly transcribePath: string;\n\n constructor(options: StudioServerTranscriberOptions) {\n super(options);\n this.transcribePath = options.transcribePath ?? '/v1/transcribe';\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes: request.maxBytes,\n adapter: this.type,\n signal: request.signal,\n deriveExtension: false,\n });\n const form = new FormData();\n form.append('audio', audio.blob, audio.filename);\n appendOptionalFormValue(form, 'language', request.language);\n\n return this.post(\n this.type,\n this.transcribePath,\n {\n body: form,\n headers: request.headers,\n signal: request.signal,\n },\n (response) => readTranscriptResponse(response, this.type),\n );\n }\n}\n\nexport class StudioServerSpeechSynthesizer\n extends HttpSpeechAdapter\n implements SpeechSynthesizer\n{\n readonly type = 'studio-server' as const;\n private readonly synthesizePath: string;\n private readonly defaultVoice?: string;\n\n constructor(options: StudioServerSpeechSynthesizerOptions) {\n super(options);\n this.synthesizePath = options.synthesizePath ?? '/v1/tts/synthesize';\n this.defaultVoice = options.defaultVoice;\n }\n\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n const form = createHappyVerticalSynthesisForm(request, this.defaultVoice);\n\n return this.post(\n this.type,\n this.synthesizePath,\n {\n body: form,\n signal: request.signal,\n },\n (response) => readSynthesizedSpeechResponse(response, this.type),\n );\n }\n}\n","/**\n * Streaming transcriber factory, environment configuration, and the\n * record-then-send `Transcriber` wrapper.\n */\n\nimport { OpenAIRealtimeTranscriber } from '../adapters/openai-realtime.js';\nimport { VoxtralRealtimeTranscriber } from '../adapters/voxtral-realtime.js';\nimport { DEFAULT_MAX_AUDIO_BYTES, normalizeAudioInput } from './audio.js';\nimport {\n defaultEnv,\n mergeHeaderInits,\n parseHeadersEnv,\n parseOptionalInteger,\n readEnv,\n type SpeechEnv,\n} from './env.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n SpeechError,\n} from './errors.js';\nimport { unwrapRawAudio } from './pcm.js';\nimport type {\n GetStreamingTranscriberOptions,\n StreamingSessionOptions,\n StreamingTranscriber,\n StreamingTranscriberType,\n StreamingTurnDetection,\n} from './streaming-types.js';\nimport type {\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from './types.js';\n\n/** Streaming adapters `getStreamingTranscriber()` can build. */\nexport const STREAMING_TRANSCRIBER_TYPES: readonly StreamingTranscriberType[] =\n ['openai-realtime', 'voxtral-realtime'];\n\n/** Environment variable names read for each streaming setting, in priority order. */\nexport const STREAMING_TRANSCRIBER_ENV_KEYS = {\n type: ['HAVE_SPEECH_STREAMING_TYPE'],\n baseUrl: ['HAVE_SPEECH_STREAMING_BASE_URL'],\n model: ['HAVE_SPEECH_STREAMING_MODEL'],\n apiKey: ['HAVE_SPEECH_STREAMING_API_KEY'],\n language: ['HAVE_SPEECH_STREAMING_LANGUAGE'],\n turnDetection: ['HAVE_SPEECH_STREAMING_TURN_DETECTION'],\n timeoutMs: [\n 'HAVE_SPEECH_STREAMING_TIMEOUT',\n 'HAVE_SPEECH_STREAMING_TIMEOUT_MS',\n ],\n connectTimeoutMs: ['HAVE_SPEECH_STREAMING_CONNECT_TIMEOUT_MS'],\n headers: ['HAVE_SPEECH_STREAMING_HEADERS'],\n maxTurnSeconds: ['HAVE_SPEECH_STREAMING_MAX_TURN_SECONDS'],\n} as const;\n\nexport interface StreamingFactoryContext {\n env?: SpeechEnv;\n /** Headers merged under explicit `headers` (env → context → options). */\n headers?: HeadersInit;\n}\n\nexport function isStreamingTranscriberType(\n type: string | undefined,\n): type is StreamingTranscriberType {\n return (\n type !== undefined &&\n (STREAMING_TRANSCRIBER_TYPES as readonly string[]).includes(type)\n );\n}\n\n/**\n * Creates a realtime streaming transcriber. Explicit options win over\n * `HAVE_SPEECH_STREAMING_*` environment variables. The type defaults to\n * `openai-realtime`.\n */\nexport function getStreamingTranscriber(\n options: GetStreamingTranscriberOptions = {},\n context: StreamingFactoryContext = {},\n): StreamingTranscriber {\n const env = context.env ?? defaultEnv();\n const keys = STREAMING_TRANSCRIBER_ENV_KEYS;\n const type = options.type ?? readEnv(env, ...keys.type) ?? 'openai-realtime';\n\n if (!isStreamingTranscriberType(type)) {\n throw new InvalidSpeechAdapterError(type, 'streaming STT');\n }\n\n const headers = mergeHeaderInits(\n parseHeadersEnv(env, keys.headers),\n context.headers,\n options.headers,\n );\n const resolved = {\n ...options,\n type,\n baseUrl: (options.baseUrl ?? readEnv(env, ...keys.baseUrl))?.trim(),\n model: options.model ?? readEnv(env, ...keys.model),\n apiKey: options.apiKey ?? readEnv(env, ...keys.apiKey),\n language: options.language ?? readEnv(env, ...keys.language),\n turnDetection:\n options.turnDetection ??\n parseTurnDetection(readEnv(env, ...keys.turnDetection)),\n timeoutMs:\n options.timeoutMs ??\n parseOptionalInteger(readEnv(env, ...keys.timeoutMs)),\n connectTimeoutMs:\n options.connectTimeoutMs ??\n parseOptionalInteger(readEnv(env, ...keys.connectTimeoutMs)),\n maxTurnSeconds:\n options.maxTurnSeconds ??\n parseMaxTurnSeconds(readEnv(env, ...keys.maxTurnSeconds)),\n headers,\n };\n\n switch (type) {\n case 'openai-realtime':\n return new OpenAIRealtimeTranscriber({ ...resolved, type });\n case 'voxtral-realtime':\n return new VoxtralRealtimeTranscriber({ ...resolved, type });\n default:\n throw new InvalidSpeechAdapterError(type, 'streaming STT');\n }\n}\n\nexport function parseTurnDetection(\n value: string | undefined,\n): StreamingTurnDetection | undefined {\n if (!value) {\n return undefined;\n }\n const normalized = value.toLowerCase();\n if (normalized === 'server_vad' || normalized === 'semantic_vad') {\n return { type: normalized };\n }\n if (normalized === 'manual' || normalized === 'none') {\n return { type: 'manual' };\n }\n throw new SpeechConfigurationError(\n 'HAVE_SPEECH_STREAMING_TURN_DETECTION must be server_vad, semantic_vad, or manual',\n );\n}\n\n/**\n * Parses `HAVE_SPEECH_STREAMING_MAX_TURN_SECONDS`: a positive number of\n * seconds, or `Infinity` for no limit.\n */\nexport function parseMaxTurnSeconds(\n value: string | undefined,\n): number | undefined {\n if (!value) {\n return undefined;\n }\n const parsed = Number(value);\n if (Number.isNaN(parsed) || parsed <= 0) {\n throw new SpeechConfigurationError(\n 'HAVE_SPEECH_STREAMING_MAX_TURN_SECONDS must be a positive number of seconds or Infinity',\n );\n }\n return parsed;\n}\n\nexport interface StreamingTranscriberWrapperOptions {\n /** Bytes per `write()`. Default 64 KiB. */\n chunkBytes?: number;\n /** Upload limit in bytes. Default 25 MB; `Infinity` disables it. */\n maxBytes?: number;\n /**\n * Turn detection for wrapped sessions. Default `manual`: the whole clip is\n * one turn, committed by `end()`.\n */\n turnDetection?: StreamingTurnDetection;\n}\n\n/**\n * Adapts a streaming transcriber to the request/response `Transcriber`\n * interface for record-then-send callers. Each `transcribe()` opens one\n * session, streams the recording, and returns `session.end()`.\n *\n * Input must be raw audio: a PCM16/G.711 WAV file, `audio/pcm` or\n * `audio/L16` (optionally with `rate`/`channels` parameters), `audio/pcmu`,\n * `audio/pcma`, or untyped bytes in the adapter's default format. WAV headers\n * and MIME parameters set the session format; compressed recordings are\n * rejected, so use an HTTP transcriber for WebM/MP4/Ogg.\n *\n * A clip longer than the adapter's per-turn limit (`maxTurnSeconds`) is\n * never sent as one turn: with rollover (the default) the session splits it\n * into several turns at quiet boundaries, and with `rollover: false` the clip\n * is rejected with `SPEECH_TURN_TOO_LONG` before connecting.\n */\nexport function wrapStreamingTranscriber(\n streaming: StreamingTranscriber,\n options: StreamingTranscriberWrapperOptions = {},\n): Transcriber {\n const chunkBytes = options.chunkBytes ?? 64 * 1024;\n if (!Number.isInteger(chunkBytes) || chunkBytes <= 0) {\n throw new SpeechConfigurationError(\n 'chunkBytes must be a positive integer',\n streaming.type,\n );\n }\n\n return {\n type: streaming.type,\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes:\n request.maxBytes ?? options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES,\n adapter: streaming.type,\n signal: request.signal,\n deriveExtension: false,\n });\n const bytes = new Uint8Array(await audio.blob.arrayBuffer());\n const raw = unwrapRawAudio(bytes, audio.mimeType, streaming.type);\n const declared =\n request.audio &&\n typeof request.audio === 'object' &&\n 'data' in request.audio\n ? request.audio\n : undefined;\n const format = {\n ...raw.format,\n sampleRate: raw.format.sampleRate ?? declared?.sampleRate,\n channels: raw.format.channels ?? declared?.channels,\n };\n\n const sessionOptions: StreamingSessionOptions = {\n format: Object.fromEntries(\n Object.entries(format).filter(([, value]) => value !== undefined),\n ),\n language: request.language,\n model: request.model,\n prompt: request.prompt,\n headers: request.headers,\n signal: request.signal,\n onUsage: request.onUsage,\n metadata: request.metadata,\n turnDetection: options.turnDetection ?? { type: 'manual' },\n };\n const limit = streaming.turnLimit?.(sessionOptions);\n if (\n limit &&\n !limit.rollover &&\n raw.bytes.byteLength > limit.maxTurnBytes\n ) {\n throw new SpeechError(\n `The recording is longer than one turn may be (maxTurnSeconds ${limit.maxTurnSeconds} s) and rollover is disabled; enable rollover or split the recording`,\n 'SPEECH_TURN_TOO_LONG',\n streaming.type,\n );\n }\n\n const session = streaming.start(sessionOptions);\n\n try {\n for (\n let offset = 0;\n offset < raw.bytes.byteLength;\n offset += chunkBytes\n ) {\n await session.write(raw.bytes.subarray(offset, offset + chunkBytes));\n }\n } catch (error) {\n session.abort(error);\n throw error;\n }\n return session.end();\n },\n };\n}\n","import { OpenAICompatibleSpeechSynthesizer } from '../adapters/openai-compatible.js';\nimport { OpenAICompatibleTranscriber } from '../adapters/openai-compatible-transcriber.js';\nimport { Qwen3SpeechSynthesizer } from '../adapters/qwen3.js';\nimport {\n StudioServerSpeechSynthesizer,\n StudioServerTranscriber,\n} from '../adapters/studio-server.js';\nimport {\n defaultEnv,\n hasAnyEnv,\n hasTranscriberEnv,\n parseOptionalInteger,\n readEnv,\n resolveTranscriberConfig,\n type SpeechEnv,\n TRANSCRIBER_ENV_KEYS,\n} from './env.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n} from './errors.js';\nimport { compactJson } from './http.js';\nimport {\n getStreamingTranscriber,\n isStreamingTranscriberType,\n STREAMING_TRANSCRIBER_TYPES,\n wrapStreamingTranscriber,\n} from './streaming-factory.js';\nimport type {\n GetStreamingTranscriberOptions,\n StreamingTranscriberType,\n} from './streaming-types.js';\nimport {\n getOptionalTranscriberFactory,\n optionalTranscriberEntry,\n registeredOptionalTranscribers,\n} from './registry.js';\nimport type {\n GetSpeechOptions,\n GetSpeechSynthesizerOptions,\n GetTranscriberOptions,\n OpenAICompatibleSpeechSynthesizerOptions,\n OpenAICompatibleTranscriberOptions,\n Qwen3SpeechSynthesizerOptions,\n Speech,\n SpeechAdapterAvailability,\n SpeechFetch,\n SpeechSynthesizer,\n StudioServerSpeechSynthesizerOptions,\n StudioServerTranscriberOptions,\n SynthesisRequest,\n SynthesizedSpeech,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from './types.js';\n\nexport interface SpeechFactoryContext {\n env?: SpeechEnv;\n fetch?: SpeechFetch;\n headers?: HeadersInit;\n}\n\n/**\n * Creates a speech service from explicit options and/or environment variables.\n *\n * Explicit options win over environment defaults. If no STT or TTS config is\n * available, the returned service is still usable for dependency injection, but\n * calling the missing operation throws a configuration error.\n */\nexport async function getSpeech(\n options: GetSpeechOptions = {},\n context: SpeechFactoryContext = {},\n): Promise<Speech> {\n const transcriber =\n options.transcriber === false\n ? undefined\n : await getOptionalTranscriber(options.transcriber, context);\n const synthesizer =\n options.synthesizer === false\n ? undefined\n : await getOptionalSpeechSynthesizer(options.synthesizer, context);\n\n return {\n transcriber,\n synthesizer,\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n if (!transcriber) {\n throw new SpeechConfigurationError('No STT provider configured');\n }\n return transcriber.transcribe(request);\n },\n async synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech> {\n if (!synthesizer) {\n throw new SpeechConfigurationError('No TTS provider configured');\n }\n return synthesizer.synthesize(request);\n },\n };\n}\n\nexport async function getTranscriber(\n options: GetTranscriberOptions = {},\n context: SpeechFactoryContext = {},\n): Promise<Transcriber> {\n const streamingType =\n options.type ??\n readEnv(context.env ?? defaultEnv(), ...TRANSCRIBER_ENV_KEYS.type);\n if (isStreamingTranscriberType(streamingType)) {\n return getWrappedStreamingTranscriber(streamingType, options, context);\n }\n\n const optional = await getRegisteredTranscriber(options, context);\n if (optional) {\n return optional;\n }\n\n const resolved = normalizeTranscriberOptions(options, context);\n\n switch (resolved.type) {\n case 'studio-server':\n return new StudioServerTranscriber(resolved);\n case 'openai-compatible':\n return new OpenAICompatibleTranscriber(resolved);\n default:\n throw new InvalidSpeechAdapterError(\n (resolved as { type?: string }).type ?? 'unknown',\n 'STT',\n );\n }\n}\n\nexport async function getSpeechSynthesizer(\n options?: GetSpeechSynthesizerOptions,\n context: SpeechFactoryContext = {},\n): Promise<SpeechSynthesizer> {\n const resolved = normalizeSpeechSynthesizerOptions(options, context);\n\n switch (resolved.type) {\n case 'studio-server':\n return new StudioServerSpeechSynthesizer(resolved);\n case 'qwen3-tts':\n return new Qwen3SpeechSynthesizer(resolved);\n case 'openai-compatible':\n return new OpenAICompatibleSpeechSynthesizer(resolved);\n default:\n throw new InvalidSpeechAdapterError(\n (resolved as { type?: string }).type ?? 'unknown',\n 'TTS',\n );\n }\n}\n\nexport function getAvailableSpeechAdapters(): SpeechAdapterAvailability {\n return {\n transcribers: [\n 'studio-server',\n 'openai-compatible',\n ...registeredOptionalTranscribers(),\n ...STREAMING_TRANSCRIBER_TYPES,\n ],\n streamingTranscribers: [...STREAMING_TRANSCRIBER_TYPES],\n synthesizers: ['studio-server', 'qwen3-tts', 'openai-compatible'],\n };\n}\n\n/**\n * Builds a streaming adapter from `getTranscriber()` options and wraps it as\n * a record-then-send `Transcriber`. Settings come from explicit options, then\n * `options.streaming`, then `HAVE_SPEECH_STREAMING_*`.\n */\nfunction getWrappedStreamingTranscriber(\n type: StreamingTranscriberType,\n options: GetTranscriberOptions,\n context: SpeechFactoryContext,\n): Transcriber {\n const streaming = getStreamingTranscriber(\n {\n ...options.streaming,\n ...(compactJson({\n type,\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n model: options.model,\n headers: options.headers,\n timeoutMs: options.timeoutMs,\n onUsage: options.onUsage,\n }) as GetStreamingTranscriberOptions),\n },\n { env: context.env, headers: context.headers },\n );\n return wrapStreamingTranscriber(streaming, { maxBytes: options.maxBytes });\n}\n\n/**\n * Builds opt-in adapters (e.g. `local`) registered by their subpath entry.\n * Returns `undefined` for built-in types.\n */\nasync function getRegisteredTranscriber(\n options: GetTranscriberOptions,\n context: SpeechFactoryContext,\n): Promise<Transcriber | undefined> {\n const type = resolveTranscriberConfig(options, context).type;\n const entry = type ? optionalTranscriberEntry(type) : undefined;\n if (!type || !entry) {\n return undefined;\n }\n\n const factory = getOptionalTranscriberFactory(type);\n if (!factory) {\n throw new SpeechConfigurationError(\n `The '${type}' transcriber is opt-in: import '${entry}' before calling getTranscriber()`,\n type,\n );\n }\n return factory({ ...options, type: type as Transcriber['type'] }, context);\n}\n\nasync function getOptionalTranscriber(\n options: GetTranscriberOptions | undefined,\n context: SpeechFactoryContext,\n): Promise<Transcriber | undefined> {\n if (!options && !hasTranscriberEnv(context.env ?? defaultEnv())) {\n return undefined;\n }\n\n return getTranscriber(options, context);\n}\n\nasync function getOptionalSpeechSynthesizer(\n options: GetSpeechSynthesizerOptions | undefined,\n context: SpeechFactoryContext,\n): Promise<SpeechSynthesizer | undefined> {\n if (!options && !hasSynthesizerEnv(context.env ?? defaultEnv())) {\n return undefined;\n }\n\n return getSpeechSynthesizer(options, context);\n}\n\nfunction normalizeTranscriberOptions(\n options: GetTranscriberOptions = {},\n context: SpeechFactoryContext,\n): StudioServerTranscriberOptions | OpenAICompatibleTranscriberOptions {\n const resolved = resolveTranscriberConfig(options, context);\n const type = resolved.type ?? 'studio-server';\n\n if (type !== 'studio-server' && type !== 'openai-compatible') {\n throw new InvalidSpeechAdapterError(type, 'STT');\n }\n\n if (!resolved.baseUrl) {\n throw new SpeechConfigurationError('STT baseUrl is required', type);\n }\n\n const shared = {\n baseUrl: resolved.baseUrl,\n fetch: resolved.fetch,\n headers: resolved.headers,\n apiKey: resolved.apiKey,\n timeoutMs: resolved.timeoutMs,\n };\n\n if (type === 'openai-compatible') {\n return {\n ...shared,\n type,\n model: resolved.model,\n responseFormat: options.responseFormat,\n maxBytes: resolved.maxBytes,\n retry: options.retry,\n onUsage: options.onUsage,\n };\n }\n\n return {\n ...shared,\n type,\n transcribePath: options.transcribePath ?? resolved.path,\n };\n}\n\nfunction normalizeSpeechSynthesizerOptions(\n options: GetSpeechSynthesizerOptions | undefined,\n context: SpeechFactoryContext,\n):\n | StudioServerSpeechSynthesizerOptions\n | Qwen3SpeechSynthesizerOptions\n | OpenAICompatibleSpeechSynthesizerOptions {\n const env = context.env ?? defaultEnv();\n const type =\n options?.type ??\n (readEnv(\n env,\n 'HAVE_SPEECH_TTS_TYPE',\n 'HAVE_SPEECH_TTS_ADAPTER',\n 'TTS_ADAPTER',\n ) as GetSpeechSynthesizerOptions['type'] | undefined);\n const baseUrl =\n options?.baseUrl ??\n readEnv(env, 'HAVE_SPEECH_TTS_BASE_URL', 'TTS_BASE_URL');\n\n if (!type) {\n throw new SpeechConfigurationError('TTS provider type is required');\n }\n\n if (\n type !== 'studio-server' &&\n type !== 'qwen3-tts' &&\n type !== 'openai-compatible'\n ) {\n throw new InvalidSpeechAdapterError(type, 'TTS');\n }\n\n if (!baseUrl?.trim()) {\n throw new SpeechConfigurationError('TTS baseUrl is required', type);\n }\n\n const shared = {\n type,\n baseUrl: baseUrl.trim(),\n fetch: options?.fetch ?? context.fetch,\n headers: options?.headers ?? context.headers,\n apiKey:\n options?.apiKey ??\n readEnv(env, 'HAVE_SPEECH_TTS_API_KEY', 'TTS_API_KEY', 'SPEECH_API_KEY'),\n timeoutMs:\n options?.timeoutMs ??\n parseOptionalInteger(\n readEnv(env, 'HAVE_SPEECH_TTS_TIMEOUT_MS', 'TTS_TIMEOUT_MS'),\n ),\n };\n\n if (type === 'studio-server') {\n return {\n ...shared,\n type,\n synthesizePath:\n options?.synthesizePath ??\n readEnv(env, 'HAVE_SPEECH_TTS_PATH', 'TTS_PATH'),\n defaultVoice:\n options?.defaultVoice ??\n readEnv(env, 'HAVE_SPEECH_TTS_VOICE', 'TTS_VOICE'),\n };\n }\n\n if (type === 'qwen3-tts') {\n return {\n ...shared,\n type,\n speechPath:\n options?.speechPath ?? readEnv(env, 'HAVE_SPEECH_TTS_PATH', 'TTS_PATH'),\n defaultModel:\n options?.defaultModel ??\n readEnv(env, 'HAVE_SPEECH_TTS_MODEL', 'TTS_MODEL'),\n defaultVoice:\n options?.defaultVoice ??\n readEnv(env, 'HAVE_SPEECH_TTS_VOICE', 'TTS_VOICE'),\n };\n }\n\n if (type === 'openai-compatible') {\n return {\n ...shared,\n type,\n speechPath:\n options?.speechPath ?? readEnv(env, 'HAVE_SPEECH_TTS_PATH', 'TTS_PATH'),\n defaultModel:\n options?.defaultModel ??\n readEnv(env, 'HAVE_SPEECH_TTS_MODEL', 'TTS_MODEL'),\n defaultVoice:\n options?.defaultVoice ??\n readEnv(env, 'HAVE_SPEECH_TTS_VOICE', 'TTS_VOICE'),\n };\n }\n\n throw new InvalidSpeechAdapterError(type, 'TTS');\n}\n\nfunction hasSynthesizerEnv(env: SpeechEnv): boolean {\n return hasAnyEnv(\n env,\n 'HAVE_SPEECH_TTS_TYPE',\n 'HAVE_SPEECH_TTS_ADAPTER',\n 'HAVE_SPEECH_TTS_BASE_URL',\n 'TTS_ADAPTER',\n 'TTS_BASE_URL',\n );\n}\n","import { VOXTRAL_REALTIME_AUDIO_FORMAT } from '../adapters/voxtral-realtime.js';\nimport type { TranscriberType } from './types.js';\n\n/** What audio a transcriber type accepts from a caller. */\nexport type TranscriberInputFormat =\n | { kind: 'compressed' }\n | { kind: 'pcm16'; sampleRate: number; channels: 1 };\n\n/** The `openai-realtime` default wire format: `audio/pcm`, 24 kHz mono. */\nconst OPENAI_REALTIME_PCM_RATE = 24_000;\n\n/**\n * Says what to record for a transcriber type. `pcm16` types reject compressed\n * containers (webm, ogg, mp3) and need 16-bit little-endian mono PCM, raw or in\n * a WAV, at exactly `sampleRate`; use `@happyvertical/speech/browser` or\n * `/pcm` to produce it. `compressed` types decode containers themselves\n * (HTTP APIs, and the on-device transcriber, which decodes with the platform).\n * For `openai-realtime` this is the default format; G.711 is an explicit\n * adapter option and not a capture format.\n */\nexport function transcriberInputFormat(\n type: TranscriberType,\n): TranscriberInputFormat {\n switch (type) {\n case 'voxtral-realtime':\n return {\n kind: 'pcm16',\n sampleRate: VOXTRAL_REALTIME_AUDIO_FORMAT.sampleRate ?? 16_000,\n channels: 1,\n };\n case 'openai-realtime':\n return {\n kind: 'pcm16',\n sampleRate: OPENAI_REALTIME_PCM_RATE,\n channels: 1,\n };\n case 'studio-server':\n case 'openai-compatible':\n case 'local':\n return { kind: 'compressed' };\n default: {\n const unknown: never = type;\n throw new Error(`Unknown transcriber type: ${String(unknown)}`);\n }\n }\n}\n","/**\n * Server-side minting of short-lived browser credentials for realtime\n * transcription.\n *\n * A browser must never hold the long-lived provider key. Your server calls\n * {@link createStreamingClientSecret} once per tenant session (after its own\n * auth check), records the returned tenant/session attribution, and hands the\n * browser only `value` and `expiresAt`. The browser passes `value` as the\n * streaming adapter's `clientSecret`.\n *\n * - `openai-realtime`: `POST /v1/realtime/client_secrets` with\n * `expires_after: { anchor: 'created_at', seconds }` and a\n * `session.type: \"transcription\"` config, returning an `ek_…` secret\n * (https://developers.openai.com/api/reference/resources/realtime/subresources/client_secrets/methods/create).\n * - `voxtral-realtime`: vLLM has no ephemeral-token mechanism, so this throws\n * `SpeechConfigurationError`. Browser Voxtral needs your own proxy or token\n * gateway in front of vLLM.\n */\n\nimport {\n OPENAI_REALTIME_DEFAULT_MODEL,\n OPENAI_REALTIME_DEFAULT_URL,\n openAIRealtimeTranscriptionSession,\n resolveOpenAIRealtimeFormat,\n} from '../adapters/openai-realtime.js';\nimport {\n defaultEnv,\n mergeHeaderInits,\n parseHeadersEnv,\n parseOptionalInteger,\n readEnv,\n type SpeechEnv,\n} from './env.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n SpeechProviderError,\n} from './errors.js';\nimport {\n HttpSpeechAdapter,\n redactSecret,\n resolveOpenAICompatibleUrl,\n} from './http.js';\nimport { headerSecrets } from './realtime-auth.js';\nimport {\n isStreamingTranscriberType,\n parseTurnDetection,\n STREAMING_TRANSCRIBER_ENV_KEYS,\n} from './streaming-factory.js';\nimport type {\n OpenAIRealtimeTranscriberOptions,\n StreamingAudioFormat,\n StreamingTranscriberType,\n StreamingTurnDetection,\n} from './streaming-types.js';\nimport type { SpeechFetch } from './types.js';\nimport { isBrowserRuntime } from './websocket.js';\n\n/** Default client-secret lifetime: long enough to connect, short if leaked. */\nexport const DEFAULT_CLIENT_SECRET_TTL_SECONDS = 60;\n/** OpenAI's accepted `expires_after.seconds` range. */\nexport const CLIENT_SECRET_TTL_RANGE = { min: 10, max: 7200 } as const;\n\nexport interface CreateStreamingClientSecretOptions {\n /** Streaming adapter. Default `HAVE_SPEECH_STREAMING_TYPE`, then `openai-realtime`. */\n type?: StreamingTranscriberType;\n /** Long-lived server key. Default `HAVE_SPEECH_STREAMING_API_KEY`. */\n apiKey?: string;\n /**\n * API base. Accepts the same values as the streaming adapter\n * (`https://api.openai.com`, `…/v1`, or `wss://…/v1/realtime`). Default\n * `HAVE_SPEECH_STREAMING_BASE_URL`, then `https://api.openai.com/v1`.\n */\n baseUrl?: string;\n /** Secret lifetime in seconds, 10–7200. Default 60. */\n ttlSeconds?: number;\n /**\n * Tenant the secret is minted for. Echoed in the result for your usage\n * ledger; OpenAI's transcription client secrets carry no attribution field.\n */\n tenantId?: string;\n /** Your session id (e.g. the dictation session), echoed in the result. */\n sessionId?: string;\n /** Extra attribution echoed in the result. Never sent to the provider. */\n metadata?: Record<string, unknown>;\n /** Transcription model the secret is bound to. Default `HAVE_SPEECH_STREAMING_MODEL`, then `gpt-4o-transcribe`. */\n model?: string;\n language?: string;\n prompt?: string;\n /** Default `HAVE_SPEECH_STREAMING_TURN_DETECTION`, then `server_vad`. */\n turnDetection?: StreamingTurnDetection;\n /** Default PCM16 24 kHz mono. */\n format?: Partial<StreamingAudioFormat>;\n noiseReduction?: OpenAIRealtimeTranscriberOptions['noiseReduction'];\n include?: string[];\n /**\n * Extra `transcription` fields. The typed `model`, `language`, and `prompt`\n * take precedence, so the secret is bound to the model the result reports.\n */\n transcriptionOptions?: Record<string, unknown>;\n /**\n * Headers for the mint request, e.g. a per-tenant gateway virtual key\n * (`x-bf-vk`) for provider-side attribution. Merged over\n * `HAVE_SPEECH_STREAMING_HEADERS`.\n */\n headers?: HeadersInit;\n fetch?: SpeechFetch;\n signal?: AbortSignal;\n /** Request timeout. Default `HAVE_SPEECH_STREAMING_TIMEOUT[_MS]`, then 10000. */\n timeoutMs?: number;\n}\n\n/** A minted browser credential plus the attribution it was minted for. */\nexport interface StreamingClientSecretResult {\n type: StreamingTranscriberType;\n /** The short-lived secret. Send it to the browser as `clientSecret`. */\n value: string;\n /** Expiry as epoch milliseconds. */\n expiresAt: number;\n ttlSeconds: number;\n model: string;\n tenantId?: string;\n sessionId?: string;\n metadata?: Record<string, unknown>;\n /** The provider's echoed session config. */\n session?: Record<string, unknown>;\n}\n\nexport interface StreamingClientSecretContext {\n env?: SpeechEnv;\n}\n\nconst OPENAI_CLIENT_SECRET_ENDPOINT = 'realtime/client_secrets';\n\n/**\n * Mints a short-lived, session-scoped client secret for browser streaming.\n * Server-side only: throws in a browser runtime, and for adapters without an\n * ephemeral-token mechanism (`voxtral-realtime`).\n */\nexport async function createStreamingClientSecret(\n options: CreateStreamingClientSecretOptions = {},\n context: StreamingClientSecretContext = {},\n): Promise<StreamingClientSecretResult> {\n const env = context.env ?? defaultEnv();\n const keys = STREAMING_TRANSCRIBER_ENV_KEYS;\n const type = options.type ?? readEnv(env, ...keys.type) ?? 'openai-realtime';\n if (!isStreamingTranscriberType(type)) {\n throw new InvalidSpeechAdapterError(type, 'streaming STT');\n }\n\n if (isBrowserRuntime()) {\n throw new SpeechConfigurationError(\n 'createStreamingClientSecret runs on your server: it needs the long-lived API key, which must never reach a browser',\n type,\n );\n }\n\n if (type === 'voxtral-realtime') {\n throw new SpeechConfigurationError(\n 'voxtral-realtime cannot mint browser credentials: vLLM has no ephemeral-token mechanism. Put your own proxy or token gateway in front of vLLM, mint its short-lived tokens yourself, and never send the vLLM API key to a browser',\n type,\n );\n }\n\n const ttlSeconds = options.ttlSeconds ?? DEFAULT_CLIENT_SECRET_TTL_SECONDS;\n if (\n !Number.isInteger(ttlSeconds) ||\n ttlSeconds < CLIENT_SECRET_TTL_RANGE.min ||\n ttlSeconds > CLIENT_SECRET_TTL_RANGE.max\n ) {\n throw new SpeechConfigurationError(\n `ttlSeconds must be an integer from ${CLIENT_SECRET_TTL_RANGE.min} to ${CLIENT_SECRET_TTL_RANGE.max}`,\n type,\n );\n }\n\n const apiKey = options.apiKey ?? readEnv(env, ...keys.apiKey);\n if (!apiKey) {\n throw new SpeechConfigurationError(\n 'createStreamingClientSecret requires apiKey (or HAVE_SPEECH_STREAMING_API_KEY)',\n type,\n );\n }\n\n const model =\n options.model?.trim() ||\n readEnv(env, ...keys.model)?.trim() ||\n OPENAI_REALTIME_DEFAULT_MODEL;\n const session = openAIRealtimeTranscriptionSession(\n {\n model,\n format: resolveOpenAIRealtimeFormat(options.format),\n turnDetection: options.turnDetection ??\n parseTurnDetection(readEnv(env, ...keys.turnDetection)) ?? {\n type: 'server_vad',\n },\n language: options.language ?? readEnv(env, ...keys.language),\n prompt: options.prompt,\n },\n options,\n );\n\n const endpoint = resolveClientSecretUrl(\n options.baseUrl?.trim() ||\n readEnv(env, ...keys.baseUrl)?.trim() ||\n OPENAI_REALTIME_DEFAULT_URL,\n );\n const headers = mergeHeaderInits(\n parseHeadersEnv(env, keys.headers),\n options.headers,\n );\n const client = new ClientSecretClient({\n baseUrl: endpoint,\n apiKey,\n fetch: options.fetch,\n headers,\n timeoutMs:\n options.timeoutMs ??\n parseOptionalInteger(readEnv(env, ...keys.timeoutMs)) ??\n 10_000,\n });\n\n const body = await client\n .mint(\n endpoint,\n {\n expires_after: { anchor: 'created_at', seconds: ttlSeconds },\n session,\n },\n options.signal,\n )\n .catch((error: unknown) => {\n throw redactProviderError(error, headerSecrets(headers));\n });\n\n const value = body.value;\n if (typeof value !== 'string' || !value) {\n throw new SpeechProviderError(\n type,\n 'openai-realtime client secret response did not include a value',\n );\n }\n const expiresAtSeconds = body.expires_at;\n\n return {\n type,\n value,\n expiresAt:\n typeof expiresAtSeconds === 'number' && Number.isFinite(expiresAtSeconds)\n ? expiresAtSeconds * 1000\n : Date.now() + ttlSeconds * 1000,\n ttlSeconds,\n model,\n tenantId: options.tenantId,\n sessionId: options.sessionId,\n metadata: options.metadata,\n session:\n body.session && typeof body.session === 'object'\n ? (body.session as Record<string, unknown>)\n : undefined,\n };\n}\n\n/**\n * Resolves the client-secret endpoint from any accepted realtime base:\n * `ws(s)` becomes `http(s)`, a trailing `/realtime` and query are dropped,\n * then `/v1/realtime/client_secrets` (or `/realtime/client_secrets` under a\n * versioned root) is appended.\n */\nexport function resolveClientSecretUrl(baseUrl: string): string {\n const url = new URL(baseUrl);\n if (url.protocol === 'ws:') {\n url.protocol = 'http:';\n } else if (url.protocol === 'wss:') {\n url.protocol = 'https:';\n }\n url.search = '';\n url.hash = '';\n url.pathname = url.pathname.replace(/\\/+$/, '').replace(/\\/realtime$/, '');\n return resolveOpenAICompatibleUrl(\n url.toString(),\n OPENAI_CLIENT_SECRET_ENDPOINT,\n );\n}\n\n/**\n * The HTTP layer redacts the API key from provider error bodies; this also\n * removes gateway credentials sent in `headers` (e.g. `x-bf-vk`).\n */\nfunction redactProviderError(error: unknown, secrets: string[]): unknown {\n if (!(error instanceof SpeechProviderError) || secrets.length === 0) {\n return error;\n }\n let responseBody = error.responseBody;\n let message = error.message;\n for (const secret of secrets) {\n responseBody = redactSecret(responseBody, secret);\n message = redactSecret(message, secret) ?? message;\n }\n if (responseBody === error.responseBody && message === error.message) {\n return error;\n }\n return new SpeechProviderError(error.adapter ?? 'openai-realtime', message, {\n status: error.status,\n responseBody,\n retryAfterMs: error.retryAfterMs,\n });\n}\n\nclass ClientSecretClient extends HttpSpeechAdapter {\n mint(\n endpoint: string,\n payload: Record<string, unknown>,\n signal: AbortSignal | undefined,\n ): Promise<Record<string, unknown>> {\n return this.post(\n 'openai-realtime',\n endpoint,\n {\n body: JSON.stringify(payload),\n headers: { 'content-type': 'application/json' },\n signal,\n },\n async (response) => {\n const json = (await response.json().catch(() => undefined)) as unknown;\n if (!json || typeof json !== 'object' || Array.isArray(json)) {\n throw new SpeechProviderError(\n 'openai-realtime',\n 'openai-realtime client secret response was not a JSON object',\n );\n }\n return json as Record<string, unknown>;\n },\n );\n }\n}\n"],"mappings":"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"}