102 lines
4.0 KiB
Python
102 lines
4.0 KiB
Python
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)
|