A temporal-aware AI memory engine that extracts, stores, and retrieves long-term user memories with built-in decay, versioning, and freshness scoring
Project description
TemporalMemAI
A temporal-aware AI memory engine that extracts, stores, and retrieves long-term user memories with built-in decay, versioning, and freshness scoring.
Features
- Temporal Awareness: Built-in memory decay, versioning, and freshness scoring
- Semantic Search: Vector-based search using OpenAI embeddings and Qdrant
- Reranking Support: Optional reranking with Cohere, HuggingFace, or LLM-based rerankers
- Dual Storage: SQLite for metadata and Qdrant for vector embeddings
- Conflict Resolution: Slot-based superseding for handling memory updates
- Fact Extraction: LLM-powered extraction of structured facts from conversations
- User Isolation: Multi-user support with per-user memory management
Installation
Basic Installation
pip install temporalmemai
With Optional Dependencies
# With Cohere reranker support
pip install temporalmemai[cohere]
# With HuggingFace reranker support
pip install temporalmemai[huggingface]
# With all optional dependencies
pip install temporalmemai[all]
Development Installation
git clone https://github.com/29m10/temporalmemai.git
cd temporalmemai
pip install -e .
Prerequisites
- Python 3.13+
- Qdrant (local or cloud)
- OpenAI API key (for embeddings and fact extraction)
Quick Start
from temporalmemai import Memory
# Configure the memory engine
config = {
"sqlite_path": "./my_memory.db",
"qdrant_host": "localhost",
"qdrant_port": 6333,
"openai_api_key": "your-api-key",
}
# Create memory instance
memory = Memory(config)
# Add memories
memory.add("I love hiking on weekends.", user_id="user123")
memory.add("My favorite programming language is Python.", user_id="user123")
# Search memories
results = memory.search(
query="what are my hobbies?",
user_id="user123",
limit=5
)
for result in results["results"]:
print(result["memory"]["memory"])
Configuration
Environment Variables
You can configure the system using environment variables:
export SQLITE_PATH="./my_memory.db"
export QDRANT_HOST="localhost"
export QDRANT_PORT="6333"
export QDRANT_URL="https://your-cluster.qdrant.io" # For Qdrant Cloud
export QDRANT_API_KEY="your-qdrant-api-key"
export OPENAI_API_KEY="your-openai-api-key"
export OPENAI_EMBED_MODEL="text-embedding-3-small"
export OPENAI_LLM_MODEL="gpt-4o-mini"
Configuration Dictionary
config = {
# SQLite database path
"sqlite_path": "./my_memory.db",
# Qdrant configuration (choose one)
"qdrant_host": "localhost", # For local Qdrant
"qdrant_port": 6333,
# OR
"qdrant_url": "https://...", # For Qdrant Cloud
"qdrant_api_key": "your-key",
"qdrant_collection": "temporalmemai_default",
# OpenAI configuration
"openai_api_key": "your-key",
"embed_model": "text-embedding-3-small",
"llm_model": "gpt-4o-mini",
"llm_temperature": 0.0,
# Optional: Reranker configuration
"reranker": {
"provider": "huggingface", # or "cohere" or "llm_reranker"
"config": {
"model": "BAAI/bge-reranker-base",
"device": "cpu",
"batch_size": 8,
}
}
}
Usage Examples
Adding Memories
# Single message
memory.add("I live in San Francisco.", user_id="user123")
# Conversation history
messages = [
{"role": "user", "content": "I'm planning a trip to Japan."},
{"role": "assistant", "content": "That sounds exciting!"},
{"role": "user", "content": "I'll be there for two weeks in March."}
]
memory.add(messages, user_id="user123", metadata={"turn_id": "conv_001"})
Searching Memories
# Basic search
results = memory.search(
query="where do I live?",
user_id="user123",
limit=10
)
# Search with reranking
results = memory.search(
query="what are my travel plans?",
user_id="user123",
rerank=True, # Enable reranking if configured
limit=5
)
# Search with filters
results = memory.search(
query="my preferences",
user_id="user123",
filters={"type": "preference", "status": "active"},
limit=10
)
Listing Memories
# List all active memories
memories = memory.list(user_id="user123", status="active")
# List archived memories
archived = memory.list(user_id="user123", status="archived")
Managing Memories
# Update a memory
memory.update(memory_id="mem_123", new_content="I live in New York.")
# Delete a memory
memory.delete(memory_id="mem_123")
# Reindex user memories (rebuild Qdrant index)
stats = memory.reindex_user(user_id="user123")
print(f"Reindexed: {stats['indexed']}/{stats['total']}")
Memory Expiry and Temporal Behavior
TemporalMemAI automatically handles memory expiry based on temporal information extracted from messages. Memories with explicit durations are automatically expired when they pass their valid_until timestamp.
# Memories with explicit durations are automatically expired
# Example: Temporary location with duration
memory.add("I'm at the airport, will be here for 2 hours.", user_id="user123")
# This memory will automatically expire after 2 hours
# Example: Short-term travel plans
memory.add("I'm staying in Tokyo for 3 days.", user_id="user123")
# This memory expires after 3 days
# Example: Very short-term state
memory.add("I'm waiting for my cab, it will arrive in 30 minutes.", user_id="user123")
# This memory expires after 30 minutes
# The system automatically expires memories when you call add(), list(), or search()
# Expired memories are marked as "expired" status and excluded from active searches
How Expiry Works:
- Automatic Duration Detection: When you add a memory, the system extracts explicit durations (minutes, hours, days) from the text
- TTL Assignment: Memories with durations get a
valid_untiltimestamp automatically set - Lazy Expiration: Memories are checked and expired when you call
add(),list(), orsearch() - Type-Based Defaults: Different memory types have different default expiry behaviors:
temp_state: Short-lived, expires quicklyepisodic_event: Time-stamped, doesn't decay but may become less relevantprofile_fact: Long-lived, rarely expirespreference: Medium-lived, may change over time
Checking Memory Expiry:
# List all memories (including expired ones)
all_memories = memory.list(user_id="user123", status="active")
# Check individual memory expiry
for mem in all_memories["results"]:
if mem.get("valid_until"):
print(f"Memory '{mem['memory']}' expires at {mem['valid_until']}")
else:
print(f"Memory '{mem['memory']}' has no expiry (persistent)")
Architecture
TemporalMemAI uses a dual-storage architecture:
- SQLite: Stores metadata, relationships, and temporal information
- Qdrant: Stores vector embeddings for semantic search
The system processes memories through:
- Fact Extraction: LLM extracts structured facts from conversations
- Temporal Processing: Applies decay, versioning, and conflict resolution
- Vector Indexing: Embeds and indexes memories in Qdrant
- Search & Reranking: Semantic search with optional reranking
Rerankers
TemporalMemAI supports multiple reranking strategies:
HuggingFace Reranker (Recommended for Local)
config = {
"reranker": {
"provider": "huggingface",
"config": {
"model": "BAAI/bge-reranker-base",
"device": "cpu", # or "cuda"
"batch_size": 8,
"max_length": 512,
}
}
}
Cohere Reranker
config = {
"reranker": {
"provider": "cohere",
"config": {
"model": "rerank-english-v3.0",
"api_key": "your-cohere-api-key",
}
}
}
LLM Reranker (OpenAI)
config = {
"reranker": {
"provider": "llm_reranker",
"config": {
"model": "gpt-4o-mini",
"temperature": 0.0,
"max_tokens": 50,
}
}
}
Memory Types
The system supports different memory types with temporal characteristics:
profile_fact: Long-lived profile informationpreference: User preferences (medium-lived)episodic_event: Time-stamped eventstemp_state: Short-lived temporary statetask_state: Task-specific state
API Reference
Memory(config: dict[str, Any] | None = None)
Main memory engine class.
Methods:
-
add(messages: str | list[dict], user_id: str, metadata: dict | None = None) -> dict- Add memories from text or conversation history
-
search(query: str, user_id: str, filters: dict | None = None, limit: int = 10, rerank: bool = False) -> dict- Search memories semantically
-
list(user_id: str, status: str = "active") -> dict- List memories for a user
-
update(memory_id: str, new_content: str) -> dict | None- Update an existing memory
-
delete(memory_id: str) -> None- Delete a memory
-
reindex_user(user_id: str, status: str = "active") -> dict- Rebuild Qdrant index for a user
Development
Setup Development Environment
git clone https://github.com/29m10/temporalmemai.git
cd temporalmemai
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -e ".[all]"
Running Tests
# Install test dependencies
pip install pytest pytest-cov
# Run tests
pytest
Code Quality
# Format code
ruff format .
# Lint code
ruff check .
# Type checking
mypy temporalmemai
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Links
- Repository: https://github.com/29m10/temporalmemai
- Issues: https://github.com/29m10/temporalmemai/issues
- Documentation: https://github.com/29m10/temporalmemai#readme
Acknowledgments
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 temporalmemai-0.1.0.tar.gz.
File metadata
- Download URL: temporalmemai-0.1.0.tar.gz
- Upload date:
- Size: 32.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c45a99b900c1bade8f6e50beb6b6339052e6e5fead576e29c56c6787b310f6b2
|
|
| MD5 |
a66f8efc614de8a0a43e47a7f841e17f
|
|
| BLAKE2b-256 |
b3d8d630e25305c0169cd89823380d4a3fb7a12101df52e039363094ebd3a248
|
File details
Details for the file temporalmemai-0.1.0-py3-none-any.whl.
File metadata
- Download URL: temporalmemai-0.1.0-py3-none-any.whl
- Upload date:
- Size: 34.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f91a14e4e10c83f5a93606eed731e198ec7e832dc598e6dcbf8d42f910ccfb1b
|
|
| MD5 |
491e13a1c1c862d72cbdb93ebc7d89c5
|
|
| BLAKE2b-256 |
1e20fe8b007b57788131b70cc9360b2e3fccd8c09c53d0c91bfba0647b9fe0a9
|