Skip to content

store

store

Persistent stores for automatically extracted long-term memory facts.

A fact is a short, durable statement worth remembering about the user (e.g. "Prefers concise answers"). Facts are produced by the memory service's background extractor and persisted here so they survive across sessions. The store is intentionally small and self-contained: it dedupes, caps the total number of facts, and is safe to call from multiple threads.

Classes

Fact dataclass

Fact(text: str, source: str = '', created_at: float = 0.0)

A single durable memory entry.

FactStore

Bases: ABC

Abstract persistent store for extracted memory facts.

Methods:
add abstractmethod
add(text: str, source: str = '') -> bool

Store text as a fact. Returns True if a new fact was stored.

Source code in src/diapason/memory/store.py
@abstractmethod
def add(self, text: str, source: str = "") -> bool:
    """Store *text* as a fact. Returns True if a new fact was stored."""
add_many
add_many(texts: Iterable[str], source: str = '') -> int

Store several facts, returning the count of newly stored ones.

Source code in src/diapason/memory/store.py
def add_many(self, texts: Iterable[str], source: str = "") -> int:
    """Store several facts, returning the count of newly stored ones."""
    added = 0
    for text in texts:
        if self.add(text, source=source):
            added += 1
    return added
list abstractmethod
list() -> List[Fact]

Return all stored facts, oldest first.

Source code in src/diapason/memory/store.py
@abstractmethod
def list(self) -> List[Fact]:
    """Return all stored facts, oldest first."""
clear abstractmethod
clear() -> int

Remove all stored facts, returning the number removed.

Source code in src/diapason/memory/store.py
@abstractmethod
def clear(self) -> int:
    """Remove all stored facts, returning the number removed."""
count abstractmethod
count() -> int

Return the number of stored facts.

Source code in src/diapason/memory/store.py
@abstractmethod
def count(self) -> int:
    """Return the number of stored facts."""

LocalFactStore

LocalFactStore(
    path: str | Path | None = None, *, max_facts: int = 1000
)

Bases: FactStore

Append-only JSONL fact store on the local filesystem.

Facts are kept human-readable (one JSON object per line) so they can be inspected or edited by hand. Writes are atomic (temp file + rename) and guarded by a lock, so concurrent add calls from the extraction worker and list/clear from the CLI never corrupt the file.

Source code in src/diapason/memory/store.py
def __init__(
    self,
    path: str | Path | None = None,
    *,
    max_facts: int = 1000,
) -> None:
    self._path = (
        Path(path).expanduser() if path is not None else _default_fact_path()
    )
    self._max_facts = max(0, int(max_facts))
    self._lock = threading.Lock()
    self._facts: List[Fact] = self._load()
Attributes
path property
path: Path

Filesystem location of the JSONL store.

SearchableFactStore

SearchableFactStore(journal: FactStore, backend: Any)

Bases: FactStore

Écrit chaque fait DEUX fois : dans le journal, et là où on le relira.

Constaté le 22 août 2026. Le service d'extraction rangeait ses faits dans memory_facts.jsonl ; l'injection de contexte, elle, interrogeait le magasin vectoriel de memory.db. Deux magasins qui ne se croisaient jamais : Diapason extrayait correctement « le projet Olala doit être publié sur l'App Store avant fin septembre », le rangeait, et ne le retrouvait plus jamais. La mémoire automatique écrivait dans le vide.

Le journal reste : il est append-only, lisible et modifiable à la main, survit à une base corrompue, et c'est LUI qui dédoublonne. Seul un fait réellement nouveau part à l'indexation — réindexer un doublon coûterait un calcul d'embedding pour rien et polluerait les résultats.

Une panne du magasin de recherche ne fait pas perdre le fait : le journal a déjà écrit, et l'échec est journalisé plutôt qu'avalé.

Source code in src/diapason/memory/store.py
def __init__(self, journal: FactStore, backend: Any) -> None:
    self._journal = journal
    self._backend = backend

Functions:

create_fact_store

create_fact_store(
    backend: str = "local",
    *,
    path: str | Path | None = None,
    max_facts: int = 1000,
) -> FactStore

Construct a fact store for the configured backend.

Only the "local" (on-disk JSONL) backend is supported today; the registry-backed constructor exists so additional backends can be added without changing the service or CLI wiring.

Source code in src/diapason/memory/store.py
def create_fact_store(
    backend: str = "local",
    *,
    path: str | Path | None = None,
    max_facts: int = 1000,
) -> FactStore:
    """Construct a fact store for the configured *backend*.

    Only the ``"local"`` (on-disk JSONL) backend is supported today; the
    registry-backed constructor exists so additional backends can be added
    without changing the service or CLI wiring.
    """
    _ensure_fact_store_backends_registered()
    key = (backend or "local").strip().lower()
    if not FactStoreRegistry.contains(key):
        supported = ", ".join(FactStoreRegistry.keys())
        raise ValueError(
            f"Unknown memory backend '{backend}'. Supported backends: {supported}"
        )
    return FactStoreRegistry.create(key, path, max_facts=max_facts)