Migrated to Qdrant

This commit is contained in:
Pratik Narola 2025-09-09 12:54:46 +05:30
parent 28a8953ac5
commit f625f8f556
6 changed files with 78 additions and 111 deletions

View file

@ -10,18 +10,16 @@ class Settings(BaseSettings):
"""Application settings loaded from environment variables.""" """Application settings loaded from environment variables."""
# API Configuration # API Configuration
openai_api_key: str = Field(..., env="OPENAI_API_KEY") openai_api_key: str = Field(..., env="OPENAI_COMPAT_API_KEY")
openai_base_url: str = Field(..., env="OPENAI_BASE_URL") openai_base_url: str = Field(..., env="OPENAI_COMPAT_BASE_URL")
embedder_api_key: str = Field(..., env="EMBEDDER_API_KEY") embedder_api_key: str = Field(..., env="EMBEDDER_API_KEY")
# ollama_base_url: str = Field(..., env="OLLAMA_BASE_URL") # ollama_base_url: str = Field(..., env="OLLAMA_BASE_URL")
# Database Configuration # Database Configuration
postgres_host: str = Field("localhost", env="POSTGRES_HOST") qdrant_host: str = Field("localhost", env="QDRANT_HOST")
postgres_port: int = Field(5432, env="POSTGRES_PORT") qdrant_port: int = Field(6333, env="QDRANT_PORT")
postgres_db: str = Field("mem0_db", env="POSTGRES_DB") qdrant_collection_name: str = Field("mem0", env="QDRANT_COLLECTION_NAME")
postgres_user: str = Field("mem0_user", env="POSTGRES_USER")
postgres_password: str = Field("mem0_password", env="POSTGRES_PASSWORD")
# Neo4j Configuration # Neo4j Configuration
neo4j_uri: str = Field("bolt://localhost:7687", env="NEO4J_URI") neo4j_uri: str = Field("bolt://localhost:7687", env="NEO4J_URI")
@ -35,10 +33,6 @@ class Settings(BaseSettings):
# Model Configuration - Ultra-minimal (single model) # Model Configuration - Ultra-minimal (single model)
default_model: str = Field("claude-sonnet-4", env="DEFAULT_MODEL") default_model: str = Field("claude-sonnet-4", env="DEFAULT_MODEL")
@property
def postgres_url(self) -> str:
"""Build PostgreSQL connection URL."""
return f"postgresql://{self.postgres_user}:{self.postgres_password}@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"
@property @property
def cors_origins_list(self) -> List[str]: def cors_origins_list(self) -> List[str]:
@ -51,4 +45,4 @@ class Settings(BaseSettings):
# Global settings instance # Global settings instance
settings = Settings() settings = Settings()

View file

@ -39,14 +39,13 @@ class Mem0Manager:
} }
}, },
"vector_store": { "vector_store": {
"provider": "pgvector", "provider": "qdrant",
"config": { "config": {
"dbname": settings.postgres_db, "collection_name": settings.qdrant_collection_name,
"user": settings.postgres_user, "host": settings.qdrant_host,
"password": settings.postgres_password, "port": settings.qdrant_port,
"host": settings.postgres_host, "embedding_model_dims": 2560,
"port": settings.postgres_port, "on_disk": True
"embedding_model_dims": 2560
} }
}, },
"graph_store": { "graph_store": {

View file

@ -9,8 +9,7 @@ openai
google-genai google-genai
# Database # Database
psycopg2-binary qdrant-client
pgvector
neo4j neo4j
langchain-neo4j langchain-neo4j
rank-bm25 rank-bm25
@ -30,4 +29,4 @@ python-json-logger
# CORS and Security # CORS and Security
python-jose[cryptography] python-jose[cryptography]
passlib[bcrypt] passlib[bcrypt]

View file

@ -1,66 +0,0 @@
-- Initialize PostgreSQL database for Mem0 with pgvector extension
-- Create the pgvector extension for vector operations
CREATE EXTENSION IF NOT EXISTS vector;
-- Create user if not exists (in case it's needed)
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_catalog.pg_roles
WHERE rolname = 'mem0_user') THEN
CREATE ROLE mem0_user LOGIN PASSWORD 'mem0_password';
END IF;
END
$do$;
-- Grant necessary permissions
GRANT ALL PRIVILEGES ON DATABASE mem0_db TO mem0_user;
GRANT ALL ON SCHEMA public TO mem0_user;
-- Create table for vector embeddings (if needed by Mem0's pgvector implementation)
CREATE TABLE IF NOT EXISTS embeddings (
id SERIAL PRIMARY KEY,
user_id VARCHAR(255),
content TEXT,
embedding VECTOR(2560), -- OpenAI embedding dimension
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
-- Create index for efficient vector similarity search
CREATE INDEX IF NOT EXISTS embeddings_embedding_idx ON embeddings
USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
-- Create index for user_id lookups
CREATE INDEX IF NOT EXISTS embeddings_user_id_idx ON embeddings (user_id);
-- Create index for metadata queries
CREATE INDEX IF NOT EXISTS embeddings_metadata_idx ON embeddings USING GIN (metadata);
-- Grant permissions on the table
GRANT ALL PRIVILEGES ON TABLE embeddings TO mem0_user;
GRANT USAGE, SELECT ON SEQUENCE embeddings_id_seq TO mem0_user;
-- Create table for memory history tracking
CREATE TABLE IF NOT EXISTS memory_history (
id SERIAL PRIMARY KEY,
memory_id VARCHAR(255),
user_id VARCHAR(255),
action VARCHAR(50),
previous_value TEXT,
new_value TEXT,
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
-- Create indexes for memory history
CREATE INDEX IF NOT EXISTS memory_history_memory_id_idx ON memory_history (memory_id);
CREATE INDEX IF NOT EXISTS memory_history_user_id_idx ON memory_history (user_id);
CREATE INDEX IF NOT EXISTS memory_history_created_at_idx ON memory_history (created_at);
-- Grant permissions
GRANT ALL PRIVILEGES ON TABLE memory_history TO mem0_user;
GRANT USAGE, SELECT ON SEQUENCE memory_history_id_seq TO mem0_user;

View file

@ -1,20 +1,17 @@
services: services:
# PostgreSQL with pgvector extension for vector storage # Qdrant vector database for vector storage
postgres: qdrant:
image: pgvector/pgvector:pg17-trixie image: qdrant/qdrant:latest
container_name: mem0-postgres container_name: mem0-qdrant
environment:
POSTGRES_DB: ${POSTGRES_DB:-mem0_db}
POSTGRES_USER: ${POSTGRES_USER:-mem0_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mem0_password}
expose: expose:
- "5432" - "6333"
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - qdrant_data:/qdrant/storage
- ./config/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql command: >
sh -c "apt-get update && apt-get install -y curl && ./entrypoint.sh"
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mem0_user} -d ${POSTGRES_DB:-mem0_db}"] test: ["CMD", "curl", "-f", "http://localhost:6333/"]
interval: 5s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
restart: unless-stopped restart: unless-stopped
@ -57,11 +54,9 @@ services:
OPENAI_API_KEY: ${OPENAI_COMPAT_API_KEY} OPENAI_API_KEY: ${OPENAI_COMPAT_API_KEY}
OPENAI_BASE_URL: ${OPENAI_COMPAT_BASE_URL} OPENAI_BASE_URL: ${OPENAI_COMPAT_BASE_URL}
EMBEDDER_API_KEY: ${EMBEDDER_API_KEY:-AIzaSyA_} EMBEDDER_API_KEY: ${EMBEDDER_API_KEY:-AIzaSyA_}
POSTGRES_HOST: postgres QDRANT_HOST: qdrant
POSTGRES_PORT: 5432 QDRANT_PORT: 6333
POSTGRES_DB: ${POSTGRES_DB:-mem0_db} QDRANT_COLLECTION_NAME: ${QDRANT_COLLECTION_NAME:-mem0}
POSTGRES_USER: ${POSTGRES_USER:-mem0_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mem0_password}
NEO4J_URI: bolt://neo4j:7687 NEO4J_URI: bolt://neo4j:7687
NEO4J_USERNAME: ${NEO4J_USERNAME:-neo4j} NEO4J_USERNAME: ${NEO4J_USERNAME:-neo4j}
NEO4J_PASSWORD: ${NEO4J_PASSWORD:-mem0_neo4j_password} NEO4J_PASSWORD: ${NEO4J_PASSWORD:-mem0_neo4j_password}
@ -71,7 +66,7 @@ services:
ports: ports:
- "${BACKEND_PORT:-8000}:8000" - "${BACKEND_PORT:-8000}:8000"
depends_on: depends_on:
postgres: qdrant:
condition: service_healthy condition: service_healthy
neo4j: neo4j:
condition: service_healthy condition: service_healthy
@ -81,7 +76,7 @@ services:
command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
volumes: volumes:
postgres_data: qdrant_data:
neo4j_data: neo4j_data:
neo4j_logs: neo4j_logs:
neo4j_import: neo4j_import:
@ -89,4 +84,4 @@ volumes:
networks: networks:
default: default:
name: mem0-network name: mem0-network

View file

@ -37,7 +37,14 @@
padding: 20px; padding: 20px;
background: #fafafa; background: #fafafa;
border-bottom: 1px solid #e0e0e0; border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-header-content {
text-align: center; text-align: center;
flex: 1;
} }
.chat-header h1 { .chat-header h1 {
@ -51,6 +58,31 @@
font-size: 14px; font-size: 14px;
} }
.clear-chat-btn {
background: #f8f9fa;
color: #666;
border: 1px solid #e0e0e0;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
font-weight: 500;
display: flex;
align-items: center;
gap: 5px;
transition: all 0.2s ease;
}
.clear-chat-btn:hover {
background: #e9ecef;
border-color: #ced4da;
color: #495057;
}
.clear-chat-btn:active {
background: #dee2e6;
}
.chat-messages { .chat-messages {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
@ -242,8 +274,13 @@
<!-- Chat Section --> <!-- Chat Section -->
<div class="chat-section"> <div class="chat-section">
<div class="chat-header"> <div class="chat-header">
<h1>What can I help you with?</h1> <div class="chat-header-content">
<p>Chat with your memories - User: pratik</p> <h1>What can I help you with?</h1>
<p>Chat with your memories - User: pratik</p>
</div>
<button class="clear-chat-btn" id="clearChatBtn" title="Clear chat history">
🗑️ Clear Chat
</button>
</div> </div>
<div class="chat-messages" id="chatMessages"> <div class="chat-messages" id="chatMessages">
@ -281,6 +318,7 @@
const memoriesList = document.getElementById('memoriesList'); const memoriesList = document.getElementById('memoriesList');
const memoryCount = document.getElementById('memoryCount'); const memoryCount = document.getElementById('memoryCount');
const refreshButton = document.getElementById('refreshMemories'); const refreshButton = document.getElementById('refreshMemories');
const clearChatBtn = document.getElementById('clearChatBtn');
// Chat history in localStorage // Chat history in localStorage
let chatHistory = JSON.parse(localStorage.getItem('chatHistory') || '[]'); let chatHistory = JSON.parse(localStorage.getItem('chatHistory') || '[]');
@ -296,6 +334,7 @@
if (e.key === 'Enter') sendMessage(); if (e.key === 'Enter') sendMessage();
}); });
refreshButton.addEventListener('click', loadMemories); refreshButton.addEventListener('click', loadMemories);
clearChatBtn.addEventListener('click', clearChatWithConfirmation);
}); });
// Load chat history from localStorage // Load chat history from localStorage
@ -493,6 +532,13 @@
}, 5000); }, 5000);
} }
// Clear chat history with confirmation
function clearChatWithConfirmation() {
if (confirm('Are you sure you want to clear the chat history? This action cannot be undone.')) {
clearChatHistory();
}
}
// Clear chat history (for debugging) // Clear chat history (for debugging)
function clearChatHistory() { function clearChatHistory() {
chatHistory = []; chatHistory = [];