sera-ai 1.0.13 → 1.0.15
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 +58 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -689,7 +689,7 @@ var useAudioRecovery = (reprocessSession) => {
|
|
|
689
689
|
const store = transaction.objectStore(STORE_NAME);
|
|
690
690
|
const session = {
|
|
691
691
|
id: sessionId,
|
|
692
|
-
audioChunks:
|
|
692
|
+
audioChunks: {},
|
|
693
693
|
metadata: {
|
|
694
694
|
...metadata,
|
|
695
695
|
timestamp: Date.now(),
|
|
@@ -2692,9 +2692,11 @@ var createAudioProcessorWorker = () => {
|
|
|
2692
2692
|
}
|
|
2693
2693
|
|
|
2694
2694
|
if (event.data.command === "resetUploadChunk") {
|
|
2695
|
+
// Only reset the upload flags, NOT the buffer
|
|
2696
|
+
// The buffer is cleared after chunk data is extracted and sent
|
|
2695
2697
|
this._uploadChunk = false;
|
|
2696
2698
|
this._uploadingChunk = false;
|
|
2697
|
-
|
|
2699
|
+
// NOTE: Do NOT clear buffer here - it's cleared in the chunk upload logic after data is sent
|
|
2698
2700
|
}
|
|
2699
2701
|
|
|
2700
2702
|
if (event.data.command === "pause") {
|
|
@@ -2842,6 +2844,7 @@ var useAudioRecorder = ({
|
|
|
2842
2844
|
const [selectedModel, setSelectedModel] = React3__default.useState("new-large");
|
|
2843
2845
|
const [isRetryingSession, setIsRetryingSession] = React3__default.useState(false);
|
|
2844
2846
|
const [showRetrySessionPrompt, setShowRetrySessionPrompt] = React3__default.useState(false);
|
|
2847
|
+
const sessionHasFailedChunkRef = React3__default.useRef(false);
|
|
2845
2848
|
const effectiveApiKey = apiKey;
|
|
2846
2849
|
const {
|
|
2847
2850
|
convertTranscriptionResponse,
|
|
@@ -2867,8 +2870,8 @@ var useAudioRecorder = ({
|
|
|
2867
2870
|
markSessionComplete,
|
|
2868
2871
|
markSessionFailed,
|
|
2869
2872
|
retrySession,
|
|
2873
|
+
deleteSession,
|
|
2870
2874
|
getFailedSession,
|
|
2871
|
-
hasFailedSession,
|
|
2872
2875
|
clearFailedSessions
|
|
2873
2876
|
} = useAudioRecovery_default(async (audioChunks, metadata) => {
|
|
2874
2877
|
try {
|
|
@@ -3031,15 +3034,6 @@ var useAudioRecorder = ({
|
|
|
3031
3034
|
console.log("Speciality set, microphone validation will happen on recording start");
|
|
3032
3035
|
}
|
|
3033
3036
|
}, [speciality]);
|
|
3034
|
-
React3__default.useEffect(() => {
|
|
3035
|
-
const checkFailedSessions = async () => {
|
|
3036
|
-
const failedSession = await hasFailedSession();
|
|
3037
|
-
if (failedSession) {
|
|
3038
|
-
setShowRetrySessionPrompt(true);
|
|
3039
|
-
}
|
|
3040
|
-
};
|
|
3041
|
-
checkFailedSessions();
|
|
3042
|
-
}, [hasFailedSession]);
|
|
3043
3037
|
const clearAllSessions = React3__default.useCallback(async () => {
|
|
3044
3038
|
await clearFailedSessions();
|
|
3045
3039
|
setShowRetrySessionPrompt(false);
|
|
@@ -3054,8 +3048,8 @@ var useAudioRecorder = ({
|
|
|
3054
3048
|
isFinalChunk,
|
|
3055
3049
|
sequence,
|
|
3056
3050
|
audioDataLength: audioData?.length,
|
|
3057
|
-
requestFormat: selectedFormatRef.current
|
|
3058
|
-
|
|
3051
|
+
requestFormat: selectedFormatRef.current,
|
|
3052
|
+
sessionHasFailed: sessionHasFailedChunkRef.current
|
|
3059
3053
|
});
|
|
3060
3054
|
processorRef.current?.port.postMessage({ command: "resetUploadChunk" });
|
|
3061
3055
|
if (audioData && localSessionIdRef.current && !retry) {
|
|
@@ -3069,6 +3063,14 @@ var useAudioRecorder = ({
|
|
|
3069
3063
|
} else {
|
|
3070
3064
|
console.log(`[DB] Skipping IndexedDB save: audioData=${!!audioData}, sessionId=${localSessionIdRef.current}, retry=${retry}`);
|
|
3071
3065
|
}
|
|
3066
|
+
if (sessionHasFailedChunkRef.current && !retry) {
|
|
3067
|
+
console.log(`[SKIP] Session has failed chunk - skipping server upload for sequence ${sequence}, continuing to record audio`);
|
|
3068
|
+
if (isFinalChunk) {
|
|
3069
|
+
setShowRetrySessionPrompt(true);
|
|
3070
|
+
setIsProcessing(false);
|
|
3071
|
+
}
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3072
3074
|
try {
|
|
3073
3075
|
const data = await pRetry(
|
|
3074
3076
|
async (attemptNumber) => {
|
|
@@ -3317,11 +3319,13 @@ var useAudioRecorder = ({
|
|
|
3317
3319
|
}
|
|
3318
3320
|
const isAbortError = err instanceof Error && err.name === "AbortError";
|
|
3319
3321
|
const statusCode = err instanceof Error && err.message.includes("HTTP") ? parseInt(err.message.split("HTTP")[1].trim()) : null;
|
|
3320
|
-
if (localSessionIdRef.current) {
|
|
3322
|
+
if (localSessionIdRef.current && !retry) {
|
|
3323
|
+
sessionHasFailedChunkRef.current = true;
|
|
3321
3324
|
await markSessionFailed(
|
|
3322
3325
|
localSessionIdRef.current,
|
|
3323
3326
|
err instanceof Error ? err.message : "Unknown error"
|
|
3324
3327
|
);
|
|
3328
|
+
console.log(`[FAIL] Chunk ${sequence} failed - session marked as failed, will continue recording audio locally`);
|
|
3325
3329
|
if (isFinalChunk) {
|
|
3326
3330
|
setShowRetrySessionPrompt(true);
|
|
3327
3331
|
}
|
|
@@ -3387,16 +3391,26 @@ var useAudioRecorder = ({
|
|
|
3387
3391
|
const success = await retrySession(failedSession.id);
|
|
3388
3392
|
if (success) {
|
|
3389
3393
|
console.log(`Successfully retried session ${failedSession.id}`);
|
|
3394
|
+
await deleteSession(failedSession.id);
|
|
3395
|
+
console.log(`Deleted session ${failedSession.id} from IndexedDB after successful retry`);
|
|
3390
3396
|
setError(null);
|
|
3397
|
+
setShowRetrySessionPrompt(false);
|
|
3398
|
+
} else {
|
|
3399
|
+
console.log(`Retry failed for session ${failedSession.id} - keeping retry UI visible`);
|
|
3400
|
+
setShowRetrySessionPrompt(true);
|
|
3401
|
+
setError("Retry failed. Please check your connection and try again.");
|
|
3391
3402
|
}
|
|
3403
|
+
} else {
|
|
3404
|
+
setShowRetrySessionPrompt(false);
|
|
3392
3405
|
}
|
|
3393
3406
|
} catch (error2) {
|
|
3394
3407
|
console.error("Error retrying failed sessions:", error2);
|
|
3395
3408
|
setError("Failed to retry sessions. Please try again.");
|
|
3409
|
+
setShowRetrySessionPrompt(true);
|
|
3396
3410
|
} finally {
|
|
3397
3411
|
setIsRetryingSession(false);
|
|
3398
3412
|
}
|
|
3399
|
-
}, [retrySession, getFailedSession]);
|
|
3413
|
+
}, [retrySession, getFailedSession, deleteSession]);
|
|
3400
3414
|
const startRecording = React3__default.useCallback(async () => {
|
|
3401
3415
|
try {
|
|
3402
3416
|
setError(null);
|
|
@@ -3412,16 +3426,12 @@ var useAudioRecorder = ({
|
|
|
3412
3426
|
sequenceCounterRef.current = 0;
|
|
3413
3427
|
nextExpectedSequenceRef.current = 0;
|
|
3414
3428
|
receivedTranscriptionsRef.current.clear();
|
|
3429
|
+
sessionHasFailedChunkRef.current = false;
|
|
3430
|
+
chunkQueueRef.current = [];
|
|
3415
3431
|
const localSessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3416
3432
|
localSessionIdRef.current = localSessionId;
|
|
3417
3433
|
sessionIdRef.current = null;
|
|
3418
3434
|
console.log("Created local session ID for IndexedDB:", localSessionId);
|
|
3419
|
-
await createSession(localSessionId, {
|
|
3420
|
-
patientId,
|
|
3421
|
-
patientName: patientName || void 0,
|
|
3422
|
-
patientHistory: patientHistory || void 0,
|
|
3423
|
-
speciality
|
|
3424
|
-
});
|
|
3425
3435
|
}
|
|
3426
3436
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
3427
3437
|
audio: {
|
|
@@ -3445,6 +3455,15 @@ var useAudioRecorder = ({
|
|
|
3445
3455
|
});
|
|
3446
3456
|
}
|
|
3447
3457
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
3458
|
+
if (!isPaused && localSessionIdRef.current) {
|
|
3459
|
+
await createSession(localSessionIdRef.current, {
|
|
3460
|
+
patientId,
|
|
3461
|
+
patientName: patientName || void 0,
|
|
3462
|
+
patientHistory: patientHistory || void 0,
|
|
3463
|
+
speciality,
|
|
3464
|
+
sampleRate: audioContext.sampleRate
|
|
3465
|
+
});
|
|
3466
|
+
}
|
|
3448
3467
|
const processorUrl = createAudioProcessorWorker();
|
|
3449
3468
|
await audioContext.audioWorklet.addModule(processorUrl);
|
|
3450
3469
|
URL.revokeObjectURL(processorUrl);
|
|
@@ -3550,6 +3569,14 @@ var useAudioRecorder = ({
|
|
|
3550
3569
|
}, 500);
|
|
3551
3570
|
}
|
|
3552
3571
|
setIsRecording(false);
|
|
3572
|
+
console.log("\u{1F50D} Stop recording - checking for failed session:", {
|
|
3573
|
+
hasSessionFailed: sessionHasFailedChunkRef.current,
|
|
3574
|
+
localSessionId: localSessionIdRef.current
|
|
3575
|
+
});
|
|
3576
|
+
if (sessionHasFailedChunkRef.current && localSessionIdRef.current) {
|
|
3577
|
+
console.log("\u26A0\uFE0F Recording stopped with failed session - showing retry UI");
|
|
3578
|
+
setShowRetrySessionPrompt(true);
|
|
3579
|
+
}
|
|
3553
3580
|
}, [uploadChunkInterval]);
|
|
3554
3581
|
React3__default.useEffect(() => {
|
|
3555
3582
|
const handleDeviceChange = async () => {
|
|
@@ -3623,13 +3650,14 @@ var useAudioRecorder = ({
|
|
|
3623
3650
|
}, 47e3);
|
|
3624
3651
|
setUploadChunkInterval(intervalId);
|
|
3625
3652
|
}, [isPaused]);
|
|
3626
|
-
const processNextChunkInQueue = React3__default.useCallback(() => {
|
|
3627
|
-
if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0)
|
|
3653
|
+
const processNextChunkInQueue = React3__default.useCallback(async () => {
|
|
3654
|
+
if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0) {
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3628
3657
|
const { chunk, isFinal, sequence, isPaused: isPaused2 = false } = chunkQueueRef.current.shift();
|
|
3629
3658
|
console.log(`[QUEUE] Processing chunk ${sequence} from queue, remaining: ${chunkQueueRef.current.length}`);
|
|
3630
3659
|
isProcessingQueueRef.current = true;
|
|
3631
3660
|
uploadChunkToServer(chunk, isFinal, sequence, false, isPaused2).finally(() => {
|
|
3632
|
-
console.log(`[QUEUE] Finished processing chunk ${sequence}`);
|
|
3633
3661
|
isProcessingQueueRef.current = false;
|
|
3634
3662
|
processNextChunkInQueue();
|
|
3635
3663
|
});
|
|
@@ -3638,7 +3666,11 @@ var useAudioRecorder = ({
|
|
|
3638
3666
|
(audioData, isFinalChunk, sequence, isPausedChunk = false) => {
|
|
3639
3667
|
console.log(`[QUEUE] Enqueuing ${isFinalChunk ? "FINAL" : isPausedChunk ? "PAUSED" : "regular"} chunk ${sequence}, samples: ${audioData?.length || 0}, queue size: ${chunkQueueRef.current.length}`);
|
|
3640
3668
|
if (isFinalChunk) {
|
|
3641
|
-
|
|
3669
|
+
if (!sessionHasFailedChunkRef.current) {
|
|
3670
|
+
setIsProcessing(true);
|
|
3671
|
+
} else {
|
|
3672
|
+
console.log("\u26A0\uFE0F Session has failed - skipping processing state (IndexedDB save only)");
|
|
3673
|
+
}
|
|
3642
3674
|
}
|
|
3643
3675
|
chunkQueueRef.current.push({
|
|
3644
3676
|
chunk: audioData,
|