@happyvertical/speech 0.96.0 → 0.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/shared/errors.ts","../src/shared/http.ts","../src/adapters/openai-compatible.ts","../src/adapters/qwen3.ts","../src/adapters/studio-server.ts","../src/shared/factory.ts"],"sourcesContent":["export class SpeechError extends Error {\n constructor(\n message: string,\n public code: string,\n public adapter?: string,\n ) {\n super(message);\n this.name = 'SpeechError';\n }\n}\n\nexport class SpeechConfigurationError extends SpeechError {\n constructor(message: string, adapter?: string) {\n super(message, 'SPEECH_CONFIGURATION_ERROR', adapter);\n this.name = 'SpeechConfigurationError';\n }\n}\n\nexport class InvalidSpeechAdapterError extends SpeechError {\n constructor(type: string, kind: string) {\n super(`Invalid ${kind} speech adapter type: ${type}`, 'INVALID_ADAPTER');\n this.name = 'InvalidSpeechAdapterError';\n }\n}\n\nexport class SpeechProviderError extends SpeechError {\n readonly status?: number;\n readonly responseBody?: string;\n\n constructor(\n adapter: string,\n message: string,\n options: { status?: number; responseBody?: string; cause?: unknown } = {},\n ) {\n super(message, 'SPEECH_PROVIDER_ERROR', adapter);\n this.name = 'SpeechProviderError';\n this.status = options.status;\n this.responseBody = options.responseBody;\n if (options.cause !== undefined) {\n this.cause = options.cause;\n }\n }\n}\n","import { SpeechConfigurationError, SpeechProviderError } from './errors.js';\nimport type {\n AudioInput,\n HttpSpeechOptions,\n SpeechFetch,\n SynthesisRequest,\n SynthesizedSpeech,\n TranscriptResult,\n TranscriptSegment,\n WordTiming,\n} from './types.js';\n\nexport function normalizeBaseUrl(baseUrl: string): string {\n if (!baseUrl.trim()) {\n throw new SpeechConfigurationError('Speech adapter baseUrl is required');\n }\n\n return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n}\n\nexport function resolveSpeechUrl(baseUrl: string, path: string): string {\n return new URL(path.replace(/^\\//, ''), normalizeBaseUrl(baseUrl)).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 protected async post<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 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);\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): 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,\n },\n );\n}\n\nexport function appendAudioInput(\n form: FormData,\n audio: AudioInput,\n fieldName = 'audio',\n): void {\n const filename = audio.filename ?? 'audio';\n const data = audio.data;\n\n if (typeof Blob !== 'undefined' && data instanceof Blob) {\n const contentType =\n audio.contentType ?? (data.type || 'application/octet-stream');\n const blob =\n data.type === contentType\n ? data\n : new Blob([data], { type: contentType });\n form.append(fieldName, blob, filename);\n return;\n }\n\n const contentType = audio.contentType ?? 'application/octet-stream';\n const blobPart =\n data instanceof Uint8Array ? arrayBufferFromBytes(data) : data;\n form.append(fieldName, new Blob([blobPart], { type: contentType }), filename);\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","import {\n compactJson,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\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 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 ?? '/v1/audio/speech';\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 const speech = await this.post(\n this.type,\n this.speechPath,\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 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 {\n appendAudioInput,\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 form = new FormData();\n appendAudioInput(form, request.audio);\n appendOptionalFormValue(form, 'language', request.language);\n\n return this.post(\n this.type,\n this.transcribePath,\n {\n body: form,\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","import { OpenAICompatibleSpeechSynthesizer } from '../adapters/openai-compatible.js';\nimport { Qwen3SpeechSynthesizer } from '../adapters/qwen3.js';\nimport {\n StudioServerSpeechSynthesizer,\n StudioServerTranscriber,\n} from '../adapters/studio-server.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n} from './errors.js';\nimport type {\n GetSpeechOptions,\n GetSpeechSynthesizerOptions,\n GetTranscriberOptions,\n OpenAICompatibleSpeechSynthesizerOptions,\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\ninterface SpeechEnv {\n [key: string]: string | undefined;\n}\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 resolved = normalizeTranscriberOptions(options, context);\n\n switch (resolved.type) {\n case 'studio-server':\n return new StudioServerTranscriber(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: ['studio-server'],\n synthesizers: ['studio-server', 'qwen3-tts', 'openai-compatible'],\n };\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 {\n const env = context.env ?? defaultEnv();\n const type =\n options.type ??\n readEnv(\n env,\n 'HAVE_SPEECH_STT_TYPE',\n 'HAVE_SPEECH_STT_ADAPTER',\n 'STT_ADAPTER',\n ) ??\n 'studio-server';\n const baseUrl =\n options.baseUrl ?? readEnv(env, 'HAVE_SPEECH_STT_BASE_URL', 'STT_BASE_URL');\n\n if (type !== 'studio-server') {\n throw new InvalidSpeechAdapterError(type, 'STT');\n }\n\n if (!baseUrl?.trim()) {\n throw new SpeechConfigurationError('STT baseUrl is required', type);\n }\n\n return {\n ...options,\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_STT_API_KEY', 'STT_API_KEY', 'SPEECH_API_KEY'),\n timeoutMs:\n options.timeoutMs ??\n parseOptionalInteger(\n readEnv(env, 'HAVE_SPEECH_STT_TIMEOUT_MS', 'STT_TIMEOUT_MS'),\n ),\n transcribePath:\n options.transcribePath ??\n readEnv(env, 'HAVE_SPEECH_STT_PATH', 'STT_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 hasTranscriberEnv(env: SpeechEnv): boolean {\n return hasAnyEnv(\n env,\n 'HAVE_SPEECH_STT_TYPE',\n 'HAVE_SPEECH_STT_ADAPTER',\n 'HAVE_SPEECH_STT_BASE_URL',\n 'STT_ADAPTER',\n 'STT_BASE_URL',\n );\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\nfunction readEnv(env: SpeechEnv, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const value = env[key];\n if (value?.trim()) {\n return value.trim();\n }\n }\n\n return undefined;\n}\n\nfunction hasAnyEnv(env: SpeechEnv, ...keys: string[]): boolean {\n return keys.some((key) => Boolean(env[key]?.trim()));\n}\n\nfunction parseOptionalInteger(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction defaultEnv(): SpeechEnv {\n return globalThis.process?.env ?? {};\n}\n"],"mappings":";AAAA,IAAa,cAAb,cAAiC,MAAM;CAG5B;CACA;CAHT,YACE,SACA,MACA,SACA;EACA,MAAM,OAAO;EAHN,KAAA,OAAA;EACA,KAAA,UAAA;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,2BAAb,cAA8C,YAAY;CACxD,YAAY,SAAiB,SAAkB;EAC7C,MAAM,SAAS,8BAA8B,OAAO;EACpD,KAAK,OAAO;CACd;AACF;AAEA,IAAa,4BAAb,cAA+C,YAAY;CACzD,YAAY,MAAc,MAAc;EACtC,MAAM,WAAW,KAAK,wBAAwB,QAAQ,iBAAiB;EACvE,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,YAAY;CACnD;CACA;CAEA,YACE,SACA,SACA,UAAuE,CAAC,GACxE;EACA,MAAM,SAAS,yBAAyB,OAAO;EAC/C,KAAK,OAAO;EACZ,KAAK,SAAS,QAAQ;EACtB,KAAK,eAAe,QAAQ;EAC5B,IAAI,QAAQ,UAAU,KAAA,GACpB,KAAK,QAAQ,QAAQ;CAEzB;AACF;;;AC9BA,SAAgB,iBAAiB,SAAyB;CACxD,IAAI,CAAC,QAAQ,KAAK,GAChB,MAAM,IAAI,yBAAyB,oCAAoC;CAGzE,OAAO,QAAQ,SAAS,GAAG,IAAI,UAAU,GAAG,QAAQ;AACtD;AAEA,SAAgB,iBAAiB,SAAiB,MAAsB;CACtE,OAAO,IAAI,IAAI,KAAK,QAAQ,OAAO,EAAE,GAAG,iBAAiB,OAAO,CAAC,CAAC,CAAC,SAAS;AAC9E;AAEA,SAAgB,aAAa,eAA0C;CACrE,IAAI,eACF,OAAO;CAGT,MAAM,cAAc,WAAW;CAC/B,IAAI,CAAC,aACH,MAAM,IAAI,yBACR,sDACF;CAGF,OAAO,YAAY,KAAK,UAAU;AACpC;AAEA,SAAgB,aACd,SACA,OACS;CACT,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;CAE3C,IAAI,QAAQ,UAAU,CAAC,QAAQ,IAAI,eAAe,GAChD,QAAQ,IAAI,iBAAiB,UAAU,QAAQ,QAAQ;CAGzD,IAAI,OACF,IAAI,QAAQ,KAAK,CAAC,CAAC,SAAS,OAAO,QAAQ;EACzC,QAAQ,IAAI,KAAK,KAAK;CACxB,CAAC;CAGH,OAAO;AACT;AAEA,IAAsB,oBAAtB,MAAwC;CACtC;CACA;CACA;CACA;CACA;CAEA,YAAY,SAA4B;EACtC,KAAK,UAAU,QAAQ;EACvB,KAAK,YAAY,aAAa,QAAQ,KAAK;EAC3C,KAAK,SAAS,QAAQ;EACtB,KAAK,UAAU,QAAQ;EACvB,KAAK,YAAY,QAAQ;CAC3B;CAEA,MAAgB,KACd,aACA,MACA,MACA,cACY;EACZ,MAAM,oBACJ,KAAK,aAAa,KAAK,YAAY,IAAI,IAAI,gBAAgB,IAAI,KAAA;EACjE,MAAM,UACJ,qBAAqB,KAAK,YACtB,iBAAiB,kBAAkB,MAAM,GAAG,KAAK,SAAS,IAC1D,KAAA;EACN,MAAM,gBAAgB,oBACpB,KAAK,QACL,mBAAmB,MACrB;EAEA,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,UAC1B,iBAAiB,KAAK,SAAS,IAAI,GACnC;IACE,GAAG;IACH,QAAQ;IACR,SAAS,aACP;KAAE,QAAQ,KAAK;KAAQ,SAAS,KAAK;IAAQ,GAC7C,KAAK,OACP;IACA,QAAQ,cAAc;GACxB,CACF;GACA,MAAM,SAAS,UAAU,WAAW;GACpC,OAAO,MAAM,aAAa,QAAQ;EACpC,UAAU;GACR,IAAI,SACF,aAAa,OAAO;GAEtB,cAAc,QAAQ;EACxB;CACF;AACF;AAEA,SAAS,oBACP,GAAG,SAC4C;CAC/C,MAAM,gBAAgB,QAAQ,QAAQ,WACpC,QAAQ,MAAM,CAChB;CAEA,IAAI,cAAc,WAAW,GAC3B,OAAO,EAAE,eAAe,KAAA,EAAU;CAGpC,IAAI,cAAc,WAAW,GAC3B,OAAO;EAAE,QAAQ,cAAc;EAAI,eAAe,KAAA;CAAU;CAG9D,MAAM,aAAa,IAAI,gBAAgB;CACvC,IAAI,gBAAgB,KAAA;CAEpB,MAAM,aAAa,WAAwB;EACzC,IAAI,CAAC,WAAW,OAAO,SACrB,WAAW,MAAM,OAAO,MAAM;EAEhC,QAAQ;CACV;CAEA,MAAM,WAAW,UAAiB;EAChC,UAAU,MAAM,MAAqB;CACvC;CAEA,gBAAgB;EACd,KAAK,MAAM,UAAU,eACnB,OAAO,oBAAoB,SAAS,OAAO;CAE/C;CAEA,KAAK,MAAM,UAAU,eACnB,IAAI,OAAO,SAAS;EAClB,UAAU,MAAM;EAChB,OAAO;GAAE,QAAQ,WAAW;GAAQ,eAAe,KAAA;EAAU;CAC/D;CAGF,KAAK,MAAM,UAAU,eACnB,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAG1D,OAAO;EAAE,QAAQ,WAAW;EAAQ;CAAQ;AAC9C;AAEA,eAAe,SACb,UACA,aACe;CACf,IAAI,SAAS,IACX;CAGF,MAAM,eAAe,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,KAAA,CAAS;CAChE,MAAM,IAAI,oBACR,aACA,GAAG,YAAY,mCAAmC,SAAS,UAC3D;EACE,QAAQ,SAAS;EACjB;CACF,CACF;AACF;AAEA,SAAgB,iBACd,MACA,OACA,YAAY,SACN;CACN,MAAM,WAAW,MAAM,YAAY;CACnC,MAAM,OAAO,MAAM;CAEnB,IAAI,OAAO,SAAS,eAAe,gBAAgB,MAAM;EACvD,MAAM,cACJ,MAAM,gBAAgB,KAAK,QAAQ;EACrC,MAAM,OACJ,KAAK,SAAS,cACV,OACA,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;EAC5C,KAAK,OAAO,WAAW,MAAM,QAAQ;EACrC;CACF;CAEA,MAAM,cAAc,MAAM,eAAe;CACzC,MAAM,WACJ,gBAAgB,aAAa,qBAAqB,IAAI,IAAI;CAC5D,KAAK,OAAO,WAAW,IAAI,KAAK,CAAC,QAAQ,GAAG,EAAE,MAAM,YAAY,CAAC,GAAG,QAAQ;AAC9E;AAEA,SAAgB,wBACd,MACA,MACA,OACM;CACN,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC;CAGF,KAAK,OAAO,MAAM,OAAO,KAAK,CAAC;AACjC;AAEA,SAAgB,iCACd,SACA,cACU;CACV,MAAM,OAAO,IAAI,SAAS;CAC1B,MAAM,QACJ,QAAQ,SAAS,OAAO,QAAQ,UAAU,WACtC,QAAQ,QACR,KAAA;CACN,KAAK,IAAI,QAAQ,QAAQ,IAAI;CAC7B,wBACE,MACA,YACA,QAAQ,YAAY,OAAO,QAC7B;CACA,wBACE,MACA,WACA,cAAc,QAAQ,OAAO,YAAY,CAC3C;CACA,wBAAwB,MAAM,SAAS,QAAQ,KAAK;CAEpD,wBAAwB,MAAM,gBAAgB,OAAO,MAAM;CAE3D,OAAO;AACT;AAEA,SAAgB,cACd,OAIA,UACoB;CACpB,IAAI,CAAC,OACH,OAAO;CAGT,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,OAAO,MAAM,MAAM,MAAM,QAAQ,MAAM,aAAa;AACtD;AAEA,SAAgB,YACd,OACyB;CACzB,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,gBAAgB,eAAe,KAAA,CAAS,CAC3E;AACF;AAEA,SAAgB,oBAAoB,aAAyC;CAE3E,OADc,kBAAkB,KAAK,WAC9B,CAAA,GAAQ;AACjB;AAEA,SAAgB,qBAAqB,OAAgC;CACnE,MAAM,OAAO,IAAI,WAAW,MAAM,UAAU;CAC5C,KAAK,IAAI,KAAK;CACd,OAAO,KAAK;AACd;AAEA,SAAgB,aAAa,QAA6B;CACxD,IAAI,OAAO,WAAW,aACpB,OAAO,qBAAqB,OAAO,KAAK,QAAQ,QAAQ,CAAC;CAG3D,MAAM,SAAS,KAAK,MAAM;CAC1B,MAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;CAC1C,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAClD,MAAM,SAAS,OAAO,WAAW,KAAK;CAExC,OAAO,MAAM;AACf;AAEA,SAAS,UACP,OACA,GAAG,MACiB;CACpB,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,YAAY,MAAM;EACxB,IAAI,OAAO,cAAc,UACvB,OAAO;CAEX;AAGF;AAEA,SAAS,UACP,OACA,GAAG,MACiB;CACpB,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,YAAY,MAAM;EACxB,IAAI,OAAO,cAAc,UACvB,OAAO;CAEX;AAGF;AAEA,SAAgB,qBAAqB,OAA0C;CAC7E,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB;CAGF,OAAO,MAAM,SAAS,UAAwB;EAC5C,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO,CAAC;EAGV,MAAM,SAAS;EACf,MAAM,OAAO,UAAU,QAAQ,QAAQ,QAAQ,OAAO;EACtD,MAAM,eAAe,UACnB,QACA,gBACA,SACA,YACF;EACA,MAAM,aAAa,UAAU,QAAQ,cAAc,OAAO,UAAU;EAEpE,IAAI,CAAC,QAAQ,iBAAiB,KAAA,KAAa,eAAe,KAAA,GACxD,OAAO,CAAC;EAGV,OAAO,CACL;GACE;GACA;GACA;GACA,YAAY,UAAU,QAAQ,YAAY;GAC1C,WAAW,UAAU,QAAQ,aAAa,WAAW,YAAY;EACnE,CACF;CACF,CAAC;AACH;AAEA,SAAgB,4BACd,OACiC;CACjC,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB;CAGF,OAAO,MAAM,SAAS,UAA+B;EACnD,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO,CAAC;EAGV,MAAM,SAAS;EACf,MAAM,OAAO,UAAU,QAAQ,QAAQ,YAAY;EACnD,IAAI,CAAC,MACH,OAAO,CAAC;EAGV,OAAO,CACL;GACE;GACA,cAAc,UAAU,QAAQ,gBAAgB,SAAS,YAAY;GACrE,YAAY,UAAU,QAAQ,cAAc,OAAO,UAAU;GAC7D,YAAY,UAAU,QAAQ,YAAY;GAC1C,WAAW,UAAU,QAAQ,aAAa,WAAW,YAAY;EACnE,CACF;CACF,CAAC;AACH;AAEA,eAAsB,uBACpB,UACA,aAC2B;CAG3B,IAAI,EAFgB,SAAS,QAAQ,IAAI,cAAc,KAAK,GAAA,CAE3C,SAAS,kBAAkB,GAE1C,OAAO;EACL,MAAA,MAFiB,SAAS,KAAK;EAG/B,UAAU;CACZ;CAGF,MAAM,OAAQ,MAAM,SAAS,KAAK;CAGlC,OAAO;EACL,MAHW,UAAU,MAAM,QAAQ,cAAc,eAAe,KAAK;EAIrE,UAAU,UAAU,MAAM,YAAY,MAAM;EAC5C,iBAAiB,UAAU,MAAM,mBAAmB,UAAU;EAC9D,OAAO,qBACL,KAAK,SAAS,KAAK,eAAe,KAAK,YACzC;EACA,UAAU,4BAA4B,KAAK,QAAQ;EACnD,UAAU,UAAU,MAAM,UAAU,KAAK;EACzC,OAAO,UAAU,MAAM,OAAO;EAC9B,KAAK;CACP;AACF;AAEA,eAAsB,8BACpB,UACA,aAC4B;CAC5B,MAAM,sBACJ,SAAS,QAAQ,IAAI,cAAc,KAAK;CAE1C,IAAI,CAAC,oBAAoB,SAAS,kBAAkB,GAAG;EACrD,MAAM,QAAQ,MAAM,SAAS,YAAY;EACzC,MAAM,aAAa,kBAAkB,UAAU,eAAe;EAC9D,OAAO;GACL;GACA,aAAa;GACb,QAAQ,oBAAoB,mBAAmB;GAC/C;GACA,UAAU;EACZ;CACF;CAEA,MAAM,OAAQ,MAAM,SAAS,KAAK;CAClC,MAAM,cACJ,UAAU,MAAM,eAAe,gBAAgB,YAAY,WAAW,KACtE;CACF,MAAM,eAAe,UACnB,MACA,SACA,gBACA,eACF;CAEA,IAAI,CAAC,cACH,MAAM,IAAI,oBACR,aACA,GAAG,YAAY,mDACjB;CAGF,OAAO;EACL,OAAO,aAAa,YAAY;EAChC;EACA,QACE,UAAU,MAAM,UAAU,iBAAiB,KAC3C,oBAAoB,WAAW;EACjC,YAAY,UAAU,MAAM,cAAc,aAAa;EACvD,UAAU,UAAU,MAAM,UAAU;EACpC,iBAAiB,UAAU,MAAM,mBAAmB,UAAU;EAC9D,OAAO,qBACL,KAAK,SAAS,KAAK,eAAe,KAAK,YACzC;EACA,UAAU,UAAU,MAAM,UAAU,KAAK;EACzC,OAAO,UAAU,MAAM,OAAO;EAC9B,KAAK;CACP;AACF;AAEA,SAAS,kBACP,UACA,YACoB;CACpB,MAAM,QAAQ,SAAS,QAAQ,IAAI,UAAU;CAC7C,IAAI,CAAC,OACH;CAGF,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS,KAAA;AAC5C;;;AC1dA,IAAa,oCAAb,cACU,kBAEV;CACE,OAAgB;CAEhB;CACA;CACA;CAEA,YAAY,SAAmD;EAC7D,MAAM,OAAO;EACb,KAAK,aAAa,QAAQ,cAAc;EACxC,KAAK,eAAe,QAAQ,gBAAgB;EAC5C,KAAK,eAAe,QAAQ,gBAAgB;CAC9C;CAEA,MAAM,WAAW,SAAuD;EACtE,MAAM,eAAe,QAAQ,gBAAgB;EAC7C,MAAM,UAAU,YAAY;GAC1B,OAAO,QAAQ,SAAS,KAAK;GAC7B,OAAO,QAAQ;GACf,OAAO,cAAc,QAAQ,OAAO,KAAK,YAAY;GAErD,iBAAiB;GACjB,OAAO,QAAQ;EACjB,CAAC;EAED,MAAM,SAAS,MAAM,KAAK,KACxB,KAAK,MACL,KAAK,YACL;GACE,SAAS,EACP,gBAAgB,mBAClB;GACA,MAAM,KAAK,UAAU,OAAO;GAC5B,QAAQ,QAAQ;EAClB,IACC,aAAa,8BAA8B,UAAU,KAAK,IAAI,CACjE;EAEA,OAAO;GACL,GAAG;GACH,QAAQ,OAAO,UAAU;GACzB,OAAO,OAAO,SAAS,OAAO,QAAQ,KAAK;EAC7C;CACF;AACF;;;AChDA,IAAa,yBAAb,cACU,kBAEV;CACE,OAAgB;CAEhB;CACA;CACA;CAEA,YAAY,SAAwC;EAClD,MAAM,OAAO;EACb,KAAK,aAAa,QAAQ,cAAc;EACxC,KAAK,eAAe,QAAQ,gBAAgB;EAC5C,KAAK,eAAe,QAAQ;CAC9B;CAEA,MAAM,WAAW,SAAuD;EACtE,MAAM,OAAO,iCAAiC,SAAS,KAAK,YAAY;EACxE,MAAM,SAAS,MAAM,KAAK,KACxB,KAAK,MACL,KAAK,YACL;GACE,MAAM;GACN,QAAQ,QAAQ;EAClB,IACC,aAAa,8BAA8B,UAAU,KAAK,IAAI,CACjE;EAEA,OAAO;GACL,GAAG;GACH,QAAQ,OAAO,UAAU,QAAQ;GACjC,OAAO,OAAO,SAAS,QAAQ,SAAS,KAAK;EAC/C;CACF;AACF;;;AC5BA,IAAa,0BAAb,cACU,kBAEV;CACE,OAAgB;CAChB;CAEA,YAAY,SAAyC;EACnD,MAAM,OAAO;EACb,KAAK,iBAAiB,QAAQ,kBAAkB;CAClD;CAEA,MAAM,WAAW,SAA0D;EACzE,MAAM,OAAO,IAAI,SAAS;EAC1B,iBAAiB,MAAM,QAAQ,KAAK;EACpC,wBAAwB,MAAM,YAAY,QAAQ,QAAQ;EAE1D,OAAO,KAAK,KACV,KAAK,MACL,KAAK,gBACL;GACE,MAAM;GACN,QAAQ,QAAQ;EAClB,IACC,aAAa,uBAAuB,UAAU,KAAK,IAAI,CAC1D;CACF;AACF;AAEA,IAAa,gCAAb,cACU,kBAEV;CACE,OAAgB;CAChB;CACA;CAEA,YAAY,SAA+C;EACzD,MAAM,OAAO;EACb,KAAK,iBAAiB,QAAQ,kBAAkB;EAChD,KAAK,eAAe,QAAQ;CAC9B;CAEA,MAAM,WAAW,SAAuD;EACtE,MAAM,OAAO,iCAAiC,SAAS,KAAK,YAAY;EAExE,OAAO,KAAK,KACV,KAAK,MACL,KAAK,gBACL;GACE,MAAM;GACN,QAAQ,QAAQ;EAClB,IACC,aAAa,8BAA8B,UAAU,KAAK,IAAI,CACjE;CACF;AACF;;;;;;;;;;AC7BA,eAAsB,UACpB,UAA4B,CAAC,GAC7B,UAAgC,CAAC,GAChB;CACjB,MAAM,cACJ,QAAQ,gBAAgB,QACpB,KAAA,IACA,MAAM,uBAAuB,QAAQ,aAAa,OAAO;CAC/D,MAAM,cACJ,QAAQ,gBAAgB,QACpB,KAAA,IACA,MAAM,6BAA6B,QAAQ,aAAa,OAAO;CAErE,OAAO;EACL;EACA;EACA,MAAM,WAAW,SAA0D;GACzE,IAAI,CAAC,aACH,MAAM,IAAI,yBAAyB,4BAA4B;GAEjE,OAAO,YAAY,WAAW,OAAO;EACvC;EACA,MAAM,WAAW,SAAuD;GACtE,IAAI,CAAC,aACH,MAAM,IAAI,yBAAyB,4BAA4B;GAEjE,OAAO,YAAY,WAAW,OAAO;EACvC;CACF;AACF;AAEA,eAAsB,eACpB,UAAiC,CAAC,GAClC,UAAgC,CAAC,GACX;CACtB,MAAM,WAAW,4BAA4B,SAAS,OAAO;CAE7D,QAAQ,SAAS,MAAjB;EACE,KAAK,iBACH,OAAO,IAAI,wBAAwB,QAAQ;EAC7C,SACE,MAAM,IAAI,0BACP,SAA+B,QAAQ,WACxC,KACF;CACJ;AACF;AAEA,eAAsB,qBACpB,SACA,UAAgC,CAAC,GACL;CAC5B,MAAM,WAAW,kCAAkC,SAAS,OAAO;CAEnE,QAAQ,SAAS,MAAjB;EACE,KAAK,iBACH,OAAO,IAAI,8BAA8B,QAAQ;EACnD,KAAK,aACH,OAAO,IAAI,uBAAuB,QAAQ;EAC5C,KAAK,qBACH,OAAO,IAAI,kCAAkC,QAAQ;EACvD,SACE,MAAM,IAAI,0BACP,SAA+B,QAAQ,WACxC,KACF;CACJ;AACF;AAEA,SAAgB,6BAAwD;CACtE,OAAO;EACL,cAAc,CAAC,eAAe;EAC9B,cAAc;GAAC;GAAiB;GAAa;EAAmB;CAClE;AACF;AAEA,eAAe,uBACb,SACA,SACkC;CAClC,IAAI,CAAC,WAAW,CAAC,kBAAkB,QAAQ,OAAO,WAAW,CAAC,GAC5D;CAGF,OAAO,eAAe,SAAS,OAAO;AACxC;AAEA,eAAe,6BACb,SACA,SACwC;CACxC,IAAI,CAAC,WAAW,CAAC,kBAAkB,QAAQ,OAAO,WAAW,CAAC,GAC5D;CAGF,OAAO,qBAAqB,SAAS,OAAO;AAC9C;AAEA,SAAS,4BACP,UAAiC,CAAC,GAClC,SACgC;CAChC,MAAM,MAAM,QAAQ,OAAO,WAAW;CACtC,MAAM,OACJ,QAAQ,QACR,QACE,KACA,wBACA,2BACA,aACF,KACA;CACF,MAAM,UACJ,QAAQ,WAAW,QAAQ,KAAK,4BAA4B,cAAc;CAE5E,IAAI,SAAS,iBACX,MAAM,IAAI,0BAA0B,MAAM,KAAK;CAGjD,IAAI,CAAC,SAAS,KAAK,GACjB,MAAM,IAAI,yBAAyB,2BAA2B,IAAI;CAGpE,OAAO;EACL,GAAG;EACH;EACA,SAAS,QAAQ,KAAK;EACtB,OAAO,QAAQ,SAAS,QAAQ;EAChC,SAAS,QAAQ,WAAW,QAAQ;EACpC,QACE,QAAQ,UACR,QAAQ,KAAK,2BAA2B,eAAe,gBAAgB;EACzE,WACE,QAAQ,aACR,qBACE,QAAQ,KAAK,8BAA8B,gBAAgB,CAC7D;EACF,gBACE,QAAQ,kBACR,QAAQ,KAAK,wBAAwB,UAAU;CACnD;AACF;AAEA,SAAS,kCACP,SACA,SAI2C;CAC3C,MAAM,MAAM,QAAQ,OAAO,WAAW;CACtC,MAAM,OACJ,SAAS,QACR,QACC,KACA,wBACA,2BACA,aACF;CACF,MAAM,UACJ,SAAS,WACT,QAAQ,KAAK,4BAA4B,cAAc;CAEzD,IAAI,CAAC,MACH,MAAM,IAAI,yBAAyB,+BAA+B;CAGpE,IACE,SAAS,mBACT,SAAS,eACT,SAAS,qBAET,MAAM,IAAI,0BAA0B,MAAM,KAAK;CAGjD,IAAI,CAAC,SAAS,KAAK,GACjB,MAAM,IAAI,yBAAyB,2BAA2B,IAAI;CAGpE,MAAM,SAAS;EACb;EACA,SAAS,QAAQ,KAAK;EACtB,OAAO,SAAS,SAAS,QAAQ;EACjC,SAAS,SAAS,WAAW,QAAQ;EACrC,QACE,SAAS,UACT,QAAQ,KAAK,2BAA2B,eAAe,gBAAgB;EACzE,WACE,SAAS,aACT,qBACE,QAAQ,KAAK,8BAA8B,gBAAgB,CAC7D;CACJ;CAEA,IAAI,SAAS,iBACX,OAAO;EACL,GAAG;EACH;EACA,gBACE,SAAS,kBACT,QAAQ,KAAK,wBAAwB,UAAU;EACjD,cACE,SAAS,gBACT,QAAQ,KAAK,yBAAyB,WAAW;CACrD;CAGF,IAAI,SAAS,aACX,OAAO;EACL,GAAG;EACH;EACA,YACE,SAAS,cAAc,QAAQ,KAAK,wBAAwB,UAAU;EACxE,cACE,SAAS,gBACT,QAAQ,KAAK,yBAAyB,WAAW;EACnD,cACE,SAAS,gBACT,QAAQ,KAAK,yBAAyB,WAAW;CACrD;CAGF,IAAI,SAAS,qBACX,OAAO;EACL,GAAG;EACH;EACA,YACE,SAAS,cAAc,QAAQ,KAAK,wBAAwB,UAAU;EACxE,cACE,SAAS,gBACT,QAAQ,KAAK,yBAAyB,WAAW;EACnD,cACE,SAAS,gBACT,QAAQ,KAAK,yBAAyB,WAAW;CACrD;CAGF,MAAM,IAAI,0BAA0B,MAAM,KAAK;AACjD;AAEA,SAAS,kBAAkB,KAAyB;CAClD,OAAO,UACL,KACA,wBACA,2BACA,4BACA,eACA,cACF;AACF;AAEA,SAAS,kBAAkB,KAAyB;CAClD,OAAO,UACL,KACA,wBACA,2BACA,4BACA,eACA,cACF;AACF;AAEA,SAAS,QAAQ,KAAgB,GAAG,MAAoC;CACtE,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,IAAI;EAClB,IAAI,OAAO,KAAK,GACd,OAAO,MAAM,KAAK;CAEtB;AAGF;AAEA,SAAS,UAAU,KAAgB,GAAG,MAAyB;CAC7D,OAAO,KAAK,MAAM,QAAQ,QAAQ,IAAI,IAAI,EAAE,KAAK,CAAC,CAAC;AACrD;AAEA,SAAS,qBAAqB,OAA+C;CAC3E,IAAI,CAAC,OACH;CAGF,MAAM,SAAS,OAAO,SAAS,OAAO,EAAE;CACxC,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS,KAAA;AAC5C;AAEA,SAAS,aAAwB;CAC/B,OAAO,WAAW,SAAS,OAAO,CAAC;AACrC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/shared/errors.ts","../src/shared/audio.ts","../src/shared/env.ts","../src/shared/retry.ts","../src/shared/http.ts","../src/adapters/openai-compatible.ts","../src/shared/usage.ts","../src/adapters/openai-compatible-transcriber.ts","../src/adapters/qwen3.ts","../src/adapters/studio-server.ts","../src/shared/factory.ts"],"sourcesContent":["export class SpeechError extends Error {\n constructor(\n message: string,\n public code: string,\n public adapter?: string,\n ) {\n super(message);\n this.name = 'SpeechError';\n }\n}\n\nexport class SpeechConfigurationError extends SpeechError {\n constructor(message: string, adapter?: string) {\n super(message, 'SPEECH_CONFIGURATION_ERROR', adapter);\n this.name = 'SpeechConfigurationError';\n }\n}\n\nexport class InvalidSpeechAdapterError extends SpeechError {\n constructor(type: string, kind: string) {\n super(`Invalid ${kind} speech adapter type: ${type}`, 'INVALID_ADAPTER');\n this.name = 'InvalidSpeechAdapterError';\n }\n}\n\nexport class SpeechProviderError extends SpeechError {\n readonly status?: number;\n readonly responseBody?: string;\n /** Parsed `Retry-After` delay in milliseconds, when the provider sent one. */\n readonly retryAfterMs?: number;\n\n constructor(\n adapter: string,\n message: string,\n options: {\n status?: number;\n responseBody?: string;\n retryAfterMs?: number;\n cause?: unknown;\n } = {},\n ) {\n super(message, 'SPEECH_PROVIDER_ERROR', adapter);\n this.name = 'SpeechProviderError';\n this.status = options.status;\n this.responseBody = options.responseBody;\n this.retryAfterMs = options.retryAfterMs;\n if (options.cause !== undefined) {\n this.cause = options.cause;\n }\n }\n}\n","/**\n * Audio input normalisation shared by speech adapters: accepts the input\n * shapes callers actually have (Blob, Buffer, Uint8Array, ArrayBuffer,\n * ReadableStream), enforces byte limits while buffering, and derives a\n * multipart filename whose extension matches the MIME type.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\nimport type { AudioInput, AudioSource } from './types.js';\n\n/** OpenAI caps `/audio/transcriptions` uploads at 25 MB. */\nexport const DEFAULT_MAX_AUDIO_BYTES = 25 * 1024 * 1024;\n\nconst MIME_EXTENSIONS: Record<string, string> = {\n 'audio/webm': 'webm',\n 'video/webm': 'webm',\n 'audio/mp4': 'm4a',\n 'audio/m4a': 'm4a',\n 'audio/x-m4a': 'm4a',\n 'video/mp4': 'mp4',\n 'audio/wav': 'wav',\n 'audio/wave': 'wav',\n 'audio/x-wav': 'wav',\n 'audio/vnd.wave': 'wav',\n 'audio/ogg': 'ogg',\n 'application/ogg': 'ogg',\n 'audio/mpeg': 'mp3',\n 'audio/mp3': 'mp3',\n 'audio/mpga': 'mp3',\n 'audio/flac': 'flac',\n 'audio/x-flac': 'flac',\n};\n\n/**\n * Strips parameters (e.g. `;codecs=opus`) and lower-cases a MIME type.\n * Returns `undefined` for blank input.\n */\nexport function normalizeMimeType(\n mimeType: string | undefined | null,\n): string | undefined {\n const essence = mimeType?.split(';')[0]?.trim().toLowerCase();\n return essence ? essence : undefined;\n}\n\n/**\n * Maps an audio MIME type to the file extension OpenAI-compatible servers use\n * to detect the container format. Codec parameters are ignored, so\n * `audio/webm;codecs=opus` maps to `webm`. Returns `undefined` when unknown.\n */\nexport function mimeTypeToAudioExtension(\n mimeType: string | undefined | null,\n): string | undefined {\n const essence = normalizeMimeType(mimeType);\n return essence ? MIME_EXTENSIONS[essence] : undefined;\n}\n\nexport interface NormalizeAudioOptions {\n /** MIME type override; wins over `AudioInput.mimeType`/`contentType` and Blob type. */\n mimeType?: string;\n /**\n * Maximum payload size in bytes. `undefined` or `Infinity` disables the\n * limit. Streams are cancelled as soon as they exceed it.\n */\n maxBytes?: number;\n /** Adapter name recorded on configuration errors. */\n adapter?: string;\n /** Aborts buffering (including a pending stream read) with the signal's reason. */\n signal?: AbortSignal;\n /** Base filename (without extension) used when the input has none. */\n defaultBasename?: string;\n /** Append a MIME-derived extension to the default filename. Default `true`. */\n deriveExtension?: boolean;\n}\n\nexport interface NormalizedAudio {\n blob: Blob;\n /** Full MIME type including parameters, or `application/octet-stream`. */\n mimeType: string;\n bytes: number;\n /** Explicit filename, or `<basename>.<ext>` derived from the MIME type. */\n filename: string;\n durationSeconds?: number;\n}\n\n/**\n * Accepts either an {@link AudioInput} wrapper or a bare {@link AudioSource}\n * and returns a size-checked Blob plus the metadata needed for multipart\n * uploads.\n */\nexport async function normalizeAudioInput(\n input: AudioInput | AudioSource,\n options: NormalizeAudioOptions = {},\n): Promise<NormalizedAudio> {\n const wrapped: AudioInput = isAudioInput(input) ? input : { data: input };\n const { adapter, signal } = options;\n signal?.throwIfAborted();\n const maxBytes = resolveMaxBytes(options.maxBytes, adapter);\n const data = wrapped.data;\n\n const blobType =\n typeof Blob !== 'undefined' && data instanceof Blob ? data.type : undefined;\n const mimeType =\n options.mimeType ??\n wrapped.mimeType ??\n wrapped.contentType ??\n (blobType || undefined) ??\n 'application/octet-stream';\n\n let blob: Blob;\n if (typeof Blob !== 'undefined' && data instanceof Blob) {\n assertWithinLimit(data.size, maxBytes, adapter);\n blob = data.type === mimeType ? data : new Blob([data], { type: mimeType });\n } else if (data instanceof Uint8Array) {\n assertWithinLimit(data.byteLength, maxBytes, adapter);\n blob = new Blob([copyBytes(data)], { type: mimeType });\n } else if (data instanceof ArrayBuffer) {\n assertWithinLimit(data.byteLength, maxBytes, adapter);\n blob = new Blob([data], { type: mimeType });\n } else if (isReadableStream(data)) {\n const chunks = await readStreamWithLimit(data, maxBytes, adapter, signal);\n blob = new Blob(chunks, { type: mimeType });\n } else {\n throw new SpeechConfigurationError(\n 'Unsupported audio input: expected Blob, Buffer, Uint8Array, ArrayBuffer, or ReadableStream',\n adapter,\n );\n }\n\n if (blob.size === 0) {\n throw new SpeechConfigurationError('Audio input is empty', adapter);\n }\n\n const extension =\n options.deriveExtension === false\n ? undefined\n : mimeTypeToAudioExtension(mimeType);\n const basename = options.defaultBasename ?? 'audio';\n const filename =\n wrapped.filename ?? (extension ? `${basename}.${extension}` : basename);\n\n return {\n blob,\n mimeType,\n bytes: blob.size,\n filename,\n durationSeconds: wrapped.durationSeconds,\n };\n}\n\nfunction isAudioInput(value: AudioInput | AudioSource): value is AudioInput {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'data' in value &&\n !(typeof Blob !== 'undefined' && value instanceof Blob) &&\n !(value instanceof Uint8Array) &&\n !(value instanceof ArrayBuffer) &&\n !isReadableStream(value)\n );\n}\n\nfunction isReadableStream(value: unknown): value is ReadableStream<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ReadableStream).getReader === 'function'\n );\n}\n\nfunction resolveMaxBytes(\n maxBytes: number | undefined,\n adapter: string | undefined,\n): number {\n if (maxBytes === undefined) {\n return Number.POSITIVE_INFINITY;\n }\n\n if (Number.isNaN(maxBytes) || maxBytes <= 0) {\n throw new SpeechConfigurationError(\n 'maxBytes must be a positive number',\n adapter,\n );\n }\n\n return maxBytes;\n}\n\nfunction assertWithinLimit(\n bytes: number,\n maxBytes: number,\n adapter: string | undefined,\n): void {\n if (bytes > maxBytes) {\n throw new SpeechConfigurationError(\n `Audio input exceeds maxBytes (${bytes} > ${maxBytes} bytes)`,\n adapter,\n );\n }\n}\n\nasync function readStreamWithLimit(\n stream: ReadableStream<unknown>,\n maxBytes: number,\n adapter: string | undefined,\n signal: AbortSignal | undefined,\n): Promise<Uint8Array<ArrayBuffer>[]> {\n const reader = stream.getReader();\n const chunks: Uint8Array<ArrayBuffer>[] = [];\n let total = 0;\n // Cancelling the reader settles any pending read, so an abort cannot hang.\n const onAbort = () => {\n reader.cancel(signal?.reason).catch(() => undefined);\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n signal?.throwIfAborted();\n if (done) {\n break;\n }\n\n // Check the size before copying so one oversized chunk is never duplicated.\n total += chunkByteLength(value, adapter);\n if (total > maxBytes) {\n await reader.cancel().catch(() => undefined);\n throw new SpeechConfigurationError(\n `Audio stream exceeds maxBytes (more than ${maxBytes} bytes)`,\n adapter,\n );\n }\n chunks.push(toBytes(value));\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n reader.releaseLock();\n }\n\n return chunks;\n}\n\nfunction chunkByteLength(value: unknown, adapter: string | undefined): number {\n if (value instanceof Uint8Array || value instanceof ArrayBuffer) {\n return value.byteLength;\n }\n\n throw new SpeechConfigurationError(\n 'Audio stream chunks must be Uint8Array or ArrayBuffer',\n adapter,\n );\n}\n\nfunction toBytes(value: unknown): Uint8Array<ArrayBuffer> {\n return value instanceof Uint8Array\n ? copyBytes(value)\n : new Uint8Array((value as ArrayBuffer).slice(0));\n}\n\nfunction copyBytes(bytes: Uint8Array): Uint8Array<ArrayBuffer> {\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n return copy;\n}\n","/**\n * Environment configuration for speech adapters.\n *\n * Transcriber settings resolve in this order (first wins):\n * explicit options → factory context → `HAVE_SPEECH_TRANSCRIBER_*` →\n * legacy `HAVE_SPEECH_STT_*` → gateway `STT_*` aliases.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\nimport type { SpeechFetch } from './types.js';\n\nexport interface SpeechEnv {\n [key: string]: string | undefined;\n}\n\n/** Environment variable names read for each transcriber setting, in priority order. */\nexport const TRANSCRIBER_ENV_KEYS = {\n type: [\n 'HAVE_SPEECH_TRANSCRIBER_TYPE',\n 'HAVE_SPEECH_STT_TYPE',\n 'HAVE_SPEECH_STT_ADAPTER',\n 'STT_ADAPTER',\n ],\n baseUrl: [\n 'HAVE_SPEECH_TRANSCRIBER_BASE_URL',\n 'HAVE_SPEECH_STT_BASE_URL',\n 'STT_BASE_URL',\n ],\n model: [\n 'HAVE_SPEECH_TRANSCRIBER_MODEL',\n 'HAVE_SPEECH_STT_MODEL',\n 'STT_MODEL',\n ],\n apiKey: [\n 'HAVE_SPEECH_TRANSCRIBER_API_KEY',\n 'HAVE_SPEECH_STT_API_KEY',\n 'STT_API_KEY',\n 'SPEECH_API_KEY',\n ],\n timeoutMs: [\n 'HAVE_SPEECH_TRANSCRIBER_TIMEOUT',\n 'HAVE_SPEECH_TRANSCRIBER_TIMEOUT_MS',\n 'HAVE_SPEECH_STT_TIMEOUT_MS',\n 'STT_TIMEOUT_MS',\n ],\n maxBytes: ['HAVE_SPEECH_TRANSCRIBER_MAX_BYTES', 'STT_MAX_BYTES'],\n headers: ['HAVE_SPEECH_TRANSCRIBER_HEADERS', 'STT_HEADERS'],\n path: ['HAVE_SPEECH_TRANSCRIBER_PATH', 'HAVE_SPEECH_STT_PATH', 'STT_PATH'],\n} as const;\n\n/** Transcriber settings read from the environment. All fields are optional. */\nexport interface TranscriberEnvConfig {\n type?: string;\n baseUrl?: string;\n model?: string;\n apiKey?: string;\n timeoutMs?: number;\n maxBytes?: number;\n headers?: Record<string, string>;\n path?: string;\n}\n\n/** Options shape accepted by {@link resolveTranscriberConfig}. */\nexport interface TranscriberConfigInput {\n type?: string;\n baseUrl?: string;\n model?: string;\n apiKey?: string;\n timeoutMs?: number;\n maxBytes?: number;\n headers?: HeadersInit;\n fetch?: SpeechFetch;\n}\n\nexport interface TranscriberConfigContext {\n env?: SpeechEnv;\n fetch?: SpeechFetch;\n headers?: HeadersInit;\n}\n\n/** Resolved transcriber settings. `headers` merges env → context → options. */\nexport interface ResolvedTranscriberConfig {\n type?: string;\n baseUrl?: string;\n model?: string;\n apiKey?: string;\n timeoutMs?: number;\n maxBytes?: number;\n headers?: Headers;\n fetch?: SpeechFetch;\n path?: string;\n}\n\nexport function readTranscriberEnv(env: SpeechEnv): TranscriberEnvConfig {\n return {\n type: readEnv(env, ...TRANSCRIBER_ENV_KEYS.type),\n baseUrl: readEnv(env, ...TRANSCRIBER_ENV_KEYS.baseUrl),\n model: readEnv(env, ...TRANSCRIBER_ENV_KEYS.model),\n apiKey: readEnv(env, ...TRANSCRIBER_ENV_KEYS.apiKey),\n timeoutMs: parseOptionalInteger(\n readEnv(env, ...TRANSCRIBER_ENV_KEYS.timeoutMs),\n ),\n maxBytes: parseOptionalInteger(\n readEnv(env, ...TRANSCRIBER_ENV_KEYS.maxBytes),\n ),\n headers: parseHeadersEnv(env, TRANSCRIBER_ENV_KEYS.headers),\n path: readEnv(env, ...TRANSCRIBER_ENV_KEYS.path),\n };\n}\n\nexport function hasTranscriberEnv(env: SpeechEnv): boolean {\n return hasAnyEnv(\n env,\n ...TRANSCRIBER_ENV_KEYS.type,\n ...TRANSCRIBER_ENV_KEYS.baseUrl,\n );\n}\n\n/**\n * Merges explicit transcriber options with the factory context and the\n * environment. Explicit options always win; headers are merged so a\n * per-deployment env header (e.g. a gateway virtual key) can be combined with\n * explicit ones.\n */\nexport function resolveTranscriberConfig(\n options: TranscriberConfigInput = {},\n context: TranscriberConfigContext = {},\n): ResolvedTranscriberConfig {\n const fromEnv = readTranscriberEnv(context.env ?? defaultEnv());\n const headers = mergeHeaderInits(\n fromEnv.headers,\n context.headers,\n options.headers,\n );\n\n return {\n type: options.type ?? fromEnv.type,\n baseUrl: (options.baseUrl ?? fromEnv.baseUrl)?.trim(),\n model: options.model ?? fromEnv.model,\n apiKey: options.apiKey ?? fromEnv.apiKey,\n timeoutMs: options.timeoutMs ?? fromEnv.timeoutMs,\n maxBytes: options.maxBytes ?? fromEnv.maxBytes,\n headers,\n fetch: options.fetch ?? context.fetch,\n path: fromEnv.path,\n };\n}\n\n/** Merges header sets left to right; later sets win per header name. */\nexport function mergeHeaderInits(\n ...sets: Array<HeadersInit | undefined>\n): Headers | undefined {\n const present = sets.filter((set): set is HeadersInit => Boolean(set));\n if (present.length === 0) {\n return undefined;\n }\n\n const merged = new Headers();\n for (const set of present) {\n new Headers(set).forEach((value, key) => {\n merged.set(key, value);\n });\n }\n return merged;\n}\n\nexport function readEnv(env: SpeechEnv, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const value = env[key];\n if (value?.trim()) {\n return value.trim();\n }\n }\n\n return undefined;\n}\n\nexport function hasAnyEnv(env: SpeechEnv, ...keys: string[]): boolean {\n return keys.some((key) => Boolean(env[key]?.trim()));\n}\n\nexport function parseOptionalInteger(\n value: string | undefined,\n): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nexport function defaultEnv(): SpeechEnv {\n return globalThis.process?.env ?? {};\n}\n\n/**\n * Parses a JSON object of header names to string values. The raw value is\n * never echoed in errors because it may carry credentials.\n */\nfunction parseHeadersEnv(\n env: SpeechEnv,\n keys: readonly string[],\n): Record<string, string> | undefined {\n for (const key of keys) {\n const raw = env[key]?.trim();\n if (!raw) {\n continue;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new SpeechConfigurationError(\n `${key} must be a JSON object of header names to string values`,\n );\n }\n\n if (\n !parsed ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed) ||\n !Object.values(parsed).every((value) => typeof value === 'string')\n ) {\n throw new SpeechConfigurationError(\n `${key} must be a JSON object of header names to string values`,\n );\n }\n\n return parsed as Record<string, string>;\n }\n\n return undefined;\n}\n","/**\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","import {\n compactJson,\n HttpSpeechAdapter,\n readSynthesizedSpeechResponse,\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 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 ?? '/v1/audio/speech';\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 const speech = await this.post(\n this.type,\n this.speechPath,\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","/**\n * Usage reporting shared by speech adapters.\n *\n * Adapters attach a {@link SpeechUsage} record to their results and call the\n * optional {@link SpeechUsageCallback} so callers can attribute each provider\n * call (for example to a tenant for billing).\n */\n\nexport type SpeechOperation = 'transcription' | 'synthesis';\n\nexport interface SpeechUsage {\n /** Operation that produced this usage record. */\n operation: SpeechOperation;\n /** Adapter type that made the provider call, e.g. `openai-compatible`. */\n provider: string;\n /** Model identifier sent to (or reported by) the provider. */\n model?: string;\n /** Audio duration in seconds, when the provider or caller reports it. */\n audioSeconds?: number;\n /** Size of the audio payload in bytes (uploaded for STT, received for TTS). */\n bytes?: number;\n /** Provider usage block, passed through untouched (e.g. OpenAI `usage`). */\n providerUsage?: Record<string, unknown>;\n}\n\nexport type SpeechUsageCallback = (usage: SpeechUsage) => void | Promise<void>;\n\n/**\n * Invokes every configured usage callback in order (adapter-level first, then\n * request-level). Callback errors propagate to the caller so that billing\n * hooks never fail silently.\n */\nexport async function reportSpeechUsage(\n usage: SpeechUsage,\n ...callbacks: Array<SpeechUsageCallback | undefined>\n): Promise<void> {\n for (const callback of callbacks) {\n if (callback) {\n await callback(usage);\n }\n }\n}\n\n/**\n * Extracts audio seconds from an OpenAI-style `usage` block\n * (`{ type: 'duration', seconds }`).\n */\nexport function audioSecondsFromProviderUsage(\n providerUsage: Record<string, unknown> | undefined,\n): number | undefined {\n if (!providerUsage) {\n return undefined;\n }\n\n const seconds = providerUsage.seconds;\n return typeof seconds === 'number' && Number.isFinite(seconds)\n ? seconds\n : undefined;\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","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} from './env.js';\nimport {\n InvalidSpeechAdapterError,\n SpeechConfigurationError,\n} from './errors.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 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: ['studio-server', 'openai-compatible'],\n synthesizers: ['studio-server', 'qwen3-tts', 'openai-compatible'],\n };\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"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { AudioInput, AudioSource } from './types.js';
2
+ /** OpenAI caps `/audio/transcriptions` uploads at 25 MB. */
3
+ export declare const DEFAULT_MAX_AUDIO_BYTES: number;
4
+ /**
5
+ * Strips parameters (e.g. `;codecs=opus`) and lower-cases a MIME type.
6
+ * Returns `undefined` for blank input.
7
+ */
8
+ export declare function normalizeMimeType(mimeType: string | undefined | null): string | undefined;
9
+ /**
10
+ * Maps an audio MIME type to the file extension OpenAI-compatible servers use
11
+ * to detect the container format. Codec parameters are ignored, so
12
+ * `audio/webm;codecs=opus` maps to `webm`. Returns `undefined` when unknown.
13
+ */
14
+ export declare function mimeTypeToAudioExtension(mimeType: string | undefined | null): string | undefined;
15
+ export interface NormalizeAudioOptions {
16
+ /** MIME type override; wins over `AudioInput.mimeType`/`contentType` and Blob type. */
17
+ mimeType?: string;
18
+ /**
19
+ * Maximum payload size in bytes. `undefined` or `Infinity` disables the
20
+ * limit. Streams are cancelled as soon as they exceed it.
21
+ */
22
+ maxBytes?: number;
23
+ /** Adapter name recorded on configuration errors. */
24
+ adapter?: string;
25
+ /** Aborts buffering (including a pending stream read) with the signal's reason. */
26
+ signal?: AbortSignal;
27
+ /** Base filename (without extension) used when the input has none. */
28
+ defaultBasename?: string;
29
+ /** Append a MIME-derived extension to the default filename. Default `true`. */
30
+ deriveExtension?: boolean;
31
+ }
32
+ export interface NormalizedAudio {
33
+ blob: Blob;
34
+ /** Full MIME type including parameters, or `application/octet-stream`. */
35
+ mimeType: string;
36
+ bytes: number;
37
+ /** Explicit filename, or `<basename>.<ext>` derived from the MIME type. */
38
+ filename: string;
39
+ durationSeconds?: number;
40
+ }
41
+ /**
42
+ * Accepts either an {@link AudioInput} wrapper or a bare {@link AudioSource}
43
+ * and returns a size-checked Blob plus the metadata needed for multipart
44
+ * uploads.
45
+ */
46
+ export declare function normalizeAudioInput(input: AudioInput | AudioSource, options?: NormalizeAudioOptions): Promise<NormalizedAudio>;
47
+ //# sourceMappingURL=audio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../src/shared/audio.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,QAAmB,CAAC;AAsBxD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAClC,MAAM,GAAG,SAAS,CAGpB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAClC,MAAM,GAAG,SAAS,CAGpB;AAED,MAAM,WAAW,qBAAqB;IACpC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,CAAC;IACX,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,UAAU,GAAG,WAAW,EAC/B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAuD1B"}
@@ -0,0 +1,70 @@
1
+ import { SpeechFetch } from './types.js';
2
+ export interface SpeechEnv {
3
+ [key: string]: string | undefined;
4
+ }
5
+ /** Environment variable names read for each transcriber setting, in priority order. */
6
+ export declare const TRANSCRIBER_ENV_KEYS: {
7
+ readonly type: readonly ["HAVE_SPEECH_TRANSCRIBER_TYPE", "HAVE_SPEECH_STT_TYPE", "HAVE_SPEECH_STT_ADAPTER", "STT_ADAPTER"];
8
+ readonly baseUrl: readonly ["HAVE_SPEECH_TRANSCRIBER_BASE_URL", "HAVE_SPEECH_STT_BASE_URL", "STT_BASE_URL"];
9
+ readonly model: readonly ["HAVE_SPEECH_TRANSCRIBER_MODEL", "HAVE_SPEECH_STT_MODEL", "STT_MODEL"];
10
+ readonly apiKey: readonly ["HAVE_SPEECH_TRANSCRIBER_API_KEY", "HAVE_SPEECH_STT_API_KEY", "STT_API_KEY", "SPEECH_API_KEY"];
11
+ readonly timeoutMs: readonly ["HAVE_SPEECH_TRANSCRIBER_TIMEOUT", "HAVE_SPEECH_TRANSCRIBER_TIMEOUT_MS", "HAVE_SPEECH_STT_TIMEOUT_MS", "STT_TIMEOUT_MS"];
12
+ readonly maxBytes: readonly ["HAVE_SPEECH_TRANSCRIBER_MAX_BYTES", "STT_MAX_BYTES"];
13
+ readonly headers: readonly ["HAVE_SPEECH_TRANSCRIBER_HEADERS", "STT_HEADERS"];
14
+ readonly path: readonly ["HAVE_SPEECH_TRANSCRIBER_PATH", "HAVE_SPEECH_STT_PATH", "STT_PATH"];
15
+ };
16
+ /** Transcriber settings read from the environment. All fields are optional. */
17
+ export interface TranscriberEnvConfig {
18
+ type?: string;
19
+ baseUrl?: string;
20
+ model?: string;
21
+ apiKey?: string;
22
+ timeoutMs?: number;
23
+ maxBytes?: number;
24
+ headers?: Record<string, string>;
25
+ path?: string;
26
+ }
27
+ /** Options shape accepted by {@link resolveTranscriberConfig}. */
28
+ export interface TranscriberConfigInput {
29
+ type?: string;
30
+ baseUrl?: string;
31
+ model?: string;
32
+ apiKey?: string;
33
+ timeoutMs?: number;
34
+ maxBytes?: number;
35
+ headers?: HeadersInit;
36
+ fetch?: SpeechFetch;
37
+ }
38
+ export interface TranscriberConfigContext {
39
+ env?: SpeechEnv;
40
+ fetch?: SpeechFetch;
41
+ headers?: HeadersInit;
42
+ }
43
+ /** Resolved transcriber settings. `headers` merges env → context → options. */
44
+ export interface ResolvedTranscriberConfig {
45
+ type?: string;
46
+ baseUrl?: string;
47
+ model?: string;
48
+ apiKey?: string;
49
+ timeoutMs?: number;
50
+ maxBytes?: number;
51
+ headers?: Headers;
52
+ fetch?: SpeechFetch;
53
+ path?: string;
54
+ }
55
+ export declare function readTranscriberEnv(env: SpeechEnv): TranscriberEnvConfig;
56
+ export declare function hasTranscriberEnv(env: SpeechEnv): boolean;
57
+ /**
58
+ * Merges explicit transcriber options with the factory context and the
59
+ * environment. Explicit options always win; headers are merged so a
60
+ * per-deployment env header (e.g. a gateway virtual key) can be combined with
61
+ * explicit ones.
62
+ */
63
+ export declare function resolveTranscriberConfig(options?: TranscriberConfigInput, context?: TranscriberConfigContext): ResolvedTranscriberConfig;
64
+ /** Merges header sets left to right; later sets win per header name. */
65
+ export declare function mergeHeaderInits(...sets: Array<HeadersInit | undefined>): Headers | undefined;
66
+ export declare function readEnv(env: SpeechEnv, ...keys: string[]): string | undefined;
67
+ export declare function hasAnyEnv(env: SpeechEnv, ...keys: string[]): boolean;
68
+ export declare function parseOptionalInteger(value: string | undefined): number | undefined;
69
+ export declare function defaultEnv(): SpeechEnv;
70
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/shared/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,uFAAuF;AACvF,eAAO,MAAM,oBAAoB;;;;;;;;;CAgCvB,CAAC;AAEX,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,+EAA+E;AAC/E,MAAM,WAAW,yBAAyB;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,oBAAoB,CAevE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAMzD;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,sBAA2B,EACpC,OAAO,GAAE,wBAA6B,GACrC,yBAAyB,CAmB3B;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,GAAG,IAAI,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GACtC,OAAO,GAAG,SAAS,CAarB;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAS7E;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAEpE;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,GAAG,SAAS,CAOpB;AAED,wBAAgB,UAAU,IAAI,SAAS,CAEtC"}
@@ -12,9 +12,12 @@ export declare class InvalidSpeechAdapterError extends SpeechError {
12
12
  export declare class SpeechProviderError extends SpeechError {
13
13
  readonly status?: number;
14
14
  readonly responseBody?: string;
15
+ /** Parsed `Retry-After` delay in milliseconds, when the provider sent one. */
16
+ readonly retryAfterMs?: number;
15
17
  constructor(adapter: string, message: string, options?: {
16
18
  status?: number;
17
19
  responseBody?: string;
20
+ retryAfterMs?: number;
18
21
  cause?: unknown;
19
22
  });
20
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAG3B,IAAI,EAAE,MAAM;IACZ,OAAO,CAAC,EAAE,MAAM;gBAFvB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,YAAA;CAK1B;AAED,qBAAa,wBAAyB,SAAQ,WAAW;gBAC3C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAI9C;AAED,qBAAa,yBAA0B,SAAQ,WAAW;gBAC5C,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAIvC;AAED,qBAAa,mBAAoB,SAAQ,WAAW;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAG7B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAU5E"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAG3B,IAAI,EAAE,MAAM;IACZ,OAAO,CAAC,EAAE,MAAM;gBAFvB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,YAAA;CAK1B;AAED,qBAAa,wBAAyB,SAAQ,WAAW;gBAC3C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAI9C;AAED,qBAAa,yBAA0B,SAAQ,WAAW;gBAC5C,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAIvC;AAED,qBAAa,mBAAoB,SAAQ,WAAW;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAG7B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,OAAO,CAAC;KACZ;CAWT"}
@@ -1,7 +1,5 @@
1
+ import { SpeechEnv } from './env.js';
1
2
  import { GetSpeechOptions, GetSpeechSynthesizerOptions, GetTranscriberOptions, Speech, SpeechAdapterAvailability, SpeechFetch, SpeechSynthesizer, Transcriber } from './types.js';
2
- interface SpeechEnv {
3
- [key: string]: string | undefined;
4
- }
5
3
  export interface SpeechFactoryContext {
6
4
  env?: SpeechEnv;
7
5
  fetch?: SpeechFetch;
@@ -18,5 +16,4 @@ export declare function getSpeech(options?: GetSpeechOptions, context?: SpeechFa
18
16
  export declare function getTranscriber(options?: GetTranscriberOptions, context?: SpeechFactoryContext): Promise<Transcriber>;
19
17
  export declare function getSpeechSynthesizer(options?: GetSpeechSynthesizerOptions, context?: SpeechFactoryContext): Promise<SpeechSynthesizer>;
20
18
  export declare function getAvailableSpeechAdapters(): SpeechAdapterAvailability;
21
- export {};
22
19
  //# sourceMappingURL=factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/shared/factory.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EAGrB,MAAM,EACN,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EAKjB,WAAW,EAGZ,MAAM,YAAY,CAAC;AAEpB,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,OAAO,GAAE,gBAAqB,EAC9B,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,EACnC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,WAAW,CAAC,CAYtB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,CAAC,EAAE,2BAA2B,EACrC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AAED,wBAAgB,0BAA0B,IAAI,yBAAyB,CAKtE"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/shared/factory.ts"],"names":[],"mappings":"AAOA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAKlB,OAAO,KAAK,EACV,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EAIrB,MAAM,EACN,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EAKjB,WAAW,EAGZ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,OAAO,GAAE,gBAAqB,EAC9B,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,EACnC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,WAAW,CAAC,CActB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,CAAC,EAAE,2BAA2B,EACrC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AAED,wBAAgB,0BAA0B,IAAI,yBAAyB,CAKtE"}
@@ -1,6 +1,24 @@
1
- import { AudioInput, HttpSpeechOptions, SpeechFetch, SynthesisRequest, SynthesizedSpeech, TranscriptResult, TranscriptSegment, WordTiming } from './types.js';
1
+ import { SpeechRetryOptions } from './retry.js';
2
+ import { HttpSpeechOptions, SpeechFetch, SynthesisRequest, SynthesizedSpeech, TranscriptResult, TranscriptSegment, WordTiming } from './types.js';
3
+ /**
4
+ * Returns `baseUrl` with a trailing `/` on its path so relative paths resolve
5
+ * beneath it. The query string is preserved and any fragment is dropped.
6
+ */
2
7
  export declare function normalizeBaseUrl(baseUrl: string): string;
8
+ /**
9
+ * Resolves `path` against `baseUrl`. A relative path without its own query
10
+ * inherits the base URL's query (for example a gateway `api-version`); an
11
+ * absolute URL is returned unchanged.
12
+ */
3
13
  export declare function resolveSpeechUrl(baseUrl: string, path: string): string;
14
+ /**
15
+ * Resolves an OpenAI-compatible endpoint from a base URL. Accepts a server
16
+ * root (`https://api.openai.com` → `/v1/<resource>`), an API root ending in a
17
+ * version segment (`http://gateway/stt/v1` → `/<resource>`), or the full
18
+ * endpoint URL (used as-is). Only the path is rewritten: the query string is
19
+ * preserved and any fragment is dropped.
20
+ */
21
+ export declare function resolveOpenAICompatibleUrl(baseUrl: string, resource: string): string;
4
22
  export declare function resolveFetch(fetchOverride?: SpeechFetch): SpeechFetch;
5
23
  export declare function mergeHeaders(options: Pick<HttpSpeechOptions, 'apiKey' | 'headers'>, extra?: HeadersInit): Headers;
6
24
  export declare abstract class HttpSpeechAdapter {
@@ -10,9 +28,17 @@ export declare abstract class HttpSpeechAdapter {
10
28
  protected readonly headers?: HeadersInit;
11
29
  protected readonly timeoutMs?: number;
12
30
  constructor(options: HttpSpeechOptions);
13
- protected post<T>(adapterName: string, path: string, init: Omit<RequestInit, 'method'>, readResponse: (response: Response) => Promise<T>): Promise<T>;
31
+ /**
32
+ * POSTs to `path` (relative to `baseUrl`, or an absolute URL). The optional
33
+ * `retry` policy retries 429/5xx responses; `timeoutMs` applies per attempt.
34
+ * The request body must be re-sendable (string, Blob, FormData) when
35
+ * retries are enabled.
36
+ */
37
+ protected post<T>(adapterName: string, path: string, init: Omit<RequestInit, 'method'>, readResponse: (response: Response) => Promise<T>, retry?: Required<SpeechRetryOptions> | false): Promise<T>;
38
+ private postOnce;
14
39
  }
15
- export declare function appendAudioInput(form: FormData, audio: AudioInput, fieldName?: string): void;
40
+ /** Removes an API key from provider-supplied text before it is surfaced. */
41
+ export declare function redactSecret(text: string | undefined, secret: string | undefined): string | undefined;
16
42
  export declare function appendOptionalFormValue(form: FormData, name: string, value: unknown): void;
17
43
  export declare function createHappyVerticalSynthesisForm(request: SynthesisRequest, defaultVoice?: string): FormData;
18
44
  export declare function voiceToString(voice: string | {
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/shared/http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/shared/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Retry with exponential backoff for speech provider calls. Retries only
3
+ * rate-limit (429) and server (5xx) responses, honours `Retry-After`, and
4
+ * stops immediately when the caller aborts.
5
+ */
6
+ export interface SpeechRetryOptions {
7
+ /** Retries after the first attempt. Default `2`. `0` disables retries. */
8
+ maxRetries?: number;
9
+ /** First backoff delay in milliseconds. Default `500`. */
10
+ initialDelayMs?: number;
11
+ /**
12
+ * Upper bound for any single wait in milliseconds. Default `30000`. A
13
+ * `Retry-After` longer than this ends retrying rather than retrying early.
14
+ */
15
+ maxDelayMs?: number;
16
+ }
17
+ export declare const DEFAULT_SPEECH_RETRY: Required<SpeechRetryOptions>;
18
+ export declare function resolveRetryOptions(retry: SpeechRetryOptions | false | undefined, defaults?: Required<SpeechRetryOptions> | false): Required<SpeechRetryOptions> | false;
19
+ export declare function isRetryableStatus(status: number | undefined): boolean;
20
+ /**
21
+ * Parses a `Retry-After` header (delta-seconds or HTTP-date) into
22
+ * milliseconds. Returns `undefined` when absent or unparseable.
23
+ */
24
+ export declare function parseRetryAfter(value: string | null | undefined, now?: number): number | undefined;
25
+ /**
26
+ * Runs `attempt` until it succeeds, throws a non-retryable error, or the
27
+ * retry budget is exhausted. Retryable errors are {@link SpeechProviderError}
28
+ * instances whose `status` is 429 or 5xx.
29
+ */
30
+ export declare function withSpeechRetry<T>(attempt: (attemptNumber: number) => Promise<T>, retry: Required<SpeechRetryOptions> | false, signal?: AbortSignal): Promise<T>;
31
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/shared/retry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,kBAAkB,CAI7D,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,GAAG,KAAK,GAAG,SAAS,EAC7C,QAAQ,GAAE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,KAA4B,GACpE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,KAAK,CAyBtC;AAwBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAErE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,GAAG,GAAE,MAAmB,GACvB,MAAM,GAAG,SAAS,CAYpB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,OAAO,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC9C,KAAK,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,KAAK,EAC3C,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CA6BZ"}