from sqlalchemy import select from sqlalchemy.orm import Session from app.character.service import CharacterService from app.chat.notices import format_phase_completed_notice from app.db.models import ChatSession, Message, PomodoroSession from app.llm.client import LLMClient from app.pomodoro.cycle import PHASE_LONG_BREAK, PHASE_SHORT_BREAK, PHASE_WORK, CycleManager from app.pomodoro.service import PomodoroService PHASE_LABELS = { PHASE_WORK: "работа", PHASE_SHORT_BREAK: "короткий перерыв", PHASE_LONG_BREAK: "длинный перерыв", } class PomodoroCompletionHandler: def __init__(self, db: Session): self.db = db self.pomodoro = PomodoroService(db) self.cycle = CycleManager(db) self.llm = LLMClient() self.character = CharacterService() def _latest_chat_session_id(self) -> int | None: stmt = select(ChatSession).order_by(ChatSession.updated_at.desc()).limit(1) session = self.db.scalar(stmt) return session.id if session else None def _save_chat_message(self, session_id: int, role: str, content: str) -> None: self.db.add(Message(session_id=session_id, role=role, content=content)) chat = self.db.get(ChatSession, session_id) if chat: chat.updated_at = chat.updated_at # trigger onupdate self.db.commit() async def _generate_llm_comment( self, session: PomodoroSession, next_phase: str | None, ) -> str: cycle = self.cycle.to_dict() phase_label = PHASE_LABELS.get(session.phase, session.phase) next_label = PHASE_LABELS.get(next_phase, "пауза") if next_phase else "отдых, цикл сброшен" work_done = cycle["completed_work_sessions"] if session.phase == PHASE_WORK: work_done += 1 system = self.character.get_system_prompt() user_prompt = f"""Фаза помидоро «{phase_label}» только что завершилась. Задача: {session.task_note or 'без описания'} Прогресс цикла: {work_done}/{cycle['sessions_until_long_break']} работ. Следующая фаза: {next_label}. Напиши пользователю короткое сообщение (2-4 предложения) на русском: поздравь, поддержи или предложи отдохнуть. Без markdown.""" result = await self.llm.complete( [ {"role": "system", "content": system}, {"role": "user", "content": user_prompt}, ] ) return (result.get("content") or "").strip() or "Фаза завершена! Отличная работа." def _resolve_next_phase(self, session: PomodoroSession) -> str | None: phase = session.phase cycle = self.cycle.get() if phase == PHASE_WORK: if cycle.completed_work_sessions + 1 >= cycle.sessions_until_long_break: return PHASE_LONG_BREAK return PHASE_SHORT_BREAK if phase == PHASE_SHORT_BREAK: return PHASE_WORK if phase == PHASE_LONG_BREAK: return None return None async def process(self, session: PomodoroSession) -> None: if session.completion_notified: return next_phase = self._resolve_next_phase(session) notice = format_phase_completed_notice(session, next_phase) chat_id = self._latest_chat_session_id() if not chat_id: chat = ChatSession(title="Помидоро") self.db.add(chat) self.db.commit() self.db.refresh(chat) chat_id = chat.id self._save_chat_message(chat_id, "notice", notice) comment = await self._generate_llm_comment(session, next_phase) self._save_chat_message(chat_id, "assistant", comment) self.cycle.bump_notify_seq() self.pomodoro.mark_notified(session) self.pomodoro.advance_after_completion(session)