fix(trigger): hide external trigger inputs
This commit is contained in:
@@ -18,6 +18,7 @@ from nanobot.bus.outbound_events import (
|
||||
from nanobot.bus.queue import MessageBus
|
||||
from nanobot.cron.session_turns import CRON_HISTORY_META, CRON_TRIGGER_META
|
||||
from nanobot.providers.base import LLMResponse
|
||||
from nanobot.session.automation_turns import AUTOMATION_HISTORY_META
|
||||
from nanobot.session.goal_state import GOAL_STATE_KEY
|
||||
from nanobot.session.manager import Session, SessionManager
|
||||
from nanobot.session.turn_continuation import (
|
||||
@@ -33,6 +34,7 @@ from nanobot.session.webui_turns import (
|
||||
clean_generated_title,
|
||||
maybe_generate_webui_title,
|
||||
)
|
||||
from nanobot.triggers.session_turns import EXTERNAL_TRIGGER_META
|
||||
from nanobot.utils.llm_runtime import LLMRuntime
|
||||
|
||||
|
||||
@@ -101,6 +103,13 @@ def test_persist_cron_turn_uses_distinct_history_marker(tmp_path: Path) -> None:
|
||||
assert persisted is True
|
||||
message = session.messages[-1]
|
||||
assert message["content"] == "Scheduled cron job triggered: Daily check"
|
||||
assert message[AUTOMATION_HISTORY_META] == {
|
||||
"kind": "cron",
|
||||
"cron_job_id": "job-1",
|
||||
"cron_job_name": "Daily check",
|
||||
"cron_run_id": "job-1:1",
|
||||
"cron_prompt_ref": prompt_ref,
|
||||
}
|
||||
assert message[CRON_HISTORY_META] is True
|
||||
assert CRON_TRIGGER_META not in message
|
||||
assert message["cron_job_id"] == "job-1"
|
||||
@@ -109,6 +118,44 @@ def test_persist_cron_turn_uses_distinct_history_marker(tmp_path: Path) -> None:
|
||||
assert message["cron_prompt_ref"] == prompt_ref
|
||||
|
||||
|
||||
def test_persist_external_trigger_turn_uses_hidden_automation_marker(tmp_path: Path) -> None:
|
||||
loop = _make_full_loop(tmp_path)
|
||||
session = loop.sessions.get_or_create("websocket:auto")
|
||||
|
||||
persisted = loop._persist_user_message_early(
|
||||
InboundMessage(
|
||||
channel="websocket",
|
||||
sender_id="trigger",
|
||||
chat_id="auto",
|
||||
content="Review PR #4502",
|
||||
metadata={
|
||||
EXTERNAL_TRIGGER_META: {
|
||||
"trigger_id": "trg_123",
|
||||
"trigger_name": "PR review",
|
||||
"delivery_id": "tdel_456",
|
||||
"created_at_ms": 1_700_000_000_000,
|
||||
}
|
||||
},
|
||||
),
|
||||
session,
|
||||
)
|
||||
|
||||
assert persisted is True
|
||||
message = session.messages[-1]
|
||||
assert message["content"] == "External trigger received: PR review"
|
||||
assert "Review PR #4502" not in message["content"]
|
||||
assert message[AUTOMATION_HISTORY_META] == {
|
||||
"kind": "trigger",
|
||||
"trigger_id": "trg_123",
|
||||
"trigger_name": "PR review",
|
||||
"trigger_delivery_id": "tdel_456",
|
||||
}
|
||||
assert EXTERNAL_TRIGGER_META not in message
|
||||
assert message["trigger_id"] == "trg_123"
|
||||
assert message["trigger_name"] == "PR review"
|
||||
assert message["trigger_delivery_id"] == "tdel_456"
|
||||
|
||||
|
||||
def test_clean_generated_title_strips_reasoning_tags() -> None:
|
||||
assert clean_generated_title("<think>reasoning</think> WebUI polish") == "WebUI polish"
|
||||
assert clean_generated_title("Title: <think> The user said hello") == ""
|
||||
|
||||
@@ -2975,3 +2975,49 @@ def test_handle_webui_thread_get_does_not_backfill_cron_internal_prompt(
|
||||
body = json.loads(resp.body.decode())
|
||||
assert [message["role"] for message in body["messages"]] == ["assistant"]
|
||||
assert [message["content"] for message in body["messages"]] == ["提醒已经到期。"]
|
||||
|
||||
|
||||
def test_handle_webui_thread_get_does_not_backfill_trigger_internal_prompt(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
from urllib.parse import quote
|
||||
|
||||
from websockets.datastructures import Headers
|
||||
from websockets.http11 import Request
|
||||
|
||||
from nanobot.session.automation_turns import AUTOMATION_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-trigger"
|
||||
session = sessions.get_or_create(key)
|
||||
session.add_message(
|
||||
"user",
|
||||
"External trigger received: PR review",
|
||||
**{AUTOMATION_HISTORY_META: {"kind": "trigger", "trigger_id": "trg_123"}},
|
||||
)
|
||||
session.add_message("assistant", "PR #4502 已经开始 review。")
|
||||
sessions.save(session)
|
||||
append_transcript_object(
|
||||
key,
|
||||
{"event": "message", "chat_id": "c-trigger", "text": "PR #4502 已经开始 review。"},
|
||||
)
|
||||
|
||||
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"]] == ["PR #4502 已经开始 review。"]
|
||||
|
||||
@@ -473,6 +473,24 @@ def test_replay_reused_turn_id_after_turn_end_starts_new_turn(tmp_path, monkeypa
|
||||
assert msgs[2]["source"] == {"kind": "cron", "label": "drink water"}
|
||||
|
||||
|
||||
def test_replay_preserves_trigger_source_metadata(tmp_path, monkeypatch) -> None:
|
||||
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
|
||||
key = "websocket:t-trigger-source"
|
||||
append_transcript_object(
|
||||
key,
|
||||
{
|
||||
"event": "message",
|
||||
"chat_id": "t-trigger-source",
|
||||
"text": "PR #4502 review started.",
|
||||
"source": {"kind": "trigger", "label": "PR review"},
|
||||
},
|
||||
)
|
||||
|
||||
msgs = replay_transcript_to_ui_messages(read_transcript_lines(key))
|
||||
|
||||
assert msgs[0]["source"] == {"kind": "trigger", "label": "PR review"}
|
||||
|
||||
|
||||
def test_build_response_restores_session_users_for_legacy_transcript(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
|
||||
@@ -6,6 +6,7 @@ from pathlib import Path
|
||||
|
||||
import nanobot.webui.session_list_index as session_list_index
|
||||
from nanobot.cron.session_turns import CRON_HISTORY_META
|
||||
from nanobot.session.automation_turns import AUTOMATION_HISTORY_META
|
||||
from nanobot.session.manager import SessionManager
|
||||
|
||||
|
||||
@@ -88,6 +89,20 @@ def test_webui_session_list_skips_cron_internal_user_preview(tmp_path: Path) ->
|
||||
assert list_webui_sessions(manager)[0]["preview"] == "提醒已经到期。"
|
||||
|
||||
|
||||
def test_webui_session_list_skips_trigger_internal_user_preview(tmp_path: Path) -> None:
|
||||
manager = SessionManager(tmp_path)
|
||||
session = manager.get_or_create("websocket:trigger-preview")
|
||||
session.add_message(
|
||||
"user",
|
||||
"External trigger received: PR review",
|
||||
**{AUTOMATION_HISTORY_META: {"kind": "trigger", "trigger_id": "trg_123"}},
|
||||
)
|
||||
session.add_message("assistant", "PR #4502 已经开始 review。")
|
||||
manager.save(session)
|
||||
|
||||
assert list_webui_sessions(manager)[0]["preview"] == "PR #4502 已经开始 review。"
|
||||
|
||||
|
||||
def test_webui_session_list_uses_webui_transcript_activity_for_sort(
|
||||
tmp_path: Path,
|
||||
monkeypatch,
|
||||
|
||||
Reference in New Issue
Block a user