arshenoy commited on
Commit
a4b5979
·
verified ·
1 Parent(s): 9647ddc

logic fix: TTS

Browse files
Files changed (1) hide show
  1. services/geminiService.ts +21 -13
services/geminiService.ts CHANGED
@@ -29,10 +29,10 @@ const ai = new GoogleGenAI({ apiKey: API_KEY });
29
 
30
  // --- TIERED MODEL STRATEGY ---
31
  // 1. Primary: Gemini 2.5 Flash (Highest Quality/Speed Balance)
32
- // 2. Secondary: Gemini 2.5 Flash-Lite (Quota Rescue)
33
  // 3. Tertiary: Local/HuggingFace Backends (Privacy/Offline/No-Quota Fallback)
34
  const MODEL_PRIMARY = 'gemini-2.5-flash';
35
- const MODEL_SECONDARY = 'gemini-2.5-flash-lite';
36
  const MODEL_TTS = 'gemini-2.5-flash-preview-tts';
37
 
38
  // --- UTILITIES ---
@@ -108,7 +108,7 @@ const callBackend = async (baseUrl: string, endpoint: string, payload: any, onSt
108
  if (typeof data === 'string') return data;
109
  if (data.text) return data.text;
110
  if (data.response) return data.response;
111
- if (data.audio) return data.audio; // For TTS
112
 
113
  return JSON.stringify(data);
114
 
@@ -142,14 +142,13 @@ async function executePipeline<T>(
142
  if (onStatus) onStatus("⚡ Using Gemini Flash...");
143
  return await geminiTask(MODEL_PRIMARY);
144
  } catch (error: any) {
145
- // Check for Quota/Rate Limits
146
- if (error.toString().includes('429') || error.toString().includes('Quota') || error.toString().includes('Resource has been exhausted')) {
147
  try {
148
  // 2. Secondary Model
149
  if (onStatus) onStatus("⚠️ Quota limit. Switching to Flash-Lite...");
150
  return await geminiTask(MODEL_SECONDARY);
151
  } catch (secondaryError) {
152
- // Fall through to backend
153
  console.warn("Secondary model failed:", secondaryError);
154
  }
155
  }
@@ -210,13 +209,22 @@ export const generateSpeech = async (text: string): Promise<string | null> => {
210
  return response.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data || null;
211
  };
212
 
213
- // Fallback (somAI-media / TTS endpoint)
214
- // Note: Backend must support /tts. If missing, this will fail gracefully.
 
215
  const fallbackTask = async () => {
216
- return await callBackend(MEDIA_BACKEND_BASE, '/tts', { text });
217
  };
218
 
219
- return executePipeline(geminiTask, fallbackTask);
 
 
 
 
 
 
 
 
220
  };
221
 
222
  // --- STT (Transcription) ---
@@ -287,7 +295,7 @@ export const analyzeRisk = async (
287
  });
288
 
289
  const parsed = parseRiskResponse(response.text || "{}", calculatedScore);
290
- return { ...parsed, source: model === MODEL_PRIMARY ? 'Gemini 2.5 Flash' : 'Gemini 2.5 Flash-Lite' };
291
  };
292
 
293
  const fallbackTask = async () => {
@@ -321,7 +329,7 @@ export const generateChatResponse = async (
321
  contents.push({ role: 'user', parts: [{ text: context + "\nUser: " + currentMessage }, ...(image ? [{ inlineData: { mimeType: 'image/jpeg', data: image.split('base64,')[1] } }] : [])] });
322
 
323
  const geminiTask = async (model: string) => {
324
- onSource(model === MODEL_PRIMARY ? 'Gemini 2.5 Flash' : 'Gemini 2.5 Flash-Lite');
325
  const response = await ai.models.generateContent({
326
  model: model,
327
  contents: contents,
@@ -344,7 +352,7 @@ export const generateChatResponse = async (
344
  const parseRiskResponse = (text: string, calculatedScore: number): RiskAnalysisResult => {
345
  try {
346
  let jsonStr = text;
347
- // Clean markdown code blocks
348
  jsonStr = jsonStr.replace(/```json/g, '').replace(/```/g, '');
349
  const data = JSON.parse(jsonStr);
350
 
 
29
 
30
  // --- TIERED MODEL STRATEGY ---
31
  // 1. Primary: Gemini 2.5 Flash (Highest Quality/Speed Balance)
32
+ // 2. Secondary: Gemini Flash Lite (Quota Rescue)
33
  // 3. Tertiary: Local/HuggingFace Backends (Privacy/Offline/No-Quota Fallback)
34
  const MODEL_PRIMARY = 'gemini-2.5-flash';
35
+ const MODEL_SECONDARY = 'gemini-flash-lite-latest';
36
  const MODEL_TTS = 'gemini-2.5-flash-preview-tts';
37
 
38
  // --- UTILITIES ---
 
108
  if (typeof data === 'string') return data;
109
  if (data.text) return data.text;
110
  if (data.response) return data.response;
111
+ // Note: Backend does not support TTS, so we don't check for audio here.
112
 
113
  return JSON.stringify(data);
114
 
 
142
  if (onStatus) onStatus("⚡ Using Gemini Flash...");
143
  return await geminiTask(MODEL_PRIMARY);
144
  } catch (error: any) {
145
+ // Check for Quota/Rate Limits or Model Overload
146
+ if (error.toString().includes('429') || error.toString().includes('Quota') || error.toString().includes('503')) {
147
  try {
148
  // 2. Secondary Model
149
  if (onStatus) onStatus("⚠️ Quota limit. Switching to Flash-Lite...");
150
  return await geminiTask(MODEL_SECONDARY);
151
  } catch (secondaryError) {
 
152
  console.warn("Secondary model failed:", secondaryError);
153
  }
154
  }
 
209
  return response.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data || null;
210
  };
211
 
212
+ // Fallback: Return NULL.
213
+ // The Frontend (Chat.tsx) will detect NULL and use `window.speechSynthesis` (Browser Native TTS).
214
+ // The backend does not have a /tts endpoint.
215
  const fallbackTask = async () => {
216
+ return null;
217
  };
218
 
219
+ // We manually handle pipeline here to ensure fallback returns null instead of throwing
220
+ if (API_KEY) {
221
+ try {
222
+ return await geminiTask();
223
+ } catch (e) {
224
+ // Fallthrough
225
+ }
226
+ }
227
+ return await fallbackTask();
228
  };
229
 
230
  // --- STT (Transcription) ---
 
295
  });
296
 
297
  const parsed = parseRiskResponse(response.text || "{}", calculatedScore);
298
+ return { ...parsed, source: model === MODEL_PRIMARY ? 'Gemini 2.5 Flash' : 'Gemini Flash Lite' };
299
  };
300
 
301
  const fallbackTask = async () => {
 
329
  contents.push({ role: 'user', parts: [{ text: context + "\nUser: " + currentMessage }, ...(image ? [{ inlineData: { mimeType: 'image/jpeg', data: image.split('base64,')[1] } }] : [])] });
330
 
331
  const geminiTask = async (model: string) => {
332
+ onSource(model === MODEL_PRIMARY ? 'Gemini 2.5 Flash' : 'Gemini Flash Lite');
333
  const response = await ai.models.generateContent({
334
  model: model,
335
  contents: contents,
 
352
  const parseRiskResponse = (text: string, calculatedScore: number): RiskAnalysisResult => {
353
  try {
354
  let jsonStr = text;
355
+ // Clean markdown code blocks if any
356
  jsonStr = jsonStr.replace(/```json/g, '').replace(/```/g, '');
357
  const data = JSON.parse(jsonStr);
358