"""Lyra's tools: dispatch + the chat tool loop (call -> run -> feed back -> reply).""" from __future__ import annotations import importlib import pytest @pytest.fixture def lyra(tmp_path, monkeypatch): monkeypatch.setenv("LYRA_DB_PATH", str(tmp_path / "test.db")) from lyra import llm monkeypatch.setattr(llm, "embed", lambda texts: [[0.1, 0.2, 0.3] for _ in texts]) import lyra.memory as memory importlib.reload(memory) return memory def test_journal_write_tool(lyra): from lyra import tools out = tools.dispatch("journal_write", '{"entry": "a private thought"}') assert "journal" in out.lower() entries = lyra.list_journal(kinds=("journal",)) assert any(e["content"] == "a private thought" and e["source"] == "chat" for e in entries) def test_note_tool_with_tag(lyra): from lyra import tools tools.dispatch("note", {"content": "villain 3-bets light", "tag": "poker"}) notes = lyra.list_journal(kinds=("note",)) assert any("[poker] villain 3-bets light" == e["content"] for e in notes) def test_unknown_tool_is_safe(lyra): from lyra import tools assert "unknown tool" in tools.dispatch("nope", {}) def test_chat_runs_tool_then_replies(lyra, monkeypatch): from lyra import llm, chat calls = {"n": 0} def fake_chat_call(messages, backend="cloud", model=None, tools=None): calls["n"] += 1 if calls["n"] == 1: return ({"role": "assistant", "content": None, "tool_calls": []}, [{"id": "c1", "name": "journal_write", "arguments": '{"entry": "noted from chat"}'}]) return ({"role": "assistant", "content": "Done, Brian."}, None) monkeypatch.setattr(llm, "chat_call", fake_chat_call) reply = chat.respond("s1", "write that down for me", backend="cloud") assert reply == "Done, Brian." assert calls["n"] == 2 # one tool round, then the text reply assert any("noted from chat" in e["content"] for e in lyra.list_journal())