feat(webui): add assistant reply fork-from-here

This commit is contained in:
Bayern4ever-dot
2026-06-10 04:26:06 +08:00
committed by Xubin Ren
parent 4a58b83acc
commit 03bca4c0a9
30 changed files with 1358 additions and 36 deletions
+75
View File
@@ -6,8 +6,10 @@ from nanobot.webui.transcript import (
WEBUI_TRANSCRIPT_SCHEMA_VERSION,
append_transcript_object,
build_webui_thread_response,
fork_transcript_before_user_index,
read_transcript_lines,
replay_transcript_to_ui_messages,
write_session_messages_as_transcript,
)
@@ -20,6 +22,79 @@ def test_append_and_read_roundtrip(tmp_path, monkeypatch) -> None:
assert lines[0]["text"] == "hello"
def test_fork_transcript_before_user_index_copies_only_prefix(tmp_path, monkeypatch) -> None:
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
source = "websocket:source"
for ev in (
{"event": "user", "chat_id": "source", "text": "round1"},
{"event": "message", "chat_id": "source", "text": "answer1"},
{"event": "turn_end", "chat_id": "source"},
{"event": "user", "chat_id": "source", "text": "round2 fork me"},
{"event": "message", "chat_id": "source", "text": "answer2"},
{"event": "user", "chat_id": "source", "text": "round3 must not appear"},
):
append_transcript_object(source, ev)
ok = fork_transcript_before_user_index(source, "websocket:fork", 1)
assert ok is True
lines = read_transcript_lines("websocket:fork")
assert [line.get("text") for line in lines] == ["round1", "answer1", None]
assert all(line.get("chat_id") == "fork" for line in lines)
assert "round2 fork me" not in "\n".join(str(line.get("text")) for line in lines)
assert "round3 must not appear" not in "\n".join(str(line.get("text")) for line in lines)
def test_fork_transcript_rejects_out_of_range_user_index(tmp_path, monkeypatch) -> None:
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
source = "websocket:source"
append_transcript_object(source, {"event": "user", "chat_id": "source", "text": "round1"})
assert fork_transcript_before_user_index(source, "websocket:fork", 2) is False
assert read_transcript_lines("websocket:fork") == []
def test_fork_transcript_allows_index_equal_to_user_count(tmp_path, monkeypatch) -> None:
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
source = "websocket:source"
for ev in (
{"event": "user", "chat_id": "source", "text": "round1"},
{"event": "message", "chat_id": "source", "text": "answer1"},
):
append_transcript_object(source, ev)
ok = fork_transcript_before_user_index(source, "websocket:fork", 1)
assert ok is True
assert [line.get("text") for line in read_transcript_lines("websocket:fork")] == [
"round1",
"answer1",
]
def test_write_session_messages_as_transcript_builds_canonical_prefix(
tmp_path,
monkeypatch,
) -> None:
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
write_session_messages_as_transcript(
"websocket:fork",
[
{"role": "user", "content": "round1"},
{"role": "assistant", "content": "answer1"},
],
)
lines = read_transcript_lines("websocket:fork")
assert lines == [
{"event": "user", "chat_id": "fork", "text": "round1"},
{"event": "message", "chat_id": "fork", "text": "answer1"},
]
msgs = replay_transcript_to_ui_messages(lines)
assert [m["content"] for m in msgs] == ["round1", "answer1"]
def test_replay_delta_and_turn_end(tmp_path, monkeypatch) -> None:
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
key = "websocket:t2"