mirror of
https://github.com/digitalsamba/claude-code-video-toolkit.git
synced 2026-09-18 19:41:13 +08:00
fd24349fe1
EchoMimicV3 was added earlier in this same unreleased cycle and never reached a tagged release, so this removes it rather than deprecating it — no public API changes. Identity drift is why. On a controlled A/B (same photo, same 80s audio, same 544x736) it fell to 50% of frame-zero sharpness by 70s, glasses dissolving around 55s and no recognisable face by 67s, where SoulX-FlashHead held 97%. The failure is absorbing rather than gradual — each segment re-anchors on the previous segment's output, so one bad segment poisons everything after it — and colour correction recovers none of it because the damage is structural. That implied a ~30s render ceiling which shaped the surrounding design. The one capability SoulX lacks is text-prompt and CFG steering, and our own docs already recorded from testing that the prompt is close to inert. Everything else EchoMimicV3 offered, SoulX matches or beats: aspect preservation, motion quality, ~3.7x cheaper, ~3-6x faster. The genuine loss is gesture and upper-body motion, which nothing in the toolkit currently uses. Also in this commit: - Talking head docs, registry entries (tools and modal endpoints), cloud_gpu dispatch, modal-setup, NarratorPiP's comment and both env files repointed to soulx. cloud_gpu also gains a GPU tier for it, which echomimic3 never had -- its jobs silently reported no cost estimate. - The Unreleased Kiro changelog entry is dropped; it shipped in v0.19.0 and was flagged in-file for removal when this section was cut. Findings worth keeping outlived the tool and moved into docs/soulx.md: the warning against scoring talking heads with similarity metrics (two have now misled — one ranked highest a variant with a visible eye defect, the other plateaued straight through a total collapse), the volume-weights rationale, and the image guidelines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
267 lines
11 KiB
Markdown
267 lines
11 KiB
Markdown
# Modal Cloud GPU Setup
|
|
|
|
Modal is the recommended cloud GPU provider for the toolkit's AI tools. It offers $30/month free compute on the Starter plan, fast cold starts, and scale-to-zero billing.
|
|
|
|
> **Fastest path:** Run `/setup` in Claude Code — it handles Modal installation, deployment, and `.env` configuration interactively. This doc is the reference for what `/setup` does under the hood, and for manual setup.
|
|
|
|
## Create a Modal Account
|
|
|
|
1. Go to [modal.com](https://modal.com/) and sign up
|
|
2. Choose the **Starter plan** — $30/month free compute, just requires a payment method
|
|
3. Typical toolkit usage is $1-2/month, well within the free allowance
|
|
4. All apps scale to zero — no charges when idle
|
|
|
|
## Install & Authenticate
|
|
|
|
```bash
|
|
uv sync --extra modal # Installs the Modal CLI into the toolkit's .venv
|
|
uv run modal setup # Opens browser to authenticate, saves token to ~/.modal.toml
|
|
uv run modal app list # Verify it works
|
|
```
|
|
|
|
### Windows prerequisites
|
|
|
|
Two Windows-specific issues will otherwise make the commands in this guide fail in ways
|
|
that look like toolkit bugs.
|
|
|
|
**Set `PYTHONIOENCODING=utf-8` before deploying.** Modal's progress output contains
|
|
characters the default `cp1252` console codec can't encode, so `modal deploy` aborts
|
|
mid-build with `'charmap' codec can't encode characters in position ...`. The build
|
|
itself is fine — only the printing fails. Make it permanent:
|
|
|
|
```powershell
|
|
[Environment]::SetEnvironmentVariable('PYTHONIOENCODING','utf-8','User')
|
|
```
|
|
|
|
Reopen your terminal afterwards so new processes inherit it.
|
|
|
|
**Make sure `python3` isn't the Microsoft Store stub.** A default Windows install puts
|
|
`C:\Users\<you>\AppData\Local\Microsoft\WindowsApps` ahead of your real Python on
|
|
`PATH`. That directory holds zero-byte App Execution Aliases which fail with
|
|
`Permission denied` — and since this toolkit's docs and commands all invoke
|
|
`python3 tools/...`, everything breaks. Check what resolves:
|
|
|
|
```powershell
|
|
(Get-Command python3).Source
|
|
```
|
|
|
|
If it points into `WindowsApps`, either turn off the `python.exe` / `python3.exe`
|
|
aliases in *Settings → Apps → App execution aliases*, or move your Python directory
|
|
ahead of `WindowsApps` in your user `PATH`.
|
|
|
|
**FFmpeg** is not bundled on Windows and several tools need it. If `winget install
|
|
Gyan.FFmpeg` fails with a corrupted-source error, download
|
|
`ffmpeg-release-essentials.zip` from https://www.gyan.dev/ffmpeg/builds/, extract it,
|
|
and add its `bin\` directory to `PATH`.
|
|
|
|
## Deploy Tools
|
|
|
|
Each AI tool has its own Modal app. Deploy only what you need, or deploy all of them — idle apps cost nothing.
|
|
|
|
```bash
|
|
# Speech generation (most commonly used)
|
|
uv run modal deploy docker/modal-qwen3-tts/app.py
|
|
|
|
# Image generation & editing
|
|
uv run modal deploy docker/modal-flux2/app.py
|
|
uv run modal deploy docker/modal-image-edit/app.py
|
|
uv run modal deploy docker/modal-upscale/app.py
|
|
|
|
# Music generation
|
|
uv run modal deploy docker/modal-music-gen/app.py
|
|
|
|
# Video processing
|
|
uv run modal deploy docker/modal-sadtalker/app.py
|
|
uv run modal deploy docker/modal-propainter/app.py
|
|
|
|
# Talking head, diffusion-based. Weights live in a Volume, so populate it FIRST
|
|
# (one-off, ~15GB, ~5 min) or the app deploys with nothing to load.
|
|
uv run modal run docker/modal-soulx/app.py::populate_weights
|
|
uv run modal deploy docker/modal-soulx/app.py
|
|
|
|
# Video generation (see the LTX-2 prerequisites note below)
|
|
uv run modal deploy docker/modal-ltx2/app.py
|
|
```
|
|
|
|
**Deploy them one at a time.** Modal rate-limits `AppCreate`; launching several
|
|
`modal deploy` calls in parallel makes most of them fail with
|
|
`App create rate limit exceeded`. Image builds happen server-side, so serial
|
|
deploys cost you nothing extra in compute — only wall-clock.
|
|
|
|
**GPU tier gates some tools.** Most apps request an `A10G`, which works on the free
|
|
tier. Two request A100-class GPUs and fail at deploy time with
|
|
`Please add a payment method to use A100-40GB GPU functions` until a card is on the
|
|
account: `image-edit` (A100) and `ltx2` (A100-80GB). The image still gets built and
|
|
cached before that error, so re-deploying after adding a payment method is fast.
|
|
`ltx2` additionally needs a Modal secret named `huggingface-token` for its gated
|
|
weights — it fails on the missing secret *before* it ever reaches the GPU check.
|
|
|
|
Each deploy prints an endpoint URL like:
|
|
```
|
|
https://yourname--video-toolkit-qwen3-tts-ttsengine-generate.modal.run
|
|
```
|
|
|
|
Save each URL to your `.env` file:
|
|
|
|
```bash
|
|
# Add to .env (replace with your actual URLs from deploy output)
|
|
MODAL_QWEN3_TTS_ENDPOINT_URL=https://yourname--video-toolkit-qwen3-tts-...modal.run
|
|
MODAL_FLUX2_ENDPOINT_URL=https://yourname--video-toolkit-flux2-...modal.run
|
|
MODAL_IMAGE_EDIT_ENDPOINT_URL=https://yourname--video-toolkit-image-edit-...modal.run
|
|
MODAL_UPSCALE_ENDPOINT_URL=https://yourname--video-toolkit-upscale-...modal.run
|
|
MODAL_MUSIC_GEN_ENDPOINT_URL=https://yourname--video-toolkit-music-gen-...modal.run
|
|
MODAL_SADTALKER_ENDPOINT_URL=https://yourname--video-toolkit-sadtalker-...modal.run
|
|
MODAL_SOULX_ENDPOINT_URL=https://yourname--video-toolkit-soulx-...modal.run
|
|
MODAL_DEWATERMARK_ENDPOINT_URL=https://yourname--video-toolkit-dewatermark-...modal.run
|
|
MODAL_LTX2_ENDPOINT_URL=https://yourname--video-toolkit-ltx2-...modal.run
|
|
```
|
|
|
|
When an endpoint label exceeds Modal's length limit, Modal truncates it and appends a
|
|
hash — `dewatermark` typically becomes `...-dewatermark-de-3e6418.modal.run`. Copy the
|
|
URL Modal actually prints; don't reconstruct it from the pattern. Note that the deploy
|
|
log wraps long URLs across lines, so check for a `(label truncated)` marker and rejoin
|
|
the pieces.
|
|
|
|
> **Tip:** `/setup` automates this — it runs each deploy, parses the URL, and writes it to `.env` for you.
|
|
|
|
## Cloudflare R2 (Recommended)
|
|
|
|
R2 is free file storage that bridges your local machine and cloud GPUs. Without it, tools fall back to free upload services (slower, less reliable).
|
|
|
|
**R2 free tier:** 10GB storage, 10 million operations/month, zero egress fees.
|
|
|
|
See the R2 section in `/setup`, or configure manually:
|
|
|
|
1. Sign up at [dash.cloudflare.com](https://dash.cloudflare.com/)
|
|
2. Go to R2 Object Storage → Create bucket (name it `video-toolkit`)
|
|
3. Create an API token: R2 → Manage R2 API Tokens → Object Read & Write
|
|
4. Add to `.env`:
|
|
```
|
|
R2_ACCOUNT_ID=your_account_id
|
|
R2_ACCESS_KEY_ID=your_access_key_id
|
|
R2_SECRET_ACCESS_KEY=your_secret_access_key
|
|
R2_BUCKET_NAME=video-toolkit
|
|
```
|
|
|
|
## Use the Tools
|
|
|
|
All cloud GPU tools accept `--cloud modal`:
|
|
|
|
```bash
|
|
# AI voiceover
|
|
uv run tools/qwen3_tts.py --text "Hello world" --speaker Ryan --output hello.mp3 --cloud modal
|
|
|
|
# AI image generation
|
|
uv run tools/flux2.py --prompt "A sunset over mountains" --output sunset.png --cloud modal
|
|
|
|
# AI image editing
|
|
uv run tools/image_edit.py --input photo.jpg --style cyberpunk --cloud modal
|
|
|
|
# AI upscaling
|
|
uv run tools/upscale.py --input photo.jpg --output photo_4x.png --cloud modal
|
|
|
|
# AI music generation (acemusic cloud API is now default — no Modal needed)
|
|
uv run tools/music_gen.py --preset corporate-bg --duration 60 --output bg.mp3
|
|
# Or use Modal: uv run tools/music_gen.py --preset corporate-bg --duration 60 --output bg.mp3 --cloud modal
|
|
|
|
# Talking head from portrait + audio
|
|
uv run tools/sadtalker.py --image portrait.png --audio voiceover.mp3 --output talking.mp4 --cloud modal
|
|
uv run tools/soulx.py --image portrait.png --audio voiceover.mp3 --output talking.mp4
|
|
|
|
# Watermark removal
|
|
uv run tools/dewatermark.py --input video.mp4 --region 1080,660,195,40 --output clean.mp4 --cloud modal
|
|
```
|
|
|
|
## Tools & Costs
|
|
|
|
| Tool | Backend | Use Case | Est. Cost |
|
|
|------|---------|----------|-----------|
|
|
| `qwen3_tts` | Qwen3-TTS | AI speech generation | ~$0.005-0.02 |
|
|
| `flux2` | FLUX.2 Klein | AI image generation | ~$0.01-0.03 |
|
|
| `image_edit` | Qwen-Image-Edit | AI image editing, style transfer | ~$0.02-0.05 |
|
|
| `upscale` | RealESRGAN | AI image upscaling (2x/4x) | ~$0.005-0.02 |
|
|
| `music_gen` | ACE-Step 1.5 | AI music generation | Free (acemusic) / ~$0.02-0.10 (Modal) |
|
|
| `sadtalker` | SadTalker | Talking head video | ~$0.05-0.30 |
|
|
| `soulx` | SoulX-FlashHead 1.3B Pro | Talking head video, aspect-preserving | ~$0.0024 per second of output |
|
|
| `dewatermark` | ProPainter | AI video inpainting | ~$0.05-0.50 |
|
|
|
|
All apps use A10G GPUs (24GB VRAM) except `image_edit` which uses A100 for its 25GB model.
|
|
|
|
### Weight storage
|
|
|
|
Most apps **bake** their model weights into the image at build time. `soulx` is the
|
|
exception: it keeps its ~15GB in a **Modal Volume**, which is why it needs the one-off
|
|
`populate_weights` run above before its first deploy.
|
|
|
|
The split is measured, not stylistic. Cold start and generation speed are the same either
|
|
way; what differs is rebuild time after a dependency change — measured at 1.8-8.2s on a
|
|
volume against 79-385s baked, because any invalidated layer re-downloads everything below it. Apps that
|
|
still change often earn a volume; settled ones don't need one.
|
|
|
|
**Volumes are optional and free at this scale.** Modal charges $0.09/GiB/month for volume
|
|
storage with **1 TiB/month included free**, so `soulx`'s 14.7GB costs nothing — it is
|
|
~2.6% of the free allowance. There is no bill either way, which is precisely why the choice
|
|
comes down to rebuild speed versus having one self-contained artifact rather than to cost.
|
|
|
|
Every app can be built either way. `soulx` defaults to a volume; the rest bake by
|
|
default. See `docs/soulx.md` for the full
|
|
comparison.
|
|
|
|
## Cold Starts
|
|
|
|
First request after idle triggers a cold start while Modal loads the model:
|
|
|
|
| Tool | Cold Start | Warm Request |
|
|
|------|-----------|--------------|
|
|
| `qwen3_tts` | ~60-90s | ~5-15s |
|
|
| `flux2` | ~25-30s | ~1-3s |
|
|
| `image_edit` | ~5-8min | ~15-20s |
|
|
| `upscale` | ~25-30s | ~3-5s |
|
|
| `music_gen` | ~60-90s | ~10-30s |
|
|
| `sadtalker` | ~45-60s | ~30-60s |
|
|
| `soulx` | ~15s + ~600s first-call torch.compile | 6.4-7.9x realtime |
|
|
| `dewatermark` | ~60-70s | varies by video length |
|
|
|
|
After 60 seconds of no requests, containers scale back to zero. No charges while idle.
|
|
|
|
## Monitoring & Billing
|
|
|
|
```bash
|
|
# Check what's running (Tasks column should be 0 when idle)
|
|
uv run modal app list
|
|
|
|
# Check today's spend
|
|
uv run modal billing report --for today --json
|
|
|
|
# View container logs
|
|
uv run modal app logs video-toolkit-upscale
|
|
|
|
# Verify your setup
|
|
uv run tools/verify_setup.py
|
|
```
|
|
|
|
## Architecture
|
|
|
|
Each tool has its own Modal app (`docker/modal-*/app.py`), deployed independently:
|
|
|
|
- **One app per tool** — independent scaling, GPU assignment, and lifecycle
|
|
- **Web endpoints** — HTTP POST via `@modal.fastapi_endpoint`, no `modal` pip dependency needed on the client
|
|
- **R2 file transfer** — large results upload to Cloudflare R2 (if configured), otherwise base64
|
|
- **Scale to zero** — `scaledown_window=60` means containers shut down after 1 minute idle
|
|
|
|
The client-side abstraction lives in `tools/cloud_gpu.py`, which routes `call_cloud_endpoint()` to either `_call_runpod()` (submit + poll) or `_call_modal()` (synchronous POST).
|
|
|
|
## RunPod (Alternative)
|
|
|
|
RunPod is also supported as a fallback provider. Use `--cloud runpod` on any tool.
|
|
|
|
| Aspect | Modal | RunPod |
|
|
|--------|-------|--------|
|
|
| **Free tier** | $30/mo compute | None (pay-as-you-go) |
|
|
| **Setup** | `modal deploy` | `--setup` flag per tool |
|
|
| **Cold start** | Faster (cached layers) | Slower (Docker pull) |
|
|
| **Invocation** | Synchronous POST | Async submit + poll |
|
|
| **Auth** | Token optional for web endpoints | `RUNPOD_API_KEY` required |
|
|
|
|
See [runpod-setup.md](runpod-setup.md) for RunPod-specific instructions.
|