fix(heartbeat): route unified sessions to last channel

This commit is contained in:
yu-xin-c
2026-07-27 00:12:44 +08:00
committed by Xubin Ren
parent be43a54570
commit a7a6c26eab
5 changed files with 137 additions and 1 deletions
+26
View File
@@ -30,6 +30,10 @@ from nanobot.runtime_context import (
)
from nanobot.session.automation_turns import AUTOMATION_HISTORY_META
from nanobot.session.goal_state import GOAL_STATE_KEY
from nanobot.session.keys import (
LAST_CHANNEL_METADATA_KEY,
UNIFIED_SESSION_KEY,
)
from nanobot.session.manager import Session, SessionManager
from nanobot.session.turn_continuation import (
INTERNAL_CONTINUATION_META,
@@ -682,6 +686,28 @@ async def test_process_message_persists_user_message_before_turn_completes(tmp_p
assert persisted.updated_at >= persisted.created_at
@pytest.mark.asyncio
async def test_process_message_persists_unified_session_delivery_route(tmp_path: Path) -> None:
loop = _make_full_loop(tmp_path)
loop._unified_session = True
loop.consolidator.maybe_consolidate_by_tokens = AsyncMock(return_value=False) # type: ignore[method-assign]
loop._run_agent_loop = AsyncMock(side_effect=RuntimeError("boom")) # type: ignore[method-assign]
msg = InboundMessage(
channel="feishu",
sender_id="u1",
chat_id="oc_123",
content="persist my route",
session_key_override=UNIFIED_SESSION_KEY,
)
with pytest.raises(RuntimeError, match="boom"):
await loop._process_message(msg)
loop.sessions.invalidate(UNIFIED_SESSION_KEY)
persisted = loop.sessions.get_or_create(UNIFIED_SESSION_KEY)
assert persisted.metadata[LAST_CHANNEL_METADATA_KEY] == "feishu:oc_123"
# 1x1 PNG used by the media-persistence tests. ``extract_documents`` runs
# at the top of ``_process_message`` and filters ``msg.media`` down to
# paths that magic-byte-sniff as images, so the test fixture needs real
+36
View File
@@ -1787,6 +1787,42 @@ def test_heartbeat_target_skips_archived_webui_sessions():
assert target == ("websocket", "active")
def test_heartbeat_target_uses_last_channel_for_unified_session():
from nanobot.cli.commands import _pick_heartbeat_target_from_sessions
from nanobot.session.keys import LAST_CHANNEL_METADATA_KEY, UNIFIED_SESSION_KEY
target = _pick_heartbeat_target_from_sessions(
enabled_channels=["telegram", "discord"],
archived_keys=[],
sessions=[{"key": UNIFIED_SESSION_KEY}],
unified_session_metadata={LAST_CHANNEL_METADATA_KEY: "discord:chat-42"},
)
assert target == ("discord", "chat-42")
@pytest.mark.parametrize(
"metadata",
[
{"last_channel": "telegram:chat-42"},
{"last_channel": "cli:direct"},
{"last_channel": "invalid"},
],
)
def test_heartbeat_target_rejects_unroutable_unified_metadata(metadata):
from nanobot.cli.commands import _pick_heartbeat_target_from_sessions
from nanobot.session.keys import UNIFIED_SESSION_KEY
target = _pick_heartbeat_target_from_sessions(
enabled_channels=["discord"],
archived_keys=[],
sessions=[{"key": UNIFIED_SESSION_KEY}],
unified_session_metadata=metadata,
)
assert target == ("cli", "direct")
def _write_instance_config(tmp_path: Path) -> Path:
config_file = tmp_path / "instance" / "config.json"
config_file.parent.mkdir(parents=True)