From b976e965f350b26c47dc12e1f70a2b3f066f6461 Mon Sep 17 00:00:00 2001 From: grigo Date: Tue, 9 Jun 2026 14:18:02 +0300 Subject: [PATCH] Fixed Git integration --- backend/Dockerfile | 2 +- backend/app/chat/notices.py | 25 ++++++++++++++++-- backend/app/chat/service.py | 17 ++++++++----- frontend/src/context/PomodoroContext.tsx | 32 +++++++++++++++++++++--- frontend/src/pages/Chat.css | 4 +-- frontend/src/pages/Chat.tsx | 19 ++++++++++---- 6 files changed, 79 insertions(+), 20 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 3c7ec74..bac3724 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -11,4 +11,4 @@ RUN mkdir -p /app/data EXPOSE 8080 -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"] +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080", "--no-access-log"] diff --git a/backend/app/chat/notices.py b/backend/app/chat/notices.py index 4fac8d9..6e54121 100644 --- a/backend/app/chat/notices.py +++ b/backend/app/chat/notices.py @@ -36,14 +36,35 @@ def format_phase_completed_notice( return "\n".join(lines) -def format_pomodoro_notice(tool_name: str, raw_result: str) -> str | None: +POMODORO_TOOL_NAMES = frozenset({ + "get_pomodoro_status", + "start_pomodoro", + "start_short_break", + "start_long_break", + "stop_pomodoro", + "skip_pomodoro_phase", + "reset_pomodoro_cycle", + "get_pomodoro_history", +}) + +# Не засорять чат служебными ответами +TOOLS_SKIP_CHAT_NOTICE = frozenset({ + "get_pomodoro_status", +}) + + +def format_tool_notice(tool_name: str, raw_result: str) -> str | None: + if tool_name in TOOLS_SKIP_CHAT_NOTICE: + return None + try: data = json.loads(raw_result) except json.JSONDecodeError: return None if isinstance(data, dict) and "error" in data: - return f"⏱ Помидоро: {data['error']}" + prefix = "⏱" if tool_name in POMODORO_TOOL_NAMES else "📋" + return f"{prefix} {data['error']}" if tool_name == "reset_pomodoro_cycle": cycle = data.get("cycle", data) diff --git a/backend/app/chat/service.py b/backend/app/chat/service.py index 02a4bad..2975451 100644 --- a/backend/app/chat/service.py +++ b/backend/app/chat/service.py @@ -6,7 +6,11 @@ from sqlalchemy import select from sqlalchemy.orm import Session from app.character.service import CharacterService -from app.chat.notices import format_pomodoro_context, format_pomodoro_notice +from app.chat.notices import ( + POMODORO_TOOL_NAMES, + format_pomodoro_context, + format_tool_notice, +) from app.projects.context import format_projects_context, get_projects_snapshot from app.db.models import ChatSession, Message from app.llm.client import LLMClient @@ -141,15 +145,16 @@ class ChatService: messages.append(tool_message) self._save_message(session_id, "tool", result, tool_call_id=tool_call["id"]) - notice = format_pomodoro_notice(fn["name"], result) + notice = format_tool_notice(fn["name"], result) if notice: self._save_message(session_id, "notice", notice) yield self._sse("notice", {"content": notice}) - yield self._sse( - "pomodoro", - {"name": fn["name"], "result": json.loads(result)}, - ) + if fn["name"] in POMODORO_TOOL_NAMES: + yield self._sse( + "pomodoro", + {"name": fn["name"], "result": json.loads(result)}, + ) continue diff --git a/frontend/src/context/PomodoroContext.tsx b/frontend/src/context/PomodoroContext.tsx index ff4ef8f..83692e6 100644 --- a/frontend/src/context/PomodoroContext.tsx +++ b/frontend/src/context/PomodoroContext.tsx @@ -4,6 +4,7 @@ import { useCallback, useContext, useEffect, + useRef, useState, } from "react"; import { api, PomodoroStatus } from "../api/client"; @@ -16,13 +17,22 @@ interface PomodoroContextValue { const PomodoroContext = createContext(null); +const POLL_ACTIVE_MS = 1000; +const POLL_IDLE_MS = 30000; + +function isTimerActive(status: PomodoroStatus | null): boolean { + return status?.status === "running" || status?.status === "paused"; +} + export function PomodoroProvider({ children }: { children: ReactNode }) { const [status, setStatus] = useState(null); const [error, setError] = useState(null); + const statusRef = useRef(null); const refresh = useCallback(async () => { try { const data = await api.pomodoroStatus(); + statusRef.current = data; setStatus(data); setError(null); } catch (err) { @@ -32,10 +42,24 @@ export function PomodoroProvider({ children }: { children: ReactNode }) { useEffect(() => { refresh().catch(console.error); - const timer = setInterval(() => { - refresh().catch(console.error); - }, 1000); - return () => clearInterval(timer); + + let cancelled = false; + let timeoutId: ReturnType; + + const schedule = () => { + const delay = isTimerActive(statusRef.current) ? POLL_ACTIVE_MS : POLL_IDLE_MS; + timeoutId = setTimeout(async () => { + if (cancelled) return; + await refresh().catch(console.error); + schedule(); + }, delay); + }; + + schedule(); + return () => { + cancelled = true; + clearTimeout(timeoutId); + }; }, [refresh]); return ( diff --git a/frontend/src/pages/Chat.css b/frontend/src/pages/Chat.css index 3a12c4b..d586d7a 100644 --- a/frontend/src/pages/Chat.css +++ b/frontend/src/pages/Chat.css @@ -102,8 +102,8 @@ .message-notice { align-self: center; max-width: 90%; - background: #1a2a1f; - border: 1px solid #2d5a3d; + background: #1a2433; + border: 1px solid #2a3f5a; font-size: 0.92rem; } diff --git a/frontend/src/pages/Chat.tsx b/frontend/src/pages/Chat.tsx index f707799..1d641a1 100644 --- a/frontend/src/pages/Chat.tsx +++ b/frontend/src/pages/Chat.tsx @@ -11,8 +11,15 @@ function shouldShowMessage(msg: ChatMessage): boolean { return true; } -function roleLabel(role: string): string { - if (role === "notice") return "таймер"; +function noticeLabel(content: string): string { + if (content.startsWith("⏱")) return "таймер"; + if (content.startsWith("📋")) return "задачи"; + if (content.startsWith("🔀")) return "git"; + return "система"; +} + +function roleLabel(role: string, content = ""): string { + if (role === "notice") return noticeLabel(content); if (role === "user") return "вы"; return role; } @@ -111,7 +118,9 @@ export default function Chat() { } if (chunk.event === "notice") { setLiveNotices((prev) => [...prev, chunk.data.content]); - refreshPomodoro(); + if (String(chunk.data.content).startsWith("⏱")) { + refreshPomodoro(); + } } if (chunk.event === "pomodoro") { refreshPomodoro(); @@ -166,7 +175,7 @@ export default function Chat() {
{visibleMessages.map((msg) => (
-
{roleLabel(msg.role)}
+
{roleLabel(msg.role, msg.content)}
{msg.role === "assistant" || msg.role === "notice" ? ( {msg.content} @@ -179,7 +188,7 @@ export default function Chat() { {liveNotices.map((notice, idx) => (
-
таймер
+
{noticeLabel(notice)}
{notice}