"""Conversation export: speech (exchanges) + actions (tool_events) merged in order.""" from __future__ import annotations import importlib import json import pytest def _const_embed(texts): return [[1e-6] * 8 for _ in texts] @pytest.fixture def mods(tmp_path, monkeypatch): monkeypatch.setenv("LYRA_DB_PATH", str(tmp_path / "test.db")) from lyra import llm monkeypatch.setattr(llm, "embed", _const_embed) import lyra.memory as memory importlib.reload(memory) import lyra.transcript as transcript importlib.reload(transcript) return memory, transcript def _seed(memory): """A turn where Brian narrates a hand and Lyra logs it, then replies.""" memory.ensure_session("s1", name="Meadows 1/3") memory.remember("s1", "user", "got it in with a set, he had the flush draw and bricked") memory.add_tool_event("s1", "record_hand", {"result": "won", "pot": 750}, "hand #42 logged") memory.add_tool_event("s1", "log_stack", {"amount": 750, "note": "doubled up"}, "ok") memory.remember("s1", "assistant", "Clean stack-off — logged it to your timeline.") def test_tool_events_roundtrip_parses_args(mods): memory, _ = mods _seed(memory) events = memory.tool_events("s1") assert [e["tool"] for e in events] == ["record_hand", "log_stack"] assert events[0]["args"] == {"result": "won", "pot": 750} # parsed back to a dict assert events[1]["result"] == "ok" def test_markdown_interleaves_speech_and_actions_in_order(mods): memory, transcript = mods _seed(memory) md = transcript.as_markdown("s1", name="Meadows 1/3") # user message, then both tool calls, then assistant reply — in that order i_user = md.index("got it in with a set") i_hand = md.index("record_hand") i_stack = md.index("log_stack") i_reply = md.index("Clean stack-off") assert i_user < i_hand < i_stack < i_reply assert "**Brian**" in md and "**Lyra**" in md assert "⚙" in md def test_json_export_is_machine_readable(mods): memory, transcript = mods _seed(memory) payload = transcript.as_json("s1", name="Meadows 1/3") assert payload["session_id"] == "s1" types = [e["type"] for e in payload["events"]] assert types == ["message", "tool", "tool", "message"] json.dumps(payload) # must be serializable def test_build_returns_filename_and_media_type(mods): memory, transcript = mods _seed(memory) body_md, mt_md, fn_md = transcript.build("s1", "md", "Meadows 1/3") body_js, mt_js, fn_js = transcript.build("s1", "json", "Meadows 1/3") assert fn_md.endswith(".md") and "markdown" in mt_md assert fn_js.endswith(".json") and mt_js == "application/json" assert body_md and body_js def test_delete_session_clears_tool_events(mods): memory, _ = mods _seed(memory) memory.delete_session("s1") assert memory.tool_events("s1") == []