Files
javis_bot/docs/DEPLOY.md
javis-bot bb60c4db3b feat(tts): default to offline GPU MeloTTS with brain-role worker
Switch the default TTS engine from online Edge TTS to offline GPU
MeloTTS (privacy-first: reply text never leaves the host). Adds a
supervisord melo-worker program that runs in full/bot/brain roles and
synthesises Korean on the GPU (MELO_DEVICE=cuda, MELO_SPEED=1.5), a
baked /opt/melo py3.11 venv layer in the Dockerfile, and compose/env
defaults. Edge TTS stays available as an opt-in online fallback via
TTS_ENGINE=edge.

Verified on the RTX 5050 (Blackwell sm_120): melo-worker logs
"ready (lang=KR speed=1.5 device=cuda)", /health returns ok, and a
/synth round-trip returns a 177KB WAV.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-22 20:39:54 +09:00

162 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deployment layouts
One image, three roles (`JARVIS_ROLE`), selected in `.env`. GPU is added per OS
via a compose override picked with `COMPOSE_FILE`.
> `COMPOSE_FILE`'s file separator is OS-specific: Linux/macOS use `:`, Windows
> uses `;` (a colon collides with the `C:` drive letter). Using `:` on Windows
> yields `... The system cannot find the file specified`. If in doubt, leave
> `COMPOSE_FILE` unset and pass the files explicitly:
> `docker compose -f docker-compose.yml -f docker-compose.gpu-windows.yml up -d`.
## A. All-in-one (single machine)
Everything (desktop + Chrome + bridge + bot + TTS) in one container.
```
# .env
JARVIS_ROLE=full
COMPOSE_FILE=docker-compose.yml:docker-compose.gpu-linux.yml # Ubuntu/macOS (":" )
# COMPOSE_FILE=docker-compose.yml;docker-compose.gpu-windows.yml # Windows 11 (";" )
DISCORD_SELFBOT_TOKEN=...
DISCORD_GUILD_ID=...
docker compose up -d # Ollama + javis (COMPOSE_FILE adds GPU)
```
## B. Split: browser host (LAN) + bot on your PC
The on-screen Chrome, real mouse/keyboard (xdotool) and screen live on the
**browser host**. Your PC runs the **bot** and drives that browser over the
internal network — no auth (internal only).
### Browser host (the LAN machine that shows Chrome, e.g. 192.168.10.5)
```
# .env
JARVIS_ROLE=browser
CDP_BIND=0.0.0.0
BROWSER_CONTROL_BIND=0.0.0.0
CDP_PUBLISH_BIND=0.0.0.0
# no GPU needed → leave COMPOSE_FILE unset (base compose only)
docker compose up -d javis # desktop + Chrome + control-server (port 8777)
```
Watch it on this machines VNC (`localhost:5901`) / noVNC (`localhost:6080`).
### Bot host (your PC — Ubuntu or Windows 11)
```
# .env
JARVIS_ROLE=bot
BROWSER_CONTROL_URL=http://192.168.10.5:8777 # the browser host's LAN IP
COMPOSE_FILE=docker-compose.yml:docker-compose.gpu-linux.yml # Ubuntu/macOS (":" )
# COMPOSE_FILE=docker-compose.yml;docker-compose.gpu-windows.yml # Windows 11 (";" )
DISCORD_SELFBOT_TOKEN=...
DISCORD_GUILD_ID=...
docker compose up -d # bot + bridge + TTS + Ollama (GPU per OS)
```
The bots `controlBrowser` tool posts commands to `BROWSER_CONTROL_URL`, so
"네이버에서 X 검색", "구글로 돌아가" etc. drive the **browser hosts** Chrome with real
human-style input (visible on its VNC).
### External Ollama (LLM on a separate host)
To run Ollama on a different LAN machine (e.g. a dedicated GPU host at
`192.168.10.9`) instead of inside this stack:
```
# .env on the app host
OLLAMA_BASE_URL=http://192.168.10.9:11434
```
Then start the app WITHOUT the in-stack LLM services: `docker compose up -d javis`
(do not `docker compose up -d`, which would also start `ollama`/`ollama-init`).
On the LLM host, make Ollama listen on the LAN (`OLLAMA_HOST=0.0.0.0:11434`) and
pull the models there (`ollama pull <chat>`, `<intent>`, `<embed>`). The compose
respects `OLLAMA_BASE_URL`, defaulting to the in-stack `ollama` service when unset.
## C. GPU split: brain host (.9, GPU) + app host (.5, no GPU)
All the GPU-heavy compute (Whisper STT, the Ollama LLM, and TTS) runs on the
**brain host** (a GPU box, e.g. `192.168.10.9`). The **app host** (no GPU, e.g.
`192.168.10.5`) runs the Discord bot and the on-screen browser, and offloads
every STT/LLM/TTS call to the brain over the LAN.
Flow: the bot (app host) POSTs captured voice to the brain's bridge; the brain
runs STT -> LLM -> TTS on the GPU and returns the reply audio; when the LLM
drives the browser, the brain's `controlBrowser` tool POSTs to the app host's
control-server (port 8777), so Chrome moves on the app host's screen.
### Brain host (.9 — GPU: STT + LLM + TTS)
```
# .env
JARVIS_ROLE=brain
BRIDGE_HOST=0.0.0.0 # bridge listens on the LAN
SETTINGS_PUBLISH_BIND=0.0.0.0 # publish bridge port 8765 to the LAN
OLLAMA_BASE_URL=http://192.168.10.9:11434 # Ollama on this host (bind 0.0.0.0)
OLLAMA_CHAT_MODEL=qwen2.5:7b
WHISPER_DEVICE=cuda # STT on the GPU
WHISPER_COMPUTE_TYPE=float16
TTS_ENGINE=melo # GPU MeloTTS (offline, privacy-first)
MELO_DEVICE=cuda # TTS on the GPU (melo-worker)
BROWSER_CONTROL_URL=http://192.168.10.5:8777 # drive the app host's Chrome
COMPOSE_FILE=docker-compose.yml:docker-compose.gpu-linux.yml # GPU (":" on Linux)
# STT (Whisper), LLM (Ollama) and TTS (MeloTTS) all run on this host's GPU.
# Ollama listens on 0.0.0.0:11434 (systemd or a container).
docker compose up -d javis # brain bridge + melo-worker only (no bot, no desktop)
```
### App host (.5 — bot + browser, no GPU)
```
# .env
JARVIS_ROLE=app
BRIDGE_URL=http://192.168.10.9:8765 # offload STT/LLM/TTS to the brain
CDP_PUBLISH_BIND=0.0.0.0 # publish control-server 8777 to the LAN
STREAM_HW=0 # no NVENC on a no-GPU host
DISCORD_SELFBOT_TOKEN=...
DISCORD_GUILD_ID=...
# no GPU → leave COMPOSE_FILE unset (base compose only)
docker compose up -d javis # bot + desktop + Chrome + control-server
```
The `brain` and `app` roles are backward-compatible additions to `full`/`browser`
/`bot`; see `docker/run-if-role.sh`.
## Windows 11 notes
- Install the NVIDIA driver on Windows and enable GPU in Docker Desktop
(Settings → Resources → WSL Integration). Use the `gpu-windows.yml` override.
- Paths: named volumes are cross-platform. The Gemini OAuth login (for
`GEMINI_AUTH=oauth`) is bind-mounted from the project-local `./docker/gemini-oauth`
into the container's `~/.gemini`. A project-relative path is used so it resolves
the same on Windows Docker Desktop and Linux (`${HOME}` is often unset when
compose runs from PowerShell/cmd). Seed it once from a machine with a browser and
the logged-in Gemini CLI (`npm i -g @google/gemini-cli`, then `gemini` ->
"Sign in with Google"), copying the login state:
(Note: as of 2026-06 Google blocks personal Google accounts on this CLI login
with "This client is no longer supported for Gemini Code Assist for
individuals". Workspace/org accounts may still work; personal accounts should
use `GEMINI_AUTH=apikey` with a key from https://aistudio.google.com/app/apikey
instead. Real-time search fail-opens to DDG/Brave/Wikipedia either way.)
`cp -r ~/.gemini/. docker/gemini-oauth/`. The essential file is `oauth_creds.json`
(it holds the refresh token; `GOOGLE_GENAI_USE_GCA=true` forces OAuth, so that is
the file the startup readiness check looks for) - copying the whole dir simply also
carries the cached account/settings. To reuse an existing host login without
copying, set `GEMINI_OAUTH_DIR=~/.gemini` in `.env`. If unseeded, real-time search
fail-opens to DDG/Brave and the container logs a `🔑` warning on startup.
## Known limitation
Discord Go-Live broadcast of the **browser host's** screen from a **remote** bot
is not supported (the bot's WebRTC screen capture is local to the bot machine).
Use the browser host's VNC to view it. A full remote-broadcast path is separate,
larger work.