@voiceinput/deepgram 0.1.0-beta.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hirad Arshadiyarahmadi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # `@voiceinput/deepgram`
2
+
3
+ Deepgram live transcription adapter for VoiceInput. The browser-safe root opens
4
+ the realtime stream; `@voiceinput/deepgram/server` exchanges a long-lived API
5
+ key for a temporary JWT.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @voiceinput/react@next @voiceinput/deepgram@next
11
+ ```
12
+
13
+ ## Browser adapter
14
+
15
+ ```ts
16
+ import { deepgram } from "@voiceinput/deepgram";
17
+
18
+ const provider = deepgram({
19
+ tokenEndpoint: "/api/voice-token",
20
+ smartFormat: true,
21
+ punctuate: true,
22
+ });
23
+ ```
24
+
25
+ ### Defaults and shared-option mapping
26
+
27
+ - Model: `nova-3` (`DEEPGRAM_DEFAULT_MODEL`)
28
+ - Audio: mono linear PCM16 at 16 kHz
29
+ - Omitted language: `multi` for `nova-2`, `nova-2-general`, `nova-3`, and
30
+ `nova-3-general`; other models require an explicit BCP 47 language
31
+ - General Nova-2 and Nova-3 preserve supported regional English tags and
32
+ normalize unsupported tags such as `en-CA` to `en`; specialized models keep
33
+ their regional language tags exact
34
+ - `vocabulary`: Deepgram key terms, supported only by Nova-3 model IDs
35
+ - `endpointing`: provider default when omitted, disabled when `false`, or the
36
+ supplied positive integer silence threshold
37
+ - `smartFormat` and `punctuate`: both default to `true`
38
+
39
+ Invalid or unsupported settings fail before microphone permission with distinct
40
+ error codes.
41
+
42
+ ### `DeepgramVoiceInputProviderOptions`
43
+
44
+ | Option | Purpose |
45
+ | ----------------------------------- | ---------------------------------------------------------- |
46
+ | `tokenEndpoint` | Required same-origin endpoint that returns a temporary JWT |
47
+ | `model` | Model ID; default `nova-3` |
48
+ | `smartFormat` | Deepgram smart formatting; default `true` |
49
+ | `punctuate` | Punctuation; default `true` |
50
+ | `profanityFilter` | Provider profanity filter |
51
+ | `numerals` | Provider numeral conversion |
52
+ | `fetch`, `webSocket`, `realtimeUrl` | Transport/endpoint overrides |
53
+
54
+ ## Server token handler
55
+
56
+ ```ts
57
+ import { createDeepgramTokenHandler } from "@voiceinput/deepgram/server";
58
+
59
+ export const POST = createDeepgramTokenHandler({
60
+ apiKey: process.env.DEEPGRAM_API_KEY!,
61
+ ttlSeconds: 30,
62
+ authorize: async (request) => {
63
+ const user = await authenticate(request);
64
+ return user ? { subject: user.id } : null;
65
+ },
66
+ });
67
+ ```
68
+
69
+ The handler accepts only `POST`, requires authorization, sets
70
+ `Cache-Control: no-store`, and returns a temporary token rather than the API
71
+ key. Requests must be JSON and are limited to 16 KiB. Authorization and
72
+ rate-limit callbacks receive independent request bodies. If `onTokenIssued`
73
+ throws, token delivery fails closed.
74
+
75
+ The default `ttlSeconds` is 30; overrides must be integers from 1 to 3600. Use
76
+ the shortest practical lifetime—the token only needs to remain valid for the
77
+ WebSocket handshake. Deepgram grant tokens carry `usage::write` across core
78
+ voice APIs rather than a speech-to-text-only scope. Isolate the backing Member
79
+ key in a dedicated project, apply project spending controls, and use separate
80
+ projects/keys for production and testing. See Deepgram's official
81
+ [token grant](https://developers.deepgram.com/reference/auth/tokens/grant) and
82
+ [authentication guide](https://developers.deepgram.com/guides/fundamentals/token-based-authentication).
83
+
84
+ ### `CreateDeepgramTokenHandlerOptions`
85
+
86
+ | Option | Purpose |
87
+ | ------------------------- | --------------------------------------------------------------- |
88
+ | `apiKey` | Required server-only Deepgram key |
89
+ | `authorize(request)` | Required application authorization |
90
+ | `model` | Default model; default `nova-3` |
91
+ | `allowedModels` | Browser-selectable models; defaults to only `model` |
92
+ | `ttlSeconds` | Temporary-token lifetime, default 30; 1–3600 seconds |
93
+ | `rateLimit(context)` | Optional application quota check |
94
+ | `onTokenIssued(metadata)` | Metadata-only callback with subject, model, and expiry duration |
95
+ | `fetch`, `grantUrl` | Transport/endpoint overrides |
96
+
97
+ `DeepgramTokenHandlerContext` contains `request`, `subject`, and `model`.
98
+ `DeepgramTokenIssuedMetadata` also contains `expiresIn`.
99
+
100
+ ## Public API
101
+
102
+ Browser root:
103
+
104
+ - `deepgram(options)`
105
+ - `DEEPGRAM_DEFAULT_MODEL`
106
+ - `DeepgramVoiceInputProviderOptions`
107
+
108
+ Server-only entry point:
109
+
110
+ - `createDeepgramTokenHandler(options)`
111
+ - `CreateDeepgramTokenHandlerOptions`
112
+ - `DeepgramAuthorization`
113
+ - `DeepgramRateLimitResult`
114
+ - `DeepgramTokenHandlerContext`
115
+ - `DeepgramTokenIssuedMetadata`
116
+
117
+ ## Security
118
+
119
+ Import `/server` only from server code; the export is disabled under the browser
120
+ condition. Never expose `DEEPGRAM_API_KEY` to the client. The browser uses the
121
+ temporary JWT to stream audio directly to Deepgram.
122
+
123
+ See the
124
+ [secure integration guides](https://github.com/VoiceInput/voiceinput/blob/main/docs/vite-hono.md).
package/dist/index.cjs ADDED
@@ -0,0 +1,427 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_session_config = require("./session-config-aoO2eGMT.cjs");
3
+ let _voiceinput_provider_transport = require("@voiceinput/provider/transport");
4
+ let _voiceinput_provider = require("@voiceinput/provider");
5
+ //#region src/index.ts
6
+ const DEFAULT_REALTIME_URL = "wss://api.deepgram.com/v1/listen";
7
+ function deepgram(options) {
8
+ const model = factoryString(options.model ?? "nova-3", "model");
9
+ const tokenEndpoint = factoryString(String(options.tokenEndpoint), "tokenEndpoint");
10
+ const realtimeUrl = factoryString(options.realtimeUrl ?? DEFAULT_REALTIME_URL, "realtimeUrl");
11
+ const providerSettings = {
12
+ ...options.smartFormat === void 0 ? {} : { smartFormat: options.smartFormat },
13
+ ...options.punctuate === void 0 ? {} : { punctuate: options.punctuate },
14
+ ...options.profanityFilter === void 0 ? {} : { profanityFilter: options.profanityFilter },
15
+ ...options.numerals === void 0 ? {} : { numerals: options.numerals }
16
+ };
17
+ const validateProviderOptions = (transcriptionOptions) => {
18
+ try {
19
+ require_session_config.validateDeepgramConfiguration({
20
+ model,
21
+ ...providerSettings,
22
+ ...transcriptionOptions
23
+ });
24
+ } catch (cause) {
25
+ if (_voiceinput_provider.VoiceInputError.isInstance(cause)) throw cause;
26
+ throw invalidConfiguration(cause);
27
+ }
28
+ };
29
+ return Object.freeze({
30
+ specificationVersion: "v1",
31
+ provider: "deepgram",
32
+ modelId: model,
33
+ sampleRate: require_session_config.DEEPGRAM_SAMPLE_RATE,
34
+ validateOptions: validateProviderOptions,
35
+ async doOpen(callOptions) {
36
+ validateProviderOptions(callOptions);
37
+ const configuration = require_session_config.validateDeepgramConfiguration({
38
+ model,
39
+ ...providerSettings,
40
+ ...callOptions
41
+ });
42
+ return await openSession({
43
+ abortSignal: callOptions.abortSignal,
44
+ configuration,
45
+ fetchImplementation: options.fetch ?? globalThis.fetch,
46
+ realtimeUrl,
47
+ tokenEndpoint,
48
+ WebSocketImplementation: options.webSocket ?? globalThis.WebSocket
49
+ });
50
+ }
51
+ });
52
+ }
53
+ async function openSession(options) {
54
+ throwIfAborted(options.abortSignal);
55
+ requireBrowserFunction(options.fetchImplementation, "fetch");
56
+ requireBrowserFunction(options.WebSocketImplementation, "WebSocket");
57
+ const token = await requestToken(options.fetchImplementation, options.tokenEndpoint, options.configuration.model, options.abortSignal);
58
+ throwIfAborted(options.abortSignal);
59
+ return await createSession(new options.WebSocketImplementation(require_session_config.createDeepgramRealtimeUrl(options.realtimeUrl, options.configuration), ["bearer", token]), options.abortSignal);
60
+ }
61
+ async function requestToken(fetchImplementation, tokenEndpoint, model, abortSignal) {
62
+ let response;
63
+ try {
64
+ response = await fetchImplementation(tokenEndpoint, {
65
+ method: "POST",
66
+ headers: { "Content-Type": "application/json" },
67
+ body: JSON.stringify({ model }),
68
+ credentials: "same-origin",
69
+ signal: abortSignal
70
+ });
71
+ } catch (cause) {
72
+ throwIfAborted(abortSignal);
73
+ throw new _voiceinput_provider.VoiceInputError({
74
+ code: "network-error",
75
+ message: "Unable to reach the Deepgram token endpoint.",
76
+ provider: "deepgram",
77
+ retryable: true,
78
+ cause
79
+ });
80
+ }
81
+ if (!response.ok) throw await tokenResponseError(response);
82
+ let value;
83
+ try {
84
+ value = await response.json();
85
+ } catch (cause) {
86
+ throw new _voiceinput_provider.VoiceInputError({
87
+ code: "token-error",
88
+ message: "The Deepgram token endpoint returned invalid JSON.",
89
+ provider: "deepgram",
90
+ cause
91
+ });
92
+ }
93
+ if (!isRecord(value) || !nonEmpty(value["access_token"])) throw new _voiceinput_provider.VoiceInputError({
94
+ code: "token-error",
95
+ message: "The Deepgram token endpoint returned an invalid token.",
96
+ provider: "deepgram"
97
+ });
98
+ return value["access_token"];
99
+ }
100
+ async function createSession(socket, abortSignal) {
101
+ let controller;
102
+ const stream = new ReadableStream({ start(value) {
103
+ controller = value;
104
+ } });
105
+ if (controller === void 0) throw new _voiceinput_provider.VoiceInputError({
106
+ code: "provider-error",
107
+ message: "Unable to initialize the Deepgram transcript stream.",
108
+ provider: "deepgram"
109
+ });
110
+ let audioSent = false;
111
+ let closed = false;
112
+ let failed = false;
113
+ let finishing = false;
114
+ const closedSegments = /* @__PURE__ */ new Set();
115
+ let lastInterim = "";
116
+ let speechActive = false;
117
+ const closeSocket = (reason) => {
118
+ if (socket.readyState === 0 || socket.readyState === 1) socket.close(1e3, reason);
119
+ };
120
+ const closeStream = () => {
121
+ if (closed) return;
122
+ closed = true;
123
+ abortSignal.removeEventListener("abort", abort);
124
+ controller?.close();
125
+ };
126
+ const finishCleanly = () => {
127
+ closeStream();
128
+ closeSocket("finished");
129
+ };
130
+ const fail = (error) => {
131
+ if (closed || failed) return;
132
+ failed = true;
133
+ controller?.enqueue({
134
+ type: "error",
135
+ error
136
+ });
137
+ closeStream();
138
+ closeSocket("aborted");
139
+ };
140
+ const startSpeech = () => {
141
+ if (!speechActive) {
142
+ speechActive = true;
143
+ controller?.enqueue({ type: "speech-start" });
144
+ }
145
+ };
146
+ const endSpeech = () => {
147
+ if (speechActive) {
148
+ speechActive = false;
149
+ controller?.enqueue({ type: "speech-end" });
150
+ }
151
+ };
152
+ const handleResults = (value) => {
153
+ const channel = value["channel"];
154
+ if (!isRecord(channel) || !Array.isArray(channel["alternatives"])) throw new TypeError("Deepgram Results did not contain alternatives.");
155
+ const alternative = channel["alternatives"][0];
156
+ if (!isRecord(alternative) || typeof alternative["transcript"] !== "string") throw new TypeError("Deepgram Results did not contain a transcript.");
157
+ const text = alternative["transcript"];
158
+ const isFinal = value["is_final"] === true;
159
+ const start = value["start"];
160
+ if (typeof start !== "number" || !Number.isFinite(start) || start < 0) throw new TypeError("Deepgram Results did not contain an audio start boundary.");
161
+ const segmentId = `audio:${start}`;
162
+ if (closedSegments.has(segmentId)) return;
163
+ if (text.length > 0 || isFinal) {
164
+ if (text.length > 0) startSpeech();
165
+ if (isFinal) {
166
+ closedSegments.add(segmentId);
167
+ controller?.enqueue({
168
+ type: "final",
169
+ text,
170
+ segmentId
171
+ });
172
+ lastInterim = "";
173
+ } else if (text !== lastInterim) {
174
+ lastInterim = text;
175
+ controller?.enqueue({
176
+ type: "interim",
177
+ text,
178
+ segmentId
179
+ });
180
+ }
181
+ }
182
+ if (value["speech_final"] === true) endSpeech();
183
+ };
184
+ const handleMessage = (event) => {
185
+ if (closed) return;
186
+ try {
187
+ const value = JSON.parse(String(event.data));
188
+ if (!isRecord(value) || !nonEmpty(value["type"])) throw new TypeError("Deepgram sent an invalid streaming event.");
189
+ const type = value["type"];
190
+ if (type === "Results") handleResults(value);
191
+ else if (type === "SpeechStarted") startSpeech();
192
+ else if (type === "UtteranceEnd") endSpeech();
193
+ else if (type === "Error") fail(normalizeMessageError(value));
194
+ } catch (cause) {
195
+ fail(new _voiceinput_provider.VoiceInputError({
196
+ code: "provider-error",
197
+ message: "Deepgram sent an invalid streaming event.",
198
+ provider: "deepgram",
199
+ cause
200
+ }));
201
+ }
202
+ };
203
+ const handleClose = (event) => {
204
+ if (closed) return;
205
+ if (event.code === 1e3) closeStream();
206
+ else fail(normalizeCloseError(event));
207
+ };
208
+ function abort() {
209
+ if (closed) return;
210
+ closeStream();
211
+ closeSocket("aborted");
212
+ }
213
+ socket.addEventListener("message", handleMessage);
214
+ socket.addEventListener("close", handleClose);
215
+ socket.addEventListener("error", () => {
216
+ fail(new _voiceinput_provider.VoiceInputError({
217
+ code: "network-error",
218
+ message: "The Deepgram streaming connection failed.",
219
+ provider: "deepgram",
220
+ retryable: true
221
+ }));
222
+ });
223
+ abortSignal.addEventListener("abort", abort, { once: true });
224
+ await waitForOpen(socket, abortSignal);
225
+ return {
226
+ stream,
227
+ sendAudio(chunk) {
228
+ if (closed || finishing || chunk.length === 0) return;
229
+ audioSent = true;
230
+ try {
231
+ return (0, _voiceinput_provider_transport.sendWithBackpressure)(socket, chunk.byteLength, abortSignal, "deepgram", () => socket.send(new Int16Array(chunk).buffer));
232
+ } catch (cause) {
233
+ throw new _voiceinput_provider.VoiceInputError({
234
+ code: "network-error",
235
+ message: "Unable to send audio to Deepgram.",
236
+ provider: "deepgram",
237
+ retryable: true,
238
+ cause
239
+ });
240
+ }
241
+ },
242
+ finish() {
243
+ if (closed || finishing) return;
244
+ finishing = true;
245
+ if (!audioSent) {
246
+ finishCleanly();
247
+ return;
248
+ }
249
+ sendJson(socket, { type: "CloseStream" });
250
+ },
251
+ abort
252
+ };
253
+ }
254
+ function normalizeMessageError(value) {
255
+ const code = typeof value["code"] === "string" ? value["code"] : "";
256
+ const description = typeof value["description"] === "string" ? value["description"] : typeof value["message"] === "string" ? value["message"] : "";
257
+ const source = `${code} ${description}`;
258
+ const rateLimited = /rate|quota|429/iu.test(source);
259
+ const unauthorized = /auth|unauthorized|401|403/iu.test(source);
260
+ return new _voiceinput_provider.VoiceInputError({
261
+ code: unauthorized ? "unauthorized" : rateLimited ? "rate-limited" : "provider-error",
262
+ message: description || "Deepgram reported a streaming error.",
263
+ provider: "deepgram",
264
+ retryable: rateLimited || /internal|unavailable/iu.test(source),
265
+ cause: value
266
+ });
267
+ }
268
+ function normalizeCloseError(event) {
269
+ const reason = event.reason ?? "";
270
+ const rateLimited = event.code === 1013 || /rate|quota|429/iu.test(reason);
271
+ const unauthorized = /auth|unauthorized|401|403/iu.test(reason);
272
+ const invalidAudio = event.code === 1008 && /data|audio|decode/iu.test(reason);
273
+ return new _voiceinput_provider.VoiceInputError({
274
+ code: unauthorized ? "unauthorized" : rateLimited ? "rate-limited" : invalidAudio ? "audio-error" : "network-error",
275
+ message: "The Deepgram streaming connection closed unexpectedly.",
276
+ provider: "deepgram",
277
+ retryable: rateLimited || !unauthorized && !invalidAudio,
278
+ cause: event
279
+ });
280
+ }
281
+ function invalidConfiguration(cause) {
282
+ return new _voiceinput_provider.VoiceInputError({
283
+ code: "invalid-configuration",
284
+ message: cause instanceof Error ? cause.message : "Invalid Deepgram transcription options.",
285
+ provider: "deepgram",
286
+ cause
287
+ });
288
+ }
289
+ function requireBrowserFunction(value, feature) {
290
+ if (typeof value !== "function") throw new _voiceinput_provider.VoiceInputError({
291
+ code: "unsupported-browser",
292
+ message: `Deepgram voice input requires browser ${feature} support.`,
293
+ provider: "deepgram"
294
+ });
295
+ }
296
+ function factoryString(value, name) {
297
+ if (value.trim().length === 0) throw invalidConfiguration(/* @__PURE__ */ new TypeError(`${name} must be non-empty.`));
298
+ return value;
299
+ }
300
+ async function tokenResponseError(response) {
301
+ const retryAfterMs = parseRetryAfter(response.headers.get("Retry-After"));
302
+ if (response.status === 401 || response.status === 403) return new _voiceinput_provider.VoiceInputError({
303
+ code: "unauthorized",
304
+ message: "The Deepgram token endpoint rejected this request.",
305
+ provider: "deepgram"
306
+ });
307
+ if (response.status === 429) return new _voiceinput_provider.VoiceInputError({
308
+ code: "rate-limited",
309
+ message: "The Deepgram token endpoint rate limit was exceeded.",
310
+ provider: "deepgram",
311
+ retryable: true,
312
+ ...retryAfterMs === void 0 ? {} : { retryAfterMs }
313
+ });
314
+ const safeError = await readSafeTokenError(response);
315
+ if (safeError !== void 0) return new _voiceinput_provider.VoiceInputError({
316
+ ...safeError,
317
+ provider: "deepgram"
318
+ });
319
+ return new _voiceinput_provider.VoiceInputError({
320
+ code: "token-error",
321
+ message: "The Deepgram token endpoint did not issue a token.",
322
+ provider: "deepgram",
323
+ retryable: response.status >= 500
324
+ });
325
+ }
326
+ async function readSafeTokenError(response) {
327
+ if (response.status !== 400 || response.headers.get("X-VoiceInput-Error") !== "1" || response.headers.get("Content-Type")?.split(";", 1)[0]?.trim() !== "application/json") return;
328
+ const text = await readBoundedErrorText(response);
329
+ if (text === void 0) return void 0;
330
+ try {
331
+ const value = JSON.parse(text);
332
+ const error = isRecord(value) ? value["error"] : void 0;
333
+ if (!isRecord(error) || error["code"] !== "invalid-configuration" && error["code"] !== "unsupported-feature" || typeof error["message"] !== "string" || error["message"].length === 0 || error["message"].length > 1e3) return;
334
+ return {
335
+ code: error["code"],
336
+ message: error["message"]
337
+ };
338
+ } catch {
339
+ return;
340
+ }
341
+ }
342
+ async function readBoundedErrorText(response) {
343
+ const reader = response.body?.getReader();
344
+ if (reader === void 0) return void 0;
345
+ const decoder = new TextDecoder();
346
+ let bytesRead = 0;
347
+ let text = "";
348
+ try {
349
+ while (true) {
350
+ const { done, value } = await reader.read();
351
+ if (done) return text + decoder.decode();
352
+ bytesRead += value.byteLength;
353
+ if (bytesRead > 4096) {
354
+ await reader.cancel().catch(() => {});
355
+ return;
356
+ }
357
+ text += decoder.decode(value, { stream: true });
358
+ }
359
+ } catch {
360
+ return;
361
+ }
362
+ }
363
+ function waitForOpen(socket, abortSignal) {
364
+ if (socket.readyState === 1) return Promise.resolve();
365
+ return new Promise((resolve, reject) => {
366
+ const cleanup = () => {
367
+ socket.removeEventListener("open", handleOpen);
368
+ socket.removeEventListener("error", handleError);
369
+ abortSignal.removeEventListener("abort", handleAbort);
370
+ };
371
+ const handleOpen = () => {
372
+ cleanup();
373
+ resolve();
374
+ };
375
+ const handleError = (event) => {
376
+ cleanup();
377
+ reject(new _voiceinput_provider.VoiceInputError({
378
+ code: "network-error",
379
+ message: "Unable to open the Deepgram streaming connection.",
380
+ provider: "deepgram",
381
+ retryable: true,
382
+ cause: event
383
+ }));
384
+ };
385
+ const handleAbort = () => {
386
+ cleanup();
387
+ reject(abortSignal.reason);
388
+ };
389
+ socket.addEventListener("open", handleOpen, { once: true });
390
+ socket.addEventListener("error", handleError, { once: true });
391
+ abortSignal.addEventListener("abort", handleAbort, { once: true });
392
+ });
393
+ }
394
+ function sendJson(socket, value) {
395
+ try {
396
+ socket.send(JSON.stringify(value));
397
+ } catch (cause) {
398
+ throw new _voiceinput_provider.VoiceInputError({
399
+ code: "network-error",
400
+ message: "Unable to send data to Deepgram.",
401
+ provider: "deepgram",
402
+ retryable: true,
403
+ cause
404
+ });
405
+ }
406
+ }
407
+ function parseRetryAfter(value) {
408
+ if (value === null) return;
409
+ const seconds = Number(value);
410
+ if (Number.isFinite(seconds) && seconds >= 0) return Math.ceil(seconds * 1e3);
411
+ const date = Date.parse(value);
412
+ return Number.isNaN(date) ? void 0 : Math.max(0, date - Date.now());
413
+ }
414
+ function throwIfAborted(signal) {
415
+ if (signal.aborted) throw signal.reason;
416
+ }
417
+ function nonEmpty(value) {
418
+ return typeof value === "string" && value.length > 0;
419
+ }
420
+ function isRecord(value) {
421
+ return typeof value === "object" && value !== null && !Array.isArray(value);
422
+ }
423
+ //#endregion
424
+ exports.DEEPGRAM_DEFAULT_MODEL = require_session_config.DEEPGRAM_DEFAULT_MODEL;
425
+ exports.deepgram = deepgram;
426
+
427
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["VoiceInputError","DEEPGRAM_SAMPLE_RATE","validateDeepgramConfiguration","createDeepgramRealtimeUrl","sendWithBackpressure"],"sources":["../src/index.ts"],"sourcesContent":["import { sendWithBackpressure } from \"@voiceinput/provider/transport\";\nimport {\n VoiceInputError,\n type VoiceInputProviderV1,\n type VoiceInputProviderV1CallOptions,\n type VoiceInputProviderV1Session,\n type VoiceInputProviderV1StreamPart,\n type VoiceTranscriptionOptions,\n} from \"@voiceinput/provider\";\n\nimport {\n DEEPGRAM_DEFAULT_MODEL,\n DEEPGRAM_SAMPLE_RATE,\n createDeepgramRealtimeUrl,\n validateDeepgramConfiguration,\n type DeepgramRealtimeSettings,\n type DeepgramSessionConfiguration,\n} from \"./session-config.js\";\n\nconst DEFAULT_REALTIME_URL = \"wss://api.deepgram.com/v1/listen\";\n\nexport interface DeepgramVoiceInputProviderOptions extends DeepgramRealtimeSettings {\n readonly tokenEndpoint: string | URL;\n readonly model?: string;\n readonly fetch?: typeof globalThis.fetch;\n readonly webSocket?: typeof globalThis.WebSocket;\n readonly realtimeUrl?: string;\n}\n\nexport function deepgram(\n options: DeepgramVoiceInputProviderOptions,\n): VoiceInputProviderV1 {\n const model = factoryString(options.model ?? DEEPGRAM_DEFAULT_MODEL, \"model\");\n const tokenEndpoint = factoryString(\n String(options.tokenEndpoint),\n \"tokenEndpoint\",\n );\n const realtimeUrl = factoryString(\n options.realtimeUrl ?? DEFAULT_REALTIME_URL,\n \"realtimeUrl\",\n );\n const providerSettings: DeepgramRealtimeSettings = {\n ...(options.smartFormat === undefined\n ? {}\n : { smartFormat: options.smartFormat }),\n ...(options.punctuate === undefined\n ? {}\n : { punctuate: options.punctuate }),\n ...(options.profanityFilter === undefined\n ? {}\n : { profanityFilter: options.profanityFilter }),\n ...(options.numerals === undefined ? {} : { numerals: options.numerals }),\n };\n const validateProviderOptions = (\n transcriptionOptions: VoiceTranscriptionOptions,\n ): void => {\n try {\n validateDeepgramConfiguration({\n model,\n ...providerSettings,\n ...transcriptionOptions,\n });\n } catch (cause) {\n if (VoiceInputError.isInstance(cause)) {\n throw cause;\n }\n throw invalidConfiguration(cause);\n }\n };\n\n return Object.freeze({\n specificationVersion: \"v1\" as const,\n provider: \"deepgram\",\n modelId: model,\n sampleRate: DEEPGRAM_SAMPLE_RATE,\n validateOptions: validateProviderOptions,\n async doOpen(callOptions: VoiceInputProviderV1CallOptions) {\n validateProviderOptions(callOptions);\n const configuration = validateDeepgramConfiguration({\n model,\n ...providerSettings,\n ...callOptions,\n });\n return await openSession({\n abortSignal: callOptions.abortSignal,\n configuration,\n fetchImplementation: options.fetch ?? globalThis.fetch,\n realtimeUrl,\n tokenEndpoint,\n WebSocketImplementation: options.webSocket ?? globalThis.WebSocket,\n });\n },\n });\n}\n\nasync function openSession(options: {\n abortSignal: AbortSignal;\n configuration: DeepgramSessionConfiguration;\n fetchImplementation: typeof globalThis.fetch;\n realtimeUrl: string;\n tokenEndpoint: string;\n WebSocketImplementation: typeof globalThis.WebSocket;\n}): Promise<VoiceInputProviderV1Session> {\n throwIfAborted(options.abortSignal);\n requireBrowserFunction(options.fetchImplementation, \"fetch\");\n requireBrowserFunction(options.WebSocketImplementation, \"WebSocket\");\n const token = await requestToken(\n options.fetchImplementation,\n options.tokenEndpoint,\n options.configuration.model,\n options.abortSignal,\n );\n throwIfAborted(options.abortSignal);\n const socket = new options.WebSocketImplementation(\n createDeepgramRealtimeUrl(options.realtimeUrl, options.configuration),\n [\"bearer\", token],\n );\n return await createSession(socket, options.abortSignal);\n}\n\nasync function requestToken(\n fetchImplementation: typeof globalThis.fetch,\n tokenEndpoint: string,\n model: string,\n abortSignal: AbortSignal,\n): Promise<string> {\n let response: Response;\n try {\n response = await fetchImplementation(tokenEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ model }),\n credentials: \"same-origin\",\n signal: abortSignal,\n });\n } catch (cause) {\n throwIfAborted(abortSignal);\n throw new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to reach the Deepgram token endpoint.\",\n provider: \"deepgram\",\n retryable: true,\n cause,\n });\n }\n if (!response.ok) {\n throw await tokenResponseError(response);\n }\n let value: unknown;\n try {\n value = await response.json();\n } catch (cause) {\n throw new VoiceInputError({\n code: \"token-error\",\n message: \"The Deepgram token endpoint returned invalid JSON.\",\n provider: \"deepgram\",\n cause,\n });\n }\n if (!isRecord(value) || !nonEmpty(value[\"access_token\"])) {\n throw new VoiceInputError({\n code: \"token-error\",\n message: \"The Deepgram token endpoint returned an invalid token.\",\n provider: \"deepgram\",\n });\n }\n return value[\"access_token\"];\n}\n\nasync function createSession(\n socket: WebSocket,\n abortSignal: AbortSignal,\n): Promise<VoiceInputProviderV1Session> {\n let controller:\n ReadableStreamDefaultController<VoiceInputProviderV1StreamPart> | undefined;\n const stream = new ReadableStream<VoiceInputProviderV1StreamPart>({\n start(value) {\n controller = value;\n },\n });\n if (controller === undefined) {\n throw new VoiceInputError({\n code: \"provider-error\",\n message: \"Unable to initialize the Deepgram transcript stream.\",\n provider: \"deepgram\",\n });\n }\n\n let audioSent = false;\n let closed = false;\n let failed = false;\n let finishing = false;\n const closedSegments = new Set<string>();\n let lastInterim = \"\";\n let speechActive = false;\n\n const closeSocket = (reason: \"aborted\" | \"finished\"): void => {\n if (socket.readyState === 0 || socket.readyState === 1) {\n socket.close(1000, reason);\n }\n };\n const closeStream = (): void => {\n if (closed) {\n return;\n }\n closed = true;\n abortSignal.removeEventListener(\"abort\", abort);\n controller?.close();\n };\n const finishCleanly = (): void => {\n closeStream();\n closeSocket(\"finished\");\n };\n const fail = (error: VoiceInputError): void => {\n if (closed || failed) {\n return;\n }\n failed = true;\n controller?.enqueue({ type: \"error\", error });\n closeStream();\n closeSocket(\"aborted\");\n };\n const startSpeech = (): void => {\n if (!speechActive) {\n speechActive = true;\n controller?.enqueue({ type: \"speech-start\" });\n }\n };\n const endSpeech = (): void => {\n if (speechActive) {\n speechActive = false;\n controller?.enqueue({ type: \"speech-end\" });\n }\n };\n const handleResults = (value: Record<string, unknown>): void => {\n const channel = value[\"channel\"];\n if (!isRecord(channel) || !Array.isArray(channel[\"alternatives\"])) {\n throw new TypeError(\"Deepgram Results did not contain alternatives.\");\n }\n const alternative = channel[\"alternatives\"][0];\n if (\n !isRecord(alternative) ||\n typeof alternative[\"transcript\"] !== \"string\"\n ) {\n throw new TypeError(\"Deepgram Results did not contain a transcript.\");\n }\n const text = alternative[\"transcript\"];\n const isFinal = value[\"is_final\"] === true;\n const start = value[\"start\"];\n if (typeof start !== \"number\" || !Number.isFinite(start) || start < 0)\n throw new TypeError(\n \"Deepgram Results did not contain an audio start boundary.\",\n );\n const segmentId = `audio:${start}`;\n if (closedSegments.has(segmentId)) return;\n if (text.length > 0 || isFinal) {\n if (text.length > 0) startSpeech();\n if (isFinal) {\n closedSegments.add(segmentId);\n controller?.enqueue({ type: \"final\", text, segmentId });\n lastInterim = \"\";\n } else if (text !== lastInterim) {\n lastInterim = text;\n controller?.enqueue({ type: \"interim\", text, segmentId });\n }\n }\n if (value[\"speech_final\"] === true) {\n endSpeech();\n }\n };\n const handleMessage = (event: MessageEvent): void => {\n if (closed) {\n return;\n }\n try {\n const value = JSON.parse(String(event.data)) as unknown;\n if (!isRecord(value) || !nonEmpty(value[\"type\"])) {\n throw new TypeError(\"Deepgram sent an invalid streaming event.\");\n }\n const type = value[\"type\"];\n if (type === \"Results\") {\n handleResults(value);\n } else if (type === \"SpeechStarted\") {\n startSpeech();\n } else if (type === \"UtteranceEnd\") {\n endSpeech();\n } else if (type === \"Error\") {\n fail(normalizeMessageError(value));\n }\n } catch (cause) {\n fail(\n new VoiceInputError({\n code: \"provider-error\",\n message: \"Deepgram sent an invalid streaming event.\",\n provider: \"deepgram\",\n cause,\n }),\n );\n }\n };\n const handleClose = (event: CloseEvent): void => {\n if (closed) {\n return;\n }\n if (event.code === 1000) {\n closeStream();\n } else {\n fail(normalizeCloseError(event));\n }\n };\n function abort(): void {\n if (closed) {\n return;\n }\n closeStream();\n closeSocket(\"aborted\");\n }\n\n socket.addEventListener(\"message\", handleMessage);\n socket.addEventListener(\"close\", handleClose);\n socket.addEventListener(\"error\", () => {\n fail(\n new VoiceInputError({\n code: \"network-error\",\n message: \"The Deepgram streaming connection failed.\",\n provider: \"deepgram\",\n retryable: true,\n }),\n );\n });\n abortSignal.addEventListener(\"abort\", abort, { once: true });\n await waitForOpen(socket, abortSignal);\n\n return {\n stream,\n sendAudio(chunk) {\n if (closed || finishing || chunk.length === 0) {\n return;\n }\n audioSent = true;\n try {\n return sendWithBackpressure(\n socket,\n chunk.byteLength,\n abortSignal,\n \"deepgram\",\n () => socket.send(new Int16Array(chunk).buffer),\n );\n } catch (cause) {\n throw new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to send audio to Deepgram.\",\n provider: \"deepgram\",\n retryable: true,\n cause,\n });\n }\n },\n finish() {\n if (closed || finishing) {\n return;\n }\n finishing = true;\n if (!audioSent) {\n finishCleanly();\n return;\n }\n sendJson(socket, { type: \"CloseStream\" });\n },\n abort,\n };\n}\n\nfunction normalizeMessageError(\n value: Record<string, unknown>,\n): VoiceInputError {\n const code = typeof value[\"code\"] === \"string\" ? value[\"code\"] : \"\";\n const description =\n typeof value[\"description\"] === \"string\"\n ? value[\"description\"]\n : typeof value[\"message\"] === \"string\"\n ? value[\"message\"]\n : \"\";\n const source = `${code} ${description}`;\n const rateLimited = /rate|quota|429/iu.test(source);\n const unauthorized = /auth|unauthorized|401|403/iu.test(source);\n return new VoiceInputError({\n code: unauthorized\n ? \"unauthorized\"\n : rateLimited\n ? \"rate-limited\"\n : \"provider-error\",\n message: description || \"Deepgram reported a streaming error.\",\n provider: \"deepgram\",\n retryable: rateLimited || /internal|unavailable/iu.test(source),\n cause: value,\n });\n}\n\nfunction normalizeCloseError(event: CloseEvent): VoiceInputError {\n const reason = event.reason ?? \"\";\n const rateLimited = event.code === 1013 || /rate|quota|429/iu.test(reason);\n const unauthorized = /auth|unauthorized|401|403/iu.test(reason);\n const invalidAudio =\n event.code === 1008 && /data|audio|decode/iu.test(reason);\n return new VoiceInputError({\n code: unauthorized\n ? \"unauthorized\"\n : rateLimited\n ? \"rate-limited\"\n : invalidAudio\n ? \"audio-error\"\n : \"network-error\",\n message: \"The Deepgram streaming connection closed unexpectedly.\",\n provider: \"deepgram\",\n retryable: rateLimited || (!unauthorized && !invalidAudio),\n cause: event,\n });\n}\n\nfunction invalidConfiguration(cause: unknown): VoiceInputError {\n return new VoiceInputError({\n code: \"invalid-configuration\",\n message:\n cause instanceof Error\n ? cause.message\n : \"Invalid Deepgram transcription options.\",\n provider: \"deepgram\",\n cause,\n });\n}\n\nfunction requireBrowserFunction(value: unknown, feature: string): void {\n if (typeof value !== \"function\") {\n throw new VoiceInputError({\n code: \"unsupported-browser\",\n message: `Deepgram voice input requires browser ${feature} support.`,\n provider: \"deepgram\",\n });\n }\n}\n\nfunction factoryString(value: string, name: string): string {\n if (value.trim().length === 0) {\n throw invalidConfiguration(new TypeError(`${name} must be non-empty.`));\n }\n return value;\n}\n\nasync function tokenResponseError(\n response: Response,\n): Promise<VoiceInputError> {\n const retryAfterMs = parseRetryAfter(response.headers.get(\"Retry-After\"));\n if (response.status === 401 || response.status === 403) {\n return new VoiceInputError({\n code: \"unauthorized\",\n message: \"The Deepgram token endpoint rejected this request.\",\n provider: \"deepgram\",\n });\n }\n if (response.status === 429) {\n return new VoiceInputError({\n code: \"rate-limited\",\n message: \"The Deepgram token endpoint rate limit was exceeded.\",\n provider: \"deepgram\",\n retryable: true,\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n });\n }\n const safeError = await readSafeTokenError(response);\n if (safeError !== undefined) {\n return new VoiceInputError({ ...safeError, provider: \"deepgram\" });\n }\n return new VoiceInputError({\n code: \"token-error\",\n message: \"The Deepgram token endpoint did not issue a token.\",\n provider: \"deepgram\",\n retryable: response.status >= 500,\n });\n}\n\nasync function readSafeTokenError(response: Response): Promise<\n | {\n code: \"invalid-configuration\" | \"unsupported-feature\";\n message: string;\n }\n | undefined\n> {\n if (\n response.status !== 400 ||\n response.headers.get(\"X-VoiceInput-Error\") !== \"1\" ||\n response.headers.get(\"Content-Type\")?.split(\";\", 1)[0]?.trim() !==\n \"application/json\"\n ) {\n return undefined;\n }\n const text = await readBoundedErrorText(response);\n if (text === undefined) return undefined;\n try {\n const value = JSON.parse(text) as unknown;\n const error = isRecord(value) ? value[\"error\"] : undefined;\n if (\n !isRecord(error) ||\n (error[\"code\"] !== \"invalid-configuration\" &&\n error[\"code\"] !== \"unsupported-feature\") ||\n typeof error[\"message\"] !== \"string\" ||\n error[\"message\"].length === 0 ||\n error[\"message\"].length > 1_000\n ) {\n return undefined;\n }\n return { code: error[\"code\"], message: error[\"message\"] };\n } catch {\n return undefined;\n }\n}\n\nasync function readBoundedErrorText(\n response: Response,\n): Promise<string | undefined> {\n const reader = response.body?.getReader();\n if (reader === undefined) return undefined;\n const decoder = new TextDecoder();\n let bytesRead = 0;\n let text = \"\";\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) return text + decoder.decode();\n bytesRead += value.byteLength;\n if (bytesRead > 4_096) {\n await reader.cancel().catch(() => {});\n return undefined;\n }\n text += decoder.decode(value, { stream: true });\n }\n } catch {\n return undefined;\n }\n}\n\nfunction waitForOpen(\n socket: WebSocket,\n abortSignal: AbortSignal,\n): Promise<void> {\n if (socket.readyState === 1) {\n return Promise.resolve();\n }\n return new Promise((resolve, reject) => {\n const cleanup = (): void => {\n socket.removeEventListener(\"open\", handleOpen);\n socket.removeEventListener(\"error\", handleError);\n abortSignal.removeEventListener(\"abort\", handleAbort);\n };\n const handleOpen = (): void => {\n cleanup();\n resolve();\n };\n const handleError = (event: Event): void => {\n cleanup();\n reject(\n new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to open the Deepgram streaming connection.\",\n provider: \"deepgram\",\n retryable: true,\n cause: event,\n }),\n );\n };\n const handleAbort = (): void => {\n cleanup();\n reject(abortSignal.reason);\n };\n socket.addEventListener(\"open\", handleOpen, { once: true });\n socket.addEventListener(\"error\", handleError, { once: true });\n abortSignal.addEventListener(\"abort\", handleAbort, { once: true });\n });\n}\n\nfunction sendJson(socket: WebSocket, value: Record<string, unknown>): void {\n try {\n socket.send(JSON.stringify(value));\n } catch (cause) {\n throw new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to send data to Deepgram.\",\n provider: \"deepgram\",\n retryable: true,\n cause,\n });\n }\n}\n\nfunction parseRetryAfter(value: string | null): number | undefined {\n if (value === null) {\n return undefined;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.ceil(seconds * 1_000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nfunction throwIfAborted(signal: AbortSignal): void {\n if (signal.aborted) {\n throw signal.reason;\n }\n}\n\nfunction nonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport { DEEPGRAM_DEFAULT_MODEL } from \"./session-config.js\";\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { VoiceInputProviderV1 } from "@voiceinput/provider";
2
+ //#region src/session-config.d.ts
3
+ declare const DEEPGRAM_DEFAULT_MODEL = "nova-3";
4
+ interface DeepgramRealtimeSettings {
5
+ readonly smartFormat?: boolean;
6
+ readonly punctuate?: boolean;
7
+ readonly profanityFilter?: boolean;
8
+ readonly numerals?: boolean;
9
+ }
10
+ //#endregion
11
+ //#region src/index.d.ts
12
+ interface DeepgramVoiceInputProviderOptions extends DeepgramRealtimeSettings {
13
+ readonly tokenEndpoint: string | URL;
14
+ readonly model?: string;
15
+ readonly fetch?: typeof globalThis.fetch;
16
+ readonly webSocket?: typeof globalThis.WebSocket;
17
+ readonly realtimeUrl?: string;
18
+ }
19
+ declare function deepgram(options: DeepgramVoiceInputProviderOptions): VoiceInputProviderV1;
20
+ //#endregion
21
+ export { DEEPGRAM_DEFAULT_MODEL, DeepgramVoiceInputProviderOptions, deepgram };
22
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/session-config.ts","../src/index.ts"],"mappings":";;cAKa;UAYI;WACN;WACA;WACA;WACA;;;;UCAM,0CAA0C;WAChD,wBAAwB;WACxB;WACA,eAAe,WAAW;WAC1B,mBAAmB,WAAW;WAC9B;;iBAGK,SACd,SAAS,oCACR"}