fix: hide internal cron prompts from webui

This commit is contained in:
chengyongru 2026-06-12 18:17:28 +08:00
parent a50b3ac0f2
commit 32d8a1dd7b
6 changed files with 97 additions and 0 deletions

View File

@ -22,6 +22,7 @@ from nanobot.bus.runtime_events import (
TurnCompleted, TurnCompleted,
TurnRunStatusChanged, TurnRunStatusChanged,
) )
from nanobot.cron.session_turns import CRON_HISTORY_META
from nanobot.providers.base import LLMProvider from nanobot.providers.base import LLMProvider
from nanobot.session.goal_state import goal_state_ws_blob from nanobot.session.goal_state import goal_state_ws_blob
from nanobot.session.manager import Session, SessionManager from nanobot.session.manager import Session, SessionManager
@ -68,6 +69,8 @@ def _title_inputs(session: Session) -> tuple[str, str]:
for message in session.messages: for message in session.messages:
if message.get("_command") is True: if message.get("_command") is True:
continue continue
if message.get(CRON_HISTORY_META) is True:
continue
role = message.get("role") role = message.get("role")
content = message.get("content") content = message.get("content")
if not isinstance(content, str) or not content.strip(): if not isinstance(content, str) or not content.strip():

View File

@ -14,6 +14,7 @@ from typing import Any
from loguru import logger from loguru import logger
from nanobot.cron.session_turns import CRON_HISTORY_META
from nanobot.session.manager import ( from nanobot.session.manager import (
_SESSION_LIST_PREVIEW_MAX_CHARS, _SESSION_LIST_PREVIEW_MAX_CHARS,
_SESSION_LIST_PREVIEW_MAX_RECORDS, _SESSION_LIST_PREVIEW_MAX_RECORDS,
@ -142,6 +143,8 @@ def _preview_from_messages(messages: list[dict[str, Any]]) -> str:
or scanned_chars > _SESSION_LIST_PREVIEW_MAX_CHARS or scanned_chars > _SESSION_LIST_PREVIEW_MAX_CHARS
): ):
break break
if item.get(CRON_HISTORY_META) is True:
continue
text = _message_preview_text(item) text = _message_preview_text(item)
if not text: if not text:
continue continue
@ -193,6 +196,8 @@ def _scan_session_row(session_manager: SessionManager, path: Path) -> dict[str,
item = json.loads(line) item = json.loads(line)
if item.get("_type") == "metadata": if item.get("_type") == "metadata":
continue continue
if item.get(CRON_HISTORY_META) is True:
continue
text = _message_preview_text(item) text = _message_preview_text(item)
if not text: if not text:
continue continue

View File

@ -17,6 +17,7 @@ from urllib.parse import unquote, urlparse
from loguru import logger from loguru import logger
from nanobot.config.paths import get_webui_dir from nanobot.config.paths import get_webui_dir
from nanobot.cron.session_turns import CRON_HISTORY_META
from nanobot.session.manager import SessionManager from nanobot.session.manager import SessionManager
from nanobot.webui.metadata import WEBUI_MESSAGE_SOURCE_METADATA_KEY, WEBUI_TURN_METADATA_KEY from nanobot.webui.metadata import WEBUI_MESSAGE_SOURCE_METADATA_KEY, WEBUI_TURN_METADATA_KEY
@ -854,6 +855,8 @@ def _session_user_event(
) -> dict[str, Any] | None: ) -> dict[str, Any] | None:
if message.get("role") != "user": if message.get("role") != "user":
return None return None
if message.get(CRON_HISTORY_META) is True:
return None
content = message.get("content") content = message.get("content")
text = content if isinstance(content, str) else "" text = content if isinstance(content, str) else ""
media = message.get("media") media = message.get("media")

View File

@ -181,6 +181,31 @@ async def test_generate_webui_title_ignores_command_only_sessions(tmp_path: Path
loop.provider.chat_with_retry.assert_not_awaited() loop.provider.chat_with_retry.assert_not_awaited()
@pytest.mark.asyncio
async def test_generate_webui_title_ignores_cron_internal_turns(tmp_path: Path) -> None:
loop = _make_full_loop(tmp_path)
session = loop.sessions.get_or_create("websocket:cron-title")
session.metadata[WEBUI_SESSION_METADATA_KEY] = True
session.add_message(
"user",
"Scheduled cron job triggered: 30s-test\n\nInternal reminder prompt",
**{CRON_HISTORY_META: True},
)
session.add_message("assistant", "提醒已经到期。")
loop.sessions.save(session)
generated = await maybe_generate_webui_title(
sessions=loop.sessions,
session_key="websocket:cron-title",
provider=loop.provider,
model=loop.model,
)
assert generated is False
assert WEBUI_TITLE_METADATA_KEY not in session.metadata
loop.provider.chat_with_retry.assert_not_awaited()
def test_webui_title_update_uses_captured_llm_runtime( def test_webui_title_update_uses_captured_llm_runtime(
tmp_path: Path, tmp_path: Path,
monkeypatch: pytest.MonkeyPatch, monkeypatch: pytest.MonkeyPatch,

View File

@ -2866,3 +2866,49 @@ def test_handle_webui_thread_get_backfills_legacy_missing_user_rows(
"legacy question", "legacy question",
"legacy answer", "legacy answer",
] ]
def test_handle_webui_thread_get_does_not_backfill_cron_internal_prompt(
tmp_path,
monkeypatch,
) -> None:
from urllib.parse import quote
from websockets.datastructures import Headers
from websockets.http11 import Request
from nanobot.cron.session_turns import CRON_HISTORY_META
from nanobot.webui.transcript import append_transcript_object
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
workspace = tmp_path / "workspace"
sessions = SessionManager(workspace)
key = "websocket:c-cron"
session = sessions.get_or_create(key)
session.add_message(
"user",
"Scheduled cron job triggered: 30s-test\n\nInternal reminder prompt",
**{CRON_HISTORY_META: True},
)
session.add_message("assistant", "提醒已经到期。")
sessions.save(session)
append_transcript_object(
key,
{"event": "message", "chat_id": "c-cron", "text": "提醒已经到期。"},
)
bus = MagicMock()
channel = WebSocketChannel(
{"enabled": True, "allowFrom": ["*"]},
bus,
gateway=_basic_handler(bus, session_manager=sessions, workspace_path=workspace),
)
channel.gateway.tokens.api_tokens["tok"] = time.monotonic() + 300.0
enc = quote(key, safe="")
req = Request(f"/api/sessions/{enc}/webui-thread", Headers([("Authorization", "Bearer tok")]))
resp = channel.gateway.http._handle_webui_thread_get(req, enc)
assert resp.status_code == 200
body = json.loads(resp.body.decode())
assert [message["role"] for message in body["messages"]] == ["assistant"]
assert [message["content"] for message in body["messages"]] == ["提醒已经到期。"]

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
import nanobot.webui.session_list_index as session_list_index import nanobot.webui.session_list_index as session_list_index
from nanobot.cron.session_turns import CRON_HISTORY_META
from nanobot.session.manager import SessionManager from nanobot.session.manager import SessionManager
@ -71,5 +72,19 @@ def test_webui_session_list_drops_deleted_index_rows(tmp_path: Path) -> None:
assert list_webui_sessions(manager) == [] assert list_webui_sessions(manager) == []
def test_webui_session_list_skips_cron_internal_user_preview(tmp_path: Path) -> None:
manager = SessionManager(tmp_path)
session = manager.get_or_create("websocket:cron-preview")
session.add_message(
"user",
"Scheduled cron job triggered: 30s-test\n\nInternal reminder prompt",
**{CRON_HISTORY_META: True},
)
session.add_message("assistant", "提醒已经到期。")
manager.save(session)
assert list_webui_sessions(manager)[0]["preview"] == "提醒已经到期。"
def list_webui_sessions(manager: SessionManager) -> list[dict]: def list_webui_sessions(manager: SessionManager) -> list[dict]:
return session_list_index.list_webui_sessions(manager) return session_list_index.list_webui_sessions(manager)