mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-04-30 06:45:55 +00:00
Route heartbeat, cron, and message-tool deliveries through one gateway helper so user-visible proactive messages are available when the channel replies. Made-with: Cursor
121 lines
4.9 KiB
Python
121 lines
4.9 KiB
Python
"""Tests for heartbeat context bridge — injecting delivered messages into channel session."""
|
|
|
|
from nanobot.session.manager import SessionManager
|
|
|
|
|
|
class TestHeartbeatContextBridge:
|
|
"""Verify that on_heartbeat_notify injects the assistant message into the
|
|
channel session so user replies have conversational context."""
|
|
|
|
def test_notify_injects_into_channel_session(self, tmp_path):
|
|
"""After notify, the target channel session should contain the
|
|
heartbeat response as an assistant turn."""
|
|
session_mgr = SessionManager(tmp_path / "sessions")
|
|
target_key = "telegram:12345"
|
|
|
|
# Simulate: session exists with one user message
|
|
target_session = session_mgr.get_or_create(target_key)
|
|
target_session.add_message("user", "hello earlier")
|
|
session_mgr.save(target_session)
|
|
|
|
# Simulate what on_heartbeat_notify does
|
|
target_session = session_mgr.get_or_create(target_key)
|
|
target_session.add_message(
|
|
"assistant",
|
|
"3 new emails — invoice, meeting, proposal.",
|
|
_channel_delivery=True,
|
|
)
|
|
session_mgr.save(target_session)
|
|
|
|
# Reload and verify
|
|
reloaded = session_mgr.get_or_create(target_key)
|
|
messages = reloaded.get_history(max_messages=0)
|
|
roles = [m["role"] for m in messages]
|
|
assert roles == ["user", "assistant"]
|
|
assert "3 new emails" in messages[-1]["content"]
|
|
|
|
def test_reply_after_injection_has_context(self, tmp_path):
|
|
"""Simulates the full flow: prior conversation exists, heartbeat
|
|
injects, then user replies. The session should have the heartbeat
|
|
message visible in get_history so the model sees the context."""
|
|
session_mgr = SessionManager(tmp_path / "sessions")
|
|
target_key = "telegram:12345"
|
|
|
|
# Pre-existing conversation (user has chatted before)
|
|
session = session_mgr.get_or_create(target_key)
|
|
session.add_message("user", "Hey")
|
|
session.add_message("assistant", "Hi there!")
|
|
session_mgr.save(session)
|
|
|
|
# Step 1: heartbeat injects assistant message
|
|
session = session_mgr.get_or_create(target_key)
|
|
session.add_message(
|
|
"assistant",
|
|
"If you want, I can mark that email as read.",
|
|
_channel_delivery=True,
|
|
)
|
|
session_mgr.save(session)
|
|
|
|
# Step 2: user replies "Sure"
|
|
session = session_mgr.get_or_create(target_key)
|
|
session.add_message("user", "Sure")
|
|
session_mgr.save(session)
|
|
|
|
# Verify: get_history includes the heartbeat injection
|
|
reloaded = session_mgr.get_or_create(target_key)
|
|
history = reloaded.get_history(max_messages=0)
|
|
roles = [m["role"] for m in history]
|
|
assert roles == ["user", "assistant", "assistant", "user"]
|
|
assert "mark that email" in history[2]["content"]
|
|
assert history[3]["content"] == "Sure"
|
|
|
|
def test_injection_does_not_duplicate_on_existing_history(self, tmp_path):
|
|
"""If the channel session already has messages, the injection
|
|
appends cleanly without corruption."""
|
|
session_mgr = SessionManager(tmp_path / "sessions")
|
|
target_key = "telegram:12345"
|
|
|
|
# Pre-existing conversation
|
|
session = session_mgr.get_or_create(target_key)
|
|
session.add_message("user", "What time is it?")
|
|
session.add_message("assistant", "It's 2pm.")
|
|
session.add_message("user", "Thanks")
|
|
session_mgr.save(session)
|
|
|
|
# Heartbeat injects
|
|
session = session_mgr.get_or_create(target_key)
|
|
session.add_message(
|
|
"assistant",
|
|
"You have a meeting in 30 minutes.",
|
|
_channel_delivery=True,
|
|
)
|
|
session_mgr.save(session)
|
|
|
|
# Verify
|
|
reloaded = session_mgr.get_or_create(target_key)
|
|
history = reloaded.get_history(max_messages=0)
|
|
roles = [m["role"] for m in history]
|
|
assert roles == ["user", "assistant", "user", "assistant"]
|
|
assert "meeting in 30 minutes" in history[-1]["content"]
|
|
|
|
def test_reply_after_injection_to_empty_session_keeps_context(self, tmp_path):
|
|
"""A user replying to the first delivered message still sees that context."""
|
|
session_mgr = SessionManager(tmp_path / "sessions")
|
|
target_key = "telegram:99999"
|
|
|
|
session = session_mgr.get_or_create(target_key)
|
|
session.add_message(
|
|
"assistant",
|
|
"Weather alert: sandstorm expected at 4pm.",
|
|
_channel_delivery=True,
|
|
)
|
|
session.add_message("user", "Sure")
|
|
session_mgr.save(session)
|
|
|
|
reloaded = session_mgr.get_or_create(target_key)
|
|
history = reloaded.get_history(max_messages=0)
|
|
assert len(history) == 2
|
|
assert history[0]["role"] == "assistant"
|
|
assert "sandstorm" in history[0]["content"]
|
|
assert history[1] == {"role": "user", "content": "Sure"}
|