File size: 2,976 Bytes
6cdcdeb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1933348
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""Tests for the SQLite-backed three-tier long-term memory."""

from __future__ import annotations

from memory import LongTermMemory


def test_round_trip_semantic_facts() -> None:
    mem = LongTermMemory()
    fid = mem.remember_fact("user1", "dislike", "okra", source="user_stated")
    assert fid > 0
    facts = mem.recall_facts("user1")
    assert len(facts) == 1
    assert facts[0]["content"] == "okra"
    assert facts[0]["fact_type"] == "dislike"


def test_filter_facts_by_type_and_substring() -> None:
    mem = LongTermMemory()
    mem.remember_fact("u1", "dislike", "okra")
    mem.remember_fact("u1", "dislike", "kale")
    mem.remember_fact("u1", "preference", "high-protein")
    mem.remember_fact("u2", "dislike", "okra")  # different user

    dislikes = mem.recall_facts("u1", fact_type="dislike")
    assert {f["content"] for f in dislikes} == {"okra", "kale"}

    prefs = mem.recall_facts("u1", fact_type="preference")
    assert prefs[0]["content"] == "high-protein"

    okra_only = mem.recall_facts("u1", contains="okra")
    assert len(okra_only) == 1


def test_user_isolation() -> None:
    mem = LongTermMemory()
    mem.remember_fact("alice", "allergy", "peanut")
    mem.remember_fact("bob", "allergy", "shellfish")

    assert {f["content"] for f in mem.recall_facts("alice")} == {"peanut"}
    assert {f["content"] for f in mem.recall_facts("bob")} == {"shellfish"}


def test_forget_fact() -> None:
    mem = LongTermMemory()
    fid = mem.remember_fact("u", "dislike", "okra")
    mem.forget_fact(fid)
    assert mem.recall_facts("u") == []


def test_procedural_records_round_trip() -> None:
    mem = LongTermMemory()
    issues = [{"code": "calorie_deviation", "description": "x", "severity": "medium"}]
    mem.remember_validation("u1", "1500 kcal plan", "revise", issues)

    history = mem.recall_validations("u1")
    assert len(history) == 1
    assert history[0]["verdict"] == "revise"
    assert history[0]["issues"] == issues


def test_episodic_session_round_trip() -> None:
    mem = LongTermMemory()
    payload = {"messages": [{"role": "user", "content": "hi"}], "memory": {"x": 1}}
    mem.remember_session("u1", "session-A", payload)

    sessions = mem.recall_sessions("u1")
    assert len(sessions) == 1
    assert sessions[0]["payload"] == payload
    assert sessions[0]["session_id"] == "session-A"


def test_recall_limit_and_order() -> None:
    mem = LongTermMemory()
    for i in range(15):
        mem.remember_fact("u", "note", f"fact-{i}")
    facts = mem.recall_facts("u", limit=5)
    assert len(facts) == 5
    # newest first
    assert facts[0]["content"] == "fact-14"


# NOTE: The KnowledgeAgent tests previously here have been removed.
# KnowledgeAgent itself was removed; citation-first retrieval now happens
# inside :class:`tools.WebSearchTool`, which returns citations natively
# from Gemini's ``grounding_metadata``. Its coverage lives in the live
# Gemini audit rather than the offline test suite.