d2fed6087a
- 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.
317 lines
11 KiB
YAML
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:
|