Skip to content

Commit 58fc1a2

Browse files
CodebyKumarClaude Sonnet 5
andcommitted
Add fact-graph nudge test coverage, document graph in quickstart, fix layer count
- test_retrieval.py: cover search_all()'s fact-side graph nudge, previously untested (only the chunk-side nudge had a regression test). - quickstart.md: add a knowledge-graph section — the doc had zero mention of it despite being a headline feature. - README.md: "four memory layers" heading was stale after Graph became a fifth row in that table. - .gitignore: ignore blog.md/newwork.md (working notes) and .DS_Store/ .coverage (local artifacts). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 47fb882 commit 58fc1a2

4 files changed

Lines changed: 55 additions & 1 deletion

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ dist/
55
*.egg-info/
66
.venv/
77
uv.lock
8+
blog.md
9+
newwork.md
10+
.DS_Store
11+
.coverage

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ graph expansion at FULL tier, 1-hop at STANDARD, off entirely at LITE (zero
137137
extra queries, zero extra writes). Full design and the reasoning behind it:
138138
[governor.md's Knowledge graph section](docs/governor.md#knowledge-graph).
139139

140-
## The four memory layers
140+
## The five memory layers
141141

142142
| Layer | What it holds | Where |
143143
| -------------------- | -------------------------------------------------------------- | ------------------------------------------- |

docs/quickstart.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,28 @@ If you don't pass an embedder, Elastimem activates its own small built-in
104104
one automatically (see [governor.md](governor.md)) — semantic recall works
105105
out of the box, no setup required.
106106

107+
## The knowledge graph (with an LLM configured)
108+
109+
With `llm=` set, `record_turn` also extracts entities and relationships in
110+
the same background pass that captures facts — no extra model call, no NER
111+
library. They connect memories that share no vocabulary:
112+
113+
```python
114+
mem.record_turn("I'm building a robot called Tuffy that runs on a Jetson",
115+
"Sounds like a fun project!")
116+
mem.end_session() # graph maintenance runs here: decay, dedup, clustering
117+
118+
hits = mem.recall("what do I know about my Jetson") # finds the Tuffy turn too
119+
120+
for cluster in mem.clusters(): # entities auto-grouped into topics
121+
print(cluster["label"], cluster["members"])
122+
```
123+
124+
This is one more retrieval signal, not a separate database — off entirely
125+
at LITE tier, deeper at FULL. See [governor.md](governor.md#knowledge-graph)
126+
for the full design, and [api.md](api.md) for `explain()`/`timeline()`, the
127+
two Experimental query methods built on top of it.
128+
107129
## Where to go next
108130

109131
- [installation.md](installation.md) — optional extras, supported Python versions

tests/test_retrieval.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,34 @@ def test_graph_nudge_breaks_tie(tmp_path):
177177
s.close()
178178

179179

180+
def test_graph_nudge_breaks_tie_for_facts(tmp_path):
181+
"""Same as test_graph_nudge_breaks_tie, but for facts returned through
182+
recall() (search_all's fact leg, retrieval.py's search_all graph_nudge
183+
branch) rather than chunks — a separate code path that reuses
184+
graph_relevance's expanded names but applies the nudge to fact rows
185+
instead of chunk rows."""
186+
from elastimem import graph
187+
from elastimem.governor import Tier
188+
189+
s = make_store(tmp_path / "gf.db", embed=None, tier_override=Tier.FULL)
190+
s.remember("device_one", "Tuffy is a device that needs a new battery pack")
191+
s.remember("device_two", "the thermostat is a device that needs new batteries")
192+
193+
conn = s._conn
194+
jetson_id = graph.upsert_node(conn, "thing", "Jetson", confidence=1.0)
195+
tuffy_id = graph.upsert_node(conn, "thing", "Tuffy", confidence=1.0)
196+
graph.upsert_edge(conn, tuffy_id, jetson_id, "runs_on", confidence=1.0)
197+
198+
hits = s.recall("tell me about the device and my Jetson")
199+
fact_hits = [h for h in hits if h.kind == "fact"]
200+
assert fact_hits
201+
tuffy_facts = [h for h in fact_hits if "Tuffy" in h.text]
202+
other_facts = [h for h in fact_hits if "thermostat" in h.text]
203+
assert tuffy_facts and other_facts
204+
assert tuffy_facts[0].score > other_facts[0].score
205+
s.close()
206+
207+
180208
def test_graph_hops_zero_matches_graph_absent_ranking(tmp_path):
181209
"""LITE tier (graph_hops=0): ranking must be identical to a store with
182210
no graph data at all — the graph leg must not be reachable."""

0 commit comments

Comments
 (0)