Migrated to Qdrant
This commit is contained in:
parent
28a8953ac5
commit
f625f8f556
6 changed files with 78 additions and 111 deletions
|
@ -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()
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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;
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = [];
|
||||||
|
|
Loading…
Reference in a new issue