@happyvertical/speech 0.110.1 → 0.111.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/AGENT.md +3 -0
  2. package/README.md +46 -3
  3. package/dist/adapters/local/audio.d.ts.map +1 -1
  4. package/dist/adapters/local/worker.d.ts +8 -5
  5. package/dist/adapters/local/worker.d.ts.map +1 -1
  6. package/dist/adapters/openai-compatible-transcriber.d.ts +3 -0
  7. package/dist/adapters/openai-compatible-transcriber.d.ts.map +1 -1
  8. package/dist/adapters/openai-compatible.d.ts +3 -0
  9. package/dist/adapters/openai-compatible.d.ts.map +1 -1
  10. package/dist/adapters/openai-realtime.d.ts +8 -0
  11. package/dist/adapters/openai-realtime.d.ts.map +1 -1
  12. package/dist/adapters/qwen3.d.ts +6 -0
  13. package/dist/adapters/qwen3.d.ts.map +1 -1
  14. package/dist/adapters/studio-server.d.ts +11 -0
  15. package/dist/adapters/studio-server.d.ts.map +1 -1
  16. package/dist/adapters/voxtral-realtime.d.ts +8 -0
  17. package/dist/adapters/voxtral-realtime.d.ts.map +1 -1
  18. package/dist/browser.js +2 -2
  19. package/dist/chunks/errors-CzsR_5dE.js +122 -0
  20. package/dist/chunks/errors-CzsR_5dE.js.map +1 -0
  21. package/dist/chunks/{registry-DFSF3WoM.js → registry-DhB2589P.js} +5 -5
  22. package/dist/chunks/registry-DhB2589P.js.map +1 -0
  23. package/dist/chunks/{wav-vzrhhZbu.js → wav-PxVZJX9V.js} +15 -5
  24. package/dist/chunks/{wav-vzrhhZbu.js.map → wav-PxVZJX9V.js.map} +1 -1
  25. package/dist/chunks/{websocket-YbAfY6YI.js → websocket-DLZLLyD9.js} +2 -2
  26. package/dist/chunks/{websocket-YbAfY6YI.js.map → websocket-DLZLLyD9.js.map} +1 -1
  27. package/dist/conversation-server.js +2 -2
  28. package/dist/conversation.js +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +302 -20
  32. package/dist/index.js.map +1 -1
  33. package/dist/local.js +21 -12
  34. package/dist/local.js.map +1 -1
  35. package/dist/pcm.js +2 -2
  36. package/dist/shared/audio.d.ts.map +1 -1
  37. package/dist/shared/errors.d.ts +48 -1
  38. package/dist/shared/errors.d.ts.map +1 -1
  39. package/dist/shared/http.d.ts +3 -0
  40. package/dist/shared/http.d.ts.map +1 -1
  41. package/dist/shared/probe.d.ts +90 -0
  42. package/dist/shared/probe.d.ts.map +1 -0
  43. package/dist/shared/streaming-factory.d.ts.map +1 -1
  44. package/dist/shared/streaming-types.d.ts +10 -1
  45. package/dist/shared/streaming-types.d.ts.map +1 -1
  46. package/dist/shared/types.d.ts +14 -0
  47. package/dist/shared/types.d.ts.map +1 -1
  48. package/dist/shared/wav.d.ts +12 -1
  49. package/dist/shared/wav.d.ts.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/chunks/errors-Cs0vX-mV.js +0 -64
  52. package/dist/chunks/errors-Cs0vX-mV.js.map +0 -1
  53. package/dist/chunks/registry-DFSF3WoM.js.map +0 -1
package/AGENT.md CHANGED
@@ -66,6 +66,9 @@ New adapters (including streaming/realtime transcribers) should reuse these modu
66
66
  - `retry.ts`: `withSpeechRetry()` retries 429/5xx `SpeechProviderError`s with exponential backoff, honours `Retry-After` (`retryAfterMs`), and stops on abort.
67
67
  - `http.ts`: `HttpSpeechAdapter.post(..., retry)` sends auth/extra headers (`headers` option plus per-request `headers`), applies `timeoutMs` per attempt, and redacts the API key from provider error bodies; `resolveOpenAICompatibleUrl()` normalises OpenAI-style base URLs.
68
68
 
69
+ - `probe.ts`: `probeHttpEndpoint()` runs every adapter's `probe()` (one `GET`, typed `SpeechProbeResult`, rejects only on caller abort) and `modelsUrlFor()` derives `<api root>/models` from a resolved endpoint. HTTP adapters call it through `HttpSpeechAdapter.probeEndpoint()`; new adapters add a `probe()` the same way.
70
+ - `errors.ts`: throw `SpeechAudioTooLargeError` for `maxBytes`, `SpeechInvalidAudioError` for empty or undecodable audio, plain `SpeechConfigurationError` only for setup, and `SpeechProviderError`/`SpeechProviderUnavailableError` for provider failures; the local worker's `deserializeError()` must know every class it can receive.
71
+
69
72
  - `registry.ts`: `registerOptionalTranscriber()` lets subpath entries add opt-in transcriber types without the core entry importing them. `getTranscriber()` throws `SpeechConfigurationError` naming the subpath when such a type is requested before registration.
70
73
 
71
74
  The OpenAI-compatible transcriber holds an API key and is server-side only.
package/README.md CHANGED
@@ -98,11 +98,11 @@ result.usage; // { operation, provider, model, audioSeconds, bytes, providerUsag
98
98
 
99
99
  - **Base URL.** A server root gets `/v1/audio/transcriptions`; a base ending in a version segment (`http://gateway/stt/v1`) gets `/audio/transcriptions`; a URL already ending in `/audio/transcriptions` is used as-is. Any query string (for example `?api-version=…`) is preserved and a fragment is dropped.
100
100
  - **Audio formats.** The multipart filename extension comes from the MIME type (codec parameters are ignored): `webm`, `m4a` (`audio/mp4`), `mp4` (`video/mp4`), `wav`, `ogg`, `mp3`, `flac`. Pass `audio: { data, filename }` to choose the name yourself.
101
- - **Limits.** `maxBytes` (adapter or request) defaults to 25 MB, the OpenAI upload cap. Oversized or empty input fails with `SpeechConfigurationError` before any request; streams are cancelled as soon as they exceed the limit.
101
+ - **Limits.** `maxBytes` (adapter or request) defaults to 25 MB, the OpenAI upload cap. Oversized input fails with `SpeechAudioTooLargeError` and empty input with `SpeechInvalidAudioError` (both subclasses of `SpeechConfigurationError`) before any request; streams are cancelled as soon as they exceed the limit.
102
102
  - **Response formats.** Defaults to `verbose_json` so segments, duration, and (with `timestampGranularities`) word timings map onto `TranscriptResult`. Models that only return `json`/`text` (`gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, including gateway-prefixed names) are sent `json` without timestamps. If any other server rejects `verbose_json` or `timestamp_granularities[]` with a 400/422, the adapter retries once with plain `json`.
103
103
  - **Retries.** 429 and 5xx responses are retried with exponential backoff (default 2 retries, 500 ms initial delay, 30 s cap), waiting at least `Retry-After`. A `Retry-After` longer than `maxDelayMs` ends retrying. Configure with `retry: { maxRetries, initialDelayMs, maxDelayMs }` or disable with `retry: false`; non-finite values (`NaN`, `Infinity`) throw `SpeechConfigurationError`. `timeoutMs` applies per attempt; `signal` cancels the request and any backoff wait.
104
104
  - **Usage.** `result.usage` and `onUsage` (adapter-level, then request-level) report the model, audio seconds (provider duration, then `usage.seconds`, then `AudioInput.durationSeconds`), uploaded bytes, and the provider `usage` block untouched. Errors thrown by `onUsage` propagate.
105
- - **Errors.** HTTP failures throw `SpeechProviderError` with `status`, `responseBody`, and `retryAfterMs`. The API key never appears in error messages and is redacted from response bodies.
105
+ - **Errors.** HTTP failures throw `SpeechProviderError` with `status`, `responseBody`, `retryAfterMs`, and `retryable`; a request that gets no response at all throws `SpeechProviderUnavailableError` (see [Errors](#errors)). The API key never appears in error messages and is redacted from response bodies.
106
106
 
107
107
  ## Realtime (Streaming) Transcription
108
108
 
@@ -291,7 +291,7 @@ A base URL may be `ws(s)://` or `http(s)://`: a server root gets `/v1/realtime`,
291
291
  Three entry points, none of which loads Node-only code:
292
292
 
293
293
  - `@happyvertical/speech` exports `transcriberInputFormat` and `TranscriberInputFormat`.
294
- - `@happyvertical/speech/pcm` (browser, workers, Node): the pure voice activity detector (`createVadSegmenter`, `createUtteranceCollector`), `encodeWavPcm16(samples, sampleRate)`, `parseWavPcm16(bytes, { sampleRate?, channels? })`, `resampleMono(samples, fromRate, toRate)`, `float32ToPcm16`, `pcm16ToFloat32`, and `WavFormatError` (stable `reason`).
294
+ - `@happyvertical/speech/pcm` (browser, workers, Node): the pure voice activity detector (`createVadSegmenter`, `createUtteranceCollector`), `encodeWavPcm16(samples, sampleRate)`, `parseWavPcm16(bytes, { sampleRate?, channels?, allowUnknownLength? })` (`allowUnknownLength: true` accepts the `0xFFFFFFFF` RIFF/`data` sizes ffmpeg and Kokoro-FastAPI write when streaming a WAV, reading to the end of the buffer in whole frames; leave it off for untrusted uploads), `resampleMono(samples, fromRate, toRate)`, `float32ToPcm16`, `pcm16ToFloat32`, and `WavFormatError` (stable `reason`).
295
295
  - `@happyvertical/speech/browser` (SSR-safe to import): `createPcmCapture(stream, { sampleRate, maxDurationMs })`, `pcmCaptureSupported()`, `PcmCaptureError`, and the hands-free `createVadCapture` (see [Hands-free capture](#hands-free-capture-voice-activity-detection)).
296
296
 
297
297
  `parseWavPcm16` is strict because it reads untrusted bytes. It rejects, with `WavFormatError`, anything that is not a well-formed RIFF/WAVE with `fmt ` before `data`, format tag 1 (or `WAVE_FORMAT_EXTENSIBLE` whose subtype GUID is PCM), 16 bits, a consistent block align and byte rate, a non-empty data chunk that is a whole number of frames, and any declared size (RIFF, chunk or data) that exceeds the buffer. Nothing is clamped, nothing is allocated from a declared size, and work is linear in the input. It returns `{ sampleRate, channels, frames, durationMs, samples, data }`.
@@ -438,6 +438,49 @@ HAVE_SPEECH_TRANSCRIBER_CACHE_DIR=/var/cache/hv-models
438
438
  HAVE_SPEECH_TRANSCRIBER_MODEL_HOST=https://models.example.com/
439
439
  ```
440
440
 
441
+ ## Readiness probe
442
+
443
+ `probe()` checks that a configured endpoint answers, accepts the credentials, and (where the server lists models) serves the configured model. It sends no audio and synthesizes nothing: one `GET`, then a typed result. Use it for a "speech service not configured" or "unreachable" state.
444
+
445
+ ```typescript
446
+ const transcriber = await getTranscriber({ type: 'openai-compatible', baseUrl, apiKey, model: 'whisper-large-v3' });
447
+ const result = await transcriber.probe?.({ timeoutMs: 5000 });
448
+ if (result?.ok) {
449
+ // result.checked: 'model' (listed) or 'reachability' (server answered; model unconfirmed)
450
+ } else if (result) {
451
+ // result.reason: 'misconfigured' | 'unreachable' | 'timeout' | 'unauthorized'
452
+ // | 'model_not_found' | 'provider_error' | 'unsupported'
453
+ }
454
+ ```
455
+
456
+ | Adapter | Request | Model confirmed |
457
+ | --- | --- | --- |
458
+ | `openai-compatible` (STT and TTS) | `GET <api root>/models` | `model` / `defaultModel` |
459
+ | `voxtral-realtime`, `openai-realtime` | `GET <api root>/models` over HTTP(S) (`ws`→`http`, `wss`→`https`) | `model` |
460
+ | `qwen3-tts` | `GET` of `models` beside `speechPath` | no (reachability) |
461
+ | `studio-server` (STT and TTS) | `GET <baseUrl>` | no (reachability) |
462
+ | `local` | no `probe()` | — |
463
+
464
+ - `probe()` resolves for every outcome and rejects only when `signal` aborts. It is optional on the `Transcriber`, `SpeechSynthesizer`, and `StreamingTranscriber` interfaces, so call it as `probe?.()`; `getTranscriber()` forwards it for the realtime types.
465
+ - A server without a model list (404, 405, or 501 on `/models`, as from whisper.cpp) gives `ok: true, checked: 'reachability'`. So does any `studio-server` answer below 500.
466
+ - `401` and `403` give `unauthorized`. Other error statuses, and a `200` whose body is not an OpenAI-style `{ data: [{ id }] }` list (read up to 4 MiB), give `provider_error` with `status`.
467
+ - `openai-realtime` with only a browser `clientSecret` returns `unsupported` without a request: the ephemeral secret cannot read the model list.
468
+ - The timeout defaults to the adapter's `timeoutMs` (`connectTimeoutMs` for the realtime adapters), else 10 s; a zero or non-finite value also means 10 s, so a probe always ends. An invalid header value (such as a key with a trailing newline) gives `misconfigured`. `message` is fixed text and never contains the URL, a credential, or a response body.
469
+
470
+ ## Errors
471
+
472
+ Every error is a `SpeechError` with a stable `code` and the `adapter` name. Pick the message and retry decision from the class:
473
+
474
+ | Class (`code`) | Meaning | Retry? |
475
+ | --- | --- | --- |
476
+ | `SpeechAudioTooLargeError` (`SPEECH_AUDIO_TOO_LARGE`) | The audio exceeds `maxBytes` (`error.maxBytes`); nothing was sent. | No: send less audio. |
477
+ | `SpeechInvalidAudioError` (`SPEECH_INVALID_AUDIO`) | The audio is empty, malformed, or a format this adapter cannot decode (e.g. webm to a raw-PCM streaming adapter, a bad WAV header, raw PCM without a rate). | No: re-record or convert. |
478
+ | `SpeechConfigurationError` (`SPEECH_CONFIGURATION_ERROR`) | Setup: a missing or invalid `baseUrl`, option, adapter type, or optional peer. | No: fix the configuration. |
479
+ | `SpeechProviderUnavailableError` (`SPEECH_PROVIDER_UNAVAILABLE`) | No response at all: DNS, refused or reset connection, TLS. The fetch error is `cause`; the message names no URL. | Yes (`retryable: true`), later. |
480
+ | `SpeechProviderError` (`SPEECH_PROVIDER_ERROR`) | The provider answered with an error status (`status`, `responseBody`, `retryAfterMs`) or an unusable response. | `retryable` is true for 429 and 5xx. |
481
+
482
+ `SpeechAudioTooLargeError` and `SpeechInvalidAudioError` extend `SpeechConfigurationError`, and `SpeechProviderUnavailableError` extends `SpeechProviderError`, so `instanceof` checks written for the older classes still match. Code that compares `error.name` or `error.code` strings sees the new values. A provider's own HTTP 413 or 415 stays a `SpeechProviderError` with that `status`. A caller abort or `timeoutMs` still rejects with the abort error. `WavFormatError` (`SPEECH_INVALID_WAV`, from `@happyvertical/speech/pcm`) keeps its own `reason`. The adapters' automatic retry covers 429 and 5xx only.
483
+
441
484
  ## Environment Configuration
442
485
 
443
486
  Transcriber settings switch between cloud and self-hosted models without code changes. Explicit options win over the environment:
@@ -1 +1 @@
1
- {"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../../src/adapters/local/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAElE,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAYxC,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAOvB;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAM5D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAMT;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,GAAG,UAAU,CAAC,GAC3D,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAgBnC;AAoCD,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CA8CzD;AA2ID,6CAA6C;AAC7C,wBAAgB,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,EAAE,GAAG,YAAY,CAiB5E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,YAAY,CAqBd"}
1
+ {"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../../src/adapters/local/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAElE,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAYxC,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAOvB;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAM5D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAMT;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,GAAG,UAAU,CAAC,GAC3D,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAgBnC;AAoCD,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CA2CzD;AA2ID,6CAA6C;AAC7C,wBAAgB,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,EAAE,GAAG,YAAY,CAiB5E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,YAAY,CAqBd"}
@@ -61,11 +61,7 @@ export type LocalWorkerResponse = {
61
61
  } | {
62
62
  kind: 'error';
63
63
  id: number;
64
- error: {
65
- name: string;
66
- message: string;
67
- code?: string;
68
- };
64
+ error: SerializedError;
69
65
  } | {
70
66
  kind: 'progress';
71
67
  progress: LocalTranscriberProgress;
@@ -133,4 +129,11 @@ export declare class LocalTranscriberWorkerClient implements Transcriber {
133
129
  private call;
134
130
  private onMessage;
135
131
  }
132
+ interface SerializedError {
133
+ name: string;
134
+ message: string;
135
+ code?: string;
136
+ maxBytes?: number;
137
+ }
138
+ export {};
136
139
  //# sourceMappingURL=worker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/adapters/local/worker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/adapters/local/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAcH,OAAO,KAAK,EAEV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAC;AAU/B,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,wBAAwB,EACxB,gCAAgC,EACjC,MAAM,YAAY,CAAC;AAEpB,kGAAkG;AAClG,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC/D,gBAAgB,CACd,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACtC,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACtC,IAAI,CAAC;CACT;AAED,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GACxB;IACE,+EAA+E;IAC/E,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACD;IACE,+EAA+E;IAC/E,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN,8CAA8C;AAC9C,MAAM,MAAM,kBAAkB,GAC1B;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACjD,GACD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,8CAA8C;AAC9C,MAAM,MAAM,mBAAmB,GAC3B;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,eAAe,CAAC;CACxB,GACD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,wBAAwB,CAAA;CAAE,CAAC;AAS7D;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,uBAA4B,EACrC,QAAQ,GAAE,mBAAkE,GAC3E,MAAM,OAAO,CAAC,IAAI,CAAC,CAoErB;AAED,MAAM,WAAW,mCAAmC;IAClD,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,gCAAgC,CAAC;IAC9C,8DAA8D;IAC9D,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,qBAAa,4BAA6B,YAAW,WAAW;IAC9D,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IAEjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAMlB;IACJ,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAS;IACvB,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAC2B;gBAGlD,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,GAAE,mCAAwC;IAOnD,0EAA0E;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAahD,sEAAsE;IAChE,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgC1E;;;OAGG;IACH,KAAK,IAAI,IAAI;IAiBb,2EAA2E;YAC7D,OAAO;IAyDrB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,IAAI;IAkCZ,OAAO,CAAC,SAAS;CAoBlB;AAmDD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
@@ -1,4 +1,5 @@
1
1
  import { HttpSpeechAdapter } from '../shared/http.js';
2
+ import { type SpeechProbeOptions, type SpeechProbeResult } from '../shared/probe.js';
2
3
  import type { OpenAICompatibleTranscriberOptions, Transcriber, TranscriptionRequest, TranscriptResult } from '../shared/types.js';
3
4
  export declare function isJsonOnlyTranscriptionModel(model: string): boolean;
4
5
  /**
@@ -19,5 +20,7 @@ export declare class OpenAICompatibleTranscriber extends HttpSpeechAdapter imple
19
20
  constructor(options: OpenAICompatibleTranscriberOptions);
20
21
  transcribe(request: TranscriptionRequest): Promise<TranscriptResult>;
21
22
  private resolveWireOptions;
23
+ /** `GET <api root>/models`, confirming the configured `model` is listed. */
24
+ probe(options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
22
25
  }
23
26
  //# sourceMappingURL=openai-compatible-transcriber.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openai-compatible-transcriber.d.ts","sourceRoot":"","sources":["../../src/adapters/openai-compatible-transcriber.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,iBAAiB,EAGlB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,KAAK,EACV,kCAAkC,EAElC,WAAW,EACX,oBAAoB,EAEpB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAsB5B,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnE;AAQD;;;;;;GAMG;AACH,qBAAa,2BACX,SAAQ,iBACR,YAAW,WAAW;IAEtB,QAAQ,CAAC,IAAI,EAAG,mBAAmB,CAAU;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAA8B;IAC9D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAsB;gBAEnC,OAAO,EAAE,kCAAkC;IAajD,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0E1E,OAAO,CAAC,kBAAkB;CAsB3B"}
1
+ {"version":3,"file":"openai-compatible-transcriber.d.ts","sourceRoot":"","sources":["../../src/adapters/openai-compatible-transcriber.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,iBAAiB,EAGlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,KAAK,EACV,kCAAkC,EAElC,WAAW,EACX,oBAAoB,EAEpB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAsB5B,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnE;AAQD;;;;;;GAMG;AACH,qBAAa,2BACX,SAAQ,iBACR,YAAW,WAAW;IAEtB,QAAQ,CAAC,IAAI,EAAG,mBAAmB,CAAU;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAA8B;IAC9D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAsB;gBAEnC,OAAO,EAAE,kCAAkC;IAajD,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0E1E,OAAO,CAAC,kBAAkB;IAuB1B,4EAA4E;IAC5E,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAWhE"}
@@ -1,4 +1,5 @@
1
1
  import { HttpSpeechAdapter } from '../shared/http.js';
2
+ import { type SpeechProbeOptions, type SpeechProbeResult } from '../shared/probe.js';
2
3
  import type { OpenAICompatibleSpeechSynthesizerOptions, SpeechSynthesizer, SynthesisRequest, SynthesizedSpeech } from '../shared/types.js';
3
4
  export declare class OpenAICompatibleSpeechSynthesizer extends HttpSpeechAdapter implements SpeechSynthesizer {
4
5
  readonly type: "openai-compatible";
@@ -8,5 +9,7 @@ export declare class OpenAICompatibleSpeechSynthesizer extends HttpSpeechAdapter
8
9
  private readonly defaultVoice;
9
10
  constructor(options: OpenAICompatibleSpeechSynthesizerOptions);
10
11
  synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech>;
12
+ /** `GET <api root>/models`, confirming `defaultModel` is listed. */
13
+ probe(options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
11
14
  }
12
15
  //# sourceMappingURL=openai-compatible.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openai-compatible.d.ts","sourceRoot":"","sources":["../../src/adapters/openai-compatible.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EAIlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,wCAAwC,EACxC,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,iCACX,SAAQ,iBACR,YAAW,iBAAiB;IAE5B,QAAQ,CAAC,IAAI,EAAG,mBAAmB,CAAU;IAE7C,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,OAAO,EAAE,wCAAwC;IAOvD,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAsCxE"}
1
+ {"version":3,"file":"openai-compatible.d.ts","sourceRoot":"","sources":["../../src/adapters/openai-compatible.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EAKlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,wCAAwC,EACxC,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,iCACX,SAAQ,iBACR,YAAW,iBAAiB;IAE5B,QAAQ,CAAC,IAAI,EAAG,mBAAmB,CAAU;IAE7C,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,OAAO,EAAE,wCAAwC;IAOvD,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAuCvE,oEAAoE;IACpE,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAiBhE"}
@@ -9,6 +9,7 @@
9
9
  * Reference: https://developers.openai.com/api/docs/guides/realtime-transcription
10
10
  * and https://developers.openai.com/api/reference/resources/realtime/client-events
11
11
  */
12
+ import { type SpeechProbeOptions, type SpeechProbeResult } from '../shared/probe.js';
12
13
  import { type RealtimeProtocol, type RealtimeProtocolEvent, type RealtimeSessionConfig } from '../shared/realtime-session.js';
13
14
  import type { OpenAIRealtimeTranscriberOptions, StreamingAudioFormat, StreamingSession, StreamingSessionOptions, StreamingTranscriber, StreamingTurnLimit } from '../shared/streaming-types.js';
14
15
  export declare const OPENAI_REALTIME_DEFAULT_URL = "wss://api.openai.com/v1/realtime";
@@ -38,6 +39,13 @@ export declare class OpenAIRealtimeTranscriber implements StreamingTranscriber {
38
39
  private readonly createWebSocket;
39
40
  private readonly protocol;
40
41
  constructor(options?: OpenAIRealtimeTranscriberOptions);
42
+ /**
43
+ * `GET` of the server's model list (the realtime URL's `/models`, over
44
+ * HTTP), confirming the configured model is listed. Needs `apiKey` or no
45
+ * auth (vLLM): a browser `clientSecret` alone cannot read the list, so the
46
+ * result is then `unsupported`.
47
+ */
48
+ probe(options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
41
49
  /**
42
50
  * A turn limit applies only to `manual` sessions with `maxTurnSeconds` set;
43
51
  * under server or semantic VAD the provider ends turns itself.
@@ -1 +1 @@
1
- {"version":3,"file":"openai-realtime.d.ts","sourceRoot":"","sources":["../../src/adapters/openai-realtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,EAML,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,gCAAgC,EAChC,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EAEpB,kBAAkB,EACnB,MAAM,8BAA8B,CAAC;AAatC,eAAO,MAAM,2BAA2B,qCAAqC,CAAC;AAC9E,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAYjE,uDAAuD;AACvD,eAAO,MAAM,sBAAsB,GACjC,SAAQ,IAAI,CACV,gCAAgC,EAChC,gBAAgB,GAAG,SAAS,GAAG,sBAAsB,CACjD,KACL,gBAuBD,CAAC;AAEH;;;GAGG;AACH,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,qBAAqB,EAC7B,MAAM,GAAE,IAAI,CACV,gCAAgC,EAChC,gBAAgB,GAAG,SAAS,GAAG,sBAAsB,CACjD,GACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,GACf,qBAAqB,EAAE,CAoFzB;AAED;;;;;;;GAOG;AACH,qBAAa,yBAA0B,YAAW,oBAAoB;IACpE,QAAQ,CAAC,IAAI,qBAAW;IACxB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAE3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;gBAEhC,OAAO,GAAE,gCAAqC;IAa1D;;;OAGG;IACH,SAAS,CACP,OAAO,GAAE,uBAA4B,GACpC,kBAAkB,GAAG,SAAS;IAOjC,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,aAAa;IASrB,KAAK,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB;CAwC/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,SAAS,GAChD,oBAAoB,CAsBtB"}
1
+ {"version":3,"file":"openai-realtime.d.ts","sourceRoot":"","sources":["../../src/adapters/openai-realtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EAGvB,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAML,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,gCAAgC,EAChC,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EAEpB,kBAAkB,EACnB,MAAM,8BAA8B,CAAC;AAatC,eAAO,MAAM,2BAA2B,qCAAqC,CAAC;AAC9E,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAYjE,uDAAuD;AACvD,eAAO,MAAM,sBAAsB,GACjC,SAAQ,IAAI,CACV,gCAAgC,EAChC,gBAAgB,GAAG,SAAS,GAAG,sBAAsB,CACjD,KACL,gBAuBD,CAAC;AAEH;;;GAGG;AACH,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,qBAAqB,EAC7B,MAAM,GAAE,IAAI,CACV,gCAAgC,EAChC,gBAAgB,GAAG,SAAS,GAAG,sBAAsB,CACjD,GACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,GACf,qBAAqB,EAAE,CAoFzB;AAED;;;;;;;GAOG;AACH,qBAAa,yBAA0B,YAAW,oBAAoB;IACpE,QAAQ,CAAC,IAAI,qBAAW;IACxB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAE3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;gBAEhC,OAAO,GAAE,gCAAqC;IAa1D;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4B/D;;;OAGG;IACH,SAAS,CACP,OAAO,GAAE,uBAA4B,GACpC,kBAAkB,GAAG,SAAS;IAOjC,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,aAAa;IASrB,KAAK,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB;CAwC/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,SAAS,GAChD,oBAAoB,CAsBtB"}
@@ -1,4 +1,5 @@
1
1
  import { HttpSpeechAdapter } from '../shared/http.js';
2
+ import { type SpeechProbeOptions, type SpeechProbeResult } from '../shared/probe.js';
2
3
  import type { Qwen3SpeechSynthesizerOptions, SpeechSynthesizer, SynthesisRequest, SynthesizedSpeech } from '../shared/types.js';
3
4
  export declare class Qwen3SpeechSynthesizer extends HttpSpeechAdapter implements SpeechSynthesizer {
4
5
  readonly type: "qwen3-tts";
@@ -7,5 +8,10 @@ export declare class Qwen3SpeechSynthesizer extends HttpSpeechAdapter implements
7
8
  private readonly defaultVoice?;
8
9
  constructor(options: Qwen3SpeechSynthesizerOptions);
9
10
  synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech>;
11
+ /**
12
+ * `GET` of the model list beside `speechPath` (`/v1/models` by default).
13
+ * The model is not required to be listed: Qwen3 servers name it freely.
14
+ */
15
+ probe(options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
10
16
  }
11
17
  //# sourceMappingURL=qwen3.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"qwen3.d.ts","sourceRoot":"","sources":["../../src/adapters/qwen3.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,6BAA6B,EAC7B,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,sBACX,SAAQ,iBACR,YAAW,iBAAiB;IAE5B,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAU;IAErC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;gBAE3B,OAAO,EAAE,6BAA6B;IAO5C,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAkBxE"}
1
+ {"version":3,"file":"qwen3.d.ts","sourceRoot":"","sources":["../../src/adapters/qwen3.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EAGlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,6BAA6B,EAC7B,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,sBACX,SAAQ,iBACR,YAAW,iBAAiB;IAE5B,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAU;IAErC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;gBAE3B,OAAO,EAAE,6BAA6B;IAO5C,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmBvE;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAchE"}
@@ -1,10 +1,16 @@
1
1
  import { HttpSpeechAdapter } from '../shared/http.js';
2
+ import type { SpeechProbeOptions, SpeechProbeResult } from '../shared/probe.js';
2
3
  import type { SpeechSynthesizer, StudioServerSpeechSynthesizerOptions, StudioServerTranscriberOptions, SynthesisRequest, SynthesizedSpeech, Transcriber, TranscriptionRequest, TranscriptResult } from '../shared/types.js';
3
4
  export declare class StudioServerTranscriber extends HttpSpeechAdapter implements Transcriber {
4
5
  readonly type: "studio-server";
5
6
  private readonly transcribePath;
6
7
  constructor(options: StudioServerTranscriberOptions);
7
8
  transcribe(request: TranscriptionRequest): Promise<TranscriptResult>;
9
+ /**
10
+ * `GET` of `baseUrl`: Studio Server has no model list, so a success only
11
+ * shows a server answered (`checked: 'reachability'`).
12
+ */
13
+ probe(options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
8
14
  }
9
15
  export declare class StudioServerSpeechSynthesizer extends HttpSpeechAdapter implements SpeechSynthesizer {
10
16
  readonly type: "studio-server";
@@ -12,5 +18,10 @@ export declare class StudioServerSpeechSynthesizer extends HttpSpeechAdapter imp
12
18
  private readonly defaultVoice?;
13
19
  constructor(options: StudioServerSpeechSynthesizerOptions);
14
20
  synthesize(request: SynthesisRequest): Promise<SynthesizedSpeech>;
21
+ /**
22
+ * `GET` of `baseUrl`: Studio Server has no model list, so a success only
23
+ * shows a server answered (`checked: 'reachability'`).
24
+ */
25
+ probe(options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
15
26
  }
16
27
  //# sourceMappingURL=studio-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"studio-server.d.ts","sourceRoot":"","sources":["../../src/adapters/studio-server.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,iBAAiB,EAGlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,iBAAiB,EACjB,oCAAoC,EACpC,8BAA8B,EAC9B,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,uBACX,SAAQ,iBACR,YAAW,WAAW;IAEtB,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,OAAO,EAAE,8BAA8B;IAK7C,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAuB3E;AAED,qBAAa,6BACX,SAAQ,iBACR,YAAW,iBAAiB;IAE5B,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;gBAE3B,OAAO,EAAE,oCAAoC;IAMnD,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAaxE"}
1
+ {"version":3,"file":"studio-server.d.ts","sourceRoot":"","sources":["../../src/adapters/studio-server.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,iBAAiB,EAIlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EACV,iBAAiB,EACjB,oCAAoC,EACpC,8BAA8B,EAC9B,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,uBACX,SAAQ,iBACR,YAAW,WAAW;IAEtB,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,OAAO,EAAE,8BAA8B;IAK7C,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwB1E;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAUhE;AAED,qBAAa,6BACX,SAAQ,iBACR,YAAW,iBAAiB;IAE5B,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;gBAE3B,OAAO,EAAE,oCAAoC;IAMnD,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAcvE;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAUhE"}
@@ -32,6 +32,7 @@
32
32
  * The session fails closed on that, and on any `done` for a turn it did
33
33
  * not commit, rather than losing audio silently.
34
34
  */
35
+ import { type SpeechProbeOptions, type SpeechProbeResult } from '../shared/probe.js';
35
36
  import { type RealtimeProtocol, type RealtimeProtocolEvent } from '../shared/realtime-session.js';
36
37
  import type { StreamingAudioFormat, StreamingSession, StreamingSessionOptions, StreamingTranscriber, StreamingTurnLimit, VoxtralRealtimeTranscriberOptions } from '../shared/streaming-types.js';
37
38
  /** vLLM's default served model name for Voxtral Mini Realtime. */
@@ -65,6 +66,13 @@ export declare class VoxtralRealtimeTranscriber implements StreamingTranscriber
65
66
  private readonly createWebSocket;
66
67
  private readonly protocol;
67
68
  constructor(options?: VoxtralRealtimeTranscriberOptions);
69
+ /**
70
+ * `GET` of the server's model list (the realtime URL's `/models`, over
71
+ * HTTP), confirming the configured model is listed. Needs `apiKey` or no
72
+ * auth (vLLM): a browser `clientSecret` alone cannot read the list, so the
73
+ * result is then `unsupported`.
74
+ */
75
+ probe(options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
68
76
  turnLimit(options?: StreamingSessionOptions): StreamingTurnLimit | undefined;
69
77
  private resolveTurnLimit;
70
78
  start(options?: StreamingSessionOptions): StreamingSession;
@@ -1 +1 @@
1
- {"version":3,"file":"voxtral-realtime.d.ts","sourceRoot":"","sources":["../../src/adapters/voxtral-realtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAUH,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAE3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EAEpB,kBAAkB,EAClB,iCAAiC,EAClC,MAAM,8BAA8B,CAAC;AAetC,kEAAkE;AAClE,eAAO,MAAM,8BAA8B,4CACA,CAAC;AAE5C,8DAA8D;AAC9D,eAAO,MAAM,6BAA6B,EAAE,QAAQ,CAAC,oBAAoB,CACF,CAAC;AAExE,uCAAuC;AACvC,eAAO,MAAM,uBAAuB,QAAO,gBAuBzC,CAAC;AAEH,wEAAwE;AACxE,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,GACf,qBAAqB,EAAE,CAqDzB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,0BAA2B,YAAW,oBAAoB;IACrE,QAAQ,CAAC,IAAI,sBAAW;IACxB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAExC;IAEF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,OAAO,GAAE,iCAAsC;IAiB3D,SAAS,CACP,OAAO,GAAE,uBAA4B,GACpC,kBAAkB,GAAG,SAAS;IAQjC,OAAO,CAAC,gBAAgB;IAWxB,KAAK,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB;CA2C/D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKjE"}
1
+ {"version":3,"file":"voxtral-realtime.d.ts","sourceRoot":"","sources":["../../src/adapters/voxtral-realtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EAGvB,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAE3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EAEpB,kBAAkB,EAClB,iCAAiC,EAClC,MAAM,8BAA8B,CAAC;AAetC,kEAAkE;AAClE,eAAO,MAAM,8BAA8B,4CACA,CAAC;AAE5C,8DAA8D;AAC9D,eAAO,MAAM,6BAA6B,EAAE,QAAQ,CAAC,oBAAoB,CACF,CAAC;AAExE,uCAAuC;AACvC,eAAO,MAAM,uBAAuB,QAAO,gBAuBzC,CAAC;AAEH,wEAAwE;AACxE,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,GACf,qBAAqB,EAAE,CAqDzB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,0BAA2B,YAAW,oBAAoB;IACrE,QAAQ,CAAC,IAAI,sBAAW;IACxB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAExC;IAEF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,OAAO,GAAE,iCAAsC;IAiB3D;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4B/D,SAAS,CACP,OAAO,GAAE,uBAA4B,GACpC,kBAAkB,GAAG,SAAS;IAQjC,OAAO,CAAC,gBAAgB;IAWxB,KAAK,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB;CA2C/D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKjE"}
package/dist/browser.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as PcmCaptureError } from "./chunks/errors-Cs0vX-mV.js";
2
- import { f as resampleMono, s as createUtteranceCollector, t as encodeWavPcm16 } from "./chunks/wav-vzrhhZbu.js";
1
+ import { n as PcmCaptureError } from "./chunks/errors-CzsR_5dE.js";
2
+ import { f as resampleMono, s as createUtteranceCollector, t as encodeWavPcm16 } from "./chunks/wav-PxVZJX9V.js";
3
3
  //#region src/shared/pcm-capture.ts
4
4
  /**
5
5
  * Browser PCM capture: `MediaStream` -> mono Float32 at a requested rate and a
@@ -0,0 +1,122 @@
1
+ //#region src/shared/errors.ts
2
+ var SpeechError = class extends Error {
3
+ code;
4
+ adapter;
5
+ constructor(message, code, adapter) {
6
+ super(message);
7
+ this.code = code;
8
+ this.adapter = adapter;
9
+ this.name = "SpeechError";
10
+ }
11
+ };
12
+ /**
13
+ * A request the adapter refused before calling the provider: missing or
14
+ * invalid setup, and (through its subclasses) audio that is too large or
15
+ * cannot be decoded. Retrying the same request will not help.
16
+ */
17
+ var SpeechConfigurationError = class extends SpeechError {
18
+ constructor(message, adapter, code = "SPEECH_CONFIGURATION_ERROR") {
19
+ super(message, code, adapter);
20
+ this.name = "SpeechConfigurationError";
21
+ }
22
+ };
23
+ /**
24
+ * The audio exceeds the adapter's `maxBytes` (code `SPEECH_AUDIO_TOO_LARGE`).
25
+ * Extends {@link SpeechConfigurationError}, so existing `instanceof` checks
26
+ * still match. A provider's own HTTP 413 stays a {@link SpeechProviderError}
27
+ * with `status: 413`.
28
+ */
29
+ var SpeechAudioTooLargeError = class extends SpeechConfigurationError {
30
+ /** The limit that was exceeded, in bytes. */
31
+ maxBytes;
32
+ constructor(message, maxBytes, adapter) {
33
+ super(message, adapter, "SPEECH_AUDIO_TOO_LARGE");
34
+ this.name = "SpeechAudioTooLargeError";
35
+ this.maxBytes = maxBytes;
36
+ }
37
+ };
38
+ /**
39
+ * The audio is empty, malformed, or in a format this adapter cannot decode
40
+ * (code `SPEECH_INVALID_AUDIO`). Extends {@link SpeechConfigurationError}, so
41
+ * existing `instanceof` checks still match. `WavFormatError` from
42
+ * `@happyvertical/speech/pcm` is separate and keeps its own `reason`.
43
+ */
44
+ var SpeechInvalidAudioError = class extends SpeechConfigurationError {
45
+ constructor(message, adapter) {
46
+ super(message, adapter, "SPEECH_INVALID_AUDIO");
47
+ this.name = "SpeechInvalidAudioError";
48
+ }
49
+ };
50
+ var InvalidSpeechAdapterError = class extends SpeechError {
51
+ constructor(type, kind) {
52
+ super(`Invalid ${kind} speech adapter type: ${type}`, "INVALID_ADAPTER");
53
+ this.name = "InvalidSpeechAdapterError";
54
+ }
55
+ };
56
+ /**
57
+ * The provider failed the request: an HTTP error status, an unusable
58
+ * response, or (subclass {@link SpeechProviderUnavailableError}) no response
59
+ * at all.
60
+ */
61
+ var SpeechProviderError = class extends SpeechError {
62
+ status;
63
+ responseBody;
64
+ /** Parsed `Retry-After` delay in milliseconds, when the provider sent one. */
65
+ retryAfterMs;
66
+ /**
67
+ * Whether the same request may succeed if sent again later: a 429 or 5xx
68
+ * status, or an unreachable provider. The adapters' own automatic retry
69
+ * covers 429/5xx only.
70
+ */
71
+ retryable;
72
+ constructor(adapter, message, options = {}, code = "SPEECH_PROVIDER_ERROR") {
73
+ super(message, code, adapter);
74
+ this.name = "SpeechProviderError";
75
+ this.status = options.status;
76
+ this.responseBody = options.responseBody;
77
+ this.retryAfterMs = options.retryAfterMs;
78
+ this.retryable = options.status === 429 || options.status !== void 0 && options.status >= 500;
79
+ if (options.cause !== void 0) this.cause = options.cause;
80
+ }
81
+ };
82
+ /**
83
+ * The provider could not be reached: the request failed before any HTTP
84
+ * response (DNS, refused or reset connection, TLS). Code
85
+ * `SPEECH_PROVIDER_UNAVAILABLE`, `retryable: true`, no `status`; the
86
+ * underlying error is `cause`. Extends {@link SpeechProviderError}. A caller
87
+ * abort or the adapter's `timeoutMs` still surfaces as the abort error.
88
+ */
89
+ var SpeechProviderUnavailableError = class extends SpeechProviderError {
90
+ retryable = true;
91
+ constructor(adapter, message, cause) {
92
+ super(adapter, message, { cause }, "SPEECH_PROVIDER_UNAVAILABLE");
93
+ this.name = "SpeechProviderUnavailableError";
94
+ }
95
+ };
96
+ /**
97
+ * Thrown by the PCM/WAV utilities (`@happyvertical/speech/pcm`) for input that
98
+ * is not exactly the 16-bit PCM WAV that was asked for, and for invalid
99
+ * arguments (sample rate, sizes). `reason` is stable and machine-readable.
100
+ */
101
+ var WavFormatError = class extends SpeechError {
102
+ reason;
103
+ constructor(reason, message) {
104
+ super(message, "SPEECH_INVALID_WAV");
105
+ this.name = "WavFormatError";
106
+ this.reason = reason;
107
+ }
108
+ };
109
+ /** Thrown by `createPcmCapture` (`@happyvertical/speech/browser`). */
110
+ var PcmCaptureError = class extends SpeechError {
111
+ reason;
112
+ constructor(reason, message, cause) {
113
+ super(message, "SPEECH_PCM_CAPTURE");
114
+ this.name = "PcmCaptureError";
115
+ this.reason = reason;
116
+ if (cause !== void 0) this.cause = cause;
117
+ }
118
+ };
119
+ //#endregion
120
+ export { SpeechError as a, SpeechProviderUnavailableError as c, SpeechConfigurationError as i, WavFormatError as l, PcmCaptureError as n, SpeechInvalidAudioError as o, SpeechAudioTooLargeError as r, SpeechProviderError as s, InvalidSpeechAdapterError as t };
121
+
122
+ //# sourceMappingURL=errors-CzsR_5dE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-CzsR_5dE.js","names":[],"sources":["../../src/shared/errors.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\n/**\n * A request the adapter refused before calling the provider: missing or\n * invalid setup, and (through its subclasses) audio that is too large or\n * cannot be decoded. Retrying the same request will not help.\n */\nexport class SpeechConfigurationError extends SpeechError {\n constructor(\n message: string,\n adapter?: string,\n code = 'SPEECH_CONFIGURATION_ERROR',\n ) {\n super(message, code, adapter);\n this.name = 'SpeechConfigurationError';\n }\n}\n\n/**\n * The audio exceeds the adapter's `maxBytes` (code `SPEECH_AUDIO_TOO_LARGE`).\n * Extends {@link SpeechConfigurationError}, so existing `instanceof` checks\n * still match. A provider's own HTTP 413 stays a {@link SpeechProviderError}\n * with `status: 413`.\n */\nexport class SpeechAudioTooLargeError extends SpeechConfigurationError {\n /** The limit that was exceeded, in bytes. */\n readonly maxBytes: number;\n\n constructor(message: string, maxBytes: number, adapter?: string) {\n super(message, adapter, 'SPEECH_AUDIO_TOO_LARGE');\n this.name = 'SpeechAudioTooLargeError';\n this.maxBytes = maxBytes;\n }\n}\n\n/**\n * The audio is empty, malformed, or in a format this adapter cannot decode\n * (code `SPEECH_INVALID_AUDIO`). Extends {@link SpeechConfigurationError}, so\n * existing `instanceof` checks still match. `WavFormatError` from\n * `@happyvertical/speech/pcm` is separate and keeps its own `reason`.\n */\nexport class SpeechInvalidAudioError extends SpeechConfigurationError {\n constructor(message: string, adapter?: string) {\n super(message, adapter, 'SPEECH_INVALID_AUDIO');\n this.name = 'SpeechInvalidAudioError';\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\n/**\n * The provider failed the request: an HTTP error status, an unusable\n * response, or (subclass {@link SpeechProviderUnavailableError}) no response\n * at all.\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 * Whether the same request may succeed if sent again later: a 429 or 5xx\n * status, or an unreachable provider. The adapters' own automatic retry\n * covers 429/5xx only.\n */\n readonly retryable: boolean;\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 code = 'SPEECH_PROVIDER_ERROR',\n ) {\n super(message, code, adapter);\n this.name = 'SpeechProviderError';\n this.status = options.status;\n this.responseBody = options.responseBody;\n this.retryAfterMs = options.retryAfterMs;\n this.retryable =\n options.status === 429 ||\n (options.status !== undefined && options.status >= 500);\n if (options.cause !== undefined) {\n this.cause = options.cause;\n }\n }\n}\n\n/**\n * The provider could not be reached: the request failed before any HTTP\n * response (DNS, refused or reset connection, TLS). Code\n * `SPEECH_PROVIDER_UNAVAILABLE`, `retryable: true`, no `status`; the\n * underlying error is `cause`. Extends {@link SpeechProviderError}. A caller\n * abort or the adapter's `timeoutMs` still surfaces as the abort error.\n */\nexport class SpeechProviderUnavailableError extends SpeechProviderError {\n override readonly retryable = true;\n\n constructor(adapter: string, message: string, cause?: unknown) {\n super(adapter, message, { cause }, 'SPEECH_PROVIDER_UNAVAILABLE');\n this.name = 'SpeechProviderUnavailableError';\n }\n}\n\n/** Why a WAV buffer was rejected by `parseWavPcm16`, or an encode input refused. */\nexport type WavFormatErrorReason =\n | 'not_riff'\n | 'truncated'\n | 'malformed_chunk'\n | 'missing_fmt'\n | 'duplicate_fmt'\n | 'data_before_fmt'\n | 'missing_data'\n | 'empty_data'\n | 'unsupported_format'\n | 'unsupported_bits'\n | 'inconsistent_header'\n | 'misaligned_data'\n | 'rate_mismatch'\n | 'channel_mismatch'\n | 'invalid_argument';\n\n/**\n * Thrown by the PCM/WAV utilities (`@happyvertical/speech/pcm`) for input that\n * is not exactly the 16-bit PCM WAV that was asked for, and for invalid\n * arguments (sample rate, sizes). `reason` is stable and machine-readable.\n */\nexport class WavFormatError extends SpeechError {\n readonly reason: WavFormatErrorReason;\n\n constructor(reason: WavFormatErrorReason, message: string) {\n super(message, 'SPEECH_INVALID_WAV');\n this.name = 'WavFormatError';\n this.reason = reason;\n }\n}\n\n/** Why a browser PCM capture could not start or produce audio. */\nexport type PcmCaptureErrorReason =\n | 'unsupported'\n | 'setup_failed'\n | 'cancelled';\n\n/** Thrown by `createPcmCapture` (`@happyvertical/speech/browser`). */\nexport class PcmCaptureError extends SpeechError {\n readonly reason: PcmCaptureErrorReason;\n\n constructor(reason: PcmCaptureErrorReason, message: string, cause?: unknown) {\n super(message, 'SPEECH_PCM_CAPTURE');\n this.name = 'PcmCaptureError';\n this.reason = reason;\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\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;;;;;;AAOA,IAAa,2BAAb,cAA8C,YAAY;CACxD,YACE,SACA,SACA,OAAO,8BACP;EACA,MAAM,SAAS,MAAM,OAAO;EAC5B,KAAK,OAAO;CACd;AACF;;;;;;;AAQA,IAAa,2BAAb,cAA8C,yBAAyB;;CAErE;CAEA,YAAY,SAAiB,UAAkB,SAAkB;EAC/D,MAAM,SAAS,SAAS,wBAAwB;EAChD,KAAK,OAAO;EACZ,KAAK,WAAW;CAClB;AACF;;;;;;;AAQA,IAAa,0BAAb,cAA6C,yBAAyB;CACpE,YAAY,SAAiB,SAAkB;EAC7C,MAAM,SAAS,SAAS,sBAAsB;EAC9C,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;;;;;;AAOA,IAAa,sBAAb,cAAyC,YAAY;CACnD;CACA;;CAEA;;;;;;CAMA;CAEA,YACE,SACA,SACA,UAKI,CAAC,GACL,OAAO,yBACP;EACA,MAAM,SAAS,MAAM,OAAO;EAC5B,KAAK,OAAO;EACZ,KAAK,SAAS,QAAQ;EACtB,KAAK,eAAe,QAAQ;EAC5B,KAAK,eAAe,QAAQ;EAC5B,KAAK,YACH,QAAQ,WAAW,OAClB,QAAQ,WAAW,KAAA,KAAa,QAAQ,UAAU;EACrD,IAAI,QAAQ,UAAU,KAAA,GACpB,KAAK,QAAQ,QAAQ;CAEzB;AACF;;;;;;;;AASA,IAAa,iCAAb,cAAoD,oBAAoB;CACtE,YAA8B;CAE9B,YAAY,SAAiB,SAAiB,OAAiB;EAC7D,MAAM,SAAS,SAAS,EAAE,MAAM,GAAG,6BAA6B;EAChE,KAAK,OAAO;CACd;AACF;;;;;;AAyBA,IAAa,iBAAb,cAAoC,YAAY;CAC9C;CAEA,YAAY,QAA8B,SAAiB;EACzD,MAAM,SAAS,oBAAoB;EACnC,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;AACF;;AASA,IAAa,kBAAb,cAAqC,YAAY;CAC/C;CAEA,YAAY,QAA+B,SAAiB,OAAiB;EAC3E,MAAM,SAAS,oBAAoB;EACnC,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,IAAI,UAAU,KAAA,GACZ,KAAK,QAAQ;CAEjB;AACF"}
@@ -1,4 +1,4 @@
1
- import { r as SpeechConfigurationError } from "./errors-Cs0vX-mV.js";
1
+ import { i as SpeechConfigurationError, o as SpeechInvalidAudioError, r as SpeechAudioTooLargeError } from "./errors-CzsR_5dE.js";
2
2
  //#region src/shared/audio.ts
3
3
  /**
4
4
  * Audio input normalisation shared by speech adapters: accepts the input
@@ -71,7 +71,7 @@ async function normalizeAudioInput(input, options = {}) {
71
71
  const chunks = await readStreamWithLimit(data, maxBytes, adapter, signal);
72
72
  blob = new Blob(chunks, { type: mimeType });
73
73
  } else throw new SpeechConfigurationError("Unsupported audio input: expected Blob, Buffer, Uint8Array, ArrayBuffer, or ReadableStream", adapter);
74
- if (blob.size === 0) throw new SpeechConfigurationError("Audio input is empty", adapter);
74
+ if (blob.size === 0) throw new SpeechInvalidAudioError("Audio input is empty", adapter);
75
75
  const extension = options.deriveExtension === false ? void 0 : mimeTypeToAudioExtension(mimeType);
76
76
  const basename = options.defaultBasename ?? "audio";
77
77
  const filename = wrapped.filename ?? (extension ? `${basename}.${extension}` : basename);
@@ -95,7 +95,7 @@ function resolveMaxBytes(maxBytes, adapter) {
95
95
  return maxBytes;
96
96
  }
97
97
  function assertWithinLimit(bytes, maxBytes, adapter) {
98
- if (bytes > maxBytes) throw new SpeechConfigurationError(`Audio input exceeds maxBytes (${bytes} > ${maxBytes} bytes)`, adapter);
98
+ if (bytes > maxBytes) throw new SpeechAudioTooLargeError(`Audio input exceeds maxBytes (${bytes} > ${maxBytes} bytes)`, maxBytes, adapter);
99
99
  }
100
100
  async function readStreamWithLimit(stream, maxBytes, adapter, signal) {
101
101
  const reader = stream.getReader();
@@ -113,7 +113,7 @@ async function readStreamWithLimit(stream, maxBytes, adapter, signal) {
113
113
  total += chunkByteLength(value, adapter);
114
114
  if (total > maxBytes) {
115
115
  await reader.cancel().catch(() => void 0);
116
- throw new SpeechConfigurationError(`Audio stream exceeds maxBytes (more than ${maxBytes} bytes)`, adapter);
116
+ throw new SpeechAudioTooLargeError(`Audio stream exceeds maxBytes (more than ${maxBytes} bytes)`, maxBytes, adapter);
117
117
  }
118
118
  chunks.push(toBytes(value));
119
119
  }
@@ -303,4 +303,4 @@ function registeredOptionalTranscribers() {
303
303
  //#endregion
304
304
  export { mimeTypeToAudioExtension as _, TRANSCRIBER_ENV_KEYS as a, hasTranscriberEnv as c, parseOptionalInteger as d, readEnv as f, DEFAULT_MAX_AUDIO_BYTES as g, reportSpeechUsage as h, registeredOptionalTranscribers as i, mergeHeaderInits as l, audioSecondsFromProviderUsage as m, optionalTranscriberEntry as n, defaultEnv as o, resolveTranscriberConfig as p, registerOptionalTranscriber as r, hasAnyEnv as s, getOptionalTranscriberFactory as t, parseHeadersEnv as u, normalizeAudioInput as v, normalizeMimeType as y };
305
305
 
306
- //# sourceMappingURL=registry-DFSF3WoM.js.map
306
+ //# sourceMappingURL=registry-DhB2589P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-DhB2589P.js","names":[],"sources":["../../src/shared/audio.ts","../../src/shared/usage.ts","../../src/shared/env.ts","../../src/shared/registry.ts"],"sourcesContent":["/**\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 {\n SpeechAudioTooLargeError,\n SpeechConfigurationError,\n SpeechInvalidAudioError,\n} 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 SpeechInvalidAudioError('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 SpeechAudioTooLargeError(\n `Audio input exceeds maxBytes (${bytes} > ${maxBytes} bytes)`,\n maxBytes,\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 SpeechAudioTooLargeError(\n `Audio stream exceeds maxBytes (more than ${maxBytes} bytes)`,\n maxBytes,\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 * 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","/**\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 */\nexport function 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 * Registry for opt-in transcriber adapters that live behind subpath exports\n * (e.g. `@happyvertical/speech/local`).\n *\n * The core entry never imports those adapters, so their heavy optional peer\n * dependencies are never resolved by bundlers or loaded at runtime unless the\n * caller imports the subpath, which registers its factory here.\n */\n\nimport type { SpeechFactoryContext } from './factory.js';\nimport type {\n GetTranscriberOptions,\n Transcriber,\n TranscriberType,\n} from './types.js';\n\nexport type OptionalTranscriberFactory = (\n options: GetTranscriberOptions,\n context: SpeechFactoryContext,\n) => Transcriber | Promise<Transcriber>;\n\n/** Adapter types that are only available after importing a subpath. */\nconst OPTIONAL_TRANSCRIBER_ENTRIES: Partial<Record<TranscriberType, string>> = {\n local: '@happyvertical/speech/local',\n};\n\nconst registered = new Map<TranscriberType, OptionalTranscriberFactory>();\n\nexport function registerOptionalTranscriber(\n type: TranscriberType,\n factory: OptionalTranscriberFactory,\n): void {\n registered.set(type, factory);\n}\n\nexport function getOptionalTranscriberFactory(\n type: string,\n): OptionalTranscriberFactory | undefined {\n return registered.get(type as TranscriberType);\n}\n\n/** Subpath that registers `type`, when it is an opt-in adapter. */\nexport function optionalTranscriberEntry(type: string): string | undefined {\n return OPTIONAL_TRANSCRIBER_ENTRIES[type as TranscriberType];\n}\n\nexport function registeredOptionalTranscribers(): TranscriberType[] {\n return [...registered.keys()];\n}\n"],"mappings":"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"}