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.

Storage depends on gptme.dirs; triggered delivery lazily adapts entries to LessonMatcher. See gptme/gptme#3734.

class gptme.memory.AuditIssue#

One actionable consistency problem in a memory root.

__init__(code: str, entry: str, detail: str) → None#
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#
property index_title: str#

the title when set, else the name.

Type:

Link text for the index line

to_markdown() → str#

Render frontmatter + body. Field order is fixed so diffs stay small.

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’)

__init__(scope: str, path: Path) → None#
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’s MEMORY.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 name field (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.

budget caps 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 exceeds budget.

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, keywords: list[str] | 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 living pointers and remove retired pointers, preserving unrelated index prose and committing the curated index and the entry together as well.

The root lock serialises this write against concurrent supersede and update_index_line calls. It stays valid across index inode replacement.

supersede(old_name: str, new_name: str, *, scope: str | None = None) → tuple[MemoryEntry, MemoryEntry]#

Mark old_name superseded by new_name and 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.

exception gptme.memory.RecallBackendUnavailable#

Raised when a caller forces a recall backend that is not installed.

class gptme.memory.RecallHit#

One ranked memory entry.

__init__(entry: MemoryEntry, score: float, matched_terms: list[str]) → None#
class gptme.memory.RecallResult#

Ranked hits plus the backend that actually produced them.

__init__(backend: Literal['tfidf', 'overlap'], hits: list[RecallHit]) → None#
gptme.memory.parse_entry(path: Path, scope: str | None = None, *, strict: bool = False) → MemoryEntry#

Parse one memory file. Raises MemoryParseError for non-entries.

gptme.memory.recall(store: MemoryStore, query: str, *, limit: int = 3, backend: RecallBackend = 'auto') → RecallResult#

Recall living entries across every layered root.

auto prefers gptme-rag’s TF-IDF backend and falls back to the built-in overlap scorer only when the optional backend is unavailable. A forced tfidf request 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.slugify(name: str) → str#

Convert a name to a safe filename slug (My Fact! → my-fact).

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) — description pointer in MEMORY.md for living entries, or removes the pointer for retired entries without rewriting unrelated index prose.

Policy-managed root: regenerates the full selected view from .memory-index.json. Only the explicitly selected entries appear in the always-on index; entry is reflected only if its filename is already in the policy’s selected list. 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.