fix(image): prevent duplicate delivery and replay artifacts
This commit is contained in:
@@ -289,6 +289,18 @@ def test_build_messages_passes_channel_to_system_prompt(tmp_path) -> None:
|
||||
assert "messaging app" in system
|
||||
|
||||
|
||||
def test_system_prompt_keeps_message_tool_out_of_current_chat_replies(tmp_path) -> None:
|
||||
workspace = _make_workspace(tmp_path)
|
||||
builder = ContextBuilder(workspace)
|
||||
|
||||
prompt = builder.build_system_prompt(channel="slack")
|
||||
|
||||
assert "Do not use the 'message' tool for normal replies in the current chat" in prompt
|
||||
assert "the runtime attaches those artifacts to the final assistant reply automatically" in prompt
|
||||
assert "do not call 'message' just to announce or resend them" in prompt
|
||||
assert "Wait for the tool results, then answer once" in prompt
|
||||
|
||||
|
||||
def test_subagent_result_does_not_create_consecutive_assistant_messages(tmp_path) -> None:
|
||||
workspace = _make_workspace(tmp_path)
|
||||
builder = ContextBuilder(workspace)
|
||||
|
||||
@@ -245,14 +245,8 @@ def test_get_history_annotates_user_turns_but_not_assistant_turns():
|
||||
]
|
||||
|
||||
|
||||
def test_get_history_annotates_proactive_assistant_deliveries_with_timestamps():
|
||||
"""Cron / heartbeat assistant pushes still carry a timestamp prefix.
|
||||
|
||||
These proactive deliveries can sit hours away from the next user reply,
|
||||
so the model needs to know when they fired. They are rare enough that
|
||||
they don't act as in-context demonstrations encouraging the model to
|
||||
prefix its own normal replies with ``[Message Time: ...]``.
|
||||
"""
|
||||
def test_get_history_does_not_annotate_proactive_assistant_deliveries_with_timestamps():
|
||||
"""Assistant-side timestamp examples can leak back into future replies."""
|
||||
session = Session(key="test:proactive-timestamps")
|
||||
session.messages.append({
|
||||
"role": "assistant",
|
||||
@@ -271,7 +265,7 @@ def test_get_history_annotates_proactive_assistant_deliveries_with_timestamps():
|
||||
assert history == [
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "[Message Time: 2026-04-26T15:00:00]\n记得喝水",
|
||||
"content": "记得喝水",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
@@ -370,6 +364,41 @@ def test_get_history_ignores_media_kwarg_on_non_user_rows():
|
||||
assert history[0]["content"] == [{"type": "text", "text": "structured"}]
|
||||
|
||||
|
||||
def test_get_history_does_not_paste_assistant_media_paths_into_replay():
|
||||
session = Session(key="test:assistant-media")
|
||||
session.messages.append(
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "来了 🎨",
|
||||
"media": ["/home/user/.nanobot/media/generated/img_abc.png"],
|
||||
}
|
||||
)
|
||||
|
||||
history = session.get_history(max_messages=500)
|
||||
|
||||
assert history == [{"role": "assistant", "content": "来了 🎨"}]
|
||||
|
||||
|
||||
def test_get_history_sanitizes_existing_assistant_replay_artifacts():
|
||||
session = Session(key="test:polluted-assistant")
|
||||
session.messages.append(
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": (
|
||||
"[Message Time: 2026-05-09 00:33:48]\n"
|
||||
"来了 🎨\n"
|
||||
"[image: /home/user/.nanobot/media/generated/img_old.png]\n\n"
|
||||
"generate_image(\"16:9\")\n"
|
||||
"message(\"来了 🎨\")"
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
history = session.get_history(max_messages=500, include_timestamps=True)
|
||||
|
||||
assert history == [{"role": "assistant", "content": "来了 🎨"}]
|
||||
|
||||
|
||||
def test_get_history_respects_max_tokens(monkeypatch):
|
||||
session = Session(key="test:token-cap")
|
||||
session.messages.extend(
|
||||
|
||||
@@ -166,3 +166,13 @@ class TestMessageToolTurnTracking:
|
||||
tool._sent_in_turn = True
|
||||
tool.start_turn()
|
||||
assert not tool._sent_in_turn
|
||||
|
||||
def test_schema_discourages_current_chat_replies(self) -> None:
|
||||
tool = MessageTool()
|
||||
|
||||
assert "Do not use this for the normal reply in the current chat" in tool.description
|
||||
assert "generate_image creates images in the current chat" in tool.description
|
||||
assert (
|
||||
"Do not use this for a normal reply in the current chat"
|
||||
in tool.parameters["properties"]["content"]["description"]
|
||||
)
|
||||
|
||||
@@ -75,6 +75,7 @@ def test_generated_image_paths_from_tool_results() -> None:
|
||||
{"id": "img_2", "path": "/tmp/two.png"},
|
||||
]
|
||||
)
|
||||
payload = json.loads(result)
|
||||
|
||||
assert generated_image_paths_from_messages(
|
||||
[
|
||||
@@ -82,3 +83,5 @@ def test_generated_image_paths_from_tool_results() -> None:
|
||||
{"role": "tool", "name": "other", "content": result},
|
||||
]
|
||||
) == ["/tmp/one.png", "/tmp/two.png"]
|
||||
assert "runtime attaches generated images automatically" in payload["next_step"]
|
||||
assert "Do not call message" in payload["next_step"]
|
||||
|
||||
Reference in New Issue
Block a user