77 generateAssessmentClosing ,
88 generateAssessmentSummary ,
99} from "@/lib/gemini" ;
10- import { writeFile , mkdir } from "fs/promises" ;
10+ import { writeFile , mkdir , access } from "fs/promises" ;
1111import path from "path" ;
1212
1313function sleep ( ms : number ) {
@@ -20,7 +20,7 @@ async function fetchRecordingWithRetry(recordingUrl: string, maxRetries = 3): Pr
2020 ) . toString ( "base64" ) } `;
2121
2222 for ( let attempt = 0 ; attempt < maxRetries ; attempt ++ ) {
23- if ( attempt > 0 ) await sleep ( 2000 ) ;
23+ if ( attempt > 0 ) await sleep ( 1000 ) ;
2424
2525 // Try .mp3 format first, then raw URL
2626 for ( const url of [ `${ recordingUrl } .mp3` , recordingUrl ] ) {
@@ -107,6 +107,24 @@ export async function POST(req: NextRequest) {
107107 const audioDir = path . join ( process . cwd ( ) , "public" , "audio" ) ;
108108 await mkdir ( audioDir , { recursive : true } ) ;
109109
110+ const nextIndex = answerIndex + 1 ;
111+ const isLastQuestion = nextIndex >= session . answers . length ;
112+
113+ // Check if next question audio is already pre-generated
114+ const nextAnswer = ! isLastQuestion ? session . answers [ nextIndex ] : null ;
115+ const pregenFileName = nextAnswer ? `assessment-q-${ nextAnswer . id } .mp3` : null ;
116+ const pregenPath = pregenFileName ? path . join ( audioDir , pregenFileName ) : null ;
117+ let nextQPregenerated = false ;
118+ if ( pregenPath ) {
119+ try {
120+ await access ( pregenPath ) ;
121+ nextQPregenerated = true ;
122+ } catch {
123+ nextQPregenerated = false ;
124+ }
125+ }
126+
127+ // Transcribe recording
110128 let elderAnswer = "" ;
111129 if ( recordingUrl ) {
112130 try {
@@ -118,6 +136,7 @@ export async function POST(req: NextRequest) {
118136 }
119137 }
120138
139+ // Evaluate answer + generate response TTS in parallel where possible
121140 let result : "CORRECT" | "WRONG" | "UNCLEAR" = "UNCLEAR" ;
122141 let responseText = "I didn't quite catch that, but that's okay. Let's continue." ;
123142
@@ -136,18 +155,17 @@ export async function POST(req: NextRequest) {
136155 }
137156 }
138157
139- await prisma . assessmentAnswer . update ( {
140- where : { id : currentAnswer . id } ,
141- data : { elderAnswer : elderAnswer || null , result } ,
142- } ) ;
143-
144- const responseAudio = await generateAudioOrFallback (
145- responseText , voiceId ,
146- `assessment-resp-${ currentAnswer . id } .mp3` , audioDir , baseUrl !
147- ) ;
148-
149- const nextIndex = answerIndex + 1 ;
150- const isLastQuestion = nextIndex >= session . answers . length ;
158+ // Run DB update and response TTS in parallel
159+ const [ , responseAudio ] = await Promise . all ( [
160+ prisma . assessmentAnswer . update ( {
161+ where : { id : currentAnswer . id } ,
162+ data : { elderAnswer : elderAnswer || null , result } ,
163+ } ) ,
164+ generateAudioOrFallback (
165+ responseText , voiceId ,
166+ `assessment-resp-${ currentAnswer . id } .mp3` , audioDir , baseUrl !
167+ ) ,
168+ ] ) ;
151169
152170 if ( isLastQuestion ) {
153171 const allAnswers = await prisma . assessmentAnswer . findMany ( {
@@ -221,24 +239,29 @@ export async function POST(req: NextRequest) {
221239 } ) ;
222240 }
223241
224- const nextAnswer = session . answers [ nextIndex ] ;
225- let nextQText = `Question ${ nextIndex + 1 } : ${ nextAnswer . questionText } ` ;
226- try {
227- nextQText = await generateAssessmentQuestionAudio ( {
228- elderlyName : profile . name ,
229- questionText : nextAnswer . questionText ,
230- questionNumber : nextIndex + 1 ,
231- totalQuestions : session . answers . length ,
232- language : profile . language ,
233- } ) ;
234- } catch ( err ) {
235- console . error ( "Question audio generation failed:" , err ) ;
236- }
242+ // Use pre-generated question audio if available, otherwise generate on-the-fly
243+ let nextQAudio : { url : string ; usedTts : boolean } ;
244+ let nextQText = `Question ${ nextIndex + 1 } : ${ nextAnswer ! . questionText } ` ;
237245
238- const nextQAudio = await generateAudioOrFallback (
239- nextQText , voiceId ,
240- `assessment-q-${ nextAnswer . id } .mp3` , audioDir , baseUrl !
241- ) ;
246+ if ( nextQPregenerated ) {
247+ nextQAudio = { url : `${ baseUrl } /api/audio/${ pregenFileName } ` , usedTts : true } ;
248+ } else {
249+ try {
250+ nextQText = await generateAssessmentQuestionAudio ( {
251+ elderlyName : profile . name ,
252+ questionText : nextAnswer ! . questionText ,
253+ questionNumber : nextIndex + 1 ,
254+ totalQuestions : session . answers . length ,
255+ language : profile . language ,
256+ } ) ;
257+ } catch ( err ) {
258+ console . error ( "Question audio generation failed:" , err ) ;
259+ }
260+ nextQAudio = await generateAudioOrFallback (
261+ nextQText , voiceId ,
262+ `assessment-q-${ nextAnswer ! . id } .mp3` , audioDir , baseUrl !
263+ ) ;
264+ }
242265
243266 const fillerIndex = Math . floor ( Math . random ( ) * 5 ) ;
244267 const fillerVoice = ( voiceId || "21m00Tcm4TlvDq8ikWAM" ) . slice ( 0 , 8 ) ;
0 commit comments