更新至0.5.0_20251123版本

This commit is contained in:
2025-11-23 22:01:49 +08:00
parent 7487be2bb5
commit b53183e931
5 changed files with 179 additions and 85 deletions

View File

@@ -1,5 +1,4 @@
import { GoogleGenAI, Modality, Type } from "@google/genai";
import { PronunciationFeedback, Language, ReadingLesson, ReadingDifficulty, OCRAnalysis, ListeningLesson } from "../types";
import { base64ToUint8Array, uint8ArrayToBase64 } from "../utils/audioUtils";
@@ -63,7 +62,7 @@ const LANGUAGE_MAP = {
class GeminiService {
private getAi() {
const userKey = localStorage.getItem(USER_API_KEY_STORAGE);
const userBaseUrl = localStorage.getItem(USER_BASE_URL_STORAGE);
let userBaseUrl = localStorage.getItem(USER_BASE_URL_STORAGE);
const envKey = process.env.API_KEY;
const keyToUse = (userKey && userKey.trim().length > 0) ? userKey : envKey;
@@ -73,8 +72,11 @@ class GeminiService {
}
const config: any = { apiKey: keyToUse };
if (userBaseUrl && userBaseUrl.trim().length > 0) {
config.baseUrl = userBaseUrl.trim();
// Sanitize Base URL: remove quotes and trailing slashes
let cleanUrl = userBaseUrl.trim().replace(/['"]/g, '').replace(/\/+$/, '');
config.baseUrl = cleanUrl;
}
return new GoogleGenAI(config);
@@ -92,11 +94,19 @@ class GeminiService {
try {
return await operation();
} catch (error: any) {
const errorMsg = error?.message || '';
// Check for Network/CORS/Proxy errors specifically
if (errorMsg.includes('Failed to fetch') || errorMsg.includes('NetworkError')) {
console.error("Network Error Detected:", error);
throw new Error("Network connection failed. Please check your Base URL (Proxy) settings or internet connection.");
}
const isOverloaded =
error?.status === 503 ||
error?.response?.status === 503 ||
error?.message?.includes('503') ||
error?.message?.includes('overloaded');
errorMsg.includes('503') ||
errorMsg.includes('overloaded');
if (isOverloaded && retries > 0) {
console.warn(`Model overloaded (503). Retrying...`);
@@ -118,10 +128,14 @@ class GeminiService {
): Promise<{ text: string, model: string }> {
const ai = this.getAi();
// Ensure model name is clean
let modelName = useThinking
? 'gemini-3-pro-preview'
: (imageBase64 ? 'gemini-3-pro-preview' : (modelOverride || 'gemini-2.5-flash'));
// Extra safety: strip quotes just in case
modelName = modelName.replace(/['"]/g, '');
const targetLangName = LANGUAGE_MAP[language];
const parts: any[] = [];
@@ -191,7 +205,7 @@ class GeminiService {
return response.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data || null;
} catch (e) {
console.error("TTS Chunk Error", e);
return null;
throw e; // Throw to retryOperation to handle network errors
}
});
}
@@ -203,7 +217,11 @@ class GeminiService {
// If text is short, process directly
if (text.length <= MAX_CHUNK_LENGTH) {
return this._generateSpeechChunk(text);
try {
return await this._generateSpeechChunk(text);
} catch (e) {
return null;
}
}
// Split text into chunks by sentence to avoid breaking words
@@ -284,7 +302,7 @@ class GeminiService {
return bytes ? `data:image/jpeg;base64,${bytes}` : null;
} catch (e) {
console.error("Image Gen Error", e);
return null;
throw e;
}
});
}
@@ -310,7 +328,7 @@ class GeminiService {
return null;
} catch (e) {
console.error("Image Edit Error", e);
return null;
throw e;
}
});
}
@@ -540,4 +558,4 @@ class GeminiService {
}
}
export const geminiService = new GeminiService();
export const geminiService = new GeminiService();