diff --git a/config.yml b/config.yml index ac412d1e..521eea5a 100644 --- a/config.yml +++ b/config.yml @@ -185,19 +185,17 @@ models: words: words segments: segments memory: - provider: chronicle + provider: openmemory_mcp timeout_seconds: 1200 extraction: enabled: true - prompt: 'Extract important information from this conversation and return a JSON + prompt: Extract important information from this conversation and return a JSON object with an array named "facts". Include personal preferences, plans, names, dates, locations, numbers, and key details hehehe. Keep items concise and useful. - - ' openmemory_mcp: - server_url: http://localhost:8765 + server_url: http://host.docker.internal:8765 client_name: chronicle - user_id: default + user_id: openmemory timeout: 30 mycelia: api_url: http://localhost:5173 diff --git a/extras/openmemory-mcp/.env.template b/extras/openmemory-mcp/.env.template index 10c790bd..fff2ba90 100644 --- a/extras/openmemory-mcp/.env.template +++ b/extras/openmemory-mcp/.env.template @@ -4,8 +4,23 @@ # Required: OpenAI API Key for memory processing OPENAI_API_KEY= -# Optional: User identifier (defaults to system username) +# Optional: User identifier (defaults to 'openmemory') USER=openmemory +OPENMEMORY_USER_ID=openmemory -# Optional: Frontend URL (if using UI) -NEXT_PUBLIC_API_URL=http://localhost:8765 \ No newline at end of file +# Optional: API Key for OpenMemory MCP server authentication +API_KEY= + +# Optional: Frontend configuration +NEXT_PUBLIC_API_URL=http://localhost:8765 +NEXT_PUBLIC_USER_ID=openmemory + +# Neo4j Configuration (graph store for OpenMemory) +# Default credentials: neo4j/taketheredpillNe0 +# Access Neo4j browser at http://localhost:7474 +NEO4J_URL=bolt://neo4j:7687 +NEO4J_USERNAME=neo4j +NEO4J_PASSWORD=taketheredpillNe0 + +# Qdrant Configuration (vector store for OpenMemory) +QDRANT_URL=http://mem0_store:6333 diff --git a/extras/openmemory-mcp/.gitignore b/extras/openmemory-mcp/.gitignore index 6e25fa8f..44f346c4 100644 --- a/extras/openmemory-mcp/.gitignore +++ b/extras/openmemory-mcp/.gitignore @@ -1 +1,3 @@ -cache/ \ No newline at end of file +cache/ +mem0-fork/ +.env \ No newline at end of file diff --git a/extras/openmemory-mcp/README.md b/extras/openmemory-mcp/README.md index 940a33e5..77d1214b 100644 --- a/extras/openmemory-mcp/README.md +++ b/extras/openmemory-mcp/README.md @@ -2,6 +2,8 @@ This directory contains a local deployment of the OpenMemory MCP (Model Context Protocol) server, which can be used as an alternative memory provider for Chronicle. +**Note:** This deployment builds from the [Ushadow-io/mem0](https://github.com/Ushadow-io/mem0) fork instead of the official mem0.ai release, providing custom features and enhancements. + ## What is OpenMemory MCP? OpenMemory MCP is a memory service from mem0.ai that provides: @@ -13,23 +15,41 @@ OpenMemory MCP is a memory service from mem0.ai that provides: ## Quick Start -### 1. Configure Environment +### 1. Run Setup Script + +The setup script will: +- Clone the Ushadow-io/mem0 fork +- Configure your environment with API keys +- Prepare the service for deployment + +```bash +./setup.sh +``` +Or provide API key directly: ```bash -cp .env.template .env -# Edit .env and add your OPENAI_API_KEY +./setup.sh --openai-api-key your-api-key-here ``` ### 2. Start Services +The docker-compose.yml is located in the fork directory. You can start services using: + +**Option A: Using Chronicle's unified service manager (Recommended)** ```bash -# Start backend only (recommended) -./run.sh +# From project root +uv run --with-requirements setup-requirements.txt python services.py start openmemory-mcp --build +``` -# Or start with UI (optional) -./run.sh --with-ui +**Option B: Manually from the fork directory** +```bash +# From extras/openmemory-mcp +cd mem0-fork/openmemory +docker compose up --build -d ``` +**Note:** The first build may take several minutes as Docker builds the services from source. + ### 3. Configure Chronicle In your Chronicle backend `.env` file: @@ -48,13 +68,20 @@ The deployment includes: - FastAPI backend with MCP protocol support - Memory extraction using OpenAI - REST API and MCP endpoints + - Development mode with hot-reload enabled -2. **Qdrant Vector Database** (port 6334) +2. **Qdrant Vector Database** (port 6333) - Stores memory embeddings - Enables semantic search - - Isolated from main Chronicle Qdrant + - Note: Uses same port as Chronicle's Qdrant (services are isolated by Docker network) -3. **OpenMemory UI** (port 3001, optional) +3. **Neo4j Graph Database** (ports 7474, 7687) + - Advanced graph-based memory features + - APOC and Graph Data Science plugins enabled + - Web browser interface for visualization + - Default credentials: `neo4j/taketheredpillNe0` + +4. **OpenMemory UI** (port 3333) - Web interface for memory management - View and search memories - Debug and testing interface @@ -64,10 +91,15 @@ The deployment includes: - **MCP Server**: http://localhost:8765 - REST API: `/api/v1/memories` - MCP SSE: `/mcp/{client_name}/sse/{user_id}` - -- **Qdrant Dashboard**: http://localhost:6334/dashboard + - API Docs: http://localhost:8765/docs + +- **Qdrant Dashboard**: http://localhost:6333/dashboard -- **UI** (if enabled): http://localhost:3001 +- **Neo4j Browser**: http://localhost:7474 + - Username: `neo4j` + - Password: `taketheredpillNe0` + +- **OpenMemory UI**: http://localhost:3333 ## How It Works with Chronicle @@ -82,7 +114,22 @@ This replaces Chronicle's built-in memory processing with OpenMemory's implement ## Managing Services +**Using Chronicle's unified service manager (from project root):** ```bash +# View status +uv run --with-requirements setup-requirements.txt python services.py status + +# Stop services +uv run --with-requirements setup-requirements.txt python services.py stop openmemory-mcp + +# Restart services +uv run --with-requirements setup-requirements.txt python services.py restart openmemory-mcp --build +``` + +**Manually from the fork directory:** +```bash +cd extras/openmemory-mcp/mem0-fork/openmemory + # View logs docker compose logs -f @@ -140,10 +187,16 @@ This test verifies: ### Port Conflicts -If ports are already in use, edit `docker-compose.yml`: -- Change `8765:8765` to another port for MCP server -- Change `6334:6333` to another port for Qdrant -- Update Chronicle's `OPENMEMORY_MCP_URL` accordingly +**Qdrant Port Note**: OpenMemory uses port 6333 for Qdrant, same as Chronicle's main Qdrant. However, they are isolated by Docker networks and won't conflict. Services communicate via container names, not localhost ports. + +If you need to change ports, edit `mem0-fork/openmemory/docker-compose.yml`: +- MCP Server: Change `8765:8765` to another port +- Qdrant: Change `6333:6333` to another port +- Neo4j Browser: Change `7474:7474` to another port +- Neo4j Bolt: Change `7687:7687` to another port +- UI: Change `3333:3000` to another port + +Update Chronicle's `OPENMEMORY_MCP_URL` if you change the MCP server port. ### Memory Not Working diff --git a/extras/openmemory-mcp/docker-compose.yml b/extras/openmemory-mcp/docker-compose.yml index 34fbc8e5..7d6b798e 100644 --- a/extras/openmemory-mcp/docker-compose.yml +++ b/extras/openmemory-mcp/docker-compose.yml @@ -1,50 +1,97 @@ services: - # Qdrant vector database for OpenMemory (following original naming) + # Qdrant vector database for OpenMemory mem0_store: image: qdrant/qdrant ports: - - "6335:6333" # Different port to avoid conflict with main Qdrant + - "6335:6333" # Different port to avoid conflict with main Chronicle Qdrant volumes: - ./data/mem0_storage:/qdrant/storage + networks: + - mem0-network restart: unless-stopped - # OpenMemory MCP Server (official Docker image) + # OpenMemory MCP API Server (pre-built image from fork) openmemory-mcp: - image: mem0/openmemory-mcp:latest + image: ghcr.io/ushadow-io/u-mem0-api:latest env_file: - .env environment: + - USER=${USER:-openmemory} + - API_KEY=${API_KEY:-} - OPENAI_API_KEY=${OPENAI_API_KEY} - - OPENMEMORY_USER_ID=${OPENMEMORY_USER_ID:-openmemory} + - QDRANT_URL=http://mem0_store:6333 + - NEO4J_URL=${NEO4J_URL:-bolt://neo4j:7687} + - NEO4J_USERNAME=${NEO4J_USERNAME:-neo4j} + - NEO4J_PASSWORD=${NEO4J_PASSWORD:-taketheredpillNe0} depends_on: - mem0_store + - neo4j ports: - "8765:8765" + networks: + - mem0-network + - chronicle-network restart: unless-stopped healthcheck: - test: ["CMD", "python", "-c", "import requests; exit(0 if requests.get('http://localhost:8765/docs').status_code == 200 else 1)"] + test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8765/docs')"] interval: 30s timeout: 10s retries: 3 start_period: 30s - networks: - - default - - chronicle-network - # OpenMemory UI (starts by default with the MCP server) + # OpenMemory UI (pre-built image from fork) openmemory-ui: - image: mem0/openmemory-ui:latest + image: ghcr.io/ushadow-io/u-mem0-ui:latest ports: - - "3001:3000" # Different port to avoid conflict + - "3333:3000" environment: - - NEXT_PUBLIC_API_URL=http://localhost:8765 - - NEXT_PUBLIC_USER_ID=openmemory + - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://localhost:8765} + - NEXT_PUBLIC_USER_ID=${NEXT_PUBLIC_USER_ID:-openmemory} depends_on: - openmemory-mcp + networks: + - mem0-network + restart: unless-stopped + + # Neo4j graph database for advanced memory features + neo4j: + image: neo4j:latest + container_name: neo4j-mem0 + volumes: + - ./data/neo4j_data:/data + - ./data/neo4j_logs:/logs + - ./data/neo4j_config:/config + environment: + - NEO4J_AUTH=neo4j/taketheredpillNe0 + - NEO4J_server_memory_heap_initial__size=1G + - NEO4J_server_memory_heap_max__size=2G + - NEO4J_server_memory_pagecache_size=1G + - NEO4J_apoc_export_file_enabled=true + - NEO4J_apoc_import_file_enabled=true + - NEO4J_apoc_import_file_use__neo4j__config=true + - NEO4J_PLUGINS=["apoc", "graph-data-science"] + - NEO4J_dbms_security_procedures_unrestricted=apoc.*,gds.* + ports: + - "7474:7474" + - "7687:7687" + networks: + - mem0-network restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:7474"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 40s + +volumes: + mem0_storage: + neo4j_data: + neo4j_logs: + neo4j_config: networks: - default: - name: openmemory-mcp_default + mem0-network: + driver: bridge chronicle-network: - external: true \ No newline at end of file + external: true diff --git a/extras/openmemory-mcp/run.sh b/extras/openmemory-mcp/run.sh index 1092207a..591c8054 100755 --- a/extras/openmemory-mcp/run.sh +++ b/extras/openmemory-mcp/run.sh @@ -47,7 +47,8 @@ fi # Start services echo "🚀 Starting OpenMemory MCP services..." -docker compose up -d $PROFILE +echo " (Building from Ushadow-io/mem0 fork...)" +docker compose up --build -d $PROFILE # Wait for services to be ready echo "⏳ Waiting for services to be ready..." diff --git a/extras/openmemory-mcp/setup.sh b/extras/openmemory-mcp/setup.sh index afa8cf57..d6b28018 100755 --- a/extras/openmemory-mcp/setup.sh +++ b/extras/openmemory-mcp/setup.sh @@ -19,29 +19,48 @@ while [[ $# -gt 0 ]]; do esac done -echo "🧠 OpenMemory MCP Setup" -echo "======================" +echo "🧠 OpenMemory MCP Setup (Pre-built Images)" +echo "==========================================" +echo "" +echo "This setup uses pre-built Docker images from ghcr.io/ushadow-io" +echo "No need to clone or build from source!" +echo "" + +# Configure the .env file +ENV_FILE=".env" # Check if already configured -if [ -f ".env" ]; then - echo "⚠️ .env already exists. Backing up..." - cp .env .env.backup.$(date +%Y%m%d_%H%M%S) +if [ -f "$ENV_FILE" ]; then + echo "⚠️ $ENV_FILE already exists. Backing up..." + cp "$ENV_FILE" "$ENV_FILE.backup.$(date +%Y%m%d_%H%M%S)" fi -# Start from template - check existence first -if [ ! -r ".env.template" ]; then - echo "Error: .env.template not found or not readable" >&2 - exit 1 -fi +# Create .env from template or start fresh +if [ -f ".env.template" ]; then + cp ".env.template" "$ENV_FILE" +else + # Create minimal .env if template doesn't exist + cat > "$ENV_FILE" << 'EOF' +# OpenMemory MCP Configuration + +# Required: OpenAI API Key for memory processing +OPENAI_API_KEY= + +# User identifier +USER=openmemory +OPENMEMORY_USER_ID=openmemory + +# Optional: API Key for MCP server authentication +API_KEY= -# Copy template and set secure permissions -if ! cp .env.template .env; then - echo "Error: Failed to copy .env.template to .env" >&2 - exit 1 +# Frontend configuration +NEXT_PUBLIC_API_URL=http://localhost:8765 +NEXT_PUBLIC_USER_ID=openmemory +EOF fi # Set restrictive permissions (owner read/write only) -chmod 600 .env +chmod 600 "$ENV_FILE" # Get OpenAI API Key (prompt only if not provided via command line) if [ -z "$OPENAI_API_KEY" ]; then @@ -66,13 +85,40 @@ awk -v key="$OPENAI_API_KEY" ' /^OPENAI_API_KEY=/ { print "OPENAI_API_KEY=" key; found=1; next } { print } END { if (!found) print "OPENAI_API_KEY=" key } -' .env > "$temp_file" -mv "$temp_file" .env +' "$ENV_FILE" > "$temp_file" +mv "$temp_file" "$ENV_FILE" + +# Ensure USER is set to openmemory +awk ' + /^USER=/ { print "USER=openmemory"; found=1; next } + { print } + END { if (!found) print "USER=openmemory" } +' "$ENV_FILE" > "$temp_file" +mv "$temp_file" "$ENV_FILE" echo "" echo "✅ OpenMemory MCP configured!" -echo "📁 Configuration saved to .env" +echo "📁 Configuration saved to: $ENV_FILE" +echo "" +echo "🚀 To start services:" +echo " docker compose up -d" +echo "" +echo " (Note: First run will pull pre-built images)" +echo "" +echo "📡 Services:" +echo " 🌐 MCP Server: http://localhost:8765" +echo " 📱 Web UI: http://localhost:3333" +echo " 🗄️ Neo4j Browser: http://localhost:7474" +echo " 🔍 Qdrant: http://localhost:6335" +echo "" +echo "🔐 Neo4j credentials: neo4j/taketheredpillNe0" +echo "" +echo "⚙️ Configure Chronicle backend (.env):" +echo " MEMORY_PROVIDER=openmemory_mcp" +echo " OPENMEMORY_MCP_URL=http://openmemory-mcp:8765 (for Docker - recommended)" +echo " # or http://localhost:8765 (if backend runs outside Docker)" +echo "" +echo "💡 Using pre-built images from ghcr.io/ushadow-io:" +echo " - u-mem0-api:latest" +echo " - u-mem0-ui:latest" echo "" -echo "🚀 To start: docker compose up -d" -echo "🌐 MCP Server: http://localhost:8765" -echo "📱 Web UI: http://localhost:3001" \ No newline at end of file diff --git a/services.py b/services.py index 716e437e..b1805935 100755 --- a/services.py +++ b/services.py @@ -36,8 +36,8 @@ 'openmemory-mcp': { 'path': 'extras/openmemory-mcp', 'compose_file': 'docker-compose.yml', - 'description': 'OpenMemory MCP Server', - 'ports': ['8765'] + 'description': 'OpenMemory MCP Server', + 'ports': ['8765', '7474', '7687', '3333'] } } @@ -45,12 +45,9 @@ def check_service_configured(service_name): """Check if service is configured (has .env file)""" service = SERVICES[service_name] service_path = Path(service['path']) - - # Backend uses advanced init, others use .env - if service_name == 'backend': - return (service_path / '.env').exists() - else: - return (service_path / '.env').exists() + + # All services now use .env in their root directory + return (service_path / '.env').exists() def run_compose_command(service_name, command, build=False): """Run docker compose command for a service"""