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.

__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) 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 supersede calls. supersede replaces MEMORY.md via os.replace, which would drop a POSIX flock held on the old inode by a concurrent update_index_line. Acquiring _locked_root here prevents that interleaving.

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, 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; 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.