fixed injection watcher

This commit is contained in:
2026-06-11 08:11:51 +03:00
parent 06e09cd728
commit 481b93e84a
5 changed files with 131 additions and 37 deletions
+51 -3
View File
@@ -1,6 +1,7 @@
import asyncio
import json
import logging
import time
from collections.abc import AsyncIterator
from typing import Any
@@ -179,14 +180,21 @@ class ChatService:
self._save_message(session_id, "user", user_text)
yield self._sse("status", {"phase": "preparing"})
t0 = time.monotonic()
messages = await asyncio.to_thread(_build_messages_for_session, session_id)
prepare_sec = time.monotonic() - t0
if not messages:
yield self._sse("error", {"message": "Session not found"})
return
yield self._sse("status", {"phase": "generating"})
streamed_reply_parts: list[str] = []
all_tool_notices: list[str] = []
tools_executed = 0
tool_round = 0
for _ in range(MAX_TOOL_ROUNDS):
tool_round += 1
t_round = time.monotonic()
content_parts: list[str] = []
tool_calls: list[dict[str, Any]] = []
reasoning = ""
@@ -201,11 +209,29 @@ class ChatService:
if event.get("reasoning_details"):
reasoning_details = event["reasoning_details"]
elif event["type"] == "error":
logger.warning(
"chat session=%s llm_error round=%d prepare=%.2fs: %s",
session_id,
tool_round,
prepare_sec,
event.get("content"),
)
yield self._sse("error", {"message": event.get("content", "LLM error")})
return
elif event["type"] == "tool_calls":
tool_calls = event["tool_calls"]
logger.info(
"chat session=%s round=%d prepare=%.2fs llm=%.2fs "
"content_len=%d tool_calls=%d",
session_id,
tool_round,
prepare_sec,
time.monotonic() - t_round,
len("".join(content_parts)),
len(tool_calls),
)
if tool_calls:
round_text = "".join(content_parts)
if round_text.strip():
@@ -241,6 +267,7 @@ class ChatService:
result = await execute_tool(
self.db, fn["name"], args, session_id=session_id
)
tools_executed += 1
tool_message = {
"role": "tool",
"tool_call_id": tool_call["id"],
@@ -253,6 +280,7 @@ class ChatService:
if notice:
self._save_message(session_id, "notice", notice)
round_notices.append(notice)
all_tool_notices.append(notice)
if fn["name"] in POMODORO_TOOL_NAMES:
yield self._sse(
@@ -270,14 +298,27 @@ class ChatService:
final_content = "".join(streamed_reply_parts).strip()
if not final_content and reasoning:
final_content = reasoning.strip()
if not final_content and all_tool_notices:
# Notices уже ушли в SSE event: notice; здесь только финальный текст в БД.
final_content = "\n\n".join(all_tool_notices)
if not final_content and tools_executed:
retry = await self.llm.complete(messages, tools=None, temperature=0.4)
final_content = (retry.get("content") or "").strip()
if final_content:
yield self._sse("token", {"content": final_content})
if not final_content:
logger.warning(
"chat session=%s empty_reply tools=%d rounds=%d",
session_id,
tools_executed,
tool_round,
)
yield self._sse(
"error",
{
"message": (
"Модель не вернула текст. Для deepseek-v4-pro: "
"OPENROUTER_TOOLS_ENABLED=true и OPENROUTER_REASONING_EFFORT=none. "
"Для памяти: MEMORY_EXTRACT_MODEL=deepseek/deepseek-chat."
"Модель не вернула текст после выполнения команд. "
"Проверь OPENROUTER_MODEL и OPENROUTER_REASONING_EFFORT=none."
),
},
)
@@ -285,6 +326,13 @@ class ChatService:
self._save_message(session_id, "assistant", final_content)
logger.info(
"chat session=%s done tools=%d reply_len=%d total=%.2fs",
session_id,
tools_executed,
len(final_content),
time.monotonic() - t0,
)
yield self._sse("done", {})
if get_settings().memory_auto_extract:
asyncio.create_task(