c5f921243e
- LLM_API_KEY support across all LLM calls - TEI embedding server (bge-m3, :9500) - Hierarchical AGENTS.md documentation - CLAUDE.md updated for new architecture
414 lines
14 KiB
YAML
414 lines
14 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-compatible embeddings backend
|
|
#
|
|
# By default, the primary LLM runtime is expected on the host (LLM_URL, port 11434).
|
|
# OLLAMA_URL is retained for embeddings/RAG compatibility.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
x-common-env: &common-env
|
|
LLM_URL: "${LLM_URL:-http://host.docker.internal:11434}"
|
|
LLM_MODEL: "${LLM_MODEL:-qwen-14b-awq}"
|
|
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_TOKEN: "${ADMIN_TOKEN:-kxkm}"
|
|
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}"
|
|
CHAT_PAUSED: "${CHAT_PAUSED:-0}"
|
|
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
|
|
- /home/kxkm/openDAW/packages/app/studio/dist:/app/daw:ro
|
|
environment:
|
|
LLM_URL: "${LLM_URL:-http://localhost:11434}"
|
|
LLM_MODEL: "${LLM_MODEL:-qwen-14b-awq}"
|
|
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"
|
|
DAW_DIST_PATH: "/app/daw"
|
|
ADMIN_BOOTSTRAP_TOKEN: "${ADMIN_BOOTSTRAP_TOKEN:-}"
|
|
ADMIN_TOKEN: "${ADMIN_TOKEN:-kxkm}"
|
|
ADMIN_ALLOWED_SUBNETS: "${ADMIN_ALLOWED_SUBNETS:-}"
|
|
TTS_ENABLED: "1"
|
|
KOKORO_URL: "http://127.0.0.1:9201"
|
|
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"
|
|
DOCLING_URL: "http://localhost:9400"
|
|
RERANKER_URL: "http://localhost:9500"
|
|
RAG_CHUNK_SIZE: "${RAG_CHUNK_SIZE:-500}"
|
|
RAG_MIN_SIMILARITY: "${RAG_MIN_SIMILARITY:-0.3}"
|
|
RAG_MAX_RESULTS: "${RAG_MAX_RESULTS:-3}"
|
|
RAG_EMBEDDING_MODEL: "${RAG_EMBEDDING_MODEL:-nomic-embed-text}"
|
|
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:
|
|
LLM_URL: "${LLM_URL:-http://localhost:11434}"
|
|
LLM_MODEL: "${LLM_MODEL:-qwen-14b-awq}"
|
|
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-compatible embeddings backend (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
|
|
- /home/kxkm/openDAW/packages/app/studio/dist:/app/daw:ro
|
|
depends_on:
|
|
chatterbox:
|
|
condition: service_healthy
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Docling Serve — Document parsing (PDF, DOCX, PPTX → structured text)
|
|
# API: POST /v1/convert/source
|
|
# UI: http://localhost:9400/ui (if DOCLING_SERVE_ENABLE_UI=1)
|
|
# -------------------------------------------------------------------------
|
|
docling:
|
|
image: ghcr.io/docling-project/docling-serve:latest
|
|
restart: unless-stopped
|
|
profiles: [v2]
|
|
ports:
|
|
- "9400:5001"
|
|
environment:
|
|
DOCLING_SERVE_ENABLE_UI: "1"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:5001/health')\""]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 120s
|
|
|
|
# -------------------------------------------------------------------------
|
|
# LightRAG — Graph RAG server
|
|
# 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: "${OLLAMA_URL:-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
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Prometheus — metrics collection
|
|
# Usage: docker compose --profile monitoring up -d
|
|
# -------------------------------------------------------------------------
|
|
prometheus:
|
|
image: prom/prometheus:latest
|
|
restart: unless-stopped
|
|
profiles: [monitoring]
|
|
extra_hosts: *extra-hosts
|
|
ports:
|
|
- "9090:9090"
|
|
volumes:
|
|
- ./ops/monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
|
- prometheus-data:/prometheus
|
|
command:
|
|
- "--config.file=/etc/prometheus/prometheus.yml"
|
|
- "--storage.tsdb.retention.time=30d"
|
|
- "--web.enable-lifecycle"
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Grafana — dashboards & visualization
|
|
# Usage: docker compose --profile monitoring up -d
|
|
# Default login: admin / admin
|
|
# -------------------------------------------------------------------------
|
|
grafana:
|
|
image: grafana/grafana:latest
|
|
restart: unless-stopped
|
|
profiles: [monitoring]
|
|
ports:
|
|
- "3000:3000"
|
|
environment:
|
|
GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_PASSWORD:-kxkm}"
|
|
GF_USERS_ALLOW_SIGN_UP: "false"
|
|
volumes:
|
|
- grafana-data:/var/lib/grafana
|
|
- ./ops/monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
|
|
- ./ops/monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
|
|
depends_on:
|
|
- prometheus
|
|
|
|
# --- Text Embeddings Inference (dedicated embedding server) -----------------
|
|
# Usage: docker compose --profile embeddings up -d
|
|
# Exposes OpenAI-compatible /v1/embeddings on port 9500
|
|
# -------------------------------------------------------------------------
|
|
tei:
|
|
image: ghcr.io/huggingface/text-embeddings-inference:cpu-1.6
|
|
container_name: kxkm-tei
|
|
restart: unless-stopped
|
|
profiles: [v2, embeddings]
|
|
ports:
|
|
- "9500:80"
|
|
volumes:
|
|
- tei-models:/data
|
|
command: --model-id BAAI/bge-m3 --port 80
|
|
|
|
volumes:
|
|
app-data:
|
|
pg-data:
|
|
ollama-data:
|
|
tei-models:
|
|
prometheus-data:
|
|
grafana-data:
|