Memory#
The cross-harness memory store: Markdown entries in layered roots, with indexing,
supersession, and recall. See Memory for the user guide and
gptme-util memory.
Cross-harness memory store: CC-compatible markdown entries with layered roots.
This package imports nothing from gptme core except gptme.dirs, so it
can be extracted into a standalone gptme-memory package (sibling of
gptme-rag) as a file move. See gptme/gptme#3734.
- class gptme.memory.AuditIssue#
One actionable consistency problem in a memory root.
- class gptme.memory.MemoryEntry#
MemoryEntry(name: ‘str’, description: ‘str’ = ‘’, type: ‘str’ = ‘general’, body: ‘str’ = ‘’, title: ‘str | None’ = None, status: ‘str’ = ‘living’, supersedes: ‘list[str]’ = <factory>, superseded_by: ‘str | None’ = None, provenance: ‘dict[str, Any]’ = <factory>, confidence: ‘float | None’ = None, keywords: ‘list[str]’ = <factory>, recheck: ‘str | None’ = None, metadata: ‘dict[str, Any]’ = <factory>, path: ‘Path | None’ = None, scope: ‘str | None’ = None)
- __init__(name: str, description: str = '', type: str = 'general', body: str = '', title: str | None = None, status: str = 'living', supersedes: list[str] = <factory>, superseded_by: str | None = None, provenance: dict[str, Any] = <factory>, confidence: float | None = None, keywords: list[str] = <factory>, recheck: str | None = None, metadata: dict[str, Any] = <factory>, path: Path | None = None, scope: str | None = None) None#
- exception gptme.memory.MemoryFrontmatterError#
Raised when strict parsing is required but the YAML is invalid.
- exception gptme.memory.MemoryParseError#
Raised when a file is not a memory entry (no or unusable frontmatter).
- class gptme.memory.MemoryRoot#
MemoryRoot(scope: ‘str’, path: ‘Path’)
- class gptme.memory.MemoryStore#
Reads union every root (nearest layer wins on name); writes target one scope.
- __init__(roots: Iterable[MemoryRoot])#
- audit(*, scope: str | None = None) list[AuditIssue]#
Return parse and supersession consistency issues for one root.
- check_index(scope: str | None = None, *, budget: int | None = None) bool#
True when the on-disk index equals the regenerated one byte for byte.
Falls back to an unlocked read on read-only roots (same reasoning as
render_root_index).
- index_entries(scope: str | None = None) list[MemoryEntry]#
Entries that belong in the index of one physical root.
entries(scope=...)unions every root with that scope name, which would put later-directory filenames into the first directory’sMEMORY.md. Index generation is always per-directory.Policy-managed roots reference entries by filename, so every physical file must appear even when two share the same
namefield (deduplicate=False). Legacy roots keep the original deduplication behaviour to avoid emitting duplicate lines on upgrade.
- static render_index(entries: Iterable[MemoryEntry], *, budget: int | None = None) str#
Generate the always-on index: living entries grouped by type, one line each.
budgetcaps the output in bytes. Entries that do not fit — including every later type group — are replaced by one trailing line naming how many were omitted. The marker is included in the cap, so the returned text never exceedsbudget.
- render_root_index(scope: str | None = None, *, budget: int | None = None) str#
Render one root using its persistent selection and budget, if present.
Falls back to an unlocked read on read-only roots: no concurrent writer can mutate a read-only root, so the TOCTOU race is not a concern there.
- save(name: str, description: str, body: str = '', *, type: str | None = None, scope: str | None = None, title: str | None = None, metadata: dict[str, Any] | None = None) Path#
Write an entry, preserving existing lifecycle and omitted metadata.
A policy-managed root regenerates its selected view within the stored budget, committing entry and index together. New entries remain available to recall but are not selected automatically. Legacy roots upsert a line.
The root lock serialises this write against concurrent
supersedecalls.supersedereplacesMEMORY.mdviaos.replace, which would drop a POSIX flock held on the old inode by a concurrentupdate_index_line. Acquiring_locked_roothere prevents that interleaving.
- supersede(old_name: str, new_name: str, *, scope: str | None = None) tuple[MemoryEntry, MemoryEntry]#
Mark
old_namesuperseded bynew_nameand link both entries.Supersession is scoped to one root so a project memory can never mutate a same-named user memory. Strict parsing prevents the write from normalizing malformed frontmatter through the lenient read fallback. A root-scoped lock serializes concurrent supersedes so two replacements cannot both observe the same living entry and corrupt the links.
Raised when a caller forces a recall backend that is not installed.
- class gptme.memory.RecallHit#
One ranked memory entry.
- class gptme.memory.RecallResult#
Ranked hits plus the backend that actually produced them.
- gptme.memory.parse_entry(path: Path, scope: str | None = None, *, strict: bool = False) MemoryEntry#
Parse one memory file. Raises
MemoryParseErrorfor non-entries.
- gptme.memory.recall(store: MemoryStore, query: str, *, limit: int = 3, backend: RecallBackend = 'auto') RecallResult#
Recall living entries across every layered root.
autoprefers gptme-rag’s TF-IDF backend and falls back to the built-in overlap scorer only when the optional backend is unavailable. A forcedtfidfrequest fails instead of silently changing semantics.
- gptme.memory.render_recall(result: RecallResult, *, body_chars: int = 1200) str#
Render bounded context suitable for injection into another harness.
- gptme.memory.update_index_line(memory_dir: Path, entry: MemoryEntry) None#
Update a legacy pointer, or regenerate a policy-managed root’s view.
Legacy root (no .memory-index.json): upserts a
- [title](file) — descriptionpointer inMEMORY.md, identical to the old behaviour.Policy-managed root: regenerates the full selected view from
.memory-index.json. Only the explicitly selected entries appear in the always-on index;entryis reflected only if its filename is already in the policy’sselectedlist. Direct-file writers that want their entry visible in the always-on view must add it to the policy first. This is intentional: the policy is the authoritative selection list; appending unselected pointers would bypass its byte-budget guarantee.