Memory Model¶
Memory is a sequence of immutable, time-stamped events. There is no mutable "current state" stored anywhere. State is derived by folding the log. That single choice is what makes the history auditable: any past state can be rebuilt exactly, and a deletion is visible as an act rather than as an absence.
Fields every event carries¶
| Field | Type | Meaning |
|---|---|---|
id |
UUID |
Stable identity, generated on creation |
timestamp |
aware datetime |
When it happened. Always normalized to UTC |
seq |
int \| None |
Position in the log. None until the store assigns it |
session_id |
str \| None |
Groups events from one conversation or run |
source |
str |
Where it came from, e.g. "chat". Defaults to "unknown" |
actor |
str \| None |
Which agent or component produced it |
timestamp records when something happened; seq records the order it was written. They can
disagree. A backdated import has an old timestamp and a new seq. The log's total
order is always seq. Replay depends on seq alone, so it does not break when clocks do.
Events are frozen Pydantic models. Once created, they cannot be mutated.
The four kinds¶
Message¶
One conversational turn.
| Field | Notes |
|---|---|
speaker |
Required, non-empty |
content |
Required, non-empty |
retain_until |
Optional marker. Not enforced. See retention and deletion |
Document¶
Data someone brought.
| Field | Notes |
|---|---|
name |
Required, non-empty |
text |
Required, non-empty. Extracted plain text |
media_type |
Defaults to text/plain |
sha256 |
Optional digest of the original bytes |
retain_until |
Optional marker. Not enforced |
Documents hold extracted text, not the original file. The digest is the link back to the bytes if you keep them elsewhere.
Recall¶
A read.
| Field | Notes |
|---|---|
query |
Required, non-empty |
recalled |
Tuple of ids that entered the context |
This is the event that makes reads auditable rather than invisible. Most memory systems log what was written; the question an audit actually asks is what was used.
Deletion¶
A tombstone.
| Field | Notes |
|---|---|
targets |
At least one id. Must be existing messages or documents |
reason |
Required, non-empty |
requested_by |
Optional. Who asked |
Invariants¶
These are enforced in code and asserted in the test suite:
- Append-only. The store issues no
UPDATEand noDELETE. Every write is an insert. seqis assigned by the store and strictly increases. Appending an event that already has aseqraisesValueError.seqsurvives reopening. The store resumes from the highestseqin the database.- Replay is a pure fold. Same events in, same
MemoryStateout. Out-of-order or unsequenced events raiseValueErrorrather than replaying wrong. - Deletion targets must exist and must be content. You cannot delete a
Recall, aDeletion, or an unknown id. - Deleted content is unreachable by reads. It is excluded from
visible_messages,visible_documents, and search, so it cannot re-enter a model's context. - Tombstones do not erase. The targeted events remain in the log and remain retrievable
with
get().
Replaying¶
replay() folds the log into a MemoryState:
| Attribute | Meaning |
|---|---|
messages, documents |
Everything ever written, in log order |
visible_messages, visible_documents |
The same, minus deleted |
deleted |
Map of deleted id to the Deletion that did it |
recall_stats |
Per item: how often it was recalled and when last |
last_seq |
The point in the log this state corresponds to |
Pass up_to_seq=n to get the state as it stood after event n. That is the mechanism behind
the demo's replay slider, and behind the answer to "what did this system know last Tuesday?"
recall_stats is derived, not stored: it is accumulated from Recall events during the
fold. A memory that was never read has no entry.
What determinism does and does not cover¶
Replay is deterministic over the memory: the same log always yields the same state, and therefore the same context assembled for a model. It says nothing about what the model then writes. Language models are not deterministic, and this library does not make them so. The reproducible part is the input, which is the part an audit can act on.