Skip to main content

Memory Layer / meminfra

Memory Layer is a Python library for durable, user-scoped long-term memory in LLM applications. The GitHub project is memory-layer; the PyPI package and Python import are meminfra.

It is memory infrastructure, not a chatbot, agent framework, hosted API, document RAG system, or full context builder.

Installation

pip install meminfra

For development from a checkout:

pip install -e ".[dev]"

Configure the runtime database and providers, then apply the packaged migrations:

meminfra migrate

DATABASE_URL is the PostgreSQL runtime connection. DIRECT_URL, when set, is used by meminfra migrate. Configure LLM and embedding providers independently with the LLM_* and EMBEDDING_* environment variables; never commit credentials.

Quickstart

from meminfra import MemoryLayer
from meminfra.database import SessionLocal

with SessionLocal() as db:
    memory = MemoryLayer(db)
    memory.add(
        user_id="user-123",
        conversation_id="conversation-1",
        messages=[
            {"role": "user", "content": "I prefer PostgreSQL for backend projects."},
        ],
    )

    hits = memory.search(
        user_id="user-123",
        query="What database do I prefer?",
    )

for hit in hits:
    print(hit.memory_text)

add() creates the user and conversation scope when needed. search() returns ranked SearchHit records for that user. answer() is available when an application wants a configured LLM to answer only from retrieved memory.

What happens when you add memory

The write path is:

messages -> memory extractor -> controlled predicate canonicalization where applicable
-> deterministic validation -> ADD / NOOP / SUPERSEDE -> PostgreSQL durable state
-> FAISS derived vector state

Controlled predicates give supported single-value facts deterministic identity and lifecycle behavior. For example, an active location can be added, repeated as a NOOP, or superseded by a newer durable location. Unknown or open-world facts remain valid semantic memory; they are not forced into the controlled ontology, and not every memory has a predicate.

SUPERSEDE keeps the earlier memory for history and provenance while default retrieval returns active memories. Raw-message context is conversation-scoped; durable memory is user-scoped and can be retrieved across that user's conversations.

The read path is:

natural-language query -> QueryIntent
                         -> structured retrieval
                       + BM25 lexical retrieval
                       + FAISS vector retrieval
                         -> discounted-agreement fusion -> ranked SearchHit[]

The three retrieval branches preserve the original natural-language query for BM25 and FAISS. The production fusion is discounted agreement with lambda = 0.10: a strong rank in one branch remains competitive, while agreement across branches receives a discounted bonus. Equal reciprocal-rank fusion is retained for compatibility and ablation, not as the default.

Memory lifecycle

Existing state New statement Result
No active database preference "I use PostgreSQL" ADD
Same active preference "I use PostgreSQL" NOOP
Active database preference "I switched to SQLite" SUPERSEDE

Lifecycle rules apply only where deterministic structured identity is supported. Open semantic and episodic memories retain conservative duplicate handling and provenance.

Structured, lexical, and vector retrieval

Structured retrieval can provide exact, lifecycle-aware evidence. BM25 contributes lexical matches, and FAISS contributes semantic vector matches. Their ranks, rather than incomparable raw scores, are fused into each SearchHit.

PostgreSQL is the durable source of truth for memories, embeddings, temporal state, and provenance. Per-user FAISS indexes are rebuildable derived state; PostgreSQL resolves and state-filters vector candidates before they become results.

QueryIntent

QueryIntent is an optional best-effort LLM-derived structured hint with three fields:

  • predicate
  • value
  • temporal_scope (current, historical, or any)

For example:

memory.search(
    user_id="user-123",
    query="Where do I live?",
)

can infer predicate="location" and temporal_scope="current", allowing the structured branch to contribute relevant evidence without the application manually supplying SearchFilters(predicate="location"). Inference is not guaranteed, and unknown predicates simply add no inferred structure.

If optional intent inference fails, search continues with the existing hard structured filters plus BM25 and FAISS. Set infer_query_intent=False on search() or answer() for lower latency or fully deterministic retrieval.

SearchFilters

SearchFilters are explicit caller-supplied hard constraints. They apply to every retrieval branch. QueryIntent is inferred soft structured evidence only: it never becomes a filter and never constrains BM25 or FAISS. Memory Layer does not infer semantic or episodic memory type on reads.

Benchmarks

Internal baseline comparison under the same evaluation protocol:

Metric Internal baseline meminfra 0.2.0 candidate
Hit@5 0.457 0.657
Recall@5 0.426 0.618
MRR 0.376 0.544

Protocol: LoCoMo · conv-30 · 19 sessions · 369 turns · 105 QA · k=5.

Hit@5 means at least one relevant memory appears in the top five. Recall@5 is the fraction of relevant memories recovered in the top five. MRR measures how early the first relevant memory appears.

QueryIntent engineering ablation

Setting Hit@5 Recall@5 MRR
QueryIntent OFF 0.657 0.618 0.544
QueryIntent ON 0.657 0.618 0.544

Across 105 questions, 105/105 intent calls validated; 3/105 returned controlled predicates, 1/105 included a value, and 1/105 activated structured retrieval. LoCoMo conv-30 has little overlap with the current controlled ontology, so this dataset does not meaningfully demonstrate the value of QueryIntent. It does not imply a metric gain or a feature failure.

Write-path diagnostic ablation

Setting Hit@5 Recall@5 MRR Sessions ingested Extraction failures
OLD_WRITE 0.524 0.499 0.462 14/19 5
CURRENT_WRITE 0.629 0.589 0.502 19/19 0

The old-write side failed to ingest five sessions, so this experiment is diagnostic evidence rather than the primary public benchmark comparison.

Public API

MemoryLayer is the high-level facade:

MemoryLayer.add(*, user_id: str, conversation_id: str, messages: list[dict[str, str]]) -> AddResult
MemoryLayer.search(*, user_id: str, query: str, limit: int = 10,
                   filters: SearchFilters | None = None,
                   infer_query_intent: bool = True) -> list[SearchHit]
MemoryLayer.answer(*, user_id: str, query: str, limit: int = 5,
                   filters: SearchFilters | None = None,
                   infer_query_intent: bool = True) -> AnswerResult

answer() retrieves once through search(), then asks the configured LLM to answer only from the retrieved memory. It abstains without an answer-generation call when no memory is found.

Migrations and recovery

Run meminfra migrate after configuring DATABASE_URL (or DIRECT_URL). The command uses Alembic migrations packaged with the wheel and is safe to run repeatedly. The current schema head is 0005_structured_fact_uniqueness.

PostgreSQL is recoverable durable state. If a local FAISS index is missing, stale, or corrupt, it is rebuilt from PostgreSQL.

Development and testing

python -m pytest
python -m compileall -q src tests
python -m build
python -m twine check dist/*

Database integration tests run only when TEST_DATABASE_URL is configured; they never fall back to DATABASE_URL. Evaluation workloads use EVAL_DATABASE_URL and are not part of the normal test suite.

License

MIT

Metadata

Release files for meminfra 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for meminfra 0.2.0
File Size Uploaded
meminfra-0.2.0.tar.gz 70.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for meminfra 0.2.0
File Interpreter ABI Platform
meminfra-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 146.0 kB

Release files / meminfra-0.2.0.tar.gz

Download URL meminfra-0.2.0.tar.gz
Size 70.2 kB
Tags Source
SHA-256 checksum
How to use checksums
12f57f47d2e14fcc4398176f2bad9f9ad573332b76e844ab7416dc4cd347d055
BLAKE2b-256 checksum
How to use checksums
effd98bc33da228b44596648b8c5f50dc0e70fad378e6895ee0e28d0cab20829
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / meminfra-0.2.0-py3-none-any.whl

Download URL meminfra-0.2.0-py3-none-any.whl
Size 75.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e4992cdc02104b209cbe5c0b0eb75c3ec716f332b7e370d27febeeca873a1358
BLAKE2b-256 checksum
How to use checksums
4473408dcb6df1cebc5717b02c8dd87038eb2193eb5ee12abcbe55e0d585c3cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page