@eka-care/ekascribe-ts-sdk 1.5.12 → 1.5.14
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/audio-chunker/audio-file-manager.d.ts +1 -6
- package/dist/audio-chunker/audio-file-manager.js +122 -65
- package/dist/audio-chunker/vad-web.js +33 -25
- package/dist/constants/enums.d.ts +10 -0
- package/dist/constants/enums.js +11 -0
- package/dist/constants/types.d.ts +23 -7
- package/dist/fetch-client/index.js +22 -15
- package/dist/index.d.ts +2 -3
- package/dist/index.js +25 -6
- package/dist/main/end-recording.js +24 -1
- package/dist/main/init-transaction.js +10 -1
- package/dist/main/retry-upload-recording.js +13 -1
- package/dist/store/store.d.ts +4 -4
- package/dist/store/store.js +10 -10
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TAudioChunksInfo
|
|
1
|
+
import { TAudioChunksInfo } from '../constants/types';
|
|
2
2
|
type TUploadAudioChunkParams = {
|
|
3
3
|
audioFrames: Float32Array;
|
|
4
4
|
fileName: string;
|
|
@@ -14,7 +14,6 @@ declare class AudioFileManager {
|
|
|
14
14
|
audioChunks: TAudioChunksInfo[];
|
|
15
15
|
private uploadPromises;
|
|
16
16
|
private successfulUploads;
|
|
17
|
-
private onProgressCallback?;
|
|
18
17
|
private totalRawSamples;
|
|
19
18
|
private totalRawFrames;
|
|
20
19
|
private totalInsertedSamples;
|
|
@@ -42,10 +41,6 @@ declare class AudioFileManager {
|
|
|
42
41
|
totalInsertedSamples: number;
|
|
43
42
|
totalInsertedFrames: number;
|
|
44
43
|
};
|
|
45
|
-
/**
|
|
46
|
-
* Set callback for upload progress updates
|
|
47
|
-
*/
|
|
48
|
-
setProgressCallback(callback: TFileUploadProgressCallback): void;
|
|
49
44
|
/**
|
|
50
45
|
* Update audio information array, this will update the audio chunks info
|
|
51
46
|
* (+ the latest chunk , affects the length of chunks data struct)
|
|
@@ -2,8 +2,9 @@ import { AUDIO_EXTENSION_TYPE_MAP, OUTPUT_FORMAT } from '../constants/constant';
|
|
|
2
2
|
import pushFileToS3 from '../aws-services/upload-file-to-s3';
|
|
3
3
|
import postCogInit from '../api/post-cog-init';
|
|
4
4
|
import { configureAWS } from '../aws-services/configure-aws';
|
|
5
|
-
import { SHARED_WORKER_ACTION } from '../constants/enums';
|
|
5
|
+
import { CALLBACK_TYPE, SHARED_WORKER_ACTION } from '../constants/enums';
|
|
6
6
|
import compressAudioToMp3 from '../utils/compress-mp3-audio';
|
|
7
|
+
import EkaScribeStore from '../store/store';
|
|
7
8
|
class AudioFileManager {
|
|
8
9
|
initialiseClassInstance() {
|
|
9
10
|
this.audioChunks = [];
|
|
@@ -49,12 +50,6 @@ class AudioFileManager {
|
|
|
49
50
|
writable: true,
|
|
50
51
|
value: []
|
|
51
52
|
});
|
|
52
|
-
Object.defineProperty(this, "onProgressCallback", {
|
|
53
|
-
enumerable: true,
|
|
54
|
-
configurable: true,
|
|
55
|
-
writable: true,
|
|
56
|
-
value: void 0
|
|
57
|
-
});
|
|
58
53
|
Object.defineProperty(this, "totalRawSamples", {
|
|
59
54
|
enumerable: true,
|
|
60
55
|
configurable: true,
|
|
@@ -127,12 +122,6 @@ class AudioFileManager {
|
|
|
127
122
|
totalInsertedFrames: this.totalInsertedFrames,
|
|
128
123
|
};
|
|
129
124
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Set callback for upload progress updates
|
|
132
|
-
*/
|
|
133
|
-
setProgressCallback(callback) {
|
|
134
|
-
this.onProgressCallback = callback;
|
|
135
|
-
}
|
|
136
125
|
/**
|
|
137
126
|
* Update audio information array, this will update the audio chunks info
|
|
138
127
|
* (+ the latest chunk , affects the length of chunks data struct)
|
|
@@ -146,26 +135,46 @@ class AudioFileManager {
|
|
|
146
135
|
// new URL(relativeOrAbsolutePath, baseUrl)
|
|
147
136
|
const worker = new SharedWorker(new URL('../shared-worker/s3-file-upload.js', import.meta.url));
|
|
148
137
|
this.sharedWorkerInstance = worker;
|
|
138
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
149
139
|
this.sharedWorkerInstance.port.onmessage = async (event) => {
|
|
150
140
|
const workerResponse = event.data;
|
|
151
141
|
switch (workerResponse.action) {
|
|
152
142
|
case SHARED_WORKER_ACTION.CONFIGURE_AWS_SUCCESS: {
|
|
153
|
-
|
|
143
|
+
if (onEventCallback) {
|
|
144
|
+
onEventCallback({
|
|
145
|
+
callback_type: CALLBACK_TYPE.AWS_CONFIGURE_STATUS,
|
|
146
|
+
status: 'success',
|
|
147
|
+
message: workerResponse.message,
|
|
148
|
+
timestamp: new Date().toISOString(),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
154
151
|
return;
|
|
155
152
|
}
|
|
156
153
|
case SHARED_WORKER_ACTION.CONFIGURE_AWS_ERROR: {
|
|
157
|
-
|
|
154
|
+
if (onEventCallback) {
|
|
155
|
+
onEventCallback({
|
|
156
|
+
callback_type: CALLBACK_TYPE.AWS_CONFIGURE_STATUS,
|
|
157
|
+
status: 'error',
|
|
158
|
+
message: workerResponse.message,
|
|
159
|
+
timestamp: new Date().toISOString(),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
158
162
|
return;
|
|
159
163
|
}
|
|
160
164
|
case SHARED_WORKER_ACTION.UPLOAD_FILE_WITH_WORKER_SUCCESS: {
|
|
161
|
-
// Callback
|
|
162
165
|
const { fileCount: fileName, chunkIndex, fileBlob, compressedAudioBuffer, } = workerResponse.requestBody;
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
if (onEventCallback && compressedAudioBuffer) {
|
|
167
|
+
onEventCallback({
|
|
168
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
169
|
+
status: 'info',
|
|
170
|
+
message: 'Audioframes of chunk to store in IDB',
|
|
171
|
+
timestamp: new Date().toISOString(),
|
|
172
|
+
data: {
|
|
173
|
+
success: this.successfulUploads.length,
|
|
174
|
+
total: this.audioChunks.length,
|
|
175
|
+
fileName,
|
|
176
|
+
chunkData: compressedAudioBuffer,
|
|
177
|
+
},
|
|
169
178
|
});
|
|
170
179
|
}
|
|
171
180
|
if (workerResponse.response.success) {
|
|
@@ -180,24 +189,35 @@ class AudioFileManager {
|
|
|
180
189
|
response: workerResponse.response.success,
|
|
181
190
|
};
|
|
182
191
|
}
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
if (onEventCallback) {
|
|
193
|
+
onEventCallback({
|
|
194
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
195
|
+
status: 'success',
|
|
196
|
+
message: workerResponse.response.success,
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
198
|
+
data: {
|
|
199
|
+
success: this.successfulUploads.length,
|
|
200
|
+
total: this.audioChunks.length,
|
|
201
|
+
is_uploaded: true,
|
|
202
|
+
},
|
|
188
203
|
});
|
|
189
204
|
}
|
|
190
205
|
}
|
|
191
206
|
else {
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
if (onEventCallback) {
|
|
208
|
+
onEventCallback({
|
|
209
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
210
|
+
status: 'error',
|
|
211
|
+
message: workerResponse.response.error || 'Upload failed',
|
|
212
|
+
timestamp: new Date().toISOString(),
|
|
198
213
|
error: {
|
|
199
214
|
code: workerResponse.response.code,
|
|
200
|
-
msg:
|
|
215
|
+
msg: workerResponse.response.error,
|
|
216
|
+
details: workerResponse.response.errorCode,
|
|
217
|
+
},
|
|
218
|
+
data: {
|
|
219
|
+
fileName,
|
|
220
|
+
is_uploaded: false,
|
|
201
221
|
},
|
|
202
222
|
});
|
|
203
223
|
}
|
|
@@ -273,12 +293,19 @@ class AudioFileManager {
|
|
|
273
293
|
const audioBlob = new Blob(compressedAudioBuffer, {
|
|
274
294
|
type: AUDIO_EXTENSION_TYPE_MAP[OUTPUT_FORMAT],
|
|
275
295
|
});
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
296
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
297
|
+
if (onEventCallback) {
|
|
298
|
+
onEventCallback({
|
|
299
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
300
|
+
status: 'info',
|
|
301
|
+
message: 'Audio chunks count to display success/total file count and to store chunks in IDB',
|
|
302
|
+
timestamp: new Date().toISOString(),
|
|
303
|
+
data: {
|
|
304
|
+
success: this.successfulUploads.length,
|
|
305
|
+
total: this.audioChunks.length,
|
|
306
|
+
fileName,
|
|
307
|
+
chunkData: compressedAudioBuffer,
|
|
308
|
+
},
|
|
282
309
|
});
|
|
283
310
|
}
|
|
284
311
|
// Push upload promise to track status
|
|
@@ -302,11 +329,17 @@ class AudioFileManager {
|
|
|
302
329
|
response: response.success,
|
|
303
330
|
};
|
|
304
331
|
}
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
332
|
+
if (onEventCallback) {
|
|
333
|
+
onEventCallback({
|
|
334
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
335
|
+
status: 'success',
|
|
336
|
+
message: response.success,
|
|
337
|
+
timestamp: new Date().toISOString(),
|
|
338
|
+
data: {
|
|
339
|
+
success: this.successfulUploads.length,
|
|
340
|
+
total: this.audioChunks.length,
|
|
341
|
+
is_uploaded: true,
|
|
342
|
+
},
|
|
310
343
|
});
|
|
311
344
|
}
|
|
312
345
|
}
|
|
@@ -320,15 +353,20 @@ class AudioFileManager {
|
|
|
320
353
|
response: response.error || 'Upload failed',
|
|
321
354
|
};
|
|
322
355
|
}
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
356
|
+
if (onEventCallback) {
|
|
357
|
+
onEventCallback({
|
|
358
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
359
|
+
status: 'error',
|
|
360
|
+
message: response.error || 'Upload failed',
|
|
361
|
+
timestamp: new Date().toISOString(),
|
|
329
362
|
error: {
|
|
330
363
|
code: response.code || 500,
|
|
331
|
-
msg: '
|
|
364
|
+
msg: response.error || 'Upload failed',
|
|
365
|
+
details: response.errorCode,
|
|
366
|
+
},
|
|
367
|
+
data: {
|
|
368
|
+
fileName,
|
|
369
|
+
is_uploaded: false,
|
|
332
370
|
},
|
|
333
371
|
});
|
|
334
372
|
}
|
|
@@ -346,10 +384,17 @@ class AudioFileManager {
|
|
|
346
384
|
*/
|
|
347
385
|
async uploadAudioChunkInWorker({ audioFrames, fileName, chunkIndex, }) {
|
|
348
386
|
const s3FileName = `${this.filePath}/${fileName}`;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
387
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
388
|
+
if (onEventCallback) {
|
|
389
|
+
onEventCallback({
|
|
390
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
391
|
+
status: 'info',
|
|
392
|
+
message: 'Audio chunks count to display success/total file count',
|
|
393
|
+
timestamp: new Date().toISOString(),
|
|
394
|
+
data: {
|
|
395
|
+
success: this.successfulUploads.length,
|
|
396
|
+
total: this.audioChunks.length,
|
|
397
|
+
},
|
|
353
398
|
});
|
|
354
399
|
}
|
|
355
400
|
this.sharedWorkerInstance?.port.postMessage({
|
|
@@ -495,6 +540,7 @@ class AudioFileManager {
|
|
|
495
540
|
if (failedFiles.length === 0) {
|
|
496
541
|
return [];
|
|
497
542
|
}
|
|
543
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
498
544
|
if (this.sharedWorkerInstance) {
|
|
499
545
|
this.audioChunks.forEach((chunk, index) => {
|
|
500
546
|
const { fileName, fileBlob, status, audioFrames } = chunk;
|
|
@@ -517,10 +563,16 @@ class AudioFileManager {
|
|
|
517
563
|
}
|
|
518
564
|
else {
|
|
519
565
|
this.uploadPromises = []; // Reset upload promises for retries
|
|
520
|
-
if (
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
566
|
+
if (onEventCallback) {
|
|
567
|
+
onEventCallback({
|
|
568
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
569
|
+
status: 'info',
|
|
570
|
+
message: 'Audio chunks count to display success/total file count',
|
|
571
|
+
timestamp: new Date().toISOString(),
|
|
572
|
+
data: {
|
|
573
|
+
success: this.successfulUploads.length,
|
|
574
|
+
total: this.audioChunks.length,
|
|
575
|
+
},
|
|
524
576
|
});
|
|
525
577
|
}
|
|
526
578
|
this.audioChunks.forEach((chunk, index) => {
|
|
@@ -547,10 +599,16 @@ class AudioFileManager {
|
|
|
547
599
|
}).then((response) => {
|
|
548
600
|
if (response.success) {
|
|
549
601
|
this.successfulUploads.push(fileName);
|
|
550
|
-
if (
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
602
|
+
if (onEventCallback) {
|
|
603
|
+
onEventCallback({
|
|
604
|
+
callback_type: CALLBACK_TYPE.FILE_UPLOAD_STATUS,
|
|
605
|
+
status: 'info',
|
|
606
|
+
message: 'Audio chunks count to display success/total file count',
|
|
607
|
+
timestamp: new Date().toISOString(),
|
|
608
|
+
data: {
|
|
609
|
+
success: this.successfulUploads.length,
|
|
610
|
+
total: this.audioChunks.length,
|
|
611
|
+
},
|
|
554
612
|
});
|
|
555
613
|
}
|
|
556
614
|
this.audioChunks[index] = {
|
|
@@ -593,7 +651,6 @@ class AudioFileManager {
|
|
|
593
651
|
this.filePath = '';
|
|
594
652
|
this.businessID = '';
|
|
595
653
|
this.isAWSConfigured = false;
|
|
596
|
-
this.onProgressCallback = undefined;
|
|
597
654
|
}
|
|
598
655
|
}
|
|
599
656
|
export default AudioFileManager;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MicVAD } from '@ricky0123/vad-web';
|
|
2
2
|
import { FRAME_SIZE, LONG_SILENCE_THRESHOLD, OUTPUT_FORMAT, PRE_SPEECH_PAD_FRAMES, SDK_STATUS_CODE, SHORT_SILENCE_THRESHOLD, } from '../constants/constant';
|
|
3
3
|
import EkaScribeStore from '../store/store';
|
|
4
|
-
import { ERROR_CODE } from '../constants/enums';
|
|
4
|
+
import { CALLBACK_TYPE, ERROR_CODE } from '../constants/enums';
|
|
5
5
|
class VadWebClient {
|
|
6
6
|
/**
|
|
7
7
|
* Class that handle Vad functions and manages audio chunk
|
|
@@ -134,7 +134,7 @@ class VadWebClient {
|
|
|
134
134
|
if (!this.recording_started)
|
|
135
135
|
return;
|
|
136
136
|
const now = Date.now();
|
|
137
|
-
const
|
|
137
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
138
138
|
const silenceThreshold = 10000; // 10 seconds
|
|
139
139
|
if (isSpeech === 0) {
|
|
140
140
|
if (this.noSpeechStartTime === null) {
|
|
@@ -147,11 +147,16 @@ class VadWebClient {
|
|
|
147
147
|
// Check if enough time has passed since the last warning (cooldown period)
|
|
148
148
|
if (this.lastWarningTime === null ||
|
|
149
149
|
now - this.lastWarningTime >= this.warningCooldownPeriod) {
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
if (onEventCallback) {
|
|
151
|
+
onEventCallback({
|
|
152
|
+
callback_type: CALLBACK_TYPE.VAD_AUDIO_STATUS,
|
|
153
|
+
status: 'info',
|
|
154
|
+
message: 'No audio detected for a while. Please talk or stop the recording if done.',
|
|
155
|
+
timestamp: new Date().toISOString(),
|
|
156
|
+
data: {
|
|
157
|
+
error_code: ERROR_CODE.NO_AUDIO_CAPTURE,
|
|
158
|
+
status_code: SDK_STATUS_CODE.AUDIO_ERROR,
|
|
159
|
+
},
|
|
155
160
|
});
|
|
156
161
|
}
|
|
157
162
|
this.lastWarningTime = now;
|
|
@@ -165,11 +170,16 @@ class VadWebClient {
|
|
|
165
170
|
// Reset timers when speech is detected
|
|
166
171
|
this.noSpeechStartTime = null;
|
|
167
172
|
this.lastWarningTime = null;
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
if (onEventCallback) {
|
|
174
|
+
onEventCallback({
|
|
175
|
+
callback_type: CALLBACK_TYPE.VAD_AUDIO_STATUS,
|
|
176
|
+
status: 'info',
|
|
177
|
+
message: 'Audio captured. Recording continues.',
|
|
178
|
+
timestamp: new Date().toISOString(),
|
|
179
|
+
data: {
|
|
180
|
+
error_code: ERROR_CODE.SPEECH_DETECTED,
|
|
181
|
+
status_code: SDK_STATUS_CODE.SUCCESS,
|
|
182
|
+
},
|
|
173
183
|
});
|
|
174
184
|
}
|
|
175
185
|
}
|
|
@@ -361,28 +371,26 @@ class VadWebClient {
|
|
|
361
371
|
this.recording_started = false;
|
|
362
372
|
this.is_vad_loading = true; // Reset to initial state
|
|
363
373
|
// this.micVad = {} as MicVAD; // Clear the instance
|
|
364
|
-
if (EkaScribeStore.errorCallback) {
|
|
365
|
-
EkaScribeStore.errorCallback({
|
|
366
|
-
error_code: ERROR_CODE.SPEECH_DETECTED,
|
|
367
|
-
status_code: SDK_STATUS_CODE.SUCCESS,
|
|
368
|
-
success_message: 'Audio captured. Recording continues.',
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
374
|
}
|
|
372
375
|
/**
|
|
373
376
|
* monitor initial audio capture within starting 4 seconds
|
|
374
377
|
*/
|
|
375
378
|
monitorAudioCapture() {
|
|
376
379
|
const audioBuffer = EkaScribeStore.audioBufferInstance;
|
|
377
|
-
const
|
|
380
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
378
381
|
setTimeout(() => {
|
|
379
382
|
if (audioBuffer && audioBuffer.getCurrentSampleLength() <= 0) {
|
|
380
383
|
this.micVad.pause();
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
if (onEventCallback) {
|
|
385
|
+
onEventCallback({
|
|
386
|
+
callback_type: CALLBACK_TYPE.VAD_AUDIO_STATUS,
|
|
387
|
+
status: 'info',
|
|
388
|
+
message: 'No audio is being captured. Please check your microphone.',
|
|
389
|
+
timestamp: new Date().toISOString(),
|
|
390
|
+
data: {
|
|
391
|
+
error_code: ERROR_CODE.NO_AUDIO_CAPTURE,
|
|
392
|
+
status_code: SDK_STATUS_CODE.AUDIO_ERROR,
|
|
393
|
+
},
|
|
386
394
|
});
|
|
387
395
|
}
|
|
388
396
|
return false;
|
|
@@ -48,3 +48,13 @@ export declare enum SHARED_WORKER_ACTION {
|
|
|
48
48
|
WAIT_FOR_ALL_UPLOADS_SUCCESS = "wait_for_all_uploads_success",
|
|
49
49
|
WAIT_FOR_ALL_UPLOADS_ERROR = "wait_for_all_uploads_error"
|
|
50
50
|
}
|
|
51
|
+
export declare enum CALLBACK_TYPE {
|
|
52
|
+
AWS_CONFIGURE_STATUS = "aws_configure_status",
|
|
53
|
+
FILE_UPLOAD_STATUS = "file_upload_status",
|
|
54
|
+
TRANSACTION_STATUS = "transaction_status",
|
|
55
|
+
TEMPLATE_OPERATION_STATUS = "template_operation_status",
|
|
56
|
+
AUTHENTICATION_STATUS = "authentication_status",
|
|
57
|
+
NETWORK_STATUS = "network_status",
|
|
58
|
+
STORAGE_STATUS = "storage_status",
|
|
59
|
+
VAD_AUDIO_STATUS = "vad_audio_status"
|
|
60
|
+
}
|
package/dist/constants/enums.js
CHANGED
|
@@ -53,3 +53,14 @@ export var SHARED_WORKER_ACTION;
|
|
|
53
53
|
SHARED_WORKER_ACTION["WAIT_FOR_ALL_UPLOADS_SUCCESS"] = "wait_for_all_uploads_success";
|
|
54
54
|
SHARED_WORKER_ACTION["WAIT_FOR_ALL_UPLOADS_ERROR"] = "wait_for_all_uploads_error";
|
|
55
55
|
})(SHARED_WORKER_ACTION || (SHARED_WORKER_ACTION = {}));
|
|
56
|
+
export var CALLBACK_TYPE;
|
|
57
|
+
(function (CALLBACK_TYPE) {
|
|
58
|
+
CALLBACK_TYPE["AWS_CONFIGURE_STATUS"] = "aws_configure_status";
|
|
59
|
+
CALLBACK_TYPE["FILE_UPLOAD_STATUS"] = "file_upload_status";
|
|
60
|
+
CALLBACK_TYPE["TRANSACTION_STATUS"] = "transaction_status";
|
|
61
|
+
CALLBACK_TYPE["TEMPLATE_OPERATION_STATUS"] = "template_operation_status";
|
|
62
|
+
CALLBACK_TYPE["AUTHENTICATION_STATUS"] = "authentication_status";
|
|
63
|
+
CALLBACK_TYPE["NETWORK_STATUS"] = "network_status";
|
|
64
|
+
CALLBACK_TYPE["STORAGE_STATUS"] = "storage_status";
|
|
65
|
+
CALLBACK_TYPE["VAD_AUDIO_STATUS"] = "vad_audio_status";
|
|
66
|
+
})(CALLBACK_TYPE || (CALLBACK_TYPE = {}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ERROR_CODE } from './enums';
|
|
1
|
+
import { ERROR_CODE, CALLBACK_TYPE } from './enums';
|
|
2
2
|
export type TGetConfigV2Response = {
|
|
3
3
|
data?: {
|
|
4
4
|
supported_languages: TGetConfigItem[];
|
|
@@ -244,17 +244,33 @@ export type TSessionStatus = {
|
|
|
244
244
|
};
|
|
245
245
|
};
|
|
246
246
|
};
|
|
247
|
-
export type
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
fileName?: string;
|
|
252
|
-
chunkData?: Uint8Array<ArrayBufferLike>[];
|
|
247
|
+
export type TEventCallback = (args: {
|
|
248
|
+
callback_type: CALLBACK_TYPE;
|
|
249
|
+
status: 'success' | 'error' | 'progress' | 'info';
|
|
250
|
+
message: string;
|
|
253
251
|
error?: {
|
|
254
252
|
code: number;
|
|
255
253
|
msg: string;
|
|
254
|
+
details?: unknown;
|
|
256
255
|
};
|
|
256
|
+
data?: TFileUploadCallbackData | TTransactionCallbackData;
|
|
257
|
+
timestamp: string;
|
|
258
|
+
metadata?: Record<string, unknown>;
|
|
257
259
|
}) => void;
|
|
260
|
+
export type TFileUploadCallbackData = {
|
|
261
|
+
success?: number;
|
|
262
|
+
total?: number;
|
|
263
|
+
is_uploaded?: boolean;
|
|
264
|
+
fileName?: string;
|
|
265
|
+
chunkData?: Uint8Array<ArrayBufferLike>[];
|
|
266
|
+
};
|
|
267
|
+
export type TTransactionCallbackData = {
|
|
268
|
+
txn_id?: string;
|
|
269
|
+
request?: unknown;
|
|
270
|
+
response?: unknown;
|
|
271
|
+
status_code?: number;
|
|
272
|
+
error_code?: ERROR_CODE;
|
|
273
|
+
};
|
|
258
274
|
export interface TPostV1TemplateRequest {
|
|
259
275
|
title: string;
|
|
260
276
|
desc?: string;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { GET_CLIENT_ID, GET_AUTH_TOKEN } from './helper';
|
|
2
2
|
import EkaScribeStore from '../store/store';
|
|
3
|
-
import {
|
|
4
|
-
import { SDK_STATUS_CODE } from '../constants/constant';
|
|
3
|
+
import { CALLBACK_TYPE } from '../constants/enums';
|
|
5
4
|
const API_TIMEOUT_MS = 10000;
|
|
6
5
|
export default async function fetchWrapper(url, options = {}, timeoutMs = API_TIMEOUT_MS) {
|
|
7
|
-
const
|
|
6
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
8
7
|
const controller = new AbortController();
|
|
9
8
|
let timeoutId = null;
|
|
10
9
|
try {
|
|
@@ -25,24 +24,32 @@ export default async function fetchWrapper(url, options = {}, timeoutMs = API_TI
|
|
|
25
24
|
signal: controller.signal,
|
|
26
25
|
credentials: 'include',
|
|
27
26
|
});
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
if (onEventCallback) {
|
|
28
|
+
onEventCallback({
|
|
29
|
+
callback_type: CALLBACK_TYPE.AUTHENTICATION_STATUS,
|
|
30
|
+
status: 'success',
|
|
31
|
+
message: 'Fetch wrapper response: ' + response.status,
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
|
+
data: {
|
|
34
|
+
request: 'Request body: ' + JSON.stringify(options.body),
|
|
35
|
+
response: 'Response body: ' + JSON.stringify(response),
|
|
36
|
+
},
|
|
34
37
|
});
|
|
35
38
|
}
|
|
36
39
|
return response;
|
|
37
40
|
}
|
|
38
41
|
catch (error) {
|
|
39
42
|
console.error(error, 'error in fetch wrapper - SDK');
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
if (onEventCallback) {
|
|
44
|
+
onEventCallback({
|
|
45
|
+
callback_type: CALLBACK_TYPE.AUTHENTICATION_STATUS,
|
|
46
|
+
status: 'error',
|
|
47
|
+
message: 'Fetch wrapper response: ' + error,
|
|
48
|
+
timestamp: new Date().toISOString(),
|
|
49
|
+
data: {
|
|
50
|
+
request: 'Request body: ' + JSON.stringify(options.body),
|
|
51
|
+
response: 'Error body: ' + JSON.stringify(error),
|
|
52
|
+
},
|
|
46
53
|
});
|
|
47
54
|
}
|
|
48
55
|
throw error;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TGetStatusResponse } from './api/transaction/get-voice-api-v3-status';
|
|
2
|
-
import { TEndRecordingResponse,
|
|
2
|
+
import { TEndRecordingResponse, TEventCallback, TGetTransactionHistoryResponse, TPatchTransactionRequest, TPatchVoiceApiV2ConfigRequest, TPatchVoiceApiV3StatusRequest, TPostTransactionResponse, TPostV1ConvertToTemplateRequest, TPostV1TemplateRequest, TPostV1TemplateSectionRequest, TPostV1UploadAudioFilesRequest, TStartRecordingRequest } from './constants/types';
|
|
3
3
|
import { TSearchSessionsByPatientRequest } from './utils/search-sessions-by-patient-name';
|
|
4
4
|
declare class EkaScribe {
|
|
5
5
|
private static instance;
|
|
@@ -41,9 +41,8 @@ declare class EkaScribe {
|
|
|
41
41
|
getFailedFiles(): string[];
|
|
42
42
|
getTotalAudioFiles(): import("./constants/types").TAudioChunksInfo[];
|
|
43
43
|
resetEkaScribe(): void;
|
|
44
|
-
onError(callback: TErrorCallback): void;
|
|
45
44
|
onUserSpeechCallback(callback: (isSpeech: boolean) => void): void;
|
|
46
|
-
|
|
45
|
+
onEventCallback(callback: TEventCallback): void;
|
|
47
46
|
configureVadConstants({ pref_length, desp_length, max_length, sr, frame_size, pre_speech_pad_frames, short_thsld, long_thsld, }: {
|
|
48
47
|
pref_length: number;
|
|
49
48
|
desp_length: number;
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import AudioBufferManager from './audio-chunker/audio-buffer-manager';
|
|
|
7
7
|
import AudioFileManager from './audio-chunker/audio-file-manager';
|
|
8
8
|
import VadWebClient from './audio-chunker/vad-web';
|
|
9
9
|
import { AUDIO_BUFFER_SIZE_IN_S, DESP_CHUNK_LENGTH, FRAME_RATE, MAX_CHUNK_LENGTH, PREF_CHUNK_LENGTH, SAMPLING_RATE, SDK_STATUS_CODE, } from './constants/constant';
|
|
10
|
-
import { ERROR_CODE } from './constants/enums';
|
|
10
|
+
import { CALLBACK_TYPE, ERROR_CODE } from './constants/enums';
|
|
11
11
|
import setEnv from './fetch-client/helper';
|
|
12
12
|
import endVoiceRecording from './main/end-recording';
|
|
13
13
|
import pauseVoiceRecording from './main/pause-recording';
|
|
@@ -126,12 +126,21 @@ class EkaScribe {
|
|
|
126
126
|
}
|
|
127
127
|
async patchSessionStatus({ sessionId, processing_status, processing_error, }) {
|
|
128
128
|
try {
|
|
129
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
129
130
|
this.vadInstance.pauseVad();
|
|
130
131
|
const patchTransactionResponse = await patchTransactionStatus({
|
|
131
132
|
sessionId,
|
|
132
133
|
processing_status,
|
|
133
134
|
processing_error,
|
|
134
135
|
});
|
|
136
|
+
if (onEventCallback) {
|
|
137
|
+
onEventCallback({
|
|
138
|
+
callback_type: CALLBACK_TYPE.TRANSACTION_STATUS,
|
|
139
|
+
status: 'info',
|
|
140
|
+
message: `Transaction cancel status: ${patchTransactionResponse.code}`,
|
|
141
|
+
timestamp: new Date().toISOString(),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
135
144
|
this.resetEkaScribe();
|
|
136
145
|
return patchTransactionResponse;
|
|
137
146
|
}
|
|
@@ -145,6 +154,7 @@ class EkaScribe {
|
|
|
145
154
|
async commitTransactionCall() {
|
|
146
155
|
try {
|
|
147
156
|
const txnID = EkaScribeStore.txnID;
|
|
157
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
148
158
|
let txnCommitMsg = '';
|
|
149
159
|
if (EkaScribeStore.sessionStatus[txnID].api?.status === 'stop' ||
|
|
150
160
|
EkaScribeStore.sessionStatus[txnID].api?.status === 'commit') {
|
|
@@ -155,6 +165,17 @@ class EkaScribe {
|
|
|
155
165
|
txnId: EkaScribeStore.txnID,
|
|
156
166
|
});
|
|
157
167
|
txnCommitMsg = message;
|
|
168
|
+
if (onEventCallback) {
|
|
169
|
+
onEventCallback({
|
|
170
|
+
callback_type: CALLBACK_TYPE.TRANSACTION_STATUS,
|
|
171
|
+
status: 'info',
|
|
172
|
+
message: `Transaction commit status: ${txnCommitStatusCode}`,
|
|
173
|
+
timestamp: new Date().toISOString(),
|
|
174
|
+
data: {
|
|
175
|
+
request: audioFiles,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
158
179
|
if (txnCommitStatusCode != 200) {
|
|
159
180
|
return {
|
|
160
181
|
error_code: ERROR_CODE.TXN_COMMIT_FAILED,
|
|
@@ -242,14 +263,12 @@ class EkaScribe {
|
|
|
242
263
|
EkaScribeStore.resetStore();
|
|
243
264
|
console.log(this.audioFileManagerInstance, this.audioBufferInstance, this.vadInstance, EkaScribeStore, 'after reset ekascribe');
|
|
244
265
|
}
|
|
245
|
-
onError(callback) {
|
|
246
|
-
EkaScribeStore.errorCallback = callback;
|
|
247
|
-
}
|
|
248
266
|
onUserSpeechCallback(callback) {
|
|
249
267
|
EkaScribeStore.userSpeechCallback = callback;
|
|
250
268
|
}
|
|
251
|
-
|
|
252
|
-
|
|
269
|
+
onEventCallback(callback) {
|
|
270
|
+
EkaScribeStore.eventCallback = callback;
|
|
271
|
+
// this.audioFileManagerInstance.setProgressCallback(callback);
|
|
253
272
|
}
|
|
254
273
|
configureVadConstants({ pref_length, desp_length, max_length, sr, frame_size, pre_speech_pad_frames, short_thsld, long_thsld, }) {
|
|
255
274
|
return this.vadInstance.configureVadConstants({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import postTransactionCommit from '../api/transaction/post-transaction-commit';
|
|
2
2
|
import postTransactionStop from '../api/transaction/post-transaction-stop';
|
|
3
3
|
import { OUTPUT_FORMAT, SDK_STATUS_CODE } from '../constants/constant';
|
|
4
|
-
import { ERROR_CODE } from '../constants/enums';
|
|
4
|
+
import { CALLBACK_TYPE, ERROR_CODE } from '../constants/enums';
|
|
5
5
|
import EkaScribeStore from '../store/store';
|
|
6
6
|
const endVoiceRecording = async () => {
|
|
7
7
|
try {
|
|
@@ -9,6 +9,7 @@ const endVoiceRecording = async () => {
|
|
|
9
9
|
const fileManagerInstance = EkaScribeStore.audioFileManagerInstance;
|
|
10
10
|
const vadInstance = EkaScribeStore.vadInstance;
|
|
11
11
|
const txnID = EkaScribeStore.txnID;
|
|
12
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
12
13
|
if (!fileManagerInstance || !audioBufferInstance || !vadInstance) {
|
|
13
14
|
throw new Error('Class instances are not initialized');
|
|
14
15
|
}
|
|
@@ -54,6 +55,17 @@ const endVoiceRecording = async () => {
|
|
|
54
55
|
audioFiles,
|
|
55
56
|
txnId: txnID,
|
|
56
57
|
});
|
|
58
|
+
if (onEventCallback) {
|
|
59
|
+
onEventCallback({
|
|
60
|
+
callback_type: CALLBACK_TYPE.TRANSACTION_STATUS,
|
|
61
|
+
status: 'info',
|
|
62
|
+
message: `Transaction stop status: ${txnStopStatusCode}`,
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
data: {
|
|
65
|
+
request: audioFiles,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
57
69
|
if (txnStopStatusCode != 200) {
|
|
58
70
|
return {
|
|
59
71
|
error_code: ERROR_CODE.TXN_STOP_FAILED,
|
|
@@ -101,6 +113,17 @@ const endVoiceRecording = async () => {
|
|
|
101
113
|
txnId: txnID,
|
|
102
114
|
audioFiles,
|
|
103
115
|
});
|
|
116
|
+
if (onEventCallback) {
|
|
117
|
+
onEventCallback({
|
|
118
|
+
callback_type: CALLBACK_TYPE.TRANSACTION_STATUS,
|
|
119
|
+
status: 'info',
|
|
120
|
+
message: `Transaction commit status: ${txnCommitStatusCode}`,
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
data: {
|
|
123
|
+
request: audioFiles,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
104
127
|
if (txnCommitStatusCode != 200) {
|
|
105
128
|
return {
|
|
106
129
|
error_code: ERROR_CODE.TXN_COMMIT_FAILED,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import postTransactionInit from '../api/transaction/post-transaction-init';
|
|
2
2
|
import { S3_BUCKET_NAME, SDK_STATUS_CODE } from '../constants/constant';
|
|
3
|
-
import { ERROR_CODE } from '../constants/enums';
|
|
3
|
+
import { CALLBACK_TYPE, ERROR_CODE } from '../constants/enums';
|
|
4
4
|
import EkaScribeStore from '../store/store';
|
|
5
5
|
const initialiseTransaction = async (request) => {
|
|
6
6
|
try {
|
|
7
7
|
const { txn_id } = request;
|
|
8
8
|
const fileManagerInstance = EkaScribeStore.audioFileManagerInstance;
|
|
9
9
|
const sessionStatus = EkaScribeStore.sessionStatus;
|
|
10
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
10
11
|
let businessID = '';
|
|
11
12
|
let userOID = '';
|
|
12
13
|
let userUUID = '';
|
|
@@ -28,6 +29,14 @@ const initialiseTransaction = async (request) => {
|
|
|
28
29
|
s3Url: `s3://${S3_BUCKET_NAME}/${filePath}`,
|
|
29
30
|
});
|
|
30
31
|
const { code: txnInitStatusCode, b_id: businessId, oid, uuid, message: txnInitMessage, error: txnInitError, } = txnInitResponse;
|
|
32
|
+
if (onEventCallback) {
|
|
33
|
+
onEventCallback({
|
|
34
|
+
callback_type: CALLBACK_TYPE.TRANSACTION_STATUS,
|
|
35
|
+
status: 'info',
|
|
36
|
+
message: `Transaction init status: ${txnInitStatusCode}`,
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
31
40
|
if (txnInitStatusCode === 400 && txnInitError?.code === ERROR_CODE.TXN_LIMIT_EXCEEDED) {
|
|
32
41
|
return {
|
|
33
42
|
error_code: ERROR_CODE.TXN_LIMIT_EXCEEDED,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import postTransactionCommit from '../api/transaction/post-transaction-commit';
|
|
2
2
|
import { SDK_STATUS_CODE } from '../constants/constant';
|
|
3
|
-
import { ERROR_CODE } from '../constants/enums';
|
|
3
|
+
import { CALLBACK_TYPE, ERROR_CODE } from '../constants/enums';
|
|
4
4
|
import EkaScribeStore from '../store/store';
|
|
5
5
|
const retryUploadFailedFiles = async ({ force_commit, }) => {
|
|
6
6
|
try {
|
|
7
7
|
const fileManagerInstance = EkaScribeStore.audioFileManagerInstance;
|
|
8
|
+
const onEventCallback = EkaScribeStore.eventCallback;
|
|
8
9
|
if (!fileManagerInstance) {
|
|
9
10
|
throw new Error('Class instances are not initialized');
|
|
10
11
|
}
|
|
@@ -28,6 +29,17 @@ const retryUploadFailedFiles = async ({ force_commit, }) => {
|
|
|
28
29
|
txnId: EkaScribeStore.txnID,
|
|
29
30
|
audioFiles,
|
|
30
31
|
});
|
|
32
|
+
if (onEventCallback) {
|
|
33
|
+
onEventCallback({
|
|
34
|
+
callback_type: CALLBACK_TYPE.TRANSACTION_STATUS,
|
|
35
|
+
status: 'info',
|
|
36
|
+
message: `Transaction commit status: ${txnCommitStatusCode}`,
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
data: {
|
|
39
|
+
request: audioFiles,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
31
43
|
if (txnCommitStatusCode != 200) {
|
|
32
44
|
return {
|
|
33
45
|
error_code: ERROR_CODE.TXN_COMMIT_FAILED,
|
package/dist/store/store.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AudioBufferManager from '../audio-chunker/audio-buffer-manager';
|
|
2
2
|
import AudioFileManager from '../audio-chunker/audio-file-manager';
|
|
3
3
|
import VadWebClient from '../audio-chunker/vad-web';
|
|
4
|
-
import {
|
|
4
|
+
import { TEventCallback, TSessionStatus } from '../constants/types';
|
|
5
5
|
declare class EkaScribeStore {
|
|
6
6
|
private static instance;
|
|
7
7
|
private _txnID;
|
|
@@ -10,8 +10,8 @@ declare class EkaScribeStore {
|
|
|
10
10
|
private _audioFileManagerInstance;
|
|
11
11
|
private _audioBufferInstance;
|
|
12
12
|
private _sessionStatus;
|
|
13
|
-
private _errorCallback;
|
|
14
13
|
private _userSpeechCallback;
|
|
14
|
+
private _eventCallback;
|
|
15
15
|
static getInstance(): EkaScribeStore;
|
|
16
16
|
get vadInstance(): VadWebClient | null;
|
|
17
17
|
set vadInstance(value: VadWebClient);
|
|
@@ -25,10 +25,10 @@ declare class EkaScribeStore {
|
|
|
25
25
|
set sessionBucketPath(value: string);
|
|
26
26
|
get sessionStatus(): TSessionStatus;
|
|
27
27
|
set sessionStatus(value: TSessionStatus);
|
|
28
|
-
get errorCallback(): TErrorCallback | null;
|
|
29
|
-
set errorCallback(callback: TErrorCallback | null);
|
|
30
28
|
get userSpeechCallback(): ((isSpeech: boolean) => void) | null;
|
|
31
29
|
set userSpeechCallback(callback: ((isSpeech: boolean) => void) | null);
|
|
30
|
+
get eventCallback(): TEventCallback | null;
|
|
31
|
+
set eventCallback(callback: TEventCallback | null);
|
|
32
32
|
resetStore(): void;
|
|
33
33
|
}
|
|
34
34
|
declare const _default: EkaScribeStore;
|
package/dist/store/store.js
CHANGED
|
@@ -36,13 +36,13 @@ class EkaScribeStore {
|
|
|
36
36
|
writable: true,
|
|
37
37
|
value: {}
|
|
38
38
|
});
|
|
39
|
-
Object.defineProperty(this, "
|
|
39
|
+
Object.defineProperty(this, "_userSpeechCallback", {
|
|
40
40
|
enumerable: true,
|
|
41
41
|
configurable: true,
|
|
42
42
|
writable: true,
|
|
43
43
|
value: null
|
|
44
44
|
});
|
|
45
|
-
Object.defineProperty(this, "
|
|
45
|
+
Object.defineProperty(this, "_eventCallback", {
|
|
46
46
|
enumerable: true,
|
|
47
47
|
configurable: true,
|
|
48
48
|
writable: true,
|
|
@@ -97,13 +97,6 @@ class EkaScribeStore {
|
|
|
97
97
|
set sessionStatus(value) {
|
|
98
98
|
this._sessionStatus = value;
|
|
99
99
|
}
|
|
100
|
-
// Error Callback
|
|
101
|
-
get errorCallback() {
|
|
102
|
-
return this._errorCallback;
|
|
103
|
-
}
|
|
104
|
-
set errorCallback(callback) {
|
|
105
|
-
this._errorCallback = callback;
|
|
106
|
-
}
|
|
107
100
|
// User Speech Callback
|
|
108
101
|
get userSpeechCallback() {
|
|
109
102
|
return this._userSpeechCallback;
|
|
@@ -111,14 +104,21 @@ class EkaScribeStore {
|
|
|
111
104
|
set userSpeechCallback(callback) {
|
|
112
105
|
this._userSpeechCallback = callback;
|
|
113
106
|
}
|
|
107
|
+
// Event Callback
|
|
108
|
+
get eventCallback() {
|
|
109
|
+
return this._eventCallback;
|
|
110
|
+
}
|
|
111
|
+
set eventCallback(callback) {
|
|
112
|
+
this._eventCallback = callback;
|
|
113
|
+
}
|
|
114
114
|
// Reset store to initial state
|
|
115
115
|
resetStore() {
|
|
116
116
|
this._txnID = '';
|
|
117
117
|
this._sessionBucketPath = '';
|
|
118
118
|
this._sessionStatus = {};
|
|
119
119
|
// Clear callbacks
|
|
120
|
-
this._errorCallback = null;
|
|
121
120
|
this._userSpeechCallback = null;
|
|
121
|
+
this._eventCallback = null;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
export default EkaScribeStore.getInstance();
|