const logger = require("./logger"); const {searchInCache, saveInCache} = require("./commandResponser"); require('dotenv').config(); // request_parse_prompt= 'Получена строка, это команда ТГ бота. ' + // 'Команда может содержать период или количество сообщений, а так же имя персонажа (вероятнее всего известная личность, возможно - название музыкальной группы).' + // 'Необходимо разобрать.' + // 'Если не удалось разобрать, то вернуть со значениями persona:"Карл Маркс",messages:22.' + // 'Одновременно не может быть и количество сообщений, и количество часов, что-то одно.' + // 'Если интервал не в часах - пересчитать в часы!' + // 'Если имя принадлежит публичной или известной персоне - указать имя полностью! (например - кипелов - Валерий Кипелов, кюри - Мария Склодовская-Кюри' + // 'Если это название музыкальной группы - указать, что это музыкальная группа (группа ХХХХ)' + // 'Комментарии не оставлять' + // 'Вывод - строго в формате json' + // 'command={\n' + // ' persona:\'Карл Маркс\', // имя персонажа\n' + // ' messages:100, // сколько сообщений обработать или 0\n' + // ' hours: 24 // за сколько часов, или 0\n' + // '}' ; // // request_parse_prompt= 'Ты - эксперт по анализу текстовых команд. Твоя задача - разобрать команду Telegram бота и извлечь из неё информацию о персоне и временном периоде.\n' + 'ВХОДНЫЕ ДАННЫЕ\n' + 'Получена строка команды, которая может содержать:\n' + '\n' + 'Имя персонажа (известная личность, историческая фигура, или название музыкальной группы)\n' + 'Временной период (количество часов, дней, недель, месяцев) ИЛИ количество сообщений\n' + 'Дополнительные слова и предлоги\n' + '\n' + 'ПРАВИЛА ОБРАБОТКИ\n' + '1. ПЕРСОНА\n' + '\n' + 'Если упомянута известная личность - указать полное имя (например: "кипелов" → "Валерий Кипелов", "кюри" → "Мария Склодовская-Кюри", "летов" → "Егор Николаевич Летов")\n' + 'Если это музыкальная группа - указать с пометкой "группа" (например: "битлз" → "группа The Beatles", "гражданская оборона" → "группа Гражданская оборона")\n' + 'Если персона не определена или неясна - использовать "Карл Маркс"\n' + 'Учитывать различные формы написания (сокращения, транслитерацию, опечатки)\n' + '\n' + '2. ВРЕМЕННОЙ ПЕРИОД\n' + '\n' + 'Может быть указан в различных форматах: "2 часа", "3ч", "день", "неделю", "месяц", "за вчера"\n' + 'ВСЕ периоды пересчитывать в ЧАСЫ:\n' + '\n' + '1 день = 24 часа\n' + '1 неделя = 168 часов\n' + '1 месяц = 720 часов (30 дней)\n' + '"вчера" = 24 часа\n' + '"позавчера" = 48 часов\n' + '\n' + '\n' + 'Если период не указан - использовать 0\n' + '\n' + '3. КОЛИЧЕСТВО СООБЩЕНИЙ\n' + '\n' + 'Может быть указано как число + "сообщений", "постов", "записей"\n' + 'Если не указано - использовать 0\n' + 'ВАЖНО: одновременно НЕ МОЖЕТ быть и количество сообщений, и временной период - только что-то одно!\n' + '\n' + '4. ПРИОРИТЕТЫ\n' + '\n' + 'Если указаны и период, и количество сообщений - приоритет у количества сообщений (hours = 0)\n' + 'Если ничего не указано - messages = 22, hours = 0\n' + '\n' + 'ПРИМЕРЫ ВХОДНЫХ КОМАНД И ОЖИДАЕМЫЙ РЕЗУЛЬТАТ\n' + 'Команда: "покажи последние 50 сообщений в стиле Высоцкого"\n' + 'Результат: {"persona": "Владимир Семёнович Высоцкий", "messages": 50, "hours": 0}\n' + 'Команда: "сгенерируй как Metallica за последний день"\n' + 'Результат: {"persona": "группа Metallica", "messages": 0, "hours": 24}\n' + 'Команда: "в стиле Маска за 3 часа"\n' + 'Результат: {"persona": "Илон Маск", "messages": 0, "hours": 3}\n' + 'Команда: "как Цой последние 100 постов"\n' + 'Результат: {"persona": "Виктор Робертович Цой", "messages": 100, "hours": 0}\n' + 'Команда: "покажи Бетховена"\n' + 'Результат: {"persona": "Людвиг ван Бетховен", "messages": 22, "hours": 0}\n' + 'ИНСТРУКЦИИ ПО ВЫВОДУ\n' + '\n' + 'Комментарии НЕ оставлять\n' + 'Вывод СТРОГО в формате JSON без markdown блоков\n' + 'НЕ ИСПОЛЬЗОВАТЬ json или блоки\n' + 'Возвращать ТОЛЬКО чистый JSON объект\n' + 'Не добавлять дополнительные поля\n' + 'Использовать двойные кавычки для JSON\n' + 'Никаких объяснений до или после JSON\n' + 'ОБЯЗАТЕЛЬНО ВАЛИДНЫЙ JSON!!! ОЧЕНЬ ВАЖНО!!\n' + '\n' + 'ФОРМАТ ОТВЕТА (возвращать БЕЗ markdown блоков):\n' + '{"persona": "Полное имя персоны или группа Название (краткое описание в трех-пяти словах)", "messages": число_или_0, "hours": число_или_0}\n' + 'КРИТИЧЕСКИ ВАЖНО\n' + '\n' + 'Твой ответ должен начинаться с { и заканчиваться }\n' + 'Никаких дополнительных символов, пробелов или переносов строк до и после JSON\n' + 'НЕ оборачивать в ```json блоки\n' + 'Возвращать ТОЛЬКО валидный JSON объект\n' + '\n' + 'ОБРАБАТЫВАЙ КОМАНДУ СОГЛАСНО ВСЕМ ПРАВИЛАМ ВЫШЕ. АНАЛИЗИРУЙ КАЖДОЕ СЛОВО И КОНТЕКСТ. ВЕРНИ ТОЛЬКО JSON!' // async function handleRequest(request) { // // Попытка найти результат в кэше // const cachedResult = await searchInCache(request); // // if (cachedResult) { // return cachedResult; // Возвращаем найденное в кэше // } // // // Если в кэше ничего нет — вызываем основную функцию // const result = await someFunction(request); // // // Сохраняем результат в кэш // await saveInCache(request, result); // // return result; // } async function callAI(prompt, data, type = 'history') { // Если API ключ недоступен, используем fallback if (!process.env.OPENROUTER_API_KEY ) { logger.error('⚠️ OpenRouter API ключ недоступен'); return; } let workingPrompt = ''; switch (type) { case 'history':workingPrompt=prompt; break; case 'request':{ workingPrompt=request_parse_prompt; const cachedResult = await searchInCache(data); if (cachedResult) { return cachedResult; // Возвращаем найденное в кэше } } break; default:workingPrompt=prompt; break; } try { const formattedMessages = data; // const formattedMessages = this.formatMessagesForAI(data); let useModel switch (type) { case 'history':useModel=process.env.OPENROUTER_MODEL; break; case 'prompt':useModel=process.env.OPENROUTER_MODEL; break; case 'request':useModel=process.env.OPENROUTER_CHEAP_MODEL; break; default:useModel=process.env.OPENROUTER_MODEL; break; } // = (type!==='history' ? process.env.OPENROUTER_MODEL: process.env.OPENROUTER_CHEAP_MODEL); logger.info(`🤖 Отправляем запрос к OpenRouter (модель ${useModel})...`); const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://git.rockzo.ru/Rockzo_Develop/summy', 'X-Title': 'Telegram History Bot' }, body: JSON.stringify({ model: useModel, messages: [ { role: 'system', content: workingPrompt }, { role: 'user', content: formattedMessages } ], max_tokens: 2000, temperature: 0.8, top_p: 0.9 }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`); } const completion = await response.json(); const aiSummary = completion.choices?.[0]?.message?.content; if (!aiSummary) { logger.error(JSON.stringify(completion)); throw new Error('Пустой ответ от ИИ'); } logger.info('✅ Получен ответ от OpenRouter'); if (type==='request') { await saveInCache(data, aiSummary); } // return `🎬 История чата\n\n${aiSummary}`; if (type==='request') { console.log(typeof aiSummary, aiSummary) return JSON.parse(aiSummary); } return aiSummary; } catch (error) { logger.error('❌ Ошибка при вызове OpenRouter API:', error); // Детальная обработка различных типов ошибок if (error.message.includes('401')) { logger.error('❌ Неверный API ключ OpenRouter'); } else if (error.message.includes('429')) { logger.error('❌ Превышен лимит запросов OpenRouter'); } else if (error.message.includes('402')) { logger.error('❌ Недостаточно средств на счету OpenRouter'); } else if (error.code === 'ENOTFOUND') { logger.error('❌ Проблемы с сетевым подключением'); } return // this.generateFallbackSummary(data); } } module.exports = callAI;