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.js
CHANGED
|
@@ -713,7 +713,7 @@ var useAudioRecovery = (reprocessSession) => {
|
|
|
713
713
|
const store = transaction.objectStore(STORE_NAME);
|
|
714
714
|
const session = {
|
|
715
715
|
id: sessionId,
|
|
716
|
-
audioChunks:
|
|
716
|
+
audioChunks: {},
|
|
717
717
|
metadata: {
|
|
718
718
|
...metadata,
|
|
719
719
|
timestamp: Date.now(),
|
|
@@ -2716,9 +2716,11 @@ var createAudioProcessorWorker = () => {
|
|
|
2716
2716
|
}
|
|
2717
2717
|
|
|
2718
2718
|
if (event.data.command === "resetUploadChunk") {
|
|
2719
|
+
// Only reset the upload flags, NOT the buffer
|
|
2720
|
+
// The buffer is cleared after chunk data is extracted and sent
|
|
2719
2721
|
this._uploadChunk = false;
|
|
2720
2722
|
this._uploadingChunk = false;
|
|
2721
|
-
|
|
2723
|
+
// NOTE: Do NOT clear buffer here - it's cleared in the chunk upload logic after data is sent
|
|
2722
2724
|
}
|
|
2723
2725
|
|
|
2724
2726
|
if (event.data.command === "pause") {
|
|
@@ -2866,6 +2868,7 @@ var useAudioRecorder = ({
|
|
|
2866
2868
|
const [selectedModel, setSelectedModel] = React3__namespace.default.useState("new-large");
|
|
2867
2869
|
const [isRetryingSession, setIsRetryingSession] = React3__namespace.default.useState(false);
|
|
2868
2870
|
const [showRetrySessionPrompt, setShowRetrySessionPrompt] = React3__namespace.default.useState(false);
|
|
2871
|
+
const sessionHasFailedChunkRef = React3__namespace.default.useRef(false);
|
|
2869
2872
|
const effectiveApiKey = apiKey;
|
|
2870
2873
|
const {
|
|
2871
2874
|
convertTranscriptionResponse,
|
|
@@ -2891,8 +2894,8 @@ var useAudioRecorder = ({
|
|
|
2891
2894
|
markSessionComplete,
|
|
2892
2895
|
markSessionFailed,
|
|
2893
2896
|
retrySession,
|
|
2897
|
+
deleteSession,
|
|
2894
2898
|
getFailedSession,
|
|
2895
|
-
hasFailedSession,
|
|
2896
2899
|
clearFailedSessions
|
|
2897
2900
|
} = useAudioRecovery_default(async (audioChunks, metadata) => {
|
|
2898
2901
|
try {
|
|
@@ -3055,15 +3058,6 @@ var useAudioRecorder = ({
|
|
|
3055
3058
|
console.log("Speciality set, microphone validation will happen on recording start");
|
|
3056
3059
|
}
|
|
3057
3060
|
}, [speciality]);
|
|
3058
|
-
React3__namespace.default.useEffect(() => {
|
|
3059
|
-
const checkFailedSessions = async () => {
|
|
3060
|
-
const failedSession = await hasFailedSession();
|
|
3061
|
-
if (failedSession) {
|
|
3062
|
-
setShowRetrySessionPrompt(true);
|
|
3063
|
-
}
|
|
3064
|
-
};
|
|
3065
|
-
checkFailedSessions();
|
|
3066
|
-
}, [hasFailedSession]);
|
|
3067
3061
|
const clearAllSessions = React3__namespace.default.useCallback(async () => {
|
|
3068
3062
|
await clearFailedSessions();
|
|
3069
3063
|
setShowRetrySessionPrompt(false);
|
|
@@ -3078,8 +3072,8 @@ var useAudioRecorder = ({
|
|
|
3078
3072
|
isFinalChunk,
|
|
3079
3073
|
sequence,
|
|
3080
3074
|
audioDataLength: audioData?.length,
|
|
3081
|
-
requestFormat: selectedFormatRef.current
|
|
3082
|
-
|
|
3075
|
+
requestFormat: selectedFormatRef.current,
|
|
3076
|
+
sessionHasFailed: sessionHasFailedChunkRef.current
|
|
3083
3077
|
});
|
|
3084
3078
|
processorRef.current?.port.postMessage({ command: "resetUploadChunk" });
|
|
3085
3079
|
if (audioData && localSessionIdRef.current && !retry) {
|
|
@@ -3093,6 +3087,14 @@ var useAudioRecorder = ({
|
|
|
3093
3087
|
} else {
|
|
3094
3088
|
console.log(`[DB] Skipping IndexedDB save: audioData=${!!audioData}, sessionId=${localSessionIdRef.current}, retry=${retry}`);
|
|
3095
3089
|
}
|
|
3090
|
+
if (sessionHasFailedChunkRef.current && !retry) {
|
|
3091
|
+
console.log(`[SKIP] Session has failed chunk - skipping server upload for sequence ${sequence}, continuing to record audio`);
|
|
3092
|
+
if (isFinalChunk) {
|
|
3093
|
+
setShowRetrySessionPrompt(true);
|
|
3094
|
+
setIsProcessing(false);
|
|
3095
|
+
}
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3096
3098
|
try {
|
|
3097
3099
|
const data = await pRetry__default.default(
|
|
3098
3100
|
async (attemptNumber) => {
|
|
@@ -3341,11 +3343,13 @@ var useAudioRecorder = ({
|
|
|
3341
3343
|
}
|
|
3342
3344
|
const isAbortError = err instanceof Error && err.name === "AbortError";
|
|
3343
3345
|
const statusCode = err instanceof Error && err.message.includes("HTTP") ? parseInt(err.message.split("HTTP")[1].trim()) : null;
|
|
3344
|
-
if (localSessionIdRef.current) {
|
|
3346
|
+
if (localSessionIdRef.current && !retry) {
|
|
3347
|
+
sessionHasFailedChunkRef.current = true;
|
|
3345
3348
|
await markSessionFailed(
|
|
3346
3349
|
localSessionIdRef.current,
|
|
3347
3350
|
err instanceof Error ? err.message : "Unknown error"
|
|
3348
3351
|
);
|
|
3352
|
+
console.log(`[FAIL] Chunk ${sequence} failed - session marked as failed, will continue recording audio locally`);
|
|
3349
3353
|
if (isFinalChunk) {
|
|
3350
3354
|
setShowRetrySessionPrompt(true);
|
|
3351
3355
|
}
|
|
@@ -3411,16 +3415,26 @@ var useAudioRecorder = ({
|
|
|
3411
3415
|
const success = await retrySession(failedSession.id);
|
|
3412
3416
|
if (success) {
|
|
3413
3417
|
console.log(`Successfully retried session ${failedSession.id}`);
|
|
3418
|
+
await deleteSession(failedSession.id);
|
|
3419
|
+
console.log(`Deleted session ${failedSession.id} from IndexedDB after successful retry`);
|
|
3414
3420
|
setError(null);
|
|
3421
|
+
setShowRetrySessionPrompt(false);
|
|
3422
|
+
} else {
|
|
3423
|
+
console.log(`Retry failed for session ${failedSession.id} - keeping retry UI visible`);
|
|
3424
|
+
setShowRetrySessionPrompt(true);
|
|
3425
|
+
setError("Retry failed. Please check your connection and try again.");
|
|
3415
3426
|
}
|
|
3427
|
+
} else {
|
|
3428
|
+
setShowRetrySessionPrompt(false);
|
|
3416
3429
|
}
|
|
3417
3430
|
} catch (error2) {
|
|
3418
3431
|
console.error("Error retrying failed sessions:", error2);
|
|
3419
3432
|
setError("Failed to retry sessions. Please try again.");
|
|
3433
|
+
setShowRetrySessionPrompt(true);
|
|
3420
3434
|
} finally {
|
|
3421
3435
|
setIsRetryingSession(false);
|
|
3422
3436
|
}
|
|
3423
|
-
}, [retrySession, getFailedSession]);
|
|
3437
|
+
}, [retrySession, getFailedSession, deleteSession]);
|
|
3424
3438
|
const startRecording = React3__namespace.default.useCallback(async () => {
|
|
3425
3439
|
try {
|
|
3426
3440
|
setError(null);
|
|
@@ -3436,16 +3450,12 @@ var useAudioRecorder = ({
|
|
|
3436
3450
|
sequenceCounterRef.current = 0;
|
|
3437
3451
|
nextExpectedSequenceRef.current = 0;
|
|
3438
3452
|
receivedTranscriptionsRef.current.clear();
|
|
3453
|
+
sessionHasFailedChunkRef.current = false;
|
|
3454
|
+
chunkQueueRef.current = [];
|
|
3439
3455
|
const localSessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3440
3456
|
localSessionIdRef.current = localSessionId;
|
|
3441
3457
|
sessionIdRef.current = null;
|
|
3442
3458
|
console.log("Created local session ID for IndexedDB:", localSessionId);
|
|
3443
|
-
await createSession(localSessionId, {
|
|
3444
|
-
patientId,
|
|
3445
|
-
patientName: patientName || void 0,
|
|
3446
|
-
patientHistory: patientHistory || void 0,
|
|
3447
|
-
speciality
|
|
3448
|
-
});
|
|
3449
3459
|
}
|
|
3450
3460
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
3451
3461
|
audio: {
|
|
@@ -3469,6 +3479,15 @@ var useAudioRecorder = ({
|
|
|
3469
3479
|
});
|
|
3470
3480
|
}
|
|
3471
3481
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
3482
|
+
if (!isPaused && localSessionIdRef.current) {
|
|
3483
|
+
await createSession(localSessionIdRef.current, {
|
|
3484
|
+
patientId,
|
|
3485
|
+
patientName: patientName || void 0,
|
|
3486
|
+
patientHistory: patientHistory || void 0,
|
|
3487
|
+
speciality,
|
|
3488
|
+
sampleRate: audioContext.sampleRate
|
|
3489
|
+
});
|
|
3490
|
+
}
|
|
3472
3491
|
const processorUrl = createAudioProcessorWorker();
|
|
3473
3492
|
await audioContext.audioWorklet.addModule(processorUrl);
|
|
3474
3493
|
URL.revokeObjectURL(processorUrl);
|
|
@@ -3574,6 +3593,14 @@ var useAudioRecorder = ({
|
|
|
3574
3593
|
}, 500);
|
|
3575
3594
|
}
|
|
3576
3595
|
setIsRecording(false);
|
|
3596
|
+
console.log("\u{1F50D} Stop recording - checking for failed session:", {
|
|
3597
|
+
hasSessionFailed: sessionHasFailedChunkRef.current,
|
|
3598
|
+
localSessionId: localSessionIdRef.current
|
|
3599
|
+
});
|
|
3600
|
+
if (sessionHasFailedChunkRef.current && localSessionIdRef.current) {
|
|
3601
|
+
console.log("\u26A0\uFE0F Recording stopped with failed session - showing retry UI");
|
|
3602
|
+
setShowRetrySessionPrompt(true);
|
|
3603
|
+
}
|
|
3577
3604
|
}, [uploadChunkInterval]);
|
|
3578
3605
|
React3__namespace.default.useEffect(() => {
|
|
3579
3606
|
const handleDeviceChange = async () => {
|
|
@@ -3647,13 +3674,14 @@ var useAudioRecorder = ({
|
|
|
3647
3674
|
}, 47e3);
|
|
3648
3675
|
setUploadChunkInterval(intervalId);
|
|
3649
3676
|
}, [isPaused]);
|
|
3650
|
-
const processNextChunkInQueue = React3__namespace.default.useCallback(() => {
|
|
3651
|
-
if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0)
|
|
3677
|
+
const processNextChunkInQueue = React3__namespace.default.useCallback(async () => {
|
|
3678
|
+
if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0) {
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3652
3681
|
const { chunk, isFinal, sequence, isPaused: isPaused2 = false } = chunkQueueRef.current.shift();
|
|
3653
3682
|
console.log(`[QUEUE] Processing chunk ${sequence} from queue, remaining: ${chunkQueueRef.current.length}`);
|
|
3654
3683
|
isProcessingQueueRef.current = true;
|
|
3655
3684
|
uploadChunkToServer(chunk, isFinal, sequence, false, isPaused2).finally(() => {
|
|
3656
|
-
console.log(`[QUEUE] Finished processing chunk ${sequence}`);
|
|
3657
3685
|
isProcessingQueueRef.current = false;
|
|
3658
3686
|
processNextChunkInQueue();
|
|
3659
3687
|
});
|
|
@@ -3662,7 +3690,11 @@ var useAudioRecorder = ({
|
|
|
3662
3690
|
(audioData, isFinalChunk, sequence, isPausedChunk = false) => {
|
|
3663
3691
|
console.log(`[QUEUE] Enqueuing ${isFinalChunk ? "FINAL" : isPausedChunk ? "PAUSED" : "regular"} chunk ${sequence}, samples: ${audioData?.length || 0}, queue size: ${chunkQueueRef.current.length}`);
|
|
3664
3692
|
if (isFinalChunk) {
|
|
3665
|
-
|
|
3693
|
+
if (!sessionHasFailedChunkRef.current) {
|
|
3694
|
+
setIsProcessing(true);
|
|
3695
|
+
} else {
|
|
3696
|
+
console.log("\u26A0\uFE0F Session has failed - skipping processing state (IndexedDB save only)");
|
|
3697
|
+
}
|
|
3666
3698
|
}
|
|
3667
3699
|
chunkQueueRef.current.push({
|
|
3668
3700
|
chunk: audioData,
|