sera-ai 1.0.13 → 1.0.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/index.js +46 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +46 -25
- 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,
|
|
@@ -2892,7 +2895,6 @@ var useAudioRecorder = ({
|
|
|
2892
2895
|
markSessionFailed,
|
|
2893
2896
|
retrySession,
|
|
2894
2897
|
getFailedSession,
|
|
2895
|
-
hasFailedSession,
|
|
2896
2898
|
clearFailedSessions
|
|
2897
2899
|
} = useAudioRecovery_default(async (audioChunks, metadata) => {
|
|
2898
2900
|
try {
|
|
@@ -3055,15 +3057,6 @@ var useAudioRecorder = ({
|
|
|
3055
3057
|
console.log("Speciality set, microphone validation will happen on recording start");
|
|
3056
3058
|
}
|
|
3057
3059
|
}, [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
3060
|
const clearAllSessions = React3__namespace.default.useCallback(async () => {
|
|
3068
3061
|
await clearFailedSessions();
|
|
3069
3062
|
setShowRetrySessionPrompt(false);
|
|
@@ -3078,8 +3071,8 @@ var useAudioRecorder = ({
|
|
|
3078
3071
|
isFinalChunk,
|
|
3079
3072
|
sequence,
|
|
3080
3073
|
audioDataLength: audioData?.length,
|
|
3081
|
-
requestFormat: selectedFormatRef.current
|
|
3082
|
-
|
|
3074
|
+
requestFormat: selectedFormatRef.current,
|
|
3075
|
+
sessionHasFailed: sessionHasFailedChunkRef.current
|
|
3083
3076
|
});
|
|
3084
3077
|
processorRef.current?.port.postMessage({ command: "resetUploadChunk" });
|
|
3085
3078
|
if (audioData && localSessionIdRef.current && !retry) {
|
|
@@ -3093,6 +3086,14 @@ var useAudioRecorder = ({
|
|
|
3093
3086
|
} else {
|
|
3094
3087
|
console.log(`[DB] Skipping IndexedDB save: audioData=${!!audioData}, sessionId=${localSessionIdRef.current}, retry=${retry}`);
|
|
3095
3088
|
}
|
|
3089
|
+
if (sessionHasFailedChunkRef.current && !retry) {
|
|
3090
|
+
console.log(`[SKIP] Session has failed chunk - skipping server upload for sequence ${sequence}, continuing to record audio`);
|
|
3091
|
+
if (isFinalChunk) {
|
|
3092
|
+
setShowRetrySessionPrompt(true);
|
|
3093
|
+
setIsProcessing(false);
|
|
3094
|
+
}
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3096
3097
|
try {
|
|
3097
3098
|
const data = await pRetry__default.default(
|
|
3098
3099
|
async (attemptNumber) => {
|
|
@@ -3341,11 +3342,13 @@ var useAudioRecorder = ({
|
|
|
3341
3342
|
}
|
|
3342
3343
|
const isAbortError = err instanceof Error && err.name === "AbortError";
|
|
3343
3344
|
const statusCode = err instanceof Error && err.message.includes("HTTP") ? parseInt(err.message.split("HTTP")[1].trim()) : null;
|
|
3344
|
-
if (localSessionIdRef.current) {
|
|
3345
|
+
if (localSessionIdRef.current && !retry) {
|
|
3346
|
+
sessionHasFailedChunkRef.current = true;
|
|
3345
3347
|
await markSessionFailed(
|
|
3346
3348
|
localSessionIdRef.current,
|
|
3347
3349
|
err instanceof Error ? err.message : "Unknown error"
|
|
3348
3350
|
);
|
|
3351
|
+
console.log(`[FAIL] Chunk ${sequence} failed - session marked as failed, will continue recording audio locally`);
|
|
3349
3352
|
if (isFinalChunk) {
|
|
3350
3353
|
setShowRetrySessionPrompt(true);
|
|
3351
3354
|
}
|
|
@@ -3436,16 +3439,12 @@ var useAudioRecorder = ({
|
|
|
3436
3439
|
sequenceCounterRef.current = 0;
|
|
3437
3440
|
nextExpectedSequenceRef.current = 0;
|
|
3438
3441
|
receivedTranscriptionsRef.current.clear();
|
|
3442
|
+
sessionHasFailedChunkRef.current = false;
|
|
3443
|
+
chunkQueueRef.current = [];
|
|
3439
3444
|
const localSessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3440
3445
|
localSessionIdRef.current = localSessionId;
|
|
3441
3446
|
sessionIdRef.current = null;
|
|
3442
3447
|
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
3448
|
}
|
|
3450
3449
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
3451
3450
|
audio: {
|
|
@@ -3469,6 +3468,15 @@ var useAudioRecorder = ({
|
|
|
3469
3468
|
});
|
|
3470
3469
|
}
|
|
3471
3470
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
3471
|
+
if (!isPaused && localSessionIdRef.current) {
|
|
3472
|
+
await createSession(localSessionIdRef.current, {
|
|
3473
|
+
patientId,
|
|
3474
|
+
patientName: patientName || void 0,
|
|
3475
|
+
patientHistory: patientHistory || void 0,
|
|
3476
|
+
speciality,
|
|
3477
|
+
sampleRate: audioContext.sampleRate
|
|
3478
|
+
});
|
|
3479
|
+
}
|
|
3472
3480
|
const processorUrl = createAudioProcessorWorker();
|
|
3473
3481
|
await audioContext.audioWorklet.addModule(processorUrl);
|
|
3474
3482
|
URL.revokeObjectURL(processorUrl);
|
|
@@ -3574,6 +3582,14 @@ var useAudioRecorder = ({
|
|
|
3574
3582
|
}, 500);
|
|
3575
3583
|
}
|
|
3576
3584
|
setIsRecording(false);
|
|
3585
|
+
console.log("\u{1F50D} Stop recording - checking for failed session:", {
|
|
3586
|
+
hasSessionFailed: sessionHasFailedChunkRef.current,
|
|
3587
|
+
localSessionId: localSessionIdRef.current
|
|
3588
|
+
});
|
|
3589
|
+
if (sessionHasFailedChunkRef.current && localSessionIdRef.current) {
|
|
3590
|
+
console.log("\u26A0\uFE0F Recording stopped with failed session - showing retry UI");
|
|
3591
|
+
setShowRetrySessionPrompt(true);
|
|
3592
|
+
}
|
|
3577
3593
|
}, [uploadChunkInterval]);
|
|
3578
3594
|
React3__namespace.default.useEffect(() => {
|
|
3579
3595
|
const handleDeviceChange = async () => {
|
|
@@ -3647,13 +3663,14 @@ var useAudioRecorder = ({
|
|
|
3647
3663
|
}, 47e3);
|
|
3648
3664
|
setUploadChunkInterval(intervalId);
|
|
3649
3665
|
}, [isPaused]);
|
|
3650
|
-
const processNextChunkInQueue = React3__namespace.default.useCallback(() => {
|
|
3651
|
-
if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0)
|
|
3666
|
+
const processNextChunkInQueue = React3__namespace.default.useCallback(async () => {
|
|
3667
|
+
if (isProcessingQueueRef.current || chunkQueueRef.current.length === 0) {
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3652
3670
|
const { chunk, isFinal, sequence, isPaused: isPaused2 = false } = chunkQueueRef.current.shift();
|
|
3653
3671
|
console.log(`[QUEUE] Processing chunk ${sequence} from queue, remaining: ${chunkQueueRef.current.length}`);
|
|
3654
3672
|
isProcessingQueueRef.current = true;
|
|
3655
3673
|
uploadChunkToServer(chunk, isFinal, sequence, false, isPaused2).finally(() => {
|
|
3656
|
-
console.log(`[QUEUE] Finished processing chunk ${sequence}`);
|
|
3657
3674
|
isProcessingQueueRef.current = false;
|
|
3658
3675
|
processNextChunkInQueue();
|
|
3659
3676
|
});
|
|
@@ -3662,7 +3679,11 @@ var useAudioRecorder = ({
|
|
|
3662
3679
|
(audioData, isFinalChunk, sequence, isPausedChunk = false) => {
|
|
3663
3680
|
console.log(`[QUEUE] Enqueuing ${isFinalChunk ? "FINAL" : isPausedChunk ? "PAUSED" : "regular"} chunk ${sequence}, samples: ${audioData?.length || 0}, queue size: ${chunkQueueRef.current.length}`);
|
|
3664
3681
|
if (isFinalChunk) {
|
|
3665
|
-
|
|
3682
|
+
if (!sessionHasFailedChunkRef.current) {
|
|
3683
|
+
setIsProcessing(true);
|
|
3684
|
+
} else {
|
|
3685
|
+
console.log("\u26A0\uFE0F Session has failed - skipping processing state (IndexedDB save only)");
|
|
3686
|
+
}
|
|
3666
3687
|
}
|
|
3667
3688
|
chunkQueueRef.current.push({
|
|
3668
3689
|
chunk: audioData,
|