Skip to main content

A local-first, embedded bitemporal memory engine for LLM agents.

Project description

Memor Logo

CI Status Python Version Architecture


Memor is an embedded, local-first bitemporal memory and retrieval engine for LLM agents.

Standard vector databases store static embeddings of chat chunks. Over time, as users change their location, job, or preferences, chunk-based retrieval suffers from temporal drift—returning stale or contradictory facts. Memor solves this by extracting structured Subject-Predicate-Object facts and storing them in a bitemporal SQLite engine that tracks exactly when every fact became true, when it was invalidated, and when it was recorded.

Key Capabilities

  • Zero-Infrastructure Embedded Storage: Runs entirely on a local SQLite file (memory.db) with Write-Ahead Logging (WAL). No vector database clusters or cloud services required.
  • Bitemporal Fact Audit Log: Tracks both world time (valid_from to valid_to) and system transaction time (recorded_at). Previous states are never silently overwritten.
  • Dual-Path Contradiction Resolution: Fast-path heuristics instantly update single-valued predicates (e.g., lives_in, works_at). Ambiguous updates route through an LLM judge to determine whether new facts replace or append to existing knowledge.
  • Hybrid Retrieval Pipeline: Combines BM25 lexical search, dense semantic embeddings (sentence-transformers), and entity overlap scoring.
  • Recursive Multi-Hop Graph Traversal: Automatically traverses relationship chains (e.g., user -> brother -> Alex -> owns_pet -> dog) using SQLite WITH RECURSIVE common table expressions (CTEs) without requiring an external graph database.
  • Parallel LLM Reranking: Reranks top candidates concurrently across thread pools for sub-second relevance refinement.
  • Bounded Garbage Collection: Includes explicit, batch-based pruning (engine.prune(older_than_days=N)) that safely archives expired records to an immutable facts_archive table before deletion.
  • Strict Multi-Tenancy & Thread Safety: All operations are strictly isolated by user_id and protected by thread locks and backpressure queues.

Architecture Flow

graph TD
    A[User Turn / Dialogue] --> B[Async Extractor<br/>Structured Pydantic Schema]
    B --> C{Conflict Resolver}
    
    C -->|Single-Valued Predicate<br/>e.g. lives_in| D[Fast Path: Invalidate Old Row]
    C -->|Ambiguous Predicate| E[Slow Path: LLM Judge]
    
    E -->|Contradiction Detected| D
    E -->|Additive Fact| F[Insert New Fact<br/>valid_to = NULL]
    D --> F
    
    F --> G[(Bitemporal SQLite Store<br/>memory.db WAL)]
    
    subgraph Retrieval Pipeline
        H[Query Text] --> I[BM25 Lexical Score]
        H --> J[Dense Vector Similarity]
        H --> K[Entity Overlap Score]
        G --> L[WITH RECURSIVE<br/>Multi-Hop Graph CTE]
        L --> M[Candidate Fusion Engine]
        I --> M
        J --> M
        K --> M
        M --> N[Parallel ThreadPool Reranker]
        N --> O[Top-K High-Precision Context]
    end

Installation

The easiest way to install Memor is directly from PyPI:

pip install memor-db

To install it with the Model Context Protocol (MCP) server for AI agents:

pip install memor-db[mcp]

From Source (Development)

If you want to modify the source code, you can clone the repository and install it locally:

git clone https://github.com/Dilraj07/Memor.git
cd Memor
pip install -e .

Set your API provider credentials in .env or as an environment variable:

export GROQ_API_KEY="gsk_your_api_key_here"

Quickstart

import time
from memor.engine import MemoryEngine

# Initialize the embedded memory engine
engine = MemoryEngine()
user_id = "tenant_8842"

# 1. Add dialogue asynchronously (zero latency overhead on chat loops)
engine.add_memory(user_id, "I live in San Francisco and work as a kernel developer.")
engine.add_memory(user_id, "My brother Alex recently adopted a golden retriever.")

# 2. Add an update that supersedes a previous state
engine.add_memory(user_id, "I just relocated to Seattle last weekend.")

# 3. Flush pending background extraction queues before synchronous queries
engine.flush(user_id)

# 4. Retrieve precise context (automatically combines active facts & multi-hop chains)
context = engine.query(user_id, "Where does the user live and what pet does their family own?", k=3)
print(context)

Output:

- user lives in Seattle (Relevance: 9.50)
- user -> brother -> Alex -> adopted -> golden retriever (Relevance: 9.10)
- user works as kernel developer (Relevance: 7.20)

How Bitemporal State Tracking Works

When a user's state changes, overwriting the record destroys temporal context. Memor instead updates the previous fact's valid_to timestamp and inserts the new state:

id user_id subject predicate object valid_from valid_to status
101 tenant_8842 user lives_in San Francisco 2026-01-10T10:00:00Z 2026-07-11T14:30:00Z Superceded
102 tenant_8842 user lives_in Seattle 2026-07-11T14:30:00Z NULL Active

Retrieval queries filter by WHERE valid_to IS NULL by default, ensuring immediate access to current truths while preserving the full historical timeline for auditing or time-travel queries.


API Reference

MemoryEngine

add_memory(user_id: str, text: str, sync: bool = False) -> None

Submits natural language text for background fact extraction and bitemporal resolution.

  • sync=False: Non-blocking submission to the internal worker pool. Applies backpressure if queue depth exceeds 100 turns.
  • sync=True: Synchronous extraction and persistence.

flush(user_id: str) -> None

Blocks execution until all pending asynchronous extraction futures for user_id complete. Recommended before reading immediate read-after-write state.

query(user_id: str, query_text: str, k: int = 5) -> str

Runs multi-signal hybrid retrieval (lexical + dense embedding + graph traversal) and returns the top k reranked factual statements formatted for LLM prompt injection.

prune(older_than_days: int) -> int

Garbage collection utility. Moves invalidated records older than older_than_days into facts_archive and hard-deletes them from the active index in bounded transactional batches of 500.

shutdown() -> None

Gracefully terminates background thread pools and executor queues.


Model Context Protocol (MCP) Server

Memor includes a native Model Context Protocol (mcp) stdio server, allowing autonomous AI agents (like Claude for Desktop) to directly interface with your memory database without custom integration code.

1. Install with MCP dependencies:

pip install memor-db[mcp]

2. Configure your AI Agent: Add memor-server to your MCP client configuration (e.g., claude_desktop_config.json):

{
  "mcpServers": {
    "memor": {
      "command": "memor-server",
      "args": []
    }
  }
}

Available MCP Tools:

  • add_memory(user_id, text): Extract and resolve contradictions automatically.
  • query_memory(user_id, text, limit): Fetch the most relevant context.
  • get_user_profile(user_id): Retrieve the complete set of active facts for prompt injection.
  • forget_memory(user_id): GDPR-compliant hard delete of all user facts.

Testing & Verification

Run the internal automated test suite across extraction, bitemporal pruning, and retrieval benchmarks:

pip install -e ".[dev]"
pytest tests/ -v

Pytest Benchmark Plugin

Memor ships with a registered pytest11 plugin (pytest-temporal-memory). You can use Memor's rigorous contradiction test suite to evaluate any third-party memory database by simply inheriting from our benchmark class:

# test_my_custom_db.py
from memor.testing.plugin import BaseTemporalMemoryBenchmark
from memor.testing.adapter import MemoryBackendAdapter

class MyAdapter(MemoryBackendAdapter):
    def add_memory(self, user_id: str, text: str): ...
    def query(self, user_id: str, query_text: str, limit: int) -> str: ...
    def clear(self, user_id: str) -> None: ...

class TestMyCustomDB(BaseTemporalMemoryBenchmark):
    def get_adapter(self):
        return MyAdapter()

Run pytest test_my_custom_db.py to see how your backend handles temporal drift!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

memor_db-0.1.3.tar.gz (27.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

memor_db-0.1.3-py3-none-any.whl (24.3 kB view details)

Uploaded Python 3

File details

Details for the file memor_db-0.1.3.tar.gz.

File metadata

  • Download URL: memor_db-0.1.3.tar.gz
  • Upload date:
  • Size: 27.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for memor_db-0.1.3.tar.gz
Algorithm Hash digest
SHA256 47eabde04dbc1e68515526ce456255be22bff3533313f7bf1c149c3894785e3c
MD5 adb52d42944bd37a320ede016dc4ac75
BLAKE2b-256 250be5d105e17603fce4433c86c7a94fdb12fdbdf0af0caf1e2d08442e0ffbe6

See more details on using hashes here.

File details

Details for the file memor_db-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: memor_db-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 24.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for memor_db-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 04f520d94aa6600aa2444e283f72c28d524dda054c132fcdca41fdcad9be7a94
MD5 66b73cf598251d750891cc6b3df37a22
BLAKE2b-256 5ac69e5701cacf372c4625d7ddca49b2f22920f9953880e5a09618548147ab17

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page