@volley/recognition-client-sdk 0.1.255 → 0.1.294
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.d.ts +10 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/config-builder.d.ts +129 -0
- package/dist/config-builder.d.ts.map +1 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/factory.d.ts +36 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/index.d.ts +15 -1079
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4293 -645
- package/dist/index.js.map +7 -1
- package/dist/recog-client-sdk.browser.d.ts +10 -2
- package/dist/recog-client-sdk.browser.d.ts.map +1 -0
- package/dist/recog-client-sdk.browser.js +4127 -525
- package/dist/recog-client-sdk.browser.js.map +7 -1
- package/dist/recognition-client.d.ts +120 -0
- package/dist/recognition-client.d.ts.map +1 -0
- package/dist/recognition-client.types.d.ts +265 -0
- package/dist/recognition-client.types.d.ts.map +1 -0
- package/dist/simplified-vgf-recognition-client.d.ts +174 -0
- package/dist/simplified-vgf-recognition-client.d.ts.map +1 -0
- package/dist/utils/audio-ring-buffer.d.ts +69 -0
- package/dist/utils/audio-ring-buffer.d.ts.map +1 -0
- package/dist/utils/message-handler.d.ts +45 -0
- package/dist/utils/message-handler.d.ts.map +1 -0
- package/dist/utils/url-builder.d.ts +26 -0
- package/dist/utils/url-builder.d.ts.map +1 -0
- package/dist/vgf-recognition-mapper.d.ts +53 -0
- package/dist/vgf-recognition-mapper.d.ts.map +1 -0
- package/dist/vgf-recognition-state.d.ts +82 -0
- package/dist/vgf-recognition-state.d.ts.map +1 -0
- package/package.json +7 -8
- package/src/index.ts +4 -0
- package/src/recognition-client.spec.ts +147 -14
- package/src/recognition-client.ts +27 -0
- package/src/recognition-client.types.ts +19 -0
- package/src/simplified-vgf-recognition-client.spec.ts +246 -0
- package/src/simplified-vgf-recognition-client.ts +58 -1
- package/src/utils/url-builder.spec.ts +5 -3
- package/src/vgf-recognition-state.ts +2 -1
- package/dist/browser-BZs4BL_w.d.ts +0 -1118
|
@@ -29,6 +29,7 @@ describe('SimplifiedVGFRecognitionClient', () => {
|
|
|
29
29
|
connect: jest.fn().mockResolvedValue(undefined),
|
|
30
30
|
sendAudio: jest.fn(),
|
|
31
31
|
stopRecording: jest.fn().mockResolvedValue(undefined),
|
|
32
|
+
stopAbnormally: jest.fn(),
|
|
32
33
|
getAudioUtteranceId: jest.fn().mockReturnValue('test-uuid'),
|
|
33
34
|
getState: jest.fn().mockReturnValue(ClientState.INITIAL),
|
|
34
35
|
isConnected: jest.fn().mockReturnValue(false),
|
|
@@ -674,4 +675,249 @@ describe('SimplifiedVGFRecognitionClient', () => {
|
|
|
674
675
|
expect(callbackState).not.toBe(currentState); // Different references
|
|
675
676
|
});
|
|
676
677
|
});
|
|
678
|
+
|
|
679
|
+
describe('stopAbnormally', () => {
|
|
680
|
+
beforeEach(() => {
|
|
681
|
+
simplifiedClient = new SimplifiedVGFRecognitionClient({
|
|
682
|
+
asrRequestConfig: {
|
|
683
|
+
provider: 'deepgram',
|
|
684
|
+
language: 'en',
|
|
685
|
+
sampleRate: 16000,
|
|
686
|
+
encoding: AudioEncoding.LINEAR16
|
|
687
|
+
},
|
|
688
|
+
onStateChange: stateChangeCallback
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('should immediately set state to ABORTED with empty transcript', () => {
|
|
693
|
+
// Start recording first
|
|
694
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
695
|
+
jest.clearAllMocks();
|
|
696
|
+
|
|
697
|
+
// Call stopAbnormally
|
|
698
|
+
simplifiedClient.stopAbnormally();
|
|
699
|
+
|
|
700
|
+
// Verify state was updated to ABORTED (not FINALIZED)
|
|
701
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(1);
|
|
702
|
+
const finalState = stateChangeCallback.mock.calls[0][0];
|
|
703
|
+
|
|
704
|
+
expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
705
|
+
expect(finalState.finalTranscript).toBe('');
|
|
706
|
+
expect(finalState.startRecordingStatus).toBe(RecordingStatus.FINISHED);
|
|
707
|
+
expect(finalState.finalRecordingTimestamp).toBeDefined();
|
|
708
|
+
expect(finalState.finalTranscriptionTimestamp).toBeDefined();
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('should stop recording audio flag', () => {
|
|
712
|
+
// Start recording
|
|
713
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
714
|
+
|
|
715
|
+
// Call stopAbnormally
|
|
716
|
+
simplifiedClient.stopAbnormally();
|
|
717
|
+
|
|
718
|
+
// Send more audio - should not update recording status again
|
|
719
|
+
jest.clearAllMocks();
|
|
720
|
+
simplifiedClient.sendAudio(Buffer.from([4, 5, 6]));
|
|
721
|
+
|
|
722
|
+
// Verify recording status was set in sendAudio
|
|
723
|
+
const state = simplifiedClient.getVGFState();
|
|
724
|
+
expect(state.startRecordingStatus).toBe(RecordingStatus.RECORDING);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('should be idempotent - calling twice does not change state again', () => {
|
|
728
|
+
// Start recording
|
|
729
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
730
|
+
jest.clearAllMocks();
|
|
731
|
+
|
|
732
|
+
// Call stopAbnormally first time
|
|
733
|
+
simplifiedClient.stopAbnormally();
|
|
734
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(1);
|
|
735
|
+
|
|
736
|
+
const firstCallState = stateChangeCallback.mock.calls[0][0];
|
|
737
|
+
jest.clearAllMocks();
|
|
738
|
+
|
|
739
|
+
// Call stopAbnormally second time
|
|
740
|
+
simplifiedClient.stopAbnormally();
|
|
741
|
+
|
|
742
|
+
// Should not trigger state change callback again (already aborted)
|
|
743
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(0);
|
|
744
|
+
|
|
745
|
+
const currentState = simplifiedClient.getVGFState();
|
|
746
|
+
expect(currentState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
747
|
+
expect(currentState.finalTranscript).toBe('');
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('should work even if called before any recording', () => {
|
|
751
|
+
// Call stopAbnormally without ever recording
|
|
752
|
+
simplifiedClient.stopAbnormally();
|
|
753
|
+
|
|
754
|
+
const state = simplifiedClient.getVGFState();
|
|
755
|
+
expect(state.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
756
|
+
expect(state.finalTranscript).toBe('');
|
|
757
|
+
expect(state.startRecordingStatus).toBe(RecordingStatus.FINISHED);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it('should preserve existing state fields except for overridden ones', () => {
|
|
761
|
+
// Set up some initial state by sending audio
|
|
762
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
763
|
+
|
|
764
|
+
const initialState = simplifiedClient.getVGFState();
|
|
765
|
+
const audioUtteranceId = initialState.audioUtteranceId;
|
|
766
|
+
|
|
767
|
+
// Call stopAbnormally
|
|
768
|
+
simplifiedClient.stopAbnormally();
|
|
769
|
+
|
|
770
|
+
const finalState = simplifiedClient.getVGFState();
|
|
771
|
+
|
|
772
|
+
// Should preserve audioUtteranceId and other non-overridden fields
|
|
773
|
+
expect(finalState.audioUtteranceId).toBe(audioUtteranceId);
|
|
774
|
+
|
|
775
|
+
// Should override these fields
|
|
776
|
+
expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
777
|
+
expect(finalState.finalTranscript).toBe('');
|
|
778
|
+
expect(finalState.startRecordingStatus).toBe(RecordingStatus.FINISHED);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('should set both recording and transcription timestamps', () => {
|
|
782
|
+
const beforeTime = new Date().toISOString();
|
|
783
|
+
|
|
784
|
+
simplifiedClient.stopAbnormally();
|
|
785
|
+
|
|
786
|
+
const state = simplifiedClient.getVGFState();
|
|
787
|
+
const afterTime = new Date().toISOString();
|
|
788
|
+
|
|
789
|
+
// Timestamps should be set and within reasonable range
|
|
790
|
+
expect(state.finalRecordingTimestamp).toBeDefined();
|
|
791
|
+
expect(state.finalTranscriptionTimestamp).toBeDefined();
|
|
792
|
+
|
|
793
|
+
// Basic sanity check that timestamps are ISO strings
|
|
794
|
+
expect(state.finalRecordingTimestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
795
|
+
expect(state.finalTranscriptionTimestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
796
|
+
|
|
797
|
+
// Timestamps should be close to current time
|
|
798
|
+
if (state.finalRecordingTimestamp) {
|
|
799
|
+
expect(state.finalRecordingTimestamp >= beforeTime).toBe(true);
|
|
800
|
+
expect(state.finalRecordingTimestamp <= afterTime).toBe(true);
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it('should call underlying client stopAbnormally for cleanup', () => {
|
|
805
|
+
simplifiedClient.stopAbnormally();
|
|
806
|
+
|
|
807
|
+
// stopAbnormally on underlying client SHOULD be called for WebSocket cleanup
|
|
808
|
+
expect(mockClient.stopAbnormally).toHaveBeenCalled();
|
|
809
|
+
|
|
810
|
+
// stopRecording on underlying client should NOT be called
|
|
811
|
+
expect(mockClient.stopRecording).not.toHaveBeenCalled();
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
it('should differ from stopRecording behavior', async () => {
|
|
815
|
+
// Test that stopAbnormally and stopRecording behave differently
|
|
816
|
+
jest.clearAllMocks();
|
|
817
|
+
|
|
818
|
+
// Use the existing simplifiedClient for testing
|
|
819
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
820
|
+
|
|
821
|
+
// Test stopAbnormally - should NOT call underlying client
|
|
822
|
+
simplifiedClient.stopAbnormally();
|
|
823
|
+
expect(mockClient.stopRecording).not.toHaveBeenCalled();
|
|
824
|
+
|
|
825
|
+
// Create new client to test stopRecording
|
|
826
|
+
const client2 = new SimplifiedVGFRecognitionClient({
|
|
827
|
+
asrRequestConfig: {
|
|
828
|
+
provider: 'deepgram',
|
|
829
|
+
language: 'en',
|
|
830
|
+
sampleRate: 16000,
|
|
831
|
+
encoding: AudioEncoding.LINEAR16
|
|
832
|
+
},
|
|
833
|
+
onStateChange: jest.fn()
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
// Clear mocks to isolate client2's behavior
|
|
837
|
+
jest.clearAllMocks();
|
|
838
|
+
|
|
839
|
+
// Test stopRecording - SHOULD call underlying client
|
|
840
|
+
await client2.stopRecording();
|
|
841
|
+
expect(mockClient.stopRecording).toHaveBeenCalled();
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('should use ABORTED status to distinguish from normal completion', () => {
|
|
845
|
+
// Test that stopAbnormally uses ABORTED, not FINALIZED
|
|
846
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
847
|
+
|
|
848
|
+
// Abnormal stop - should set to ABORTED
|
|
849
|
+
simplifiedClient.stopAbnormally();
|
|
850
|
+
const abortedState = simplifiedClient.getVGFState();
|
|
851
|
+
|
|
852
|
+
// Verify ABORTED is used (not FINALIZED)
|
|
853
|
+
expect(abortedState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
854
|
+
expect(abortedState.transcriptionStatus).not.toBe(TranscriptionStatus.FINALIZED);
|
|
855
|
+
expect(abortedState.finalTranscript).toBe(''); // Empty because cancelled
|
|
856
|
+
|
|
857
|
+
// ABORTED clearly indicates user cancelled, vs FINALIZED which means completed normally
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
describe('state guards', () => {
|
|
861
|
+
it('should do nothing if already fully stopped', () => {
|
|
862
|
+
// Setup: finalize state and mark underlying client as stopped
|
|
863
|
+
mockClient.getState.mockReturnValue(ClientState.STOPPED);
|
|
864
|
+
simplifiedClient.stopAbnormally();
|
|
865
|
+
|
|
866
|
+
// Clear mocks to test second call
|
|
867
|
+
jest.clearAllMocks();
|
|
868
|
+
|
|
869
|
+
// Call again - should return early and not call anything
|
|
870
|
+
simplifiedClient.stopAbnormally();
|
|
871
|
+
|
|
872
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
873
|
+
expect(mockClient.stopAbnormally).not.toHaveBeenCalled();
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
it('should not call underlying client if already in STOPPED state', () => {
|
|
877
|
+
// Mock underlying client as already stopped
|
|
878
|
+
mockClient.getState.mockReturnValue(ClientState.STOPPED);
|
|
879
|
+
|
|
880
|
+
// But VGF state not finalized yet
|
|
881
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
882
|
+
jest.clearAllMocks();
|
|
883
|
+
|
|
884
|
+
simplifiedClient.stopAbnormally();
|
|
885
|
+
|
|
886
|
+
// Should finalize VGF state
|
|
887
|
+
expect(stateChangeCallback).toHaveBeenCalled();
|
|
888
|
+
// But should NOT call underlying client
|
|
889
|
+
expect(mockClient.stopAbnormally).not.toHaveBeenCalled();
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
it('should not call underlying client if already in FAILED state', () => {
|
|
893
|
+
// Mock underlying client as failed
|
|
894
|
+
mockClient.getState.mockReturnValue(ClientState.FAILED);
|
|
895
|
+
|
|
896
|
+
simplifiedClient.stopAbnormally();
|
|
897
|
+
|
|
898
|
+
// Should handle VGF state update
|
|
899
|
+
expect(stateChangeCallback).toHaveBeenCalled();
|
|
900
|
+
// But should NOT call underlying client (already in terminal state)
|
|
901
|
+
expect(mockClient.stopAbnormally).not.toHaveBeenCalled();
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
it('should only update VGF state if already finalized but client not stopped', () => {
|
|
905
|
+
// First call - fully stop
|
|
906
|
+
simplifiedClient.stopAbnormally();
|
|
907
|
+
const firstCallCount = stateChangeCallback.mock.calls.length;
|
|
908
|
+
|
|
909
|
+
// Mock underlying client reconnects (edge case)
|
|
910
|
+
mockClient.getState.mockReturnValue(ClientState.READY);
|
|
911
|
+
jest.clearAllMocks();
|
|
912
|
+
|
|
913
|
+
// Second call - VGF already finalized but client not stopped
|
|
914
|
+
simplifiedClient.stopAbnormally();
|
|
915
|
+
|
|
916
|
+
// Should NOT update VGF state (already finalized)
|
|
917
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
918
|
+
// But SHOULD call underlying client (not stopped)
|
|
919
|
+
expect(mockClient.stopAbnormally).toHaveBeenCalled();
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
});
|
|
677
923
|
});
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
* All functionality is delegated to the underlying client.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
RecognitionState,
|
|
13
|
+
TranscriptionStatus,
|
|
14
|
+
RecordingStatus
|
|
15
|
+
} from './vgf-recognition-state.js';
|
|
12
16
|
import {
|
|
13
17
|
IRecognitionClient,
|
|
14
18
|
IRecognitionClientConfig,
|
|
@@ -67,6 +71,25 @@ export interface ISimplifiedVGFRecognitionClient {
|
|
|
67
71
|
*/
|
|
68
72
|
stopRecording(): Promise<void>;
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Force stop and immediately close connection without waiting for server
|
|
76
|
+
*
|
|
77
|
+
* WARNING: This is an abnormal shutdown that bypasses the graceful stop flow:
|
|
78
|
+
* - Does NOT wait for server to process remaining audio
|
|
79
|
+
* - Does NOT receive final transcript from server (VGF state set to empty)
|
|
80
|
+
* - Immediately closes WebSocket connection
|
|
81
|
+
* - Cleans up resources (buffers, listeners)
|
|
82
|
+
*
|
|
83
|
+
* Use Cases:
|
|
84
|
+
* - User explicitly cancels/abandons the session
|
|
85
|
+
* - Timeout scenarios where waiting is not acceptable
|
|
86
|
+
* - Need immediate cleanup and can't wait for server
|
|
87
|
+
*
|
|
88
|
+
* RECOMMENDED: Use stopRecording() for normal shutdown.
|
|
89
|
+
* Only use this when immediate disconnection is required.
|
|
90
|
+
*/
|
|
91
|
+
stopAbnormally(): void;
|
|
92
|
+
|
|
70
93
|
// ============= VGF State Methods =============
|
|
71
94
|
/**
|
|
72
95
|
* Get the current VGF recognition state
|
|
@@ -254,6 +277,40 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
|
|
|
254
277
|
await this.client.stopRecording();
|
|
255
278
|
}
|
|
256
279
|
|
|
280
|
+
stopAbnormally(): void {
|
|
281
|
+
// Guard: If already aborted/finalized and stopped, do nothing
|
|
282
|
+
if ((this.state.transcriptionStatus === TranscriptionStatus.ABORTED ||
|
|
283
|
+
this.state.transcriptionStatus === TranscriptionStatus.FINALIZED) &&
|
|
284
|
+
this.client.getState() === ClientState.STOPPED) {
|
|
285
|
+
// Already fully stopped - nothing to do
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.isRecordingAudio = false;
|
|
290
|
+
|
|
291
|
+
// Set state to ABORTED with empty transcript
|
|
292
|
+
// This clearly indicates the session was cancelled/abandoned by user
|
|
293
|
+
if (this.state.transcriptionStatus !== TranscriptionStatus.ABORTED &&
|
|
294
|
+
this.state.transcriptionStatus !== TranscriptionStatus.FINALIZED) {
|
|
295
|
+
this.state = {
|
|
296
|
+
...this.state,
|
|
297
|
+
transcriptionStatus: TranscriptionStatus.ABORTED,
|
|
298
|
+
finalTranscript: '',
|
|
299
|
+
startRecordingStatus: RecordingStatus.FINISHED,
|
|
300
|
+
finalRecordingTimestamp: new Date().toISOString(),
|
|
301
|
+
finalTranscriptionTimestamp: new Date().toISOString()
|
|
302
|
+
};
|
|
303
|
+
this.notifyStateChange();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Delegate to underlying client for actual WebSocket cleanup
|
|
307
|
+
// Only if client is not already in a terminal state
|
|
308
|
+
const clientState = this.client.getState();
|
|
309
|
+
if (clientState !== ClientState.STOPPED && clientState !== ClientState.FAILED) {
|
|
310
|
+
this.client.stopAbnormally();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
257
314
|
// Pure delegation methods - no state logic
|
|
258
315
|
getAudioUtteranceId(): string {
|
|
259
316
|
return this.client.getAudioUtteranceId();
|
|
@@ -2,22 +2,24 @@
|
|
|
2
2
|
* Unit tests for URL Builder
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { buildWebSocketUrl, UrlBuilderConfig } from './url-builder.js';
|
|
6
5
|
import { RecognitionContextTypeV1, STAGES } from '@recog/shared-types';
|
|
7
6
|
|
|
8
|
-
// Mock the shared-config module
|
|
7
|
+
// Mock the shared-config module BEFORE importing the module under test
|
|
9
8
|
const mockGetRecognitionServiceBase = jest.fn();
|
|
10
9
|
jest.mock('@recog/shared-config', () => ({
|
|
11
10
|
getRecognitionServiceBase: mockGetRecognitionServiceBase
|
|
12
11
|
}));
|
|
13
12
|
|
|
13
|
+
import { buildWebSocketUrl, UrlBuilderConfig } from './url-builder.js';
|
|
14
|
+
|
|
14
15
|
describe('buildWebSocketUrl', () => {
|
|
15
16
|
const baseConfig: UrlBuilderConfig = {
|
|
16
17
|
audioUtteranceId: 'test-utterance-123'
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
beforeEach(() => {
|
|
20
|
-
//
|
|
21
|
+
// Clear and reset mock before each test
|
|
22
|
+
mockGetRecognitionServiceBase.mockClear();
|
|
21
23
|
mockGetRecognitionServiceBase.mockReturnValue({
|
|
22
24
|
wsBase: 'wss://recognition.volley.com'
|
|
23
25
|
});
|
|
@@ -18,7 +18,7 @@ export const RecognitionVGFStateSchema = z.object({
|
|
|
18
18
|
audioUtteranceId: z.string(),
|
|
19
19
|
startRecordingStatus: z.string().optional(), // "NOT_READY", "READY", "RECORDING", "FINISHED". States follow this order.
|
|
20
20
|
// Streaming should only start when "READY". Other states control mic UI and recording.
|
|
21
|
-
transcriptionStatus: z.string().optional(), // "NOT_STARTED", "IN_PROGRESS", "FINALIZED", "ERROR"
|
|
21
|
+
transcriptionStatus: z.string().optional(), // "NOT_STARTED", "IN_PROGRESS", "FINALIZED", "ABORTED", "ERROR"
|
|
22
22
|
finalTranscript: z.string().optional(), // Full finalized transcript for the utterance. Will not change.
|
|
23
23
|
finalConfidence: z.number().optional(),
|
|
24
24
|
|
|
@@ -57,6 +57,7 @@ export const TranscriptionStatus = {
|
|
|
57
57
|
NOT_STARTED: "NOT_STARTED",
|
|
58
58
|
IN_PROGRESS: "IN_PROGRESS",
|
|
59
59
|
FINALIZED: "FINALIZED",
|
|
60
|
+
ABORTED: "ABORTED", // Session was cancelled/abandoned by user
|
|
60
61
|
ERROR: "ERROR",
|
|
61
62
|
} as const
|
|
62
63
|
|