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

Install directly from the repository root or via pip:

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.


Testing & Verification

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

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

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.2.tar.gz (24.6 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.2-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: memor_db-0.1.2.tar.gz
  • Upload date:
  • Size: 24.6 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.2.tar.gz
Algorithm Hash digest
SHA256 a968b524203c3eddec4eaf9add6bc8b2d06ad0865d3f48cd471cef15bd70998f
MD5 3a7f0b4995ed3c84ba8cfcb73b19bb89
BLAKE2b-256 212f0dff22add94e4382fe300b53130c35c2713b0783950aa6bb0f6cf782a17a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: memor_db-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 22.4 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 06096becdecc3d2c3f65b2fcd15f60306701d0867a945cd30dddb08cedc3faf8
MD5 21e9345ef9dd5746ed67cd58f7ecb8f0
BLAKE2b-256 5ec9a16187f335436805d754d50c22f601c3a852820af5994cfe774873ad01cd

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