@volley/recognition-client-sdk 0.1.211 → 0.1.255

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19,6 +19,11 @@ var RecognitionMode;
19
19
  RecognitionMode2["STREAMING"] = "streaming";
20
20
  RecognitionMode2["BATCH"] = "batch";
21
21
  })(RecognitionMode || (RecognitionMode = {}));
22
+ var ASRApiType;
23
+ (function(ASRApiType2) {
24
+ ASRApiType2["STREAMING"] = "streaming";
25
+ ASRApiType2["FILE_BASED"] = "file-based";
26
+ })(ASRApiType || (ASRApiType = {}));
22
27
  var DeepgramModel;
23
28
  (function(DeepgramModel2) {
24
29
  DeepgramModel2["NOVA_2"] = "nova-2";
@@ -82,6 +87,8 @@ var MetadataResultSchemaV1 = z.object({
82
87
  accumulatedAudioTimeMs: z.number().optional(),
83
88
  // Cost Information
84
89
  costInUSD: z.number().default(0).optional(),
90
+ // ASR API Type
91
+ apiType: z.nativeEnum(ASRApiType).optional(),
85
92
  // ASR configuration as JSON string (no type validation)
86
93
  asrConfig: z.string().optional(),
87
94
  // Raw ASR metadata payload as provided by the provider (stringified if needed)
@@ -94,6 +101,7 @@ var ErrorTypeV1;
94
101
  ErrorTypeV12["PROVIDER_ERROR"] = "provider_error";
95
102
  ErrorTypeV12["TIMEOUT_ERROR"] = "timeout_error";
96
103
  ErrorTypeV12["QUOTA_EXCEEDED"] = "quota_exceeded";
104
+ ErrorTypeV12["CONNECTION_ERROR"] = "connection_error";
97
105
  ErrorTypeV12["UNKNOWN_ERROR"] = "unknown_error";
98
106
  })(ErrorTypeV1 || (ErrorTypeV1 = {}));
99
107
  var ErrorResultSchemaV1 = z.object({
@@ -214,7 +222,27 @@ var TranscriptMessageSchema = z.object({
214
222
  * Whether this transcript is finalized (won't change)
215
223
  * @example true
216
224
  */
217
- is_final: z.boolean()
225
+ is_final: z.boolean(),
226
+ /**
227
+ * Accumulated confirmed transcript (all finalized text received so far)
228
+ * Contains only the completed/finalized portions
229
+ * @example "hello world how are you"
230
+ */
231
+ confirmedTranscript: z.string().optional(),
232
+ /**
233
+ * New pending transcript (current interim text since last confirmation)
234
+ * Contains only the unconfirmed interim text
235
+ * @example "I'm doing"
236
+ */
237
+ newPendingTranscript: z.string().optional(),
238
+ /**
239
+ * Whether this is a fallback transcript (forced due to timeout)
240
+ * True when provider didn't send is_final=true within expected timeframe
241
+ * Used for monitoring/debugging fallback scenarios
242
+ * @example true
243
+ * @default false
244
+ */
245
+ is_fallback: z.boolean().optional()
218
246
  });
219
247
  var VADEndSignalSchema = z.object({
220
248
  type: z.literal(ProviderMessageType.VAD_END_SIGNAL),
@@ -302,7 +330,20 @@ var TimerSchema = z.object({
302
330
  * Total duration of all audio chunks sent to this provider session
303
331
  * @example 2500 (2.5 seconds of audio has been sent)
304
332
  */
305
- accumulatedAudioTimeMs: z.number().optional()
333
+ accumulatedAudioTimeMs: z.number().optional(),
334
+ /**
335
+ * Estimated cost in USD for this session
336
+ * Calculated by the job based on audio duration and provider pricing
337
+ * @example 0.0025 (quarter of a cent)
338
+ */
339
+ costInUSD: z.number().optional().default(0),
340
+ /**
341
+ * ASR API type from the job
342
+ * - STREAMING: Real-time streaming APIs (Deepgram, AssemblyAI, Google)
343
+ * - FILE_BASED: File upload/batch APIs (OpenAI Batch, Gemini Batch)
344
+ * @example ASRApiType.STREAMING
345
+ */
346
+ apiType: z.nativeEnum(ASRApiType).optional()
306
347
  });
307
348
  var RawMessageSchema = z.object({
308
349
  type: z.literal(ProviderMessageType.RAW),
@@ -391,6 +432,16 @@ var QuotaExceededExceptionSchema = BaseRecognitionExceptionSchema.extend({
391
432
  /** How long to wait in seconds before retry */
392
433
  retryAfterSeconds: z.number().optional()
393
434
  });
435
+ var ConnectionExceptionSchema = BaseRecognitionExceptionSchema.extend({
436
+ errorType: z.literal(ErrorTypeV1.CONNECTION_ERROR),
437
+ isImmediatelyAvailable: z.literal(true),
438
+ /** Number of connection attempts made */
439
+ attempts: z.number().optional(),
440
+ /** URL that failed to connect */
441
+ url: z.string().optional(),
442
+ /** Underlying error message */
443
+ underlyingError: z.string().optional()
444
+ });
394
445
  var UnknownExceptionSchema = BaseRecognitionExceptionSchema.extend({
395
446
  errorType: z.literal(ErrorTypeV1.UNKNOWN_ERROR),
396
447
  isImmediatelyAvailable: z.literal(false),
@@ -405,8 +456,32 @@ z.discriminatedUnion("errorType", [
405
456
  ProviderExceptionSchema,
406
457
  TimeoutExceptionSchema,
407
458
  QuotaExceededExceptionSchema,
459
+ ConnectionExceptionSchema,
408
460
  UnknownExceptionSchema
409
461
  ]);
462
+ function isExceptionImmediatelyAvailable(exception) {
463
+ return exception.isImmediatelyAvailable;
464
+ }
465
+ __name(isExceptionImmediatelyAvailable, "isExceptionImmediatelyAvailable");
466
+ function getUserFriendlyMessage(exception) {
467
+ if (!exception.isImmediatelyAvailable) {
468
+ return "An error occurred. Please try again or contact support.";
469
+ }
470
+ switch (exception.errorType) {
471
+ case ErrorTypeV1.VALIDATION_ERROR:
472
+ return exception.message || "Invalid input. Please check your request.";
473
+ case ErrorTypeV1.TIMEOUT_ERROR:
474
+ return exception.message || "Request timed out. Please try again.";
475
+ case ErrorTypeV1.QUOTA_EXCEEDED:
476
+ if (exception.retryAfterSeconds) {
477
+ return `Rate limit exceeded. Please try again in ${exception.retryAfterSeconds} seconds.`;
478
+ }
479
+ return exception.message || "Rate limit exceeded. Please try again later.";
480
+ case ErrorTypeV1.CONNECTION_ERROR:
481
+ return exception.message || "Connection failed. Please check your network and try again.";
482
+ }
483
+ }
484
+ __name(getUserFriendlyMessage, "getUserFriendlyMessage");
410
485
  var RecognitionContextTypeV1;
411
486
  (function(RecognitionContextTypeV12) {
412
487
  RecognitionContextTypeV12["GAME_CONTEXT"] = "GameContext";
@@ -948,6 +1023,11 @@ var STAGES = {
948
1023
  STAGING: "staging",
949
1024
  PRODUCTION: "production"
950
1025
  };
1026
+ [
1027
+ STAGES.LOCAL,
1028
+ STAGES.DEV,
1029
+ STAGES.STAGING
1030
+ ];
951
1031
 
952
1032
  // ../../libs/websocket/dist/core/audio-upload-websocket-protocol.js
953
1033
  var WebSocketCloseCode;
@@ -1209,8 +1289,16 @@ __name(getRecognitionConductorHost, "getRecognitionConductorHost");
1209
1289
 
1210
1290
  // src/utils/url-builder.ts
1211
1291
  function buildWebSocketUrl(config) {
1212
- const defaultBase = getRecognitionServiceBase("production");
1213
- const baseUrl = config.url || `${defaultBase.wsBase}/ws/v1/recognize`;
1292
+ let baseUrl;
1293
+ if (config.url) {
1294
+ baseUrl = config.url;
1295
+ } else if (config.stage) {
1296
+ const stageBase = getRecognitionServiceBase(config.stage);
1297
+ baseUrl = `${stageBase.wsBase}/ws/v1/recognize`;
1298
+ } else {
1299
+ const defaultBase = getRecognitionServiceBase("production");
1300
+ baseUrl = `${defaultBase.wsBase}/ws/v1/recognize`;
1301
+ }
1214
1302
  const url = new URL(baseUrl);
1215
1303
  url.searchParams.set("audioUtteranceId", config.audioUtteranceId);
1216
1304
  if (config.callbackUrls && config.callbackUrls.length > 0) {
@@ -1466,6 +1554,75 @@ var MessageHandler = class {
1466
1554
  }
1467
1555
  };
1468
1556
 
1557
+ // src/errors.ts
1558
+ var RecognitionError = class extends Error {
1559
+ static {
1560
+ __name(this, "RecognitionError");
1561
+ }
1562
+ errorType;
1563
+ timestamp;
1564
+ constructor(errorType, message) {
1565
+ super(message);
1566
+ this.name = "RecognitionError";
1567
+ this.errorType = errorType;
1568
+ this.timestamp = Date.now();
1569
+ if (Error.captureStackTrace) {
1570
+ Error.captureStackTrace(this, this.constructor);
1571
+ }
1572
+ }
1573
+ };
1574
+ var ConnectionError = class extends RecognitionError {
1575
+ static {
1576
+ __name(this, "ConnectionError");
1577
+ }
1578
+ attempts;
1579
+ url;
1580
+ underlyingError;
1581
+ constructor(message, attempts, url, underlyingError) {
1582
+ super(ErrorTypeV1.CONNECTION_ERROR, message);
1583
+ this.name = "ConnectionError";
1584
+ this.attempts = attempts;
1585
+ this.url = url;
1586
+ if (underlyingError !== void 0) {
1587
+ this.underlyingError = underlyingError;
1588
+ }
1589
+ }
1590
+ };
1591
+ var TimeoutError = class extends RecognitionError {
1592
+ static {
1593
+ __name(this, "TimeoutError");
1594
+ }
1595
+ timeoutMs;
1596
+ operation;
1597
+ constructor(message, timeoutMs, operation) {
1598
+ super(ErrorTypeV1.TIMEOUT_ERROR, message);
1599
+ this.name = "TimeoutError";
1600
+ this.timeoutMs = timeoutMs;
1601
+ this.operation = operation;
1602
+ }
1603
+ };
1604
+ var ValidationError = class extends RecognitionError {
1605
+ static {
1606
+ __name(this, "ValidationError");
1607
+ }
1608
+ field;
1609
+ expected;
1610
+ received;
1611
+ constructor(message, field, expected, received) {
1612
+ super(ErrorTypeV1.VALIDATION_ERROR, message);
1613
+ this.name = "ValidationError";
1614
+ if (field !== void 0) {
1615
+ this.field = field;
1616
+ }
1617
+ if (expected !== void 0) {
1618
+ this.expected = expected;
1619
+ }
1620
+ if (received !== void 0) {
1621
+ this.received = received;
1622
+ }
1623
+ }
1624
+ };
1625
+
1469
1626
  // src/recognition-client.ts
1470
1627
  function isNormalDisconnection(code) {
1471
1628
  return code === 1e3;
@@ -1507,6 +1664,9 @@ var RealTimeTwoWayWebSocketRecognitionClient = class _RealTimeTwoWayWebSocketRec
1507
1664
  ...config.url && {
1508
1665
  url: config.url
1509
1666
  },
1667
+ ...config.stage && {
1668
+ stage: config.stage
1669
+ },
1510
1670
  ...config.callbackUrls && {
1511
1671
  callbackUrls: config.callbackUrls
1512
1672
  },
@@ -1537,6 +1697,9 @@ var RealTimeTwoWayWebSocketRecognitionClient = class _RealTimeTwoWayWebSocketRec
1537
1697
  highWM: config.highWaterMark ?? 512e3,
1538
1698
  lowWM: config.lowWaterMark ?? 128e3
1539
1699
  });
1700
+ const retryConfig = config.connectionRetry || {};
1701
+ const maxAttempts = Math.max(1, Math.min(5, retryConfig.maxAttempts ?? 4));
1702
+ const delayMs = retryConfig.delayMs ?? 200;
1540
1703
  this.config = {
1541
1704
  url,
1542
1705
  audioUtteranceId,
@@ -1565,6 +1728,10 @@ var RealTimeTwoWayWebSocketRecognitionClient = class _RealTimeTwoWayWebSocketRec
1565
1728
  lowWaterMark: config.lowWaterMark ?? 128e3,
1566
1729
  maxBufferDurationSec: config.maxBufferDurationSec ?? 60,
1567
1730
  chunksPerSecond: config.chunksPerSecond ?? 100,
1731
+ connectionRetry: {
1732
+ maxAttempts,
1733
+ delayMs
1734
+ },
1568
1735
  ...config.logger && {
1569
1736
  logger: config.logger
1570
1737
  }
@@ -1622,9 +1789,8 @@ var RealTimeTwoWayWebSocketRecognitionClient = class _RealTimeTwoWayWebSocketRec
1622
1789
  // ==========================================================================
1623
1790
  async connect() {
1624
1791
  if (this.connectionPromise) {
1625
- this.log("debug", "Returning existing connection promise", {
1626
- state: this.state,
1627
- hasPromise: true
1792
+ this.log("debug", "Returning existing connection promise (already connecting)", {
1793
+ state: this.state
1628
1794
  });
1629
1795
  return this.connectionPromise;
1630
1796
  }
@@ -1634,43 +1800,97 @@ var RealTimeTwoWayWebSocketRecognitionClient = class _RealTimeTwoWayWebSocketRec
1634
1800
  });
1635
1801
  return Promise.resolve();
1636
1802
  }
1637
- this.log("debug", "Creating new connection to WebSocket", {
1638
- url: this.config.url
1639
- });
1640
- this.state = ClientState.CONNECTING;
1641
- const connectionStartTime = Date.now();
1642
- this.connectionPromise = new Promise((resolve, reject) => {
1643
- const timeout = setTimeout(() => {
1644
- this.log("warn", "Connection timeout", {
1645
- timeout: 1e4
1646
- });
1647
- this.state = ClientState.FAILED;
1648
- reject(new Error("Timeout"));
1649
- }, 1e4);
1650
- const originalOnConnected = this.onConnected.bind(this);
1651
- this.onConnected = () => {
1652
- clearTimeout(timeout);
1653
- const connectionTime = Date.now() - connectionStartTime;
1654
- this.log("debug", "Connection established successfully", {
1655
- connectionTimeMs: connectionTime,
1656
- url: this.config.url
1657
- });
1658
- this.state = ClientState.CONNECTED;
1659
- originalOnConnected();
1660
- resolve();
1661
- };
1662
- const originalOnError = this.onError.bind(this);
1663
- this.onError = (error) => {
1664
- clearTimeout(timeout);
1665
- this.log("warn", "Connection error", error);
1666
- this.state = ClientState.FAILED;
1667
- originalOnError(error);
1668
- reject(error);
1669
- };
1670
- super.connect();
1671
- });
1803
+ this.connectionPromise = this.connectWithRetry();
1672
1804
  return this.connectionPromise;
1673
1805
  }
1806
+ /**
1807
+ * Attempt to connect with retry logic
1808
+ * Only retries on initial connection establishment, not mid-stream interruptions
1809
+ */
1810
+ async connectWithRetry() {
1811
+ const { maxAttempts, delayMs } = this.config.connectionRetry;
1812
+ const connectionTimeout = 1e4;
1813
+ let lastError;
1814
+ const originalOnConnected = this.onConnected.bind(this);
1815
+ const originalOnError = this.onError.bind(this);
1816
+ try {
1817
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1818
+ const attemptLogLevel = attempt === 1 ? "debug" : "info";
1819
+ this.log(attemptLogLevel, `Connection attempt ${attempt}/${maxAttempts}`, {
1820
+ url: this.config.url,
1821
+ delayMs: attempt > 1 ? delayMs : 0
1822
+ });
1823
+ this.state = ClientState.CONNECTING;
1824
+ const connectionStartTime = Date.now();
1825
+ try {
1826
+ await new Promise((resolve, reject) => {
1827
+ let settled = false;
1828
+ const timeout = setTimeout(() => {
1829
+ if (settled) return;
1830
+ settled = true;
1831
+ this.log("warn", "Connection timeout", {
1832
+ timeout: connectionTimeout,
1833
+ attempt
1834
+ });
1835
+ this.state = ClientState.FAILED;
1836
+ reject(new Error(`Connection timeout after ${connectionTimeout}ms`));
1837
+ }, connectionTimeout);
1838
+ this.onConnected = () => {
1839
+ if (settled) return;
1840
+ settled = true;
1841
+ clearTimeout(timeout);
1842
+ const connectionTime = Date.now() - connectionStartTime;
1843
+ this.log("debug", "Connection established successfully", {
1844
+ connectionTimeMs: connectionTime,
1845
+ url: this.config.url,
1846
+ attempt
1847
+ });
1848
+ this.state = ClientState.CONNECTED;
1849
+ originalOnConnected();
1850
+ resolve();
1851
+ };
1852
+ this.onError = (error) => {
1853
+ if (settled) return;
1854
+ settled = true;
1855
+ clearTimeout(timeout);
1856
+ this.log("warn", "Connection error", {
1857
+ error,
1858
+ attempt
1859
+ });
1860
+ this.state = ClientState.FAILED;
1861
+ reject(error);
1862
+ };
1863
+ super.connect();
1864
+ });
1865
+ const successLogLevel = attempt === 1 ? "debug" : "info";
1866
+ this.log(successLogLevel, `Connection successful on attempt ${attempt}`, {
1867
+ totalAttempts: attempt
1868
+ });
1869
+ return;
1870
+ } catch (error) {
1871
+ lastError = error;
1872
+ if (attempt < maxAttempts) {
1873
+ const logLevel = attempt < 3 ? "info" : "warn";
1874
+ this.log(logLevel, `Connection attempt ${attempt} failed, retrying after ${delayMs}ms`, {
1875
+ error: lastError.message,
1876
+ nextAttempt: attempt + 1
1877
+ });
1878
+ this.state = ClientState.INITIAL;
1879
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1880
+ } else {
1881
+ this.log("warn", `All ${maxAttempts} connection attempts failed`, {
1882
+ error: lastError.message
1883
+ });
1884
+ }
1885
+ }
1886
+ }
1887
+ throw new ConnectionError(`Failed to establish connection after ${maxAttempts} attempts`, maxAttempts, this.config.url, lastError);
1888
+ } finally {
1889
+ this.onConnected = originalOnConnected;
1890
+ this.onError = originalOnError;
1891
+ this.connectionPromise = void 0;
1892
+ }
1893
+ }
1674
1894
  sendAudio(audioData) {
1675
1895
  if (audioData instanceof Blob) {
1676
1896
  blobToArrayBuffer(audioData).then((arrayBuffer) => {
@@ -1745,6 +1965,9 @@ var RealTimeTwoWayWebSocketRecognitionClient = class _RealTimeTwoWayWebSocketRec
1745
1965
  getAudioUtteranceId() {
1746
1966
  return this.config.audioUtteranceId;
1747
1967
  }
1968
+ getUrl() {
1969
+ return this.config.url;
1970
+ }
1748
1971
  getState() {
1749
1972
  return this.state;
1750
1973
  }
@@ -1897,13 +2120,27 @@ var ConfigBuilder = class {
1897
2120
  }
1898
2121
  config = {};
1899
2122
  /**
1900
- * Set the WebSocket URL
2123
+ * Set the WebSocket URL (advanced usage)
2124
+ * For standard environments, use stage() instead
1901
2125
  */
1902
2126
  url(url) {
1903
2127
  this.config.url = url;
1904
2128
  return this;
1905
2129
  }
1906
2130
  /**
2131
+ * Set the stage for automatic environment selection (recommended)
2132
+ * @param stage - STAGES.LOCAL | STAGES.DEV | STAGES.STAGING | STAGES.PRODUCTION
2133
+ * @example
2134
+ * ```typescript
2135
+ * import { STAGES } from '@recog/shared-types';
2136
+ * builder.stage(STAGES.STAGING)
2137
+ * ```
2138
+ */
2139
+ stage(stage) {
2140
+ this.config.stage = stage;
2141
+ return this;
2142
+ }
2143
+ /**
1907
2144
  * Set ASR request configuration
1908
2145
  */
1909
2146
  asrRequestConfig(config) {
@@ -2307,6 +2544,9 @@ var SimplifiedVGFRecognitionClient = class {
2307
2544
  getAudioUtteranceId() {
2308
2545
  return this.client.getAudioUtteranceId();
2309
2546
  }
2547
+ getUrl() {
2548
+ return this.client.getUrl();
2549
+ }
2310
2550
  getState() {
2311
2551
  return this.client.getState();
2312
2552
  }
@@ -2344,6 +2584,6 @@ function createSimplifiedVGFClient(config) {
2344
2584
  }
2345
2585
  __name(createSimplifiedVGFClient, "createSimplifiedVGFClient");
2346
2586
 
2347
- export { AudioEncoding, ClientState, ConfigBuilder, ControlSignalTypeV1 as ControlSignal, ControlSignalTypeV1, DeepgramModel, GeminiModel, GoogleModel, Language, OpenAIModel, RECOGNITION_CONDUCTOR_BASES, RECOGNITION_SERVICE_BASES, RealTimeTwoWayWebSocketRecognitionClient, RecognitionContextTypeV1, RecognitionProvider, RecognitionResultTypeV1, RecognitionVGFStateSchema, RecordingStatus, SampleRate, SimplifiedVGFRecognitionClient, TranscriptionStatus, createClient, createClientWithBuilder, createInitialRecognitionState, createSimplifiedVGFClient, getRecognitionConductorBase, getRecognitionConductorHost, getRecognitionConductorHttpBase, getRecognitionConductorWsBase, getRecognitionServiceBase, getRecognitionServiceHost, getRecognitionServiceHttpBase, getRecognitionServiceWsBase, isNormalDisconnection, isValidRecordingStatusTransition, normalizeStage };
2587
+ export { AudioEncoding, ClientState, ConfigBuilder, ConnectionError, ControlSignalTypeV1 as ControlSignal, ControlSignalTypeV1, DeepgramModel, ErrorTypeV1, GeminiModel, GoogleModel, Language, OpenAIModel, RECOGNITION_CONDUCTOR_BASES, RECOGNITION_SERVICE_BASES, RealTimeTwoWayWebSocketRecognitionClient, RecognitionContextTypeV1, RecognitionError, RecognitionProvider, RecognitionResultTypeV1, RecognitionVGFStateSchema, RecordingStatus, STAGES, SampleRate, SimplifiedVGFRecognitionClient, TimeoutError, TranscriptionStatus, ValidationError, createClient, createClientWithBuilder, createInitialRecognitionState, createSimplifiedVGFClient, getRecognitionConductorBase, getRecognitionConductorHost, getRecognitionConductorHttpBase, getRecognitionConductorWsBase, getRecognitionServiceBase, getRecognitionServiceHost, getRecognitionServiceHttpBase, getRecognitionServiceWsBase, getUserFriendlyMessage, isExceptionImmediatelyAvailable, isNormalDisconnection, isValidRecordingStatusTransition, normalizeStage };
2348
2588
  //# sourceMappingURL=index.js.map
2349
2589
  //# sourceMappingURL=index.js.map