import logging from sqlalchemy.orm import Session from app.character.service import CharacterService from app.chat.notice_inbox import post_character_comment_to_latest_chat, post_notice_to_latest_chat from app.chat.notices import format_phase_completed_notice from app.db.models import 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 logger = logging.getLogger(__name__) PHASE_LABELS = { PHASE_WORK: "работа", PHASE_SHORT_BREAK: "короткий перерыв", PHASE_LONG_BREAK: "длинный перерыв", } class PomodoroCompletionHandler: def __init__(self, db: Session, user_id: int): self.db = db self.user_id = user_id self.pomodoro = PomodoroService(db, user_id) self.cycle = CycleManager(db, user_id) self.llm = LLMClient() self.character = CharacterService(db, user_id) 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}, ], temperature=0.8, visible_reply=True, ) 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) post_notice_to_latest_chat(notice, self.user_id) try: comment = await self._generate_llm_comment(session, next_phase) if comment: post_character_comment_to_latest_chat(comment, self.user_id) except Exception: logger.exception("Pomodoro LLM comment failed (phase=%s)", session.phase) self.cycle.bump_notify_seq() self.pomodoro.mark_notified(session) self.pomodoro.advance_after_completion(session) logger.info("Pomodoro phase completed (phase=%s, next=%s)", session.phase, next_phase)