лучшения: 1) сегда видимый эффект затемнения с надписью 'зменить фото' 2) Сброс изображения при закрытии приложения 3) лучшенное обнаружение отказов модели при переводе
This commit is contained in:
parent
b18acdbad5
commit
dc80b683c2
@ -97,15 +97,11 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
opacity: 1; /* Всегда видимый */
|
||||
transition: opacity 0.2s ease;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.hasPreview:hover .changeOverlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.changeText {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
|
||||
@ -264,7 +264,7 @@ const CropPhoto: React.FC = () => {
|
||||
// Убираем префикс data:image/jpeg;base64, оставляем только данные
|
||||
const imageData = previewUrl.split(',')[1];
|
||||
|
||||
// Сохраняем данные в localStorage для сохранения между сеансами навигации
|
||||
// Сохраняем данные в localStorage для навигации между страницами
|
||||
localStorage.setItem('stickerPreviewUrl', previewUrl);
|
||||
localStorage.setItem('stickerImageData', imageData);
|
||||
|
||||
|
||||
@ -69,8 +69,14 @@ const Home: React.FC = () => {
|
||||
|
||||
// Проверяем, была ли ошибка перевода
|
||||
if (response.translationFailed) {
|
||||
setNotificationTitle('Ошибка перевода');
|
||||
setNotificationMessage('Не удалось перевести промпт. Генерация отменена. Пожалуйста, попробуйте другой промпт или повторите попытку позже.');
|
||||
setNotificationTitle('Недопустимый промпт');
|
||||
setNotificationMessage('Промпт содержит недопустимый контент. Пожалуйста, используйте более нейтральные формулировки.');
|
||||
|
||||
// Логирование деталей ошибки для отладки
|
||||
if (response.errorDetails) {
|
||||
console.error('Детали ошибки перевода:', response.errorDetails);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
@ -167,6 +173,21 @@ const Home: React.FC = () => {
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Эффект для обработки закрытия приложения
|
||||
useEffect(() => {
|
||||
// Обработчик события beforeunload для очистки данных при закрытии приложения
|
||||
const handleBeforeUnload = () => {
|
||||
localStorage.removeItem('stickerPreviewUrl');
|
||||
localStorage.removeItem('stickerImageData');
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Функция для получения кнопок в зависимости от блока
|
||||
const getBlockButtons = useCallback((block: any) => {
|
||||
|
||||
@ -159,12 +159,14 @@ async generateImage(imageData: string, style?: string, promptId?: string, userPr
|
||||
usedPrompt = translationResult.text;
|
||||
} else {
|
||||
// Перевод не удался после всех попыток
|
||||
console.error('Не удалось перевести промпт ни через одну из моделей');
|
||||
console.error('Не удалось перевести промпт:', translationResult.text);
|
||||
translationFailed = true;
|
||||
|
||||
// Не продолжаем генерацию, возвращаем ошибку
|
||||
// Не продолжаем генерацию, возвращаем ошибку с сообщением
|
||||
return {
|
||||
translationFailed: true
|
||||
translationFailed: true,
|
||||
usedPrompt: 'Недопустимый промпт', // Сообщение для пользователя
|
||||
errorDetails: translationResult.text // Детали ошибки для отладки
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,6 +41,83 @@ interface OpenRouterResponse {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, содержит ли текст признаки отказа от модели
|
||||
* @param text Текст для проверки
|
||||
* @returns true, если текст похож на отказ модели, иначе false
|
||||
*/
|
||||
function isModelRefusal(text: string): boolean {
|
||||
if (!text) return false;
|
||||
|
||||
// Проверка на специальный маркер отказа
|
||||
if (text.trim() === 'TRANSLATION_FAILED') {
|
||||
console.log('Обнаружен специальный маркер отказа: TRANSLATION_FAILED');
|
||||
return true;
|
||||
}
|
||||
|
||||
const lowerText = text.toLowerCase();
|
||||
|
||||
// Типичные фразы отказа от моделей
|
||||
const refusalPhrases = [
|
||||
// Специальные маркеры
|
||||
"translation_failed",
|
||||
"translation failed",
|
||||
"cannot translate",
|
||||
|
||||
// Стандартные фразы отказа
|
||||
"i'm sorry",
|
||||
"i apologize",
|
||||
"i cannot",
|
||||
"i'm not able to",
|
||||
"i am unable to",
|
||||
"i can't",
|
||||
"cannot assist",
|
||||
"unable to provide",
|
||||
"against my ethical guidelines",
|
||||
"violates content policy",
|
||||
"not going to translate",
|
||||
"contains derogatory",
|
||||
"racial slurs",
|
||||
"offensive content",
|
||||
"inappropriate language",
|
||||
"inappropriate content",
|
||||
"harmful content",
|
||||
"offensive language",
|
||||
"offensive terms",
|
||||
"violates",
|
||||
"policy",
|
||||
"guidelines"
|
||||
];
|
||||
|
||||
for (const phrase of refusalPhrases) {
|
||||
if (lowerText.includes(phrase)) {
|
||||
console.log(`Обнаружена фраза отказа модели: ${phrase}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка на наличие кавычек и ключевых слов
|
||||
const keywordsWithQuotes = [
|
||||
{ keyword: "translate", quoteRequired: true },
|
||||
{ keyword: "translation", quoteRequired: true },
|
||||
{ keyword: "content", quoteRequired: false },
|
||||
{ keyword: "inappropriate", quoteRequired: false },
|
||||
{ keyword: "offensive", quoteRequired: false }
|
||||
];
|
||||
|
||||
for (const item of keywordsWithQuotes) {
|
||||
const hasKeyword = lowerText.includes(item.keyword);
|
||||
const hasQuotes = lowerText.includes('"') || lowerText.includes("'");
|
||||
|
||||
if (hasKeyword && (!item.quoteRequired || hasQuotes)) {
|
||||
console.log(`Обнаружено ключевое слово "${item.keyword}" ${item.quoteRequired ? "с кавычками" : "без требования кавычек"}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const translateService = {
|
||||
// Старый метод перевода через LibreTranslate (сохраняем, но не используем)
|
||||
async translateToEnglish(text: string, maxRetries = 3): Promise<{ success: boolean; text: string }> {
|
||||
@ -106,6 +183,9 @@ const translateService = {
|
||||
return { success: true, text };
|
||||
}
|
||||
|
||||
// Логирование запроса
|
||||
console.log(`Запрос на перевод текста: "${text.substring(0, 50)}${text.length > 50 ? '...' : ''}"`);
|
||||
|
||||
// Последовательно пробуем каждую модель
|
||||
for (let i = 0; i < models.length; i++) {
|
||||
const model = models[i];
|
||||
@ -125,14 +205,14 @@ const translateService = {
|
||||
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.'
|
||||
content: 'You are a professional translator. Your task is to translate the following text from any language to English. ONLY return the translated text without ANY additional comments, explanations, or quotation marks. Do not include phrases like "Translation:" or "Translated text:". If you cannot translate the text for any reason (such as it containing offensive, inappropriate, or harmful content), respond with "TRANSLATION_FAILED" and nothing else.'
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: text
|
||||
}
|
||||
],
|
||||
temperature: 0.3,
|
||||
temperature: 0.2, // Снижаем температуру для более предсказуемых ответов
|
||||
max_tokens: 200
|
||||
})
|
||||
});
|
||||
@ -146,28 +226,57 @@ const translateService = {
|
||||
const data: OpenRouterResponse = await response.json();
|
||||
const translatedText = data.choices[0].message.content.trim();
|
||||
|
||||
// Логирование ответа
|
||||
console.log(`Ответ от модели ${model}: "${translatedText.substring(0, 100)}${translatedText.length > 100 ? '...' : ''}"`);
|
||||
|
||||
// Проверка, что получили непустой ответ
|
||||
if (!translatedText) {
|
||||
throw new Error('Empty translation result');
|
||||
}
|
||||
|
||||
// Проверка на отказ модели
|
||||
if (isModelRefusal(translatedText)) {
|
||||
console.warn(`Модель ${model} отказалась переводить текст. Полный ответ: "${translatedText}"`);
|
||||
|
||||
// Если это основная модель, сразу возвращаем ошибку
|
||||
// и не пытаемся использовать резервную модель
|
||||
if (model === TRANSLATION_MODELS.PRIMARY) {
|
||||
return { success: false, text: 'Недопустимый промпт' };
|
||||
} else {
|
||||
throw new Error('Model refused to translate');
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка, что перевод отличается от исходного текста
|
||||
if (translatedText.toLowerCase().trim() === text.toLowerCase().trim()) {
|
||||
console.warn(`Модель ${model} вернула исходный текст без перевода`);
|
||||
throw new Error('Translation returned original text');
|
||||
}
|
||||
|
||||
// Проверка на подозрительные паттерны в переводе
|
||||
const punctuationRatio = (translatedText.match(/[.,!?;:()[\]{}'"]/g) || []).length / translatedText.length;
|
||||
if (punctuationRatio > 0.3) { // Если более 30% текста - знаки препинания
|
||||
console.warn(`Подозрительно высокое содержание знаков препинания в переводе: ${(punctuationRatio * 100).toFixed(1)}%`);
|
||||
throw new Error('Suspicious translation result');
|
||||
}
|
||||
|
||||
console.log(`Успешный перевод через модель ${model}:`, translatedText);
|
||||
return { success: true, text: translatedText };
|
||||
} catch (error) {
|
||||
console.error(`Ошибка перевода через модель ${model}:`, error);
|
||||
// Продолжаем со следующей моделью, если она есть
|
||||
|
||||
// Если это была последняя модель, прекращаем попытки
|
||||
if (i === models.length - 1) {
|
||||
console.error('Все попытки перевода через LLM не удались');
|
||||
return { success: false, text: 'Недопустимый промпт' };
|
||||
}
|
||||
// Иначе продолжаем со следующей моделью (только при технических ошибках)
|
||||
}
|
||||
}
|
||||
|
||||
// Если все модели не сработали
|
||||
// Этот код не должен выполниться, но оставляем для полноты
|
||||
console.error('Все попытки перевода через LLM не удались');
|
||||
return { success: false, text };
|
||||
return { success: false, text: 'Недопустимый промпт' };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -66,4 +66,5 @@ export interface GenerationResult {
|
||||
result?: GenerationResponse;
|
||||
usedPrompt?: string;
|
||||
translationFailed: boolean;
|
||||
errorDetails?: string; // Детали ошибки для отладки
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user