The memlab API¶
Every lab imports from memlab, the package you spend the course building.
This page is the reference that was missing: where each thing lives, and what
it takes.
It is not the whole package. The 168 lab files import 124 distinct memlab
names between them; the 23 below account for 74% of all import occurrences,
which is why they are the ones written down. For anything else, the lab's own
solution.py sits next to its stub and shows the import it used.
Signatures here are read from the code, not typed from memory — if one looks wrong, the code wins, and it is a bug worth reporting.
The record¶
Everything the write path produces is a Memory.
from memlab.types import Memory, MemoryType, Provenance, Scope, Tier
Memory is a frozen dataclass with seventeen fields. The ones you touch early:
| field | what it holds |
|---|---|
content |
the claim, as text |
type |
MemoryType — episodic, semantic, procedural, working |
scope |
who it belongs to |
provenance |
where it came from |
happened_at / recorded_at |
when it was true / when we wrote it down |
valid_from / valid_to |
when the fact was true in the world |
invalid_at |
when the store found out — a different question |
superseded_by |
the id of the belief that replaced it |
confidence, salience, tier, access_count |
ranking and lifecycle |
entities, derived_from, id |
resolution, corroboration, identity |
Scope(user: str, agent: str | None = None, session: str | None = None)
Provenance(source_id: str, speaker: str, authority: float = 1.0)
Scope is "the key every read must filter on before it ranks anything".
Tier is scratch | working | long_term.
Retiring a belief is a method, never a delete:
memory.supersede(by: str, at: datetime, found_out: datetime | None = None, event_end: bool = False) -> Memory
The store¶
from memlab.store.jsonl import JsonlStore
store = JsonlStore(path) # str | Path
store.add(memories) -> int # append; content-addressed ids make re-ingest idempotent
store.all() -> list[Memory]
store.live() -> list[Memory] # not superseded, not expired
store.replace(memories) -> int # rewrite the log wholesale
store.clear() -> None
memlab.store.sqlite.SqliteStore has the same shape and arrives later.
Which system you are running¶
The capstone grows across the course, so a lab has to say which version of it to use. That is what a profile or a snapshot is.
from memlab.pipeline import at, get
get("beginner") # Level 1 as shipped; also "intermediate", "advanced"
at("I3") # the system exactly as module I3 left it
Snapshots exist so a number measured once stays true. I1–I8 are the
intermediate modules, A1–A9 the advanced ones. If you change shared code and
a later lesson's figure moves, that is the snapshot doing its job.
Running the write path¶
from memlab.app.chat import ask, ingest
ingest(store, scope, pipeline=None, before_session=14) -> int # memories actually written
ask(store, scope, question, k=5, pipeline=None, budget=400) -> tuple[str, list[Hit]]
before_session=14 is the default because session 14 is held out — it is
the question, not a memory.
The read path¶
from memlab.retrieve.embedding import EmbeddingRetriever, Hit
EmbeddingRetriever().search(query, memories, scope, k=5, live_only=True) -> list[Hit]
A Hit is (memory, score, query). Then pack it into a context window:
from memlab.assemble.simple import assemble, estimate_tokens
assemble(hits, budget_tokens=400) -> str # highest first, stop at budget, never split
estimate_tokens(text) -> int # crude on purpose
The model¶
Labs run offline against a deterministic fake. That is what makes "your numbers should be identical to the ones on the page" a real promise.
from memlab.llm.fake import cosine, embed_text
embed_text(text) -> list[float] # deterministic, L2-normalised, dependency-free
cosine(a, b) -> float
It is a lexical model, and that limit is teaching material rather than a shortcoming to work around.
The corpus and the answer key¶
from memlab.fixtures import load_gold, load_turns
load_turns(user_only=False) -> list[dict] # Priya's 14 sessions, in order
load_gold() -> dict # ground truth, and the index lessons cite
A turn is a dict with session, role, text, ts.
The exam¶
from memlab.eval.exam import QUESTION, exam_answer
QUESTION # "where do I work and what should I not eat?"
exam_answer(memories, scope) -> ExamAnswer
Belief updating¶
from memlab.evolve.conflict import SLOTS, detect, slot_of
slot_of(memory) -> str | None # which attribute this claim fills
detect(memories, scope, client=None) -> list[Conflict]
SLOTS # the attribute table: employer, diet, ...
slot_of is the quiet workhorse of the course — structure, not similarity, is
what generates conflict candidates. Similarity cannot: a genuine contradiction
in this corpus scores 0.285, below unrelated noise at 0.478.
Finding anything else¶
uv run python tools/show.py <lesson-id> # what a solved lab prints
uv run python tools/show.py --check <lesson-id> # whether YOUR lab is right
grep -rn "from memlab" curriculum/*/*/lab/solution.py | grep <name>
The package source is under capstone/src/memlab/.