@volley/recognition-client-sdk 0.1.384 → 0.1.417
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/browser.bundled.d.ts +23 -1
- package/dist/config-builder.d.ts +5 -0
- package/dist/config-builder.d.ts.map +1 -1
- package/dist/index.bundled.d.ts +108 -79
- package/dist/index.js +122 -46
- package/dist/index.js.map +4 -4
- package/dist/recog-client-sdk.browser.js +62 -23
- package/dist/recog-client-sdk.browser.js.map +4 -4
- package/dist/recognition-client.d.ts.map +1 -1
- package/dist/recognition-client.types.d.ts +6 -0
- package/dist/recognition-client.types.d.ts.map +1 -1
- package/dist/simplified-vgf-recognition-client.d.ts +2 -0
- package/dist/simplified-vgf-recognition-client.d.ts.map +1 -1
- package/dist/utils/url-builder.d.ts +2 -0
- package/dist/utils/url-builder.d.ts.map +1 -1
- package/dist/vgf-recognition-mapper.d.ts +1 -5
- package/dist/vgf-recognition-mapper.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/config-builder.ts +9 -0
- package/src/recognition-client.ts +3 -2
- package/src/recognition-client.types.ts +7 -0
- package/src/simplified-vgf-recognition-client.integration.spec.ts +704 -0
- package/src/simplified-vgf-recognition-client.spec.ts +199 -13
- package/src/simplified-vgf-recognition-client.ts +70 -22
- package/src/utils/audio-ring-buffer.ts +2 -2
- package/src/utils/message-handler.ts +4 -4
- package/src/utils/url-builder.ts +10 -3
- package/src/vgf-recognition-mapper.ts +1 -23
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { SimplifiedVGFRecognitionClient, createSimplifiedVGFClient } from './simplified-vgf-recognition-client.js';
|
|
6
6
|
import { RealTimeTwoWayWebSocketRecognitionClient } from './recognition-client.js';
|
|
7
7
|
import { ClientState } from './recognition-client.types.js';
|
|
8
|
-
import { AudioEncoding, RecognitionContextTypeV1 } from '@recog/shared-types';
|
|
8
|
+
import { AudioEncoding, RecognitionContextTypeV1, RecognitionResultTypeV1 } from '@recog/shared-types';
|
|
9
9
|
import {
|
|
10
10
|
RecordingStatus,
|
|
11
11
|
TranscriptionStatus,
|
|
@@ -287,21 +287,43 @@ describe('SimplifiedVGFRecognitionClient', () => {
|
|
|
287
287
|
expect(updatedState.finalTranscriptionTimestamp).toBeDefined();
|
|
288
288
|
});
|
|
289
289
|
|
|
290
|
-
it('should
|
|
290
|
+
it('should pass metadata to callback without updating VGF state', () => {
|
|
291
291
|
// Get the actual UUID from the client
|
|
292
292
|
const actualUuid = simplifiedClient.getVGFState().audioUtteranceId;
|
|
293
|
+
const originalOnMetadata = jest.fn();
|
|
293
294
|
|
|
295
|
+
// Create new client with onMetadata callback
|
|
296
|
+
const clientWithMetadata = new SimplifiedVGFRecognitionClient({
|
|
297
|
+
asrRequestConfig: {
|
|
298
|
+
provider: 'deepgram',
|
|
299
|
+
language: 'en',
|
|
300
|
+
sampleRate: 16000,
|
|
301
|
+
encoding: AudioEncoding.LINEAR16
|
|
302
|
+
},
|
|
303
|
+
onStateChange: stateChangeCallback,
|
|
304
|
+
onMetadata: originalOnMetadata
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const constructorCalls = (RealTimeTwoWayWebSocketRecognitionClient as jest.MockedClass<typeof RealTimeTwoWayWebSocketRecognitionClient>).mock.calls;
|
|
308
|
+
const latestConfig = constructorCalls[constructorCalls.length - 1]?.[0];
|
|
309
|
+
const metadataCallback = latestConfig?.onMetadata;
|
|
310
|
+
|
|
311
|
+
const clientUuid = clientWithMetadata.getVGFState().audioUtteranceId;
|
|
294
312
|
const metadata = {
|
|
295
|
-
|
|
296
|
-
|
|
313
|
+
type: RecognitionResultTypeV1.METADATA as const,
|
|
314
|
+
audioUtteranceId: clientUuid
|
|
297
315
|
};
|
|
298
316
|
|
|
299
|
-
|
|
317
|
+
// Clear previous state changes from client creation
|
|
318
|
+
stateChangeCallback.mockClear();
|
|
300
319
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
expect(
|
|
320
|
+
metadataCallback?.(metadata);
|
|
321
|
+
|
|
322
|
+
// Metadata should NOT trigger state change (state management simplified)
|
|
323
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
324
|
+
|
|
325
|
+
// But original callback should still be called
|
|
326
|
+
expect(originalOnMetadata).toHaveBeenCalledWith(metadata);
|
|
305
327
|
});
|
|
306
328
|
|
|
307
329
|
it('should handle errors and update state', () => {
|
|
@@ -325,13 +347,18 @@ describe('SimplifiedVGFRecognitionClient', () => {
|
|
|
325
347
|
// Then error occurs
|
|
326
348
|
onErrorCallback({ message: 'Error' });
|
|
327
349
|
|
|
328
|
-
//
|
|
350
|
+
// After terminal status (ERROR), no more state changes should be emitted
|
|
351
|
+
// The session is considered over, so sendAudio should not trigger callbacks
|
|
329
352
|
stateChangeCallback.mockClear();
|
|
330
353
|
simplifiedClient.sendAudio(Buffer.from([4, 5, 6]));
|
|
331
354
|
|
|
332
|
-
|
|
333
|
-
expect(
|
|
334
|
-
|
|
355
|
+
// No callback should be triggered since we're in terminal state
|
|
356
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
357
|
+
|
|
358
|
+
// However, the internal isRecordingAudio flag should still be reset
|
|
359
|
+
// (verified by the fact that the flag was set during the first sendAudio)
|
|
360
|
+
const state = simplifiedClient.getVGFState();
|
|
361
|
+
expect(state.transcriptionStatus).toBe(TranscriptionStatus.ERROR);
|
|
335
362
|
});
|
|
336
363
|
});
|
|
337
364
|
|
|
@@ -1331,4 +1358,163 @@ describe('SimplifiedVGFRecognitionClient', () => {
|
|
|
1331
1358
|
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
1332
1359
|
});
|
|
1333
1360
|
});
|
|
1361
|
+
|
|
1362
|
+
describe('Terminal Status Protection', () => {
|
|
1363
|
+
let stateChangeCallback: jest.Mock;
|
|
1364
|
+
let onTranscriptCallback: (result: any) => void;
|
|
1365
|
+
let onErrorCallback: (error: any) => void;
|
|
1366
|
+
let simplifiedClient: SimplifiedVGFRecognitionClient;
|
|
1367
|
+
let clientUuid: string;
|
|
1368
|
+
|
|
1369
|
+
beforeEach(() => {
|
|
1370
|
+
stateChangeCallback = jest.fn();
|
|
1371
|
+
simplifiedClient = new SimplifiedVGFRecognitionClient({
|
|
1372
|
+
asrRequestConfig: {
|
|
1373
|
+
provider: 'deepgram',
|
|
1374
|
+
language: 'en',
|
|
1375
|
+
sampleRate: 16000,
|
|
1376
|
+
encoding: AudioEncoding.LINEAR16
|
|
1377
|
+
},
|
|
1378
|
+
onStateChange: stateChangeCallback
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
const constructorCalls = (RealTimeTwoWayWebSocketRecognitionClient as jest.MockedClass<typeof RealTimeTwoWayWebSocketRecognitionClient>).mock.calls;
|
|
1382
|
+
const latestConfig = constructorCalls[constructorCalls.length - 1]?.[0];
|
|
1383
|
+
onTranscriptCallback = latestConfig?.onTranscript ?? jest.fn();
|
|
1384
|
+
onErrorCallback = latestConfig?.onError ?? jest.fn();
|
|
1385
|
+
clientUuid = simplifiedClient.getVGFState().audioUtteranceId;
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
it('should allow first terminal transcript callback', () => {
|
|
1389
|
+
onTranscriptCallback({
|
|
1390
|
+
type: 'Transcription',
|
|
1391
|
+
audioUtteranceId: clientUuid,
|
|
1392
|
+
finalTranscript: 'hello',
|
|
1393
|
+
finalTranscriptConfidence: 0.9,
|
|
1394
|
+
is_finished: true
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(1);
|
|
1398
|
+
expect(stateChangeCallback.mock.calls[0][0].transcriptionStatus).toBe(TranscriptionStatus.FINALIZED);
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
it('should block second terminal transcript callback with same UUID', () => {
|
|
1402
|
+
// First terminal
|
|
1403
|
+
onTranscriptCallback({
|
|
1404
|
+
type: 'Transcription',
|
|
1405
|
+
audioUtteranceId: clientUuid,
|
|
1406
|
+
finalTranscript: 'first',
|
|
1407
|
+
finalTranscriptConfidence: 0.9,
|
|
1408
|
+
is_finished: true
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
stateChangeCallback.mockClear();
|
|
1412
|
+
|
|
1413
|
+
// Second terminal - should be blocked
|
|
1414
|
+
onTranscriptCallback({
|
|
1415
|
+
type: 'Transcription',
|
|
1416
|
+
audioUtteranceId: clientUuid,
|
|
1417
|
+
finalTranscript: 'second',
|
|
1418
|
+
finalTranscriptConfidence: 0.95,
|
|
1419
|
+
is_finished: true
|
|
1420
|
+
});
|
|
1421
|
+
|
|
1422
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
it('should allow first error callback', () => {
|
|
1426
|
+
onErrorCallback({
|
|
1427
|
+
audioUtteranceId: clientUuid,
|
|
1428
|
+
message: 'Error occurred'
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(1);
|
|
1432
|
+
expect(stateChangeCallback.mock.calls[0][0].transcriptionStatus).toBe(TranscriptionStatus.ERROR);
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
it('should block second error callback with same UUID', () => {
|
|
1436
|
+
// First error
|
|
1437
|
+
onErrorCallback({
|
|
1438
|
+
audioUtteranceId: clientUuid,
|
|
1439
|
+
message: 'First error'
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
stateChangeCallback.mockClear();
|
|
1443
|
+
|
|
1444
|
+
// Second error - should be blocked
|
|
1445
|
+
onErrorCallback({
|
|
1446
|
+
audioUtteranceId: clientUuid,
|
|
1447
|
+
message: 'Second error'
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
it('should block transcript after error', () => {
|
|
1454
|
+
// Error first
|
|
1455
|
+
onErrorCallback({
|
|
1456
|
+
audioUtteranceId: clientUuid,
|
|
1457
|
+
message: 'Error'
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
stateChangeCallback.mockClear();
|
|
1461
|
+
|
|
1462
|
+
// Transcript after error - should be blocked
|
|
1463
|
+
onTranscriptCallback({
|
|
1464
|
+
type: 'Transcription',
|
|
1465
|
+
audioUtteranceId: clientUuid,
|
|
1466
|
+
finalTranscript: 'late',
|
|
1467
|
+
finalTranscriptConfidence: 0.9,
|
|
1468
|
+
is_finished: true
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
it('should block error after terminal transcript', () => {
|
|
1475
|
+
// Terminal transcript first
|
|
1476
|
+
onTranscriptCallback({
|
|
1477
|
+
type: 'Transcription',
|
|
1478
|
+
audioUtteranceId: clientUuid,
|
|
1479
|
+
finalTranscript: 'done',
|
|
1480
|
+
finalTranscriptConfidence: 0.9,
|
|
1481
|
+
is_finished: true
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
stateChangeCallback.mockClear();
|
|
1485
|
+
|
|
1486
|
+
// Error after terminal - should be blocked
|
|
1487
|
+
onErrorCallback({
|
|
1488
|
+
audioUtteranceId: clientUuid,
|
|
1489
|
+
message: 'Late error'
|
|
1490
|
+
});
|
|
1491
|
+
|
|
1492
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
it('should preserve original state after blocking duplicate', () => {
|
|
1496
|
+
// First terminal with specific transcript
|
|
1497
|
+
onTranscriptCallback({
|
|
1498
|
+
type: 'Transcription',
|
|
1499
|
+
audioUtteranceId: clientUuid,
|
|
1500
|
+
finalTranscript: 'original',
|
|
1501
|
+
finalTranscriptConfidence: 0.85,
|
|
1502
|
+
is_finished: true
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
// Attempt second terminal with different transcript
|
|
1506
|
+
onTranscriptCallback({
|
|
1507
|
+
type: 'Transcription',
|
|
1508
|
+
audioUtteranceId: clientUuid,
|
|
1509
|
+
finalTranscript: 'different',
|
|
1510
|
+
finalTranscriptConfidence: 0.99,
|
|
1511
|
+
is_finished: true
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
// State should still have original values
|
|
1515
|
+
const state = simplifiedClient.getVGFState();
|
|
1516
|
+
expect(state.finalTranscript).toBe('original');
|
|
1517
|
+
expect(state.finalConfidence).toBe(0.85);
|
|
1518
|
+
});
|
|
1519
|
+
});
|
|
1334
1520
|
});
|
|
@@ -23,7 +23,6 @@ import { RealTimeTwoWayWebSocketRecognitionClient } from './recognition-client.j
|
|
|
23
23
|
import {
|
|
24
24
|
createVGFStateFromConfig,
|
|
25
25
|
mapTranscriptionResultToState,
|
|
26
|
-
mapMetadataToState,
|
|
27
26
|
mapErrorToState,
|
|
28
27
|
updateStateOnStop
|
|
29
28
|
} from './vgf-recognition-mapper.js';
|
|
@@ -154,6 +153,7 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
154
153
|
private stateChangeCallback: ((state: RecognitionState) => void) | undefined;
|
|
155
154
|
private expectedUuid: string;
|
|
156
155
|
private logger: IRecognitionClientConfig['logger'];
|
|
156
|
+
private lastSentTerminalUuid: string | null = null;
|
|
157
157
|
|
|
158
158
|
constructor(config: SimplifiedVGFClientConfig) {
|
|
159
159
|
const { onStateChange, initialState, ...clientConfig } = config;
|
|
@@ -193,6 +193,9 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
193
193
|
// Use new UUID in client config
|
|
194
194
|
clientConfig.audioUtteranceId = newUUID;
|
|
195
195
|
|
|
196
|
+
// Reset terminal status tracking for new session
|
|
197
|
+
this.lastSentTerminalUuid = null;
|
|
198
|
+
|
|
196
199
|
// Notify state change immediately so app can update
|
|
197
200
|
if (onStateChange) {
|
|
198
201
|
onStateChange(this.state);
|
|
@@ -246,12 +249,19 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
246
249
|
if (result.audioUtteranceId && result.audioUtteranceId !== this.expectedUuid) {
|
|
247
250
|
if (this.logger) {
|
|
248
251
|
this.logger('warn',
|
|
249
|
-
`[VGF] Skipping transcript update: UUID mismatch (expected: ${this.expectedUuid}, got: ${result.audioUtteranceId})`
|
|
252
|
+
`[RecogSDK:VGF] Skipping transcript update: UUID mismatch (expected: ${this.expectedUuid}, got: ${result.audioUtteranceId})`
|
|
250
253
|
);
|
|
251
254
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Skip if terminal status already sent for THIS session
|
|
259
|
+
// (If lastSentTerminalUuid exists but doesn't match expectedUuid, treat as new session)
|
|
260
|
+
if (this.lastSentTerminalUuid === this.expectedUuid) {
|
|
261
|
+
if (this.logger) {
|
|
262
|
+
this.logger('info',
|
|
263
|
+
`[RecogSDK:VGF] Duplicate terminal status suppressed (lastSentTerminalUuid: ${this.lastSentTerminalUuid})`
|
|
264
|
+
);
|
|
255
265
|
}
|
|
256
266
|
return;
|
|
257
267
|
}
|
|
@@ -271,19 +281,12 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
271
281
|
if (metadata.audioUtteranceId && metadata.audioUtteranceId !== this.expectedUuid) {
|
|
272
282
|
if (this.logger) {
|
|
273
283
|
this.logger('warn',
|
|
274
|
-
`[VGF] Skipping metadata update: UUID mismatch (expected: ${this.expectedUuid}, got: ${metadata.audioUtteranceId})`
|
|
284
|
+
`[RecogSDK:VGF] Skipping metadata update: UUID mismatch (expected: ${this.expectedUuid}, got: ${metadata.audioUtteranceId})`
|
|
275
285
|
);
|
|
276
286
|
}
|
|
277
|
-
// Still call original callback if provided
|
|
278
|
-
if (clientConfig.onMetadata) {
|
|
279
|
-
clientConfig.onMetadata(metadata);
|
|
280
|
-
}
|
|
281
287
|
return;
|
|
282
288
|
}
|
|
283
289
|
|
|
284
|
-
this.state = mapMetadataToState(this.state, metadata);
|
|
285
|
-
this.notifyStateChange();
|
|
286
|
-
|
|
287
290
|
if (clientConfig.onMetadata) {
|
|
288
291
|
clientConfig.onMetadata(metadata);
|
|
289
292
|
}
|
|
@@ -301,13 +304,14 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
301
304
|
if (error.audioUtteranceId && error.audioUtteranceId !== this.expectedUuid) {
|
|
302
305
|
if (this.logger) {
|
|
303
306
|
this.logger('warn',
|
|
304
|
-
`[VGF] Skipping error update: UUID mismatch (expected: ${this.expectedUuid}, got: ${error.audioUtteranceId})`
|
|
307
|
+
`[RecogSDK:VGF] Skipping error update: UUID mismatch (expected: ${this.expectedUuid}, got: ${error.audioUtteranceId})`
|
|
305
308
|
);
|
|
306
309
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Skip if terminal status already sent for THIS session
|
|
314
|
+
if (this.lastSentTerminalUuid === this.expectedUuid) {
|
|
311
315
|
return;
|
|
312
316
|
}
|
|
313
317
|
|
|
@@ -362,6 +366,27 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
362
366
|
this.isRecordingAudio = false;
|
|
363
367
|
this.state = updateStateOnStop(this.state);
|
|
364
368
|
this.notifyStateChange();
|
|
369
|
+
|
|
370
|
+
// Early termination: If transcription never started (NOT_STARTED), emit synthetic finalization immediately
|
|
371
|
+
// This prevents games from getting stuck waiting for a server response that may never come
|
|
372
|
+
if (this.state.transcriptionStatus === TranscriptionStatus.NOT_STARTED) {
|
|
373
|
+
if (this.logger) {
|
|
374
|
+
this.logger('info',
|
|
375
|
+
`[RecogSDK:VGF] Early termination detected (transcriptionStatus: NOT_STARTED) - emitting synthetic finalization`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
this.state = {
|
|
379
|
+
...this.state,
|
|
380
|
+
transcriptionStatus: TranscriptionStatus.FINALIZED,
|
|
381
|
+
finalTranscript: '',
|
|
382
|
+
finalConfidence: 0,
|
|
383
|
+
pendingTranscript: '',
|
|
384
|
+
pendingConfidence: undefined,
|
|
385
|
+
finalTranscriptionTimestamp: new Date().toISOString()
|
|
386
|
+
};
|
|
387
|
+
this.notifyStateChange();
|
|
388
|
+
}
|
|
389
|
+
|
|
365
390
|
await this.client.stopRecording();
|
|
366
391
|
}
|
|
367
392
|
|
|
@@ -436,11 +461,34 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
436
461
|
return { ...this.state };
|
|
437
462
|
}
|
|
438
463
|
|
|
464
|
+
private isTerminalStatus(status: string | undefined): boolean {
|
|
465
|
+
return status === TranscriptionStatus.FINALIZED ||
|
|
466
|
+
status === TranscriptionStatus.ABORTED ||
|
|
467
|
+
status === TranscriptionStatus.ERROR;
|
|
468
|
+
}
|
|
469
|
+
|
|
439
470
|
private notifyStateChange(): void {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
471
|
+
|
|
472
|
+
// Block duplicate terminal status emissions for THIS session
|
|
473
|
+
if (this.isTerminalStatus(this.state.transcriptionStatus)) {
|
|
474
|
+
if (this.lastSentTerminalUuid === this.expectedUuid) {
|
|
475
|
+
// Already sent a terminal status for this session - suppress duplicate
|
|
476
|
+
if (this.logger) {
|
|
477
|
+
this.logger('info',
|
|
478
|
+
`[RecogSDK:VGF] Duplicate terminal status suppressed (lastSentTerminalUuid: ${this.lastSentTerminalUuid})`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
// First terminal status for this session - record it
|
|
484
|
+
this.lastSentTerminalUuid = this.expectedUuid;
|
|
443
485
|
}
|
|
486
|
+
|
|
487
|
+
if (!this.stateChangeCallback) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
this.stateChangeCallback({ ...this.state });
|
|
444
492
|
}
|
|
445
493
|
}
|
|
446
494
|
|
|
@@ -489,4 +537,4 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
489
537
|
*/
|
|
490
538
|
export function createSimplifiedVGFClient(config: SimplifiedVGFClientConfig): ISimplifiedVGFRecognitionClient {
|
|
491
539
|
return new SimplifiedVGFRecognitionClient(config);
|
|
492
|
-
}
|
|
540
|
+
}
|
|
@@ -55,7 +55,7 @@ export class AudioRingBuffer {
|
|
|
55
55
|
|
|
56
56
|
// Log buffer overflow event
|
|
57
57
|
if (this.logger) {
|
|
58
|
-
this.logger('debug', 'Buffer overflow detected', {
|
|
58
|
+
this.logger('debug', '[RecogSDK] Buffer overflow detected', {
|
|
59
59
|
bufferSize: this.bufferSize,
|
|
60
60
|
totalOverflows: this.overflowCount,
|
|
61
61
|
droppedChunk: this.buffer[this.readIndex]?.timestamp
|
|
@@ -151,7 +151,7 @@ export class AudioRingBuffer {
|
|
|
151
151
|
this.totalBufferedBytes = 0;
|
|
152
152
|
|
|
153
153
|
if (this.logger) {
|
|
154
|
-
this.logger('debug', 'Audio buffer cleared');
|
|
154
|
+
this.logger('debug', '[RecogSDK] Audio buffer cleared');
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -44,7 +44,7 @@ export class MessageHandler {
|
|
|
44
44
|
handleMessage(msg: { v: number; type: string; data: any }): void {
|
|
45
45
|
// Log ALL incoming messages for debugging
|
|
46
46
|
if (this.callbacks.logger) {
|
|
47
|
-
this.callbacks.logger('debug', 'Received WebSocket message', {
|
|
47
|
+
this.callbacks.logger('debug', '[RecogSDK] Received WebSocket message', {
|
|
48
48
|
msgType: msg.type,
|
|
49
49
|
msgDataType: msg.data && typeof msg.data === 'object' && 'type' in msg.data ? msg.data.type : 'N/A',
|
|
50
50
|
fullMessage: msg
|
|
@@ -55,7 +55,7 @@ export class MessageHandler {
|
|
|
55
55
|
// Log error if we receive primitive data (indicates server issue)
|
|
56
56
|
if (msg.data && typeof msg.data !== 'object') {
|
|
57
57
|
if (this.callbacks.logger) {
|
|
58
|
-
this.callbacks.logger('error', 'Received primitive msg.data from server', {
|
|
58
|
+
this.callbacks.logger('error', '[RecogSDK] Received primitive msg.data from server', {
|
|
59
59
|
dataType: typeof msg.data,
|
|
60
60
|
data: msg.data,
|
|
61
61
|
fullMessage: msg
|
|
@@ -90,7 +90,7 @@ export class MessageHandler {
|
|
|
90
90
|
default:
|
|
91
91
|
// Unknown message type - log if logger available
|
|
92
92
|
if (this.callbacks.logger) {
|
|
93
|
-
this.callbacks.logger('debug', 'Unknown message type', { type: msgType });
|
|
93
|
+
this.callbacks.logger('debug', '[RecogSDK] Unknown message type', { type: msgType });
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
}
|
|
@@ -106,7 +106,7 @@ export class MessageHandler {
|
|
|
106
106
|
const timeToFirstTranscript = this.firstTranscriptTime - this.sessionStartTime;
|
|
107
107
|
|
|
108
108
|
if (this.callbacks.logger) {
|
|
109
|
-
this.callbacks.logger('debug', 'First transcript received', {
|
|
109
|
+
this.callbacks.logger('debug', '[RecogSDK] First transcript received', {
|
|
110
110
|
timeToFirstTranscriptMs: timeToFirstTranscript
|
|
111
111
|
});
|
|
112
112
|
}
|
package/src/utils/url-builder.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface UrlBuilderConfig {
|
|
|
19
19
|
questionAnswerId?: string;
|
|
20
20
|
platform?: string;
|
|
21
21
|
gameContext?: GameContextV1;
|
|
22
|
+
/** Standalone gameId - takes precedence over gameContext.gameId if both provided */
|
|
23
|
+
gameId?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -75,9 +77,14 @@ export function buildWebSocketUrl(config: UrlBuilderConfig): string {
|
|
|
75
77
|
url.searchParams.set('platform', config.platform);
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
// Add gameId
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
// Add gameId - standalone gameId takes precedence over gameContext.gameId
|
|
81
|
+
const gameId = config.gameId ?? config.gameContext?.gameId;
|
|
82
|
+
if (gameId) {
|
|
83
|
+
url.searchParams.set('gameId', gameId);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add gamePhase from gameContext if provided
|
|
87
|
+
if (config.gameContext?.gamePhase) {
|
|
81
88
|
url.searchParams.set('gamePhase', config.gameContext.gamePhase);
|
|
82
89
|
}
|
|
83
90
|
|
|
@@ -17,9 +17,7 @@ import {
|
|
|
17
17
|
} from './recognition-client.types.js';
|
|
18
18
|
import {
|
|
19
19
|
TranscriptionResultV1,
|
|
20
|
-
|
|
21
|
-
ErrorResultV1,
|
|
22
|
-
ASRRequestConfig
|
|
20
|
+
ErrorResultV1
|
|
23
21
|
} from '@recog/shared-types';
|
|
24
22
|
|
|
25
23
|
/**
|
|
@@ -103,26 +101,6 @@ export function mapTranscriptionResultToState(
|
|
|
103
101
|
return newState;
|
|
104
102
|
}
|
|
105
103
|
|
|
106
|
-
/**
|
|
107
|
-
* Maps metadata result to update state timestamps
|
|
108
|
-
*/
|
|
109
|
-
export function mapMetadataToState(
|
|
110
|
-
currentState: RecognitionState,
|
|
111
|
-
metadata: MetadataResultV1
|
|
112
|
-
): RecognitionState {
|
|
113
|
-
const newState = { ...currentState };
|
|
114
|
-
|
|
115
|
-
// Update final recording timestamp when metadata arrives
|
|
116
|
-
if (!newState.finalRecordingTimestamp) {
|
|
117
|
-
newState.finalRecordingTimestamp = new Date().toISOString();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Recording is finished when metadata arrives
|
|
121
|
-
newState.startRecordingStatus = RecordingStatus.FINISHED;
|
|
122
|
-
|
|
123
|
-
return newState;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
104
|
/**
|
|
127
105
|
* Maps error to state
|
|
128
106
|
*/
|