fix(webui): persist user messages for refresh
This commit is contained in:
@@ -294,6 +294,87 @@ async def test_webui_message_envelope_marks_inbound_metadata(bus: MagicMock) ->
|
||||
assert msg.metadata["_wants_stream"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_webui_message_envelope_persists_user_transcript_for_refresh(
|
||||
bus: MagicMock,
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
from nanobot.webui.transcript import build_webui_thread_response, read_transcript_lines
|
||||
|
||||
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
|
||||
channel = _ch(bus)
|
||||
conn = AsyncMock()
|
||||
conn.remote_address = ("127.0.0.1", 50123)
|
||||
|
||||
async def answer_during_publish(_msg: Any) -> None:
|
||||
await channel.send(OutboundMessage(channel="websocket", chat_id="chat-1", content="hi back"))
|
||||
|
||||
bus.publish_inbound.side_effect = answer_during_publish
|
||||
|
||||
await channel._dispatch_envelope(
|
||||
conn,
|
||||
"webui-client",
|
||||
{"type": "message", "chat_id": "chat-1", "content": "hello", "webui": True},
|
||||
)
|
||||
|
||||
lines = read_transcript_lines("websocket:chat-1")
|
||||
assert [line["event"] for line in lines] == ["user", "message"]
|
||||
|
||||
body = build_webui_thread_response("websocket:chat-1")
|
||||
assert body is not None
|
||||
assert [message["role"] for message in body["messages"]] == ["user", "assistant"]
|
||||
assert [message["content"] for message in body["messages"]] == ["hello", "hi back"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_webui_stop_control_message_is_not_persisted_as_user_bubble(
|
||||
bus: MagicMock,
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
from nanobot.webui.transcript import read_transcript_lines
|
||||
|
||||
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
|
||||
channel = _ch(bus)
|
||||
conn = AsyncMock()
|
||||
conn.remote_address = ("127.0.0.1", 50123)
|
||||
|
||||
await channel._dispatch_envelope(
|
||||
conn,
|
||||
"webui-client",
|
||||
{"type": "message", "chat_id": "chat-1", "content": "/stop", "webui": True},
|
||||
)
|
||||
|
||||
msg = bus.publish_inbound.await_args.args[0]
|
||||
assert msg.content == "/stop"
|
||||
assert read_transcript_lines("websocket:chat-1") == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_webui_user_transcript_append_failure_does_not_block_inbound(
|
||||
bus: MagicMock,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
def fail_append(_session_key: str, _obj: dict[str, Any]) -> None:
|
||||
raise OSError("disk full")
|
||||
|
||||
monkeypatch.setattr("nanobot.channels.websocket.append_transcript_object", fail_append)
|
||||
channel = _ch(bus)
|
||||
conn = AsyncMock()
|
||||
conn.remote_address = ("127.0.0.1", 50123)
|
||||
|
||||
await channel._dispatch_envelope(
|
||||
conn,
|
||||
"webui-client",
|
||||
{"type": "message", "chat_id": "chat-1", "content": "hello", "webui": True},
|
||||
)
|
||||
|
||||
msg = bus.publish_inbound.await_args.args[0]
|
||||
assert msg.chat_id == "chat-1"
|
||||
assert msg.content == "hello"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_plain_websocket_message_does_not_mark_webui(bus: MagicMock) -> None:
|
||||
channel = _ch(bus)
|
||||
@@ -2411,3 +2492,47 @@ def test_handle_webui_thread_get_returns_json(tmp_path, monkeypatch) -> None:
|
||||
assert len(body["messages"]) == 1
|
||||
assert body["messages"][0]["role"] == "user"
|
||||
assert body["messages"][0]["content"] == "hi"
|
||||
|
||||
|
||||
def test_handle_webui_thread_get_backfills_legacy_missing_user_rows(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
from urllib.parse import quote
|
||||
|
||||
from websockets.datastructures import Headers
|
||||
from websockets.http11 import Request
|
||||
|
||||
from nanobot.webui.transcript import append_transcript_object
|
||||
|
||||
monkeypatch.setattr("nanobot.config.paths.get_data_dir", lambda: tmp_path)
|
||||
workspace = tmp_path / "workspace"
|
||||
sessions = SessionManager(workspace)
|
||||
key = "websocket:c-legacy"
|
||||
session = sessions.get_or_create(key)
|
||||
session.add_message("user", "legacy question")
|
||||
session.add_message("assistant", "legacy answer")
|
||||
sessions.save(session)
|
||||
append_transcript_object(
|
||||
key,
|
||||
{"event": "message", "chat_id": "c-legacy", "text": "legacy answer"},
|
||||
)
|
||||
|
||||
bus = MagicMock()
|
||||
channel = WebSocketChannel(
|
||||
{"enabled": True, "allowFrom": ["*"]},
|
||||
bus,
|
||||
gateway=_basic_handler(bus, session_manager=sessions, workspace_path=workspace),
|
||||
)
|
||||
channel.gateway.tokens.api_tokens["tok"] = time.monotonic() + 300.0
|
||||
enc = quote(key, safe="")
|
||||
req = Request(f"/api/sessions/{enc}/webui-thread", Headers([("Authorization", "Bearer tok")]))
|
||||
resp = channel.gateway.http._handle_webui_thread_get(req, enc)
|
||||
|
||||
assert resp.status_code == 200
|
||||
body = json.loads(resp.body.decode())
|
||||
assert [message["role"] for message in body["messages"]] == ["user", "assistant"]
|
||||
assert [message["content"] for message in body["messages"]] == [
|
||||
"legacy question",
|
||||
"legacy answer",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user