From f625f8f55659613b5d34bc9f0db9e9a32b015b0b Mon Sep 17 00:00:00 2001 From: Pratik Narola Date: Tue, 9 Sep 2025 12:54:46 +0530 Subject: [PATCH] Migrated to Qdrant --- backend/config.py | 18 ++++------- backend/mem0_manager.py | 13 ++++---- backend/requirements.txt | 5 ++- config/postgres-init.sql | 66 ---------------------------------------- docker-compose.yml | 37 ++++++++++------------ frontend/index.html | 50 ++++++++++++++++++++++++++++-- 6 files changed, 78 insertions(+), 111 deletions(-) delete mode 100644 config/postgres-init.sql diff --git a/backend/config.py b/backend/config.py index ca34492..0c0ea8a 100644 --- a/backend/config.py +++ b/backend/config.py @@ -10,18 +10,16 @@ class Settings(BaseSettings): """Application settings loaded from environment variables.""" # API Configuration - openai_api_key: str = Field(..., env="OPENAI_API_KEY") - openai_base_url: str = Field(..., env="OPENAI_BASE_URL") + openai_api_key: str = Field(..., env="OPENAI_COMPAT_API_KEY") + openai_base_url: str = Field(..., env="OPENAI_COMPAT_BASE_URL") embedder_api_key: str = Field(..., env="EMBEDDER_API_KEY") # ollama_base_url: str = Field(..., env="OLLAMA_BASE_URL") # Database Configuration - postgres_host: str = Field("localhost", env="POSTGRES_HOST") - postgres_port: int = Field(5432, env="POSTGRES_PORT") - postgres_db: str = Field("mem0_db", env="POSTGRES_DB") - postgres_user: str = Field("mem0_user", env="POSTGRES_USER") - postgres_password: str = Field("mem0_password", env="POSTGRES_PASSWORD") + qdrant_host: str = Field("localhost", env="QDRANT_HOST") + qdrant_port: int = Field(6333, env="QDRANT_PORT") + qdrant_collection_name: str = Field("mem0", env="QDRANT_COLLECTION_NAME") # Neo4j Configuration neo4j_uri: str = Field("bolt://localhost:7687", env="NEO4J_URI") @@ -35,10 +33,6 @@ class Settings(BaseSettings): # Model Configuration - Ultra-minimal (single 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 def cors_origins_list(self) -> List[str]: @@ -51,4 +45,4 @@ class Settings(BaseSettings): # Global settings instance -settings = Settings() \ No newline at end of file +settings = Settings() diff --git a/backend/mem0_manager.py b/backend/mem0_manager.py index f8e1143..c4f70b4 100644 --- a/backend/mem0_manager.py +++ b/backend/mem0_manager.py @@ -39,14 +39,13 @@ class Mem0Manager: } }, "vector_store": { - "provider": "pgvector", + "provider": "qdrant", "config": { - "dbname": settings.postgres_db, - "user": settings.postgres_user, - "password": settings.postgres_password, - "host": settings.postgres_host, - "port": settings.postgres_port, - "embedding_model_dims": 2560 + "collection_name": settings.qdrant_collection_name, + "host": settings.qdrant_host, + "port": settings.qdrant_port, + "embedding_model_dims": 2560, + "on_disk": True } }, "graph_store": { diff --git a/backend/requirements.txt b/backend/requirements.txt index 050b815..44eff2c 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -9,8 +9,7 @@ openai google-genai # Database -psycopg2-binary -pgvector +qdrant-client neo4j langchain-neo4j rank-bm25 @@ -30,4 +29,4 @@ python-json-logger # CORS and Security python-jose[cryptography] -passlib[bcrypt] \ No newline at end of file +passlib[bcrypt] diff --git a/config/postgres-init.sql b/config/postgres-init.sql deleted file mode 100644 index 21a3ee9..0000000 --- a/config/postgres-init.sql +++ /dev/null @@ -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; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 630f3d7..5277352 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,17 @@ services: - # PostgreSQL with pgvector extension for vector storage - postgres: - image: pgvector/pgvector:pg17-trixie - container_name: mem0-postgres - environment: - POSTGRES_DB: ${POSTGRES_DB:-mem0_db} - POSTGRES_USER: ${POSTGRES_USER:-mem0_user} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mem0_password} + # Qdrant vector database for vector storage + qdrant: + image: qdrant/qdrant:latest + container_name: mem0-qdrant expose: - - "5432" + - "6333" volumes: - - postgres_data:/var/lib/postgresql/data - - ./config/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql + - qdrant_data:/qdrant/storage + command: > + sh -c "apt-get update && apt-get install -y curl && ./entrypoint.sh" healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mem0_user} -d ${POSTGRES_DB:-mem0_db}"] - interval: 5s + test: ["CMD", "curl", "-f", "http://localhost:6333/"] + interval: 10s timeout: 5s retries: 5 restart: unless-stopped @@ -57,11 +54,9 @@ services: OPENAI_API_KEY: ${OPENAI_COMPAT_API_KEY} OPENAI_BASE_URL: ${OPENAI_COMPAT_BASE_URL} EMBEDDER_API_KEY: ${EMBEDDER_API_KEY:-AIzaSyA_} - POSTGRES_HOST: postgres - POSTGRES_PORT: 5432 - POSTGRES_DB: ${POSTGRES_DB:-mem0_db} - POSTGRES_USER: ${POSTGRES_USER:-mem0_user} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mem0_password} + QDRANT_HOST: qdrant + QDRANT_PORT: 6333 + QDRANT_COLLECTION_NAME: ${QDRANT_COLLECTION_NAME:-mem0} NEO4J_URI: bolt://neo4j:7687 NEO4J_USERNAME: ${NEO4J_USERNAME:-neo4j} NEO4J_PASSWORD: ${NEO4J_PASSWORD:-mem0_neo4j_password} @@ -71,7 +66,7 @@ services: ports: - "${BACKEND_PORT:-8000}:8000" depends_on: - postgres: + qdrant: condition: service_healthy neo4j: condition: service_healthy @@ -81,7 +76,7 @@ services: command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] volumes: - postgres_data: + qdrant_data: neo4j_data: neo4j_logs: neo4j_import: @@ -89,4 +84,4 @@ volumes: networks: default: - name: mem0-network \ No newline at end of file + name: mem0-network diff --git a/frontend/index.html b/frontend/index.html index 40bf99f..df72f0d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -37,7 +37,14 @@ padding: 20px; background: #fafafa; border-bottom: 1px solid #e0e0e0; + display: flex; + justify-content: space-between; + align-items: center; + } + + .chat-header-content { text-align: center; + flex: 1; } .chat-header h1 { @@ -51,6 +58,31 @@ 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 { flex: 1; overflow-y: auto; @@ -242,8 +274,13 @@
-

What can I help you with?

-

Chat with your memories - User: pratik

+
+

What can I help you with?

+

Chat with your memories - User: pratik

+
+
@@ -281,6 +318,7 @@ const memoriesList = document.getElementById('memoriesList'); const memoryCount = document.getElementById('memoryCount'); const refreshButton = document.getElementById('refreshMemories'); + const clearChatBtn = document.getElementById('clearChatBtn'); // Chat history in localStorage let chatHistory = JSON.parse(localStorage.getItem('chatHistory') || '[]'); @@ -296,6 +334,7 @@ if (e.key === 'Enter') sendMessage(); }); refreshButton.addEventListener('click', loadMemories); + clearChatBtn.addEventListener('click', clearChatWithConfirmation); }); // Load chat history from localStorage @@ -493,6 +532,13 @@ }, 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) function clearChatHistory() { chatHistory = [];