Spaces:
Running
Running
File size: 3,788 Bytes
8a652b8 | 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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | import json
from pathlib import Path
from unittest.mock import MagicMock
import pytest
import peachy.prompts as prompts_mod
from peachy.config import config
from peachy.memory import (
MAX_FACTS,
MAX_FACT_CHARS,
MemoryFact,
add_memory_fact,
list_memory_facts,
clear_memory_facts,
forget_memory_fact,
format_memory_for_prompt,
memory_path_for_instance,
)
from peachy.tools.forget import Forget
from peachy.tools.remember import Remember
from peachy.tools.core_tools import ToolDependencies
def test_memory_store_adds_dedupes_caps_and_formats(tmp_path: Path) -> None:
"""Memory facts should be normalized, deduplicated, capped, and prompt-formatted."""
first = add_memory_fact(tmp_path, " Likes jazz ")
duplicate = add_memory_fact(tmp_path, "likes jazz")
assert first is not None
assert duplicate == first
assert [fact.text for fact in list_memory_facts(tmp_path)] == ["Likes jazz"]
long_text = "x" * (MAX_FACT_CHARS + 20)
stored_long = add_memory_fact(tmp_path, long_text)
assert stored_long is not None
assert len(stored_long.text) == MAX_FACT_CHARS
assert stored_long.text.endswith("...")
for index in range(MAX_FACTS + 5):
add_memory_fact(tmp_path, f"Fact {index}")
facts = list_memory_facts(tmp_path)
assert len(facts) == MAX_FACTS
assert facts[0].text == f"Fact {MAX_FACTS + 4}"
assert "Likes jazz" not in [fact.text for fact in facts]
prompt = format_memory_for_prompt(tmp_path)
assert prompt.startswith("Things you remember about the user")
assert f"- Fact {MAX_FACTS + 4}" in prompt
def test_memory_store_reads_mobile_json_shape_and_forgets_by_query(tmp_path: Path) -> None:
"""The Python store should preserve the mobile app JSON envelope shape."""
path = memory_path_for_instance(tmp_path)
path.write_text(
json.dumps(
{
"version": 1,
"facts": [
{"id": "m_1", "text": "Likes jazz", "createdAt": 1000},
{"id": "m_2", "text": "Likes jazz piano", "createdAt": 900},
{"id": "bad"},
],
}
),
encoding="utf-8",
)
result = forget_memory_fact(tmp_path, query="jazz")
assert result.removed == MemoryFact(id="m_1", text="Likes jazz", created_at=1000)
assert [candidate.text for candidate in result.candidates] == ["Likes jazz", "Likes jazz piano"]
assert [fact.text for fact in list_memory_facts(tmp_path)] == ["Likes jazz piano"]
@pytest.mark.asyncio
async def test_memory_tools_use_instance_storage(tmp_path: Path) -> None:
"""Remember and forget tools should read/write through ToolDependencies.instance_path."""
deps = ToolDependencies(
reachy_mini=MagicMock(),
movement_manager=MagicMock(),
instance_path=tmp_path,
)
remember_result = await Remember()(deps, fact="Has a dog named Mochi")
forget_result = await Forget()(deps, query="Mochi")
assert remember_result["saved"] == "Has a dog named Mochi"
assert forget_result["removed"] == "Has a dog named Mochi"
assert list_memory_facts(tmp_path) == []
def test_prompt_includes_memory_fragment(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
"""Session instructions should prepend saved memories for the active app instance."""
monkeypatch.setattr(config, "REACHY_MINI_CUSTOM_PROFILE", None)
clear_memory_facts(tmp_path)
add_memory_fact(tmp_path, "Prefers concise answers")
instructions = prompts_mod.get_session_instructions(instance_path=tmp_path)
assert instructions.startswith("Things you remember about the user")
assert "- Prefers concise answers" in instructions
assert "## IDENTITY" in instructions
|