mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-06-15 15:24:06 +00:00
fix: hide internal cron prompts from webui
This commit is contained in:
parent
a50b3ac0f2
commit
32d8a1dd7b
@ -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():
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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"]] == ["提醒已经到期。"]
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user