mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-01 16:51:53 +03:00
feat(runtime): add user-controlled turn recovery
This commit is contained in:
@@ -36,6 +36,7 @@ from nanobot.session.keys import (
|
||||
UNIFIED_SESSION_KEY,
|
||||
)
|
||||
from nanobot.session.manager import Session
|
||||
from nanobot.session.recovery import PENDING_FOLLOWUP_ID_KEY, PENDING_FOLLOWUPS_KEY
|
||||
from nanobot.session.turn_continuation import (
|
||||
INTERNAL_CONTINUATION_META,
|
||||
INTERNAL_CONTINUATION_RUN_STARTED_AT_META,
|
||||
@@ -161,6 +162,35 @@ def test_persist_cron_turn_uses_distinct_history_marker(tmp_path: Path) -> None:
|
||||
assert message["cron_prompt_ref"] == prompt_ref
|
||||
|
||||
|
||||
def test_persist_user_message_acknowledges_durable_followup(tmp_path: Path) -> None:
|
||||
loop = _make_full_loop(tmp_path)
|
||||
session = loop.sessions.get_or_create("websocket:chat")
|
||||
session.metadata[PENDING_FOLLOWUPS_KEY] = [
|
||||
{
|
||||
"id": "followup-1",
|
||||
"sender_id": "user",
|
||||
"chat_id": "chat",
|
||||
"content": "queued while busy",
|
||||
"media": [],
|
||||
"metadata": {},
|
||||
}
|
||||
]
|
||||
|
||||
persisted = loop._persist_user_message_early(
|
||||
InboundMessage(
|
||||
channel="websocket",
|
||||
sender_id="user",
|
||||
chat_id="chat",
|
||||
content="queued while busy",
|
||||
metadata={PENDING_FOLLOWUP_ID_KEY: "followup-1"},
|
||||
),
|
||||
session,
|
||||
)
|
||||
|
||||
assert persisted is True
|
||||
assert PENDING_FOLLOWUPS_KEY not in session.metadata
|
||||
|
||||
|
||||
def test_persist_local_trigger_turn_uses_hidden_automation_marker(tmp_path: Path) -> None:
|
||||
loop = _make_full_loop(tmp_path)
|
||||
session = loop.sessions.get_or_create("websocket:auto")
|
||||
@@ -381,6 +411,34 @@ def test_save_turn_keeps_multimodal_runtime_context_for_model_replay() -> None:
|
||||
assert public_history_message(session.messages[0])["content"] == []
|
||||
|
||||
|
||||
def test_save_turn_acknowledges_every_merged_recovery_followup() -> None:
|
||||
"""Persisting a merged injected row retires every durable follow-up ID."""
|
||||
loop = _mk_loop()
|
||||
session = Session(
|
||||
key="test:recovery-followups",
|
||||
metadata={
|
||||
PENDING_FOLLOWUPS_KEY: [
|
||||
{"id": "first"},
|
||||
{"id": "second"},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
loop._save_turn(
|
||||
session,
|
||||
[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "first\n\nsecond",
|
||||
PENDING_FOLLOWUP_ID_KEY: ["first", "second"],
|
||||
}
|
||||
],
|
||||
skip=0,
|
||||
)
|
||||
|
||||
assert PENDING_FOLLOWUPS_KEY not in session.metadata
|
||||
|
||||
|
||||
def test_save_turn_keeps_image_placeholder_and_runtime_context() -> None:
|
||||
loop = _mk_loop()
|
||||
session = Session(key="test:image")
|
||||
|
||||
Reference in New Issue
Block a user