Skip to main content

Smart LLM memory with auto-domain, dedup, and hybrid search

Project description

Memora ๐Ÿง 

Zero-config LLM memory that understands context, not just text.

Memora is a smart memory layer for your AI applications. Unlike traditional vector databases that store text as isolated chunks, Memora captures concepts, relationships, and domains โ€” so your LLM gets the full picture with 90% fewer tokens.


โœจ Why Memora?

Feature Pinecone ChromaDB Memora
Setup Cloud signup + API key pip install pip install memora โœ…
Dedup โŒ Manual โŒ Manual โœ… Auto-merge (semantic)
Hybrid Search โŒ Vector only โŒ Vector only โœ… BM25 + Vector
Domain Filter โŒ Manual metadata โŒ Manual metadata โœ… Auto-detect (v8)
Related Memories โŒ No โŒ No โœ… Auto-bonding
Auto-aging โŒ No โŒ No โœ… Fractal compression
Session Memory โŒ No โŒ No โœ… Auto-expire
Dynamic Domains โŒ No โŒ No โœ… AUTO-DOMAIN v2
Token Cost ~2000/query ~2000/query ~50/query โœ…

๐Ÿš€ Quick Start

Install

pip install git+https://github.com/SPARKEDIX/memora.git

Basic Usage

import memora

# Create memory
memory = memora.Memory()

# Store memories - domains created AUTOMATICALLY
memory.add("Mujhe diabetes hai, fasting 180", user="rahul")
memory.add("Main roz 6 baje walk karta hoon", user="rahul")
memory.add("Doctor ne Metformin 500mg diya hai", user="rahul")

# Retrieve context - domain auto-detected from query
context = memory.get("Walk ke baad kya khaana chahiye?", user="rahul")
print(context)

Output:

Primary:
    Main roz 6 baje walk karta hoon

Related:
    Mujhe diabetes hai, fasting 180
    Doctor ne Metformin 500mg diya hai

Your LLM now gets the full health context โ€” not just the "walk" keyword.


๐ŸŒŸ What's New in v8: AUTO-DOMAIN v2 + Scalability Fixes

Memora v8 introduces completely dynamic domain creation โ€” no hardcoded keywords, no manual config. Also fixes critical bugs from v7: semantic dedup, domain explosion, domain filtering in queries.

memory = memora.Memory()

# These 3 texts โ†’ automatically create "health" domain
memory.add("Diabetes blood sugar 180 mg/dL", user="rahul")
memory.add("Metformin 500mg twice daily", user="rahul")
memory.add("Insulin dosage adjusted", user="rahul")

# These 3 texts โ†’ automatically create "coding" domain
memory.add("Python code for ML model", user="rahul")
memory.add("Code review for PR #42", user="rahul")
memory.add("Debug production bug in API", user="rahul")

# These 3 texts โ†’ automatically create "gaming" domain
memory.add("PUBG ranked match chicken dinner", user="rahul")
memory.add("Elden Ring boss fight guide", user="rahul")
memory.add("Steam summer sale games", user="rahul")

# Query auto-detects domain
memory.get("diabetes treatment")    # โ†’ searches health domain
memory.get("programming bug")       # โ†’ searches coding domain
memory.get("gaming headset")        # โ†’ searches gaming domain

# Rename domains later for readability
memory.rename_domain("domain_1", "health")
memory.rename_domain("domain_2", "coding")
memory.rename_domain("domain_3", "gaming")

# List all domains
memory.list_domains()
# [{'name': 'health', 'count': 3}, {'name': 'coding', 'count': 3}, {'name': 'gaming', 'count': 3}]

How AUTO-DOMAIN v2 Works

Text โ†’ Embedding โ†’ Compare with ALL domain centroids (cosine similarity)
                              โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ–ผ                   โ–ผ
            similarity โ‰ฅ 0.50      similarity < 0.50
                    โ”‚                   โ”‚
                    โ–ผ                   โ–ผ
         Assign to existing         Add to "unassigned"
         domain, update            pool. When 3 similar
         centroid (weighted        texts accumulate
         average)                  โ†’ create NEW domain
                                       (centroid = avg of 3)
                    โ”‚
                    โ–ผ
        Every 20 inserts:
        โ€ข Merge weak domains (<3 memories)
        โ€ข Merge similar domains (>0.60 centroid sim)
        โ€ข Re-check unassigned pool

๐Ÿ“– Full API Reference

Memory(db_path="memory.db", default_ttl="30d")

Create a memory instance.

Parameter Type Default Description
db_path str "memory.db" SQLite file path for storage
default_ttl str "30d" Default expiration ("7d", "30d", "forever")

Example:

# Separate DB per user
rahul_mem = memora.Memory(db_path="rahul.db")
priya_mem = memora.Memory(db_path="priya.db")

memory.add(text, user=None, ttl=None, session=False)

Store a memory. Domain auto-detected/created.

Parameter Type Default Description
text str Required The text to store
user str None Owner of this memory (isolated per user)
ttl str None Time-to-live ("7d", "30d", "forever", None = use default)
session bool False If True, auto-delete after 1 hour

Examples:

# Permanent health record
memory.add("Mujhe diabetes hai", user="rahul", ttl="forever")

# Temporary chat
memory.add("Aaj mausam accha hai", user="rahul", ttl="7d")

# Session-only (disappears after session)
memory.add("OTP is 123456", user="rahul", session=True)

# Batch insert
memory.add_many(["text1", "text2", "text3"], user="rahul")

memory.get(query, user=None, domain=None, top_k=5, include_bonded=True)

Retrieve relevant memories. Domain auto-detected from query if not specified.

Parameter Type Default Description
query str Required Your search query
user str None Filter by specific user
domain str None Force specific domain (skip auto-detect)
top_k int 5 Number of primary results
include_bonded bool True Include related memories from same domain

Examples:

# Basic query - domain auto-detected
result = memory.get("Walk ke baad kya khaana?", user="rahul")

# Force specific domain
result = memory.get("Diet plan", user="rahul", domain="health")

# More results
result = memory.get("Health tips", user="rahul", top_k=10)

# Latent vector mode (for downstream ML)
vectors = memory.get("health query", mode="latent")

Returns (text mode):

Primary:
    [Most relevant memories]

Related:
    [Bonded memories from same domain]

memory.list_domains()

List all domains with memory counts.

domains = memory.list_domains()
# [{'name': 'health', 'count': 15}, {'name': 'coding', 'count': 10}, ...]

memory.rename_domain(old_name, new_name)

Give meaningful names to auto-generated domains.

memory.rename_domain("domain_1", "health")
memory.rename_domain("domain_2", "work_projects")
memory.rename_domain("domain_3", "gaming_rpg")
# Returns True if successful

memory.delete(user=None, domain=None, older_than=None)

Delete memories.

Parameter Type Default Description
user str None Delete all memories of this user
domain str None Delete only this domain
older_than str None Delete memories older than ("30d", "90d", "1y")

Examples:

# Delete all casual chats
memory.delete(user="rahul", domain="casual")

# Delete everything older than 1 year
memory.delete(user="rahul", older_than="1y")

# Wipe everything
memory.delete()

memory.optimize()

Merge similar memories to reduce storage.

# After adding many similar memories
memory.optimize()
# Reduces crystal count by 30-50%

memory.info()

Get database stats including unassigned pool.

print(memory.info())
# {
#     'total_memories': 50,
#     'unique_users': 3,
#     'domains': {'health': 20, 'coding': 15, 'gaming': 10},
#     'levels': {0: 30, 1: 15, 2: 5},
#     'unassigned': 3,
#     'index_size': 45,
#     'persisted': True
# }

memory.export(path) / memory.import_data(path, merge=False)

Backup and restore memories.

# Export
memory.export("backup.json")

# Import (replace existing)
memory.import_data("backup.json")

# Import (merge with existing)
memory.import_data("backup.json", merge=True)

๐Ÿฅ Real-World Example: Health Assistant

import memora
from openai import OpenAI

llm = OpenAI()
memory = memora.Memory(db_path="patients.db")

def doctor_chat(patient_id, message):
    # 1. Retrieve patient history (domain auto-detected)
    context = memory.get(message, user=patient_id, top_k=3)

    # 2. Ask LLM with context
    response = llm.chat.completions.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": "You are Dr.AI. Use patient history."},
            {"role": "user", "content": f"History:\n{context}\n\nPatient: {message}"}
        ]
    )

    reply = response.choices[0].message.content

    # 3. Store both sides permanently
    memory.add(message, user=patient_id, ttl="forever")
    memory.add(reply, user=patient_id, ttl="forever")

    return reply

# Usage
print(doctor_chat("rahul_123", "Mujhe diabetes hai, fasting 180"))
print(doctor_chat("rahul_123", "Main roz walk karta hoon"))
print(doctor_chat("rahul_123", "Walk ke baad kya khaana chahiye?"))
# Output: Diabetes-aware diet advice

๐Ÿง  How It Works (v8)

Your Text
    โ†“
Sentence-Transformers (all-MiniLM-L6-v2 โ†’ 384-dim)
    โ†“
AUTO-DOMAIN v2 Engine:
    โ”œโ”€ Compare embedding with ALL domain centroids
    โ”œโ”€ similarity โ‰ฅ 0.50 โ†’ assign, update centroid (weighted avg)
    โ”œโ”€ similarity < 0.50 โ†’ unassigned pool
    โ”œโ”€ 3 similar in pool โ†’ create NEW domain (centroid = avg of 3)
    โ””โ”€ Every 20 inserts: merge weak/similar domains, re-check pool
    โ†“
Store in:
    โ”œโ”€ FAISS IVF Index (fast vector search, O(โˆšn))
    โ”œโ”€ BM25 Index (exact keyword matching)
    โ””โ”€ SQLite (metadata + domains table + unassigned table)
    โ†“
When you query:
    Hybrid Search (FAISS + BM25 merged)
    โ†“
    Domain Filter (auto-detected or forced)
    โ†“
    Crystal Bonding (surface related from same domain)
    โ†“
    Fractal Aging (level 0โ†’1โ†’2โ†’3: compress over time)
    โ†“
    Formatted Context โ†’ Your LLM

๐Ÿ“Š v7 vs v8 Comparison

Metric v7 v8 (Fixed)
30 memories added 46 crystals (dedup broken) 27 crystals
Exact duplicate New ID Same ID returned
Semantic duplicate Not merged Merged (sim โ‰ฅ 0.60)
Domain count (30 mixed) 8 (explosion) 4 (reasonable)
Health query result Mixed work/gaming Health only
Storage (30 mem) 163 KB 158 KB
Query speed O(n) O(โˆšn) via IVF

๐Ÿ“ฆ Installation

pip install git+https://github.com/SPARKEDIX/memora.git

Dependencies: sentence-transformers, faiss-cpu, numpy, rank-bm25


๐Ÿงช Run Validation Tests

python tests/validation_test.py

๐Ÿ“ License

MIT License โ€” free for personal and commercial use.


๐Ÿ™ Support

  • โญ Star this repo if you find it useful
  • ๐Ÿ› Open an issue for bugs
  • ๐Ÿ’ก Open a discussion for feature requests

Made with โค๏ธ for smarter AI memories.

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

memora_memory-0.2.0.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

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

memora_memory-0.2.0-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file memora_memory-0.2.0.tar.gz.

File metadata

  • Download URL: memora_memory-0.2.0.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.7

File hashes

Hashes for memora_memory-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e0548d70ee21cd372db50d742020c893f5bf6cb78863580e81aba81b29e65f41
MD5 cc1ae8dac330c7f5c6721b63461163b4
BLAKE2b-256 7f63e37272ca83350e5a636c17097584e859594294fb1926a91b7e86302c500b

See more details on using hashes here.

File details

Details for the file memora_memory-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: memora_memory-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 16.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.7

File hashes

Hashes for memora_memory-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8ee2ea788cbd7c0813cc3b2a7abc9237d62e21812965ea5b1629deb2997dc6d9
MD5 262503409dc8ae5d14608cbbc676167d
BLAKE2b-256 875bb4f759df07930c0e018785d8b156b51a035888d6c09e047fabdae9f161ed

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