diff --git a/src/screens/Home.tsx b/src/screens/Home.tsx index 38de5b4..adf7872 100644 --- a/src/screens/Home.tsx +++ b/src/screens/Home.tsx @@ -70,7 +70,7 @@ const Home: React.FC = () => { // Проверяем, была ли ошибка перевода if (response.translationFailed) { setNotificationTitle('Ошибка перевода'); - setNotificationMessage('Не удалось перевести промпт. Генерация отменена.'); + setNotificationMessage('Не удалось перевести промпт. Генерация отменена. Пожалуйста, попробуйте другой промпт или повторите попытку позже.'); setIsLoading(false); return; } diff --git a/src/services/api.ts b/src/services/api.ts index e1c5b84..1a7be03 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -149,8 +149,8 @@ async generateImage(imageData: string, style?: string, promptId?: string, userPr if (userPrompt && promptId === 'customPrompt') { console.log('Переводим пользовательский промпт:', userPrompt); - // Переводим промпт с 3 попытками - const translationResult = await translateService.translateToEnglish(userPrompt, 3); + // Используем новый метод перевода через LLM + const translationResult = await translateService.translateWithLLM(userPrompt); if (translationResult.success) { // Успешный перевод @@ -159,7 +159,7 @@ async generateImage(imageData: string, style?: string, promptId?: string, userPr usedPrompt = translationResult.text; } else { // Перевод не удался после всех попыток - console.error('Не удалось перевести промпт после нескольких попыток'); + console.error('Не удалось перевести промпт ни через одну из моделей'); translationFailed = true; // Не продолжаем генерацию, возвращаем ошибку diff --git a/src/services/translateService.ts b/src/services/translateService.ts index d3b8820..22e9689 100644 --- a/src/services/translateService.ts +++ b/src/services/translateService.ts @@ -1,5 +1,17 @@ +// URL для LibreTranslate API (сохраняем, но не используем) const TRANSLATE_API_URL = 'https://translate.maxdev.keenetic.pro/translate'; +// URL и ключ для OpenRouter API +const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions'; +const OPENROUTER_API_KEY = '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'; + +// Модели для перевода +const TRANSLATION_MODELS = { + PRIMARY: 'meta-llama/llama-3.3-70b-instruct:free', // Основная модель + FALLBACK: 'openchat/openchat-7b:free', // Резервная модель +}; + +// Интерфейс для ответа от LibreTranslate API interface TranslateResponse { translatedText: string; detectedLanguage: { @@ -9,7 +21,28 @@ interface TranslateResponse { alternatives: string[]; } +// Интерфейс для ответа от OpenRouter API +interface OpenRouterResponse { + choices: { + message: { + content: string; + role: string; + }; + index: number; + finish_reason: string; + }[]; + id: string; + model: string; + object: string; + usage: { + prompt_tokens: number; + completion_tokens: number; + total_tokens: number; + }; +} + const translateService = { + // Старый метод перевода через LibreTranslate (сохраняем, но не используем) async translateToEnglish(text: string, maxRetries = 3): Promise<{ success: boolean; text: string }> { let retries = 0; @@ -50,6 +83,91 @@ const translateService = { // Все попытки исчерпаны, возвращаем исходный текст с флагом неудачи console.error(`All ${maxRetries} translation attempts failed`); return { success: false, text: text }; + }, + + // Новый метод перевода через OpenRouter API + async translateWithLLM(text: string): Promise<{ success: boolean; text: string }> { + // Массив моделей для последовательных попыток + const models = [ + TRANSLATION_MODELS.PRIMARY, + TRANSLATION_MODELS.FALLBACK + ]; + + // Проверка, что текст не пустой + if (!text || text.trim() === '') { + console.error('Пустой текст для перевода'); + return { success: false, text }; + } + + // Проверка, что текст не на английском языке + const isAlreadyEnglish = /^[a-zA-Z0-9\s.,!?;:()\-"']+$/.test(text.trim()); + if (isAlreadyEnglish) { + console.log('Текст уже на английском языке, перевод не требуется'); + return { success: true, text }; + } + + // Последовательно пробуем каждую модель + for (let i = 0; i < models.length; i++) { + const model = models[i]; + console.log(`Попытка перевода через модель ${model} (${i + 1}/${models.length})`); + + try { + const response = await fetch(OPENROUTER_API_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${OPENROUTER_API_KEY}`, + 'HTTP-Referer': window.location.origin, + 'X-Title': 'Sticker Generator App' + }, + body: JSON.stringify({ + model: model, + messages: [ + { + role: 'system', + content: 'You are a professional translator. Translate the following text from any language to English. Preserve the meaning and style. Only return the translated text without any additional comments or explanations.' + }, + { + role: 'user', + content: text + } + ], + temperature: 0.3, + max_tokens: 200 + }) + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + console.error(`Ошибка API для модели ${model}:`, errorData); + throw new Error(`OpenRouter API failed with status: ${response.status}`); + } + + const data: OpenRouterResponse = await response.json(); + const translatedText = data.choices[0].message.content.trim(); + + // Проверка, что получили непустой ответ + if (!translatedText) { + throw new Error('Empty translation result'); + } + + // Проверка, что перевод отличается от исходного текста + if (translatedText.toLowerCase().trim() === text.toLowerCase().trim()) { + console.warn(`Модель ${model} вернула исходный текст без перевода`); + throw new Error('Translation returned original text'); + } + + console.log(`Успешный перевод через модель ${model}:`, translatedText); + return { success: true, text: translatedText }; + } catch (error) { + console.error(`Ошибка перевода через модель ${model}:`, error); + // Продолжаем со следующей моделью, если она есть + } + } + + // Если все модели не сработали + console.error('Все попытки перевода через LLM не удались'); + return { success: false, text }; } };