"""OpenAI-compatible DesignAPI chat client.""" from __future__ import annotations import logging from openai import AsyncOpenAI from app.config import Settings from app.memory import SQLiteMemory log = logging.getLogger(__name__) SYSTEM_PROMPT_RU = ( "Ты дружелюбный русскоязычный AI-ассистент в Telegram. " "Отвечай ясно, полезно и по делу на русском языке. " "Учитывай последние сообщения диалога, но не выдумывай факты. " "Если данных не хватает — задай один короткий уточняющий вопрос." ) SUMMARY_PROMPT_RU = ( "Сделай краткое саммари последнего разговора на русском языке. " "Выдели только важные факты, решения, вопросы и следующие шаги. " "Не добавляй того, чего не было в истории. Формат: 3-6 коротких пунктов." ) class LLMChat: def __init__(self, settings: Settings, memory: SQLiteMemory) -> None: self.settings = settings self.memory = memory self.client = AsyncOpenAI( api_key=settings.designapi_api_key, base_url=f"{settings.designapi_base_url.rstrip('/')}/v1", ) async def answer(self, user_id: int, text: str) -> str: self.memory.save(user_id, "user", text) context = self.memory.recent_context(user_id, limit=10) messages = [{"role": "system", "content": SYSTEM_PROMPT_RU}, *context] log.info( "DesignAPI chat request user_id=%s model=%s max_tokens=%s context=%s", user_id, self.settings.designapi_model, self.settings.llm_max_tokens, len(context), ) response = await self.client.chat.completions.create( model=self.settings.designapi_model, messages=messages, max_tokens=self.settings.llm_max_tokens, ) content = (response.choices[0].message.content or "").strip() if not content: content = "Извини, модель вернула пустой ответ. Попробуй переформулировать вопрос." self.memory.save(user_id, "assistant", content) return content async def summary(self, user_id: int) -> str: """Build a concise LLM summary of the latest stored conversation.""" context = self.memory.recent_context(user_id, limit=20) if not context: raise ValueError("Пока нечего суммировать: история диалога пуста. Напишите мне пару сообщений 🙂") log.info( "DesignAPI summary request user_id=%s model=%s context=%s", user_id, self.settings.designapi_model, len(context), ) response = await self.client.chat.completions.create( model=self.settings.designapi_model, messages=[{"role": "system", "content": SUMMARY_PROMPT_RU}, *context], max_tokens=min(self.settings.llm_max_tokens, 350), ) content = (response.choices[0].message.content or "").strip() if not content: raise RuntimeError("LLM returned an empty summary") return "📝 Краткое саммари последнего разговора:\n\n" + content