Files
kxkm_clown/docker-compose.yml
T
L'électron rare d2fed6087a feat: Add MCP server and smoke test scripts
- Introduced `mcp-server.js` to expose KXKM personas as MCP tools, supporting chat, persona listing, web search, and status checks.
- Implemented `mcp-server-smoke.js` for testing the MCP server functionality, ensuring compatibility with both new and legacy message formats.
- Created `setup-voice-clone.sh` for managing voice cloning environment setup, including bootstrapping, sample generation, and smoke testing.
- Added `state.json` to track project status and task outputs for various batches.
- Generated summary files for deep cycle and overall project status, capturing performance and security findings.
2026-03-19 16:18:44 +01:00

317 lines
11 KiB
YAML

# ---------------------------------------------------------------------------
# KXKM_Clown — Docker Compose
# ---------------------------------------------------------------------------
# Usage:
# docker compose up -d # postgres only
# docker compose --profile v1 up -d # V1 app + postgres
# docker compose --profile v2 up -d # V2 api + worker + postgres
# docker compose --profile v1 --profile v2 up -d # V1 + V2 + postgres
# docker compose --profile ollama up -d # include Ollama container
#
# By default, Ollama is expected to run natively on the host (port 11434).
# Set OLLAMA_URL in .env to override. Use --profile ollama to run it in Docker.
# ---------------------------------------------------------------------------
x-common-env: &common-env
OLLAMA_URL: "${OLLAMA_URL:-http://host.docker.internal:11434}"
DATABASE_URL: postgres://kxkm:kxkm@postgres:5432/kxkm_clown
NODE_ENV: production
x-extra-hosts: &extra-hosts
- "host.docker.internal:host-gateway"
services:
# -------------------------------------------------------------------------
# V1 — main Express + WebSocket server
# -------------------------------------------------------------------------
app:
build: .
restart: unless-stopped
profiles: [v1]
extra_hosts: *extra-hosts
ports:
- "${APP_PORT:-3333}:3333"
environment:
<<: *common-env
PORT: "3333"
ADMIN_BOOTSTRAP_TOKEN: "${ADMIN_BOOTSTRAP_TOKEN:-}"
ADMIN_ALLOWED_SUBNETS: "${ADMIN_ALLOWED_SUBNETS:-}"
WEB_SEARCH_API_BASE: "${WEB_SEARCH_API_BASE:-}"
OWNER_NICK: "${OWNER_NICK:-}"
MAX_GENERAL_RESPONDERS: "${MAX_GENERAL_RESPONDERS:-4}"
volumes:
- app-data:/app/data
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3333/api/ping').then(r=>{process.exit(r.ok?0:1)}).catch(()=>process.exit(1))\""]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
# -------------------------------------------------------------------------
# V2 — API server (Express, TypeScript) — main chat interface
# Serves both the REST/WebSocket API and the React web build.
# -------------------------------------------------------------------------
api:
build: .
restart: unless-stopped
profiles: [v2]
network_mode: host
command: ["node", "apps/api/dist/server.js"]
volumes:
- ./data:/app/data
- ./scripts:/app/scripts:ro
environment:
OLLAMA_URL: "http://localhost:11434"
DATABASE_URL: "postgres://kxkm:kxkm@localhost:5432/kxkm_clown"
NODE_ENV: production
V2_API_PORT: "${API_PORT:-3333}"
WEB_DIST_PATH: "/app/apps/web/dist"
ADMIN_BOOTSTRAP_TOKEN: "${ADMIN_BOOTSTRAP_TOKEN:-}"
ADMIN_ALLOWED_SUBNETS: "${ADMIN_ALLOWED_SUBNETS:-}"
TTS_ENABLED: "1"
PYTHON_BIN: "python3"
SCRIPTS_DIR: "/app/scripts"
PIPER_VOICE_DIR: "/app/data/piper-voices"
COQUI_TOS_AGREED: "1"
VISION_MODEL: "qwen3-vl:8b"
LIGHTRAG_URL: "http://localhost:9621"
TTS_URL: "http://localhost:9100"
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3333/api/v2/health').then(r=>{process.exit(r.ok?0:1)}).catch(()=>process.exit(1))\""]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
# -------------------------------------------------------------------------
# V2 — Worker (poll loop, GPU access for training)
# -------------------------------------------------------------------------
worker:
build: .
restart: unless-stopped
profiles: [v2]
network_mode: host
command: ["node", "apps/worker/dist/index.js"]
environment:
OLLAMA_URL: "http://localhost:11434"
DATABASE_URL: "postgres://kxkm:kxkm@localhost:5432/kxkm_clown"
NODE_ENV: production
PYTHON_BIN: "${PYTHON_BIN:-/home/kxkm/venv/bin/python3}"
SCRIPTS_DIR: "${SCRIPTS_DIR:-/app/scripts}"
TRAINING_TIMEOUT_MS: "${TRAINING_TIMEOUT_MS:-3600000}"
volumes:
- /home/kxkm/venv:/home/kxkm/venv:ro
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
depends_on:
postgres:
condition: service_healthy
# -------------------------------------------------------------------------
# PostgreSQL 16
# -------------------------------------------------------------------------
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: kxkm
POSTGRES_PASSWORD: kxkm
POSTGRES_DB: kxkm_clown
volumes:
- pg-data:/var/lib/postgresql/data
ports:
- "${PG_PORT:-5432}:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kxkm -d kxkm_clown"]
interval: 5s
timeout: 3s
retries: 5
start_period: 5s
# -------------------------------------------------------------------------
# Ollama — LLM inference server (optional, use --profile ollama)
# -------------------------------------------------------------------------
ollama:
image: ollama/ollama:latest
restart: unless-stopped
profiles: [ollama]
ports:
- "${OLLAMA_PORT:-11434}:11434"
volumes:
- ollama-data:/root/.ollama
# GPU passthrough — uncomment if NVIDIA GPU available:
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: all
# capabilities: [gpu]
# -------------------------------------------------------------------------
# SearXNG — self-hosted privacy search engine
# Replaces DuckDuckGo Lite scraping with structured JSON API.
# Usage: set WEB_SEARCH_API_BASE=http://localhost:8080/search in .env
# -------------------------------------------------------------------------
searxng:
image: searxng/searxng:latest
restart: unless-stopped
profiles: [v2]
ports:
- "${SEARXNG_PORT:-8080}:8080"
volumes:
- ./ops/v2/searxng:/etc/searxng:ro
environment:
SEARXNG_BASE_URL: "http://localhost:8080/"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
# -------------------------------------------------------------------------
# Discord Pharmacius Bot — bridges KXKM chat ↔ Discord
# Usage: DISCORD_BOT_TOKEN=xxx DISCORD_CHANNEL_ID=yyy docker compose --profile discord up -d
# -------------------------------------------------------------------------
discord-bot:
build: .
restart: unless-stopped
profiles: [discord]
network_mode: host
command: ["node", "scripts/discord-pharmacius.js"]
environment:
DISCORD_BOT_TOKEN: "${DISCORD_BOT_TOKEN:-}"
DISCORD_CHANNEL_ID: "${DISCORD_CHANNEL_ID:-}"
KXKM_WS_URL: "ws://localhost:3333/ws"
depends_on:
api:
condition: service_healthy
# -------------------------------------------------------------------------
# Discord Voice Bot — STT → Personas → TTS in voice channels
# Usage: DISCORD_BOT_TOKEN=xxx DISCORD_VOICE_CHANNEL=yyy docker compose --profile discord-voice up -d
# -------------------------------------------------------------------------
discord-voice:
build: .
restart: unless-stopped
profiles: [discord-voice]
network_mode: host
command: ["node", "scripts/discord-voice.js"]
environment:
DISCORD_BOT_TOKEN: "${DISCORD_BOT_TOKEN:-}"
DISCORD_VOICE_CHANNEL: "${DISCORD_VOICE_CHANNEL_ID_2:-}"
KXKM_WS_URL: "ws://localhost:3333/ws"
PYTHON_BIN: "${PYTHON_BIN:-/home/kxkm/venv/bin/python3}"
SCRIPTS_DIR: "/app/scripts"
volumes:
- /home/kxkm/venv:/home/kxkm/venv:ro
depends_on:
api:
condition: service_healthy
# -------------------------------------------------------------------------
# Chatterbox TTS — GPU-accelerated voice synthesis (Docker)
# Usage: docker compose --profile v2 up -d
# API: POST /tts, POST /v1/audio/speech
# -------------------------------------------------------------------------
chatterbox:
image: ghcr.io/devnen/chatterbox-tts-server:latest
restart: unless-stopped
profiles: [v2]
ports:
- "${CHATTERBOX_PORT:-9200}:8004"
volumes:
- ./data/voice-samples:/app/voices:ro
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
healthcheck:
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8004/get_predefined_voices')\""]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# -------------------------------------------------------------------------
# TTS Sidecar — Proxy to Chatterbox + Piper fallback
# Runs on host network, proxies /synthesize to Chatterbox :9200
# -------------------------------------------------------------------------
tts-sidecar:
build: .
restart: unless-stopped
profiles: [v2]
network_mode: host
command: ["python3", "scripts/tts-server.py", "--port", "9100", "--backend", "chatterbox-remote"]
environment:
CHATTERBOX_URL: "http://127.0.0.1:9200"
PIPER_VOICE_DIR: "/app/data/piper-voices"
KXKM_VOICE_SAMPLES_DIR: "/app/data/voice-samples"
volumes:
- ./data:/app/data
- ./scripts:/app/scripts:ro
depends_on:
chatterbox:
condition: service_healthy
# -------------------------------------------------------------------------
# LightRAG — Graph RAG server (Ollama backend)
# API: POST /query, POST /documents/text, GET /health
# Web UI: http://localhost:9621
# -------------------------------------------------------------------------
lightrag:
build:
context: .
dockerfile_inline: |
FROM python:3.12-slim
RUN pip install --no-cache-dir 'lightrag-hku[api]'
EXPOSE 9621
CMD ["lightrag-server", "--host", "0.0.0.0", "--port", "9621"]
restart: unless-stopped
profiles: [v2]
network_mode: host
environment:
LLM_MODEL: "qwen3:8b"
EMBEDDING_MODEL: "nomic-embed-text"
EMBEDDING_DIM: "768"
OLLAMA_HOST: "http://localhost:11434"
LLM_BINDING: ollama
EMBEDDING_BINDING: ollama
RAG_DIR: "/data/lightrag"
command: >
lightrag-server
--host 0.0.0.0
--port 9621
--working-dir /data/lightrag
--llm-binding ollama
--embedding-binding ollama
volumes:
- ./data/lightrag:/data/lightrag
healthcheck:
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:9621/health')\""]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
volumes:
app-data:
pg-data:
ollama-data: