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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0548d70ee21cd372db50d742020c893f5bf6cb78863580e81aba81b29e65f41
|
|
| MD5 |
cc1ae8dac330c7f5c6721b63461163b4
|
|
| BLAKE2b-256 |
7f63e37272ca83350e5a636c17097584e859594294fb1926a91b7e86302c500b
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ee2ea788cbd7c0813cc3b2a7abc9237d62e21812965ea5b1629deb2997dc6d9
|
|
| MD5 |
262503409dc8ae5d14608cbbc676167d
|
|
| BLAKE2b-256 |
875bb4f759df07930c0e018785d8b156b51a035888d6c09e047fabdae9f161ed
|