"""Associative cognition: embedding-based recall over her journal + spreading activation (what 'lights up' from a seed) + spontaneous seeding.""" from __future__ import annotations import importlib import pytest def _fake_embed(texts): """Content-sensitive embeddings: same words -> same vector, overlap -> closer. (The shared test stub returns a constant, which would make all cosines equal.)""" out = [] for t in texts: v = [0.0] * 64 for w in t.lower().split(): v[hash(w) % 64] += 1.0 out.append(v if any(v) else [1e-6] * 64) return out @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", _fake_embed) import lyra.memory as memory importlib.reload(memory) import lyra.self_state as self_state importlib.reload(self_state) import lyra.cognition as cognition importlib.reload(cognition) return memory, cognition def test_recall_journal_ranks_by_meaning(lyra): memory, _ = lyra memory.add_journal_entry("thought", "poker tilt control discipline at the table") memory.add_journal_entry("thought", "the quiet stillness between our conversations") memory.add_journal_entry("thought", "usb drive hardware windows formatting") hits = memory.recall_journal("poker tilt discipline", k=3) assert hits and "poker" in hits[0]["content"] # the on-topic entry ranks first assert "score" in hits[0] and "embedding" not in hits[0] def test_recall_journal_skips_unembedded_rows(lyra): memory, _ = lyra # simulate a pre-embedding-era entry (NULL embedding) — must be skipped, not crash conn = memory._connection() with conn: conn.execute("INSERT INTO journal (created_at, kind, content) VALUES ('2020-01-01','thought','old')") memory.add_journal_entry("thought", "fresh embedded poker thought") hits = memory.recall_journal("poker", k=5) assert all(h["content"] != "old" for h in hits) def test_activate_lights_up_related_not_unrelated(lyra): memory, cognition = lyra memory.ensure_session("s1") memory.remember("s1", "user", "I keep tilting when I'm card dead at poker") memory.add_journal_entry("thought", "tilt is really about ego and discipline") memory.add_journal_entry("thought", "spring gardening soil and seedlings") items = cognition.activate("poker tilt discipline", k=4, hops=1) assert items and all("text" in i and "source" in i for i in items) joined = " ".join(i["text"] for i in items) assert "tilt" in joined # related material surfaced def test_spontaneous_seed_fallback_then_real(lyra): memory, cognition = lyra s = cognition.spontaneous_seed() # empty DB -> wander fallback assert s["text"] and s["source"] memory.ensure_session("s1") memory.remember("s1", "user", "been thinking about impermanence lately") s2 = cognition.spontaneous_seed() # now has material to draw on assert isinstance(s2["text"], str) and s2["text"] and s2["source"] def test_constellation_block_handles_empty(lyra): _, cognition = lyra assert "quiet" in cognition.constellation_block([]).lower() block = cognition.constellation_block([{"source": "conversation", "text": "hi there"}]) assert "hi there" in block