feat: add tts-model to CI workflow and Dockerfile, include model files
All checks were successful
CI / changes (push) Successful in 19s
CI / test (push) Successful in 23s
CI / build-ai-gateway (push) Has been skipped
CI / build-ha-gateway (push) Has been skipped
CI / build-discord-bot (push) Has been skipped
CI / build-tts-gateway (push) Successful in 54s
CI / build-tts-sidecar (push) Has been skipped
CI / build-tts-model (push) Successful in 39s
All checks were successful
CI / changes (push) Successful in 19s
CI / test (push) Successful in 23s
CI / build-ai-gateway (push) Has been skipped
CI / build-ha-gateway (push) Has been skipped
CI / build-discord-bot (push) Has been skipped
CI / build-tts-gateway (push) Successful in 54s
CI / build-tts-sidecar (push) Has been skipped
CI / build-tts-model (push) Successful in 39s
This commit is contained in:
parent
0d58e46740
commit
7d9a75a3e7
@ -30,6 +30,7 @@ jobs:
|
|||||||
discord-bot: ${{ steps.filter.outputs.discord-bot }}
|
discord-bot: ${{ steps.filter.outputs.discord-bot }}
|
||||||
tts-gateway: ${{ steps.filter.outputs.tts-gateway }}
|
tts-gateway: ${{ steps.filter.outputs.tts-gateway }}
|
||||||
tts-sidecar: ${{ steps.filter.outputs.tts-sidecar }}
|
tts-sidecar: ${{ steps.filter.outputs.tts-sidecar }}
|
||||||
|
tts-model: ${{ steps.filter.outputs.tts-model }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
@ -64,8 +65,13 @@ jobs:
|
|||||||
- 'go.work.sum'
|
- 'go.work.sum'
|
||||||
- 'tts-gateway/**'
|
- 'tts-gateway/**'
|
||||||
- '!tts-gateway/sidecar/**'
|
- '!tts-gateway/sidecar/**'
|
||||||
|
- '!tts-gateway/model/**'
|
||||||
tts-sidecar:
|
tts-sidecar:
|
||||||
- 'tts-gateway/sidecar/**'
|
- 'tts-gateway/sidecar/**'
|
||||||
|
# Not a Go module - just the committed checkpoint/hparams + a Dockerfile that
|
||||||
|
# copies them in, so it doesn't need the gen/go.work paths the Go images do.
|
||||||
|
tts-model:
|
||||||
|
- 'tts-gateway/model/**'
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -241,3 +247,30 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
${{ env.IMAGE_PREFIX }}/tts-sidecar:${{ github.sha }}
|
${{ env.IMAGE_PREFIX }}/tts-sidecar:${{ github.sha }}
|
||||||
${{ env.IMAGE_PREFIX }}/tts-sidecar:latest
|
${{ env.IMAGE_PREFIX }}/tts-sidecar:latest
|
||||||
|
|
||||||
|
build-tts-model:
|
||||||
|
needs: [test, changes]
|
||||||
|
if: github.ref == 'refs/heads/main' && needs.changes.outputs.tts-model == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
|
- uses: docker/login-action@v4
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ secrets.REGISTRY_USER }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
# Just a busybox base + COPY of the committed checkpoint/hparams - no build steps
|
||||||
|
# worth caching, and this only runs when tts-gateway/model/** actually changed anyway.
|
||||||
|
- uses: docker/build-push-action@v7
|
||||||
|
with:
|
||||||
|
context: tts-gateway/model
|
||||||
|
file: tts-gateway/model/Dockerfile
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE_PREFIX }}/tts-model:${{ github.sha }}
|
||||||
|
${{ env.IMAGE_PREFIX }}/tts-model:latest
|
||||||
|
|||||||
13
CLAUDE.md
13
CLAUDE.md
@ -175,12 +175,11 @@ rather than trusting this description to stay accurate):
|
|||||||
default `RollingUpdate` — nik-gpu only has one allocatable GPU, so a rolling update would
|
default `RollingUpdate` — nik-gpu only has one allocatable GPU, so a rolling update would
|
||||||
deadlock waiting for a GPU still held by the pod it's replacing.
|
deadlock waiting for a GPU still held by the pod it's replacing.
|
||||||
- Model artifact distribution (checkpoint + hparams) is an `emptyDir` volume populated at pod
|
- Model artifact distribution (checkpoint + hparams) is an `emptyDir` volume populated at pod
|
||||||
start by an `initContainers` entry (`model-init`) that copies from a small, versioned image
|
start by an `initContainers` entry (`model-init`) that copies from `gitea.nik4nao.com/nik/tts-model:latest`
|
||||||
(`gitea.nik4nao.com/nik/tts-model:<tag>`, built from `tts-gateway/model/Dockerfile`) — not a
|
— not a raw `hostPath` into the node's disk anymore. That image is built from
|
||||||
raw `hostPath` into the node's disk anymore. That model image still can't be built by CI (the
|
`tts-gateway/model/Dockerfile`, whose checkpoint/hparams are committed directly to git (a
|
||||||
checkpoint isn't committed to git, ~455MB, and only ever existed on nik-gpu's local disk) — it's
|
deliberate exception to not committing large binaries — see that directory) and built/pushed
|
||||||
built and pushed manually, on nik-gpu, whenever the checkpoint/config change. See
|
by CI's `build-tts-model` job like the other four images, path-filtered on `tts-gateway/model/**`.
|
||||||
`tts-gateway/README.md` for the exact commands.
|
|
||||||
- mTLS (`TLS_DIR`) is currently commented out on `tts-gateway`, for plaintext `grpcurl` testing
|
- mTLS (`TLS_DIR`) is currently commented out on `tts-gateway`, for plaintext `grpcurl` testing
|
||||||
from outside the cluster during initial rollout — a live TODO, not a permanent decision, unlike
|
from outside the cluster during initial rollout — a live TODO, not a permanent decision, unlike
|
||||||
every other service here which assumes mTLS-or-trusted-network as its only access boundary (see
|
every other service here which assumes mTLS-or-trusted-network as its only access boundary (see
|
||||||
@ -188,6 +187,6 @@ rather than trusting this description to stay accurate):
|
|||||||
|
|
||||||
## CI
|
## CI
|
||||||
|
|
||||||
`.gitea/workflows/ci.yaml` always runs `go vet`/`go test` for all five modules (`gen`, `ai-gateway`, `ha-gateway`, `discord-bot`, `tts-gateway`) on every push/PR. On pushes to `main`, a `changes` job (`dorny/paths-filter`) determines which of the five images actually need rebuilding based on which paths changed, so an edit scoped to one service doesn't rebuild (and re-push) all of them — `gen/`, `go.work`, and `go.work.sum` count as shared and mark every Go-based image as changed, since a dependency bump there can affect all of them. Each `build-*` job is gated on that output and, when it runs, uses `docker/build-push-action`'s registry-based cache (`cache-from`/`cache-to: type=registry,ref=.../<image>:buildcache`) so unchanged Docker layers (e.g. `go mod download`, `apt-get`/`pip install`) don't get redone on every run — the *first* run after adding this has nothing to pull from and builds fully fresh, subsequent ones should be much faster. `tts-sidecar` (the Python/CUDA inference sidecar under `tts-gateway/sidecar/`) is a large ~13GB image; it builds fine on a generic runner since only *running* it needs a GPU, not building it.
|
`.gitea/workflows/ci.yaml` always runs `go vet`/`go test` for all five Go modules (`gen`, `ai-gateway`, `ha-gateway`, `discord-bot`, `tts-gateway`) on every push/PR. On pushes to `main`, a `changes` job (`dorny/paths-filter`) determines which of the six images (`ai-gateway`, `ha-gateway`, `discord-bot`, `tts-gateway`, `tts-sidecar`, `tts-model`) actually need rebuilding based on which paths changed, so an edit scoped to one service doesn't rebuild (and re-push) all of them — `gen/`, `go.work`, and `go.work.sum` count as shared and mark every Go-based image as changed, since a dependency bump there can affect all of them (`tts-model` isn't a Go module, so it's untouched by that rule — only `tts-gateway/model/**` triggers it). Each `build-*` job is gated on that output and, when it runs, uses `docker/build-push-action`'s registry-based cache (`cache-from`/`cache-to: type=registry,ref=.../<image>:buildcache`) so unchanged Docker layers (e.g. `go mod download`, `apt-get`/`pip install`) don't get redone on every run — the *first* run after adding this has nothing to pull from and builds fully fresh, subsequent ones should be much faster (`build-tts-model` skips this, since a busybox+`COPY` image has no build steps worth caching). `tts-sidecar` (the Python/CUDA inference sidecar under `tts-gateway/sidecar/`) is a large ~13GB image; it builds fine on a generic runner since only *running* it needs a GPU, not building it. `tts-model`'s own build context (`tts-gateway/model/`) is ~455MB (the committed checkpoint) — a deliberate, one-time exception to this repo otherwise keeping large binaries out of git.
|
||||||
|
|
||||||
Note that `ai-gateway/Dockerfile`, `ha-gateway/Dockerfile`, `discord-bot/Dockerfile`, and `tts-gateway/Dockerfile` each `COPY` every other service directory (not just their own) because `go.work` lists all five Go modules as workspace members — Go's workspace-mode module resolution needs every listed directory present in the build context, even ones a given service doesn't otherwise depend on. Adding a new module to `go.work` means adding a matching `COPY` line (both the manifest-only and full-source copies, see below) to the other Dockerfiles too, or their builds break. Each of those four Dockerfiles also copies every module's `go.mod`/`go.sum` first and runs `go mod download` *before* copying full source, so that layer's cache survives source-only edits instead of being invalidated by every commit.
|
Note that `ai-gateway/Dockerfile`, `ha-gateway/Dockerfile`, `discord-bot/Dockerfile`, and `tts-gateway/Dockerfile` each `COPY` every other service directory (not just their own) because `go.work` lists all five Go modules as workspace members — Go's workspace-mode module resolution needs every listed directory present in the build context, even ones a given service doesn't otherwise depend on. Adding a new module to `go.work` means adding a matching `COPY` line (both the manifest-only and full-source copies, see below) to the other Dockerfiles too, or their builds break. Each of those four Dockerfiles also copies every module's `go.mod`/`go.sum` first and runs `go mod download` *before* copying full source, so that layer's cache survives source-only edits instead of being invalidated by every commit.
|
||||||
|
|||||||
@ -172,9 +172,12 @@ WAV writer, `ffmpeg` transcode adapter, full `Synthesize` RPC wiring, speaker-no
|
|||||||
|
|
||||||
**Phase 5 — Containerize & deploy to nik-gpu** — **done.** Deployed and confirmed working in
|
**Phase 5 — Containerize & deploy to nik-gpu** — **done.** Deployed and confirmed working in
|
||||||
production; see `handoff.md` for status. Model artifact distribution is resolved as a small
|
production; see `handoff.md` for status. Model artifact distribution is resolved as a small
|
||||||
versioned image (`tts-gateway/model/Dockerfile`) copied into a shared volume by a k8s
|
image (`tts-gateway/model/Dockerfile`) copied into a shared volume by a k8s `initContainer` —
|
||||||
`initContainer`, built manually on nik-gpu (the checkpoint never existed anywhere CI can reach) —
|
the checkpoint/hparams are committed directly into `tts-gateway/model/` (a deliberate one-time
|
||||||
see `tts-gateway/README.md`'s "Model artifact distribution (production)" section.
|
exception to not committing large binaries) and CI builds/pushes that image like every other one
|
||||||
|
here (`build-tts-model` in `.gitea/workflows/ci.yaml`). An initial attempt at building it manually
|
||||||
|
on nik-gpu instead hit a registry-auth wall there, which is what prompted committing the
|
||||||
|
checkpoint and automating the build instead — see `tts-gateway/README.md`.
|
||||||
|
|
||||||
**Phase 6 — Client integration** — **done.** `discord-bot` registers a `/speak` command calling
|
**Phase 6 — Client integration** — **done.** `discord-bot` registers a `/speak` command calling
|
||||||
this gateway via the same secondary-adapter + gRPC-client pattern used for its
|
this gateway via the same secondary-adapter + gRPC-client pattern used for its
|
||||||
|
|||||||
14
handoff.md
14
handoff.md
@ -6,11 +6,15 @@ inference, real playable audio). This file is a punch list for picking the remai
|
|||||||
fresh session — it doesn't re-explain things that are already documented elsewhere; it points to
|
fresh session — it doesn't re-explain things that are already documented elsewhere; it points to
|
||||||
where.
|
where.
|
||||||
|
|
||||||
**Update (later same day):** items 1 and 3 below are now done — model artifact distribution is a
|
**Update (later same day):** items 1 and 3 below are now done. Model artifact distribution ended
|
||||||
versioned image + k8s `initContainer` (see `tts-gateway/README.md`), and `discord-bot` has a
|
up fully automated, not just "worked around" — after an initial attempt at a manually-built image
|
||||||
working `/speak` command (see `TTS_GATEWAY_PLAN.md` Phase 6). Only item 2 (mTLS) remains, left
|
hit a nik-gpu registry-auth wall, the checkpoint/hparams were committed directly into
|
||||||
untouched per an explicit decision to verify these two first. The two "what's left" entries below
|
`tts-gateway/model/` (a deliberate one-time exception to not committing large binaries) and CI now
|
||||||
are kept as-written for their historical reasoning/decision trail rather than rewritten in place.
|
builds+pushes the `tts-model` image itself (`build-tts-model` in `.gitea/workflows/ci.yaml`), same
|
||||||
|
as every other image here — no manual nik-gpu step at all anymore. `discord-bot` has a working
|
||||||
|
`/speak` command (see `TTS_GATEWAY_PLAN.md` Phase 6). Only item 2 (mTLS) remains, left untouched
|
||||||
|
per an explicit decision to verify these two first. The two "what's left" entries below are kept
|
||||||
|
as-written for their historical reasoning/decision trail rather than rewritten in place.
|
||||||
|
|
||||||
## Where things stand
|
## Where things stand
|
||||||
|
|
||||||
|
|||||||
@ -66,34 +66,17 @@ The server also registers gRPC health checks and reflection.
|
|||||||
| `CHECKPOINT_PATH` | `/models/G_790000.pth` | Path to the model checkpoint |
|
| `CHECKPOINT_PATH` | `/models/G_790000.pth` | Path to the model checkpoint |
|
||||||
| `PORT` | `50054` | HTTP listen port |
|
| `PORT` | `50054` | HTTP listen port |
|
||||||
|
|
||||||
Neither the checkpoint nor the hparams file is committed to git - both must
|
The checkpoint and hparams file live directly in `tts-gateway/model/` (a
|
||||||
be supplied at runtime (see below). In production, they're baked into a
|
deliberate, one-time exception to not committing large binaries - this asset
|
||||||
small versioned image (`tts-gateway/model/Dockerfile`) that a k8s
|
never changes and there's no other artifact store in play). CI builds and
|
||||||
`initContainer` copies into a shared volume before `tts-sidecar` starts -
|
pushes `gitea.nik4nao.com/nik/tts-model` from there exactly like the other
|
||||||
see "Model artifact distribution (production)" below. For local/manual
|
four images (`build-tts-model` in `.gitea/workflows/ci.yaml`), path-filtered
|
||||||
testing on nik-gpu, a plain bind mount (as shown below) is simpler.
|
so it only rebuilds when `tts-gateway/model/**` actually changes. In
|
||||||
|
production, a k8s `initContainer` copies that image's files into a shared
|
||||||
### Model artifact distribution (production)
|
volume before `tts-sidecar` starts - see
|
||||||
|
`~/repo/homelab/manifests/home-services/tts-gateway.yaml`'s `model-init`
|
||||||
CI can never build the model image - the checkpoint (~455MB) and hparams
|
container. To update the checkpoint: replace `tts-gateway/model/G_790000.pth`
|
||||||
aren't committed to git, and nik-gpu's local disk at `/data/tts-gateway` is
|
and/or `uma.json`, commit, push to `main` - CI handles the rest.
|
||||||
the only place they exist. Build and push it manually, directly on nik-gpu
|
|
||||||
(not via `docker --context nik-gpu` from elsewhere - the build context is
|
|
||||||
gathered client-side, so it would look for `/data/tts-gateway` on the wrong
|
|
||||||
machine), whenever the checkpoint or config change:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh nik-gpu
|
|
||||||
docker build -f ~/repo/home-service/tts-gateway/model/Dockerfile \
|
|
||||||
-t gitea.nik4nao.com/nik/tts-model:g790000-v1 /data/tts-gateway
|
|
||||||
docker push gitea.nik4nao.com/nik/tts-model:g790000-v1
|
|
||||||
```
|
|
||||||
|
|
||||||
Bump the tag (`v2`, `v3`, ...) rather than overwriting one - non-`:latest`
|
|
||||||
tags default to `imagePullPolicy: IfNotPresent`, so a node that already
|
|
||||||
pulled a tag won't re-pull an overwritten one. The k8s Deployment
|
|
||||||
(`~/repo/homelab/manifests/home-services/tts-gateway.yaml`) references the
|
|
||||||
tag explicitly in its `model-init` init container.
|
|
||||||
|
|
||||||
## Running And Testing On nik-gpu
|
## Running And Testing On nik-gpu
|
||||||
|
|
||||||
@ -213,8 +196,8 @@ sidecar/ # Python/libtorch inference servic
|
|||||||
- Concurrency is unbounded on the Go side, but the sidecar's model forward
|
- Concurrency is unbounded on the Go side, but the sidecar's model forward
|
||||||
pass is inherently single-threaded per GPU; heavy concurrent load will
|
pass is inherently single-threaded per GPU; heavy concurrent load will
|
||||||
just queue at the sidecar.
|
just queue at the sidecar.
|
||||||
- Model artifact distribution to production is a manually-built-and-pushed
|
- The model checkpoint is committed directly in `tts-gateway/model/` rather
|
||||||
versioned image (see above), not something CI can automate - the
|
than fetched from an external artifact store - a deliberate exception
|
||||||
checkpoint only ever existed on nik-gpu's local disk.
|
given how rarely it changes, not a pattern to repeat for other assets.
|
||||||
- `discord-bot`'s `/speak` command calls this service and plays the result
|
- `discord-bot`'s `/speak` command calls this service and plays the result
|
||||||
in a Discord voice channel (`discord-bot/internal/adapters/primary/discord/voice.go`).
|
in a Discord voice channel (`discord-bot/internal/adapters/primary/discord/voice.go`).
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
# One-time (or only-when-the-checkpoint-changes) manually built and pushed by a human on
|
# Built and pushed by CI (see .gitea/workflows/ci.yaml's build-tts-model job), same as the
|
||||||
# nik-gpu - CI can never build this: the checkpoint and hparams aren't committed to git
|
# other four images - the checkpoint/hparams are committed directly in this directory (a
|
||||||
# (~455MB, and nik-gpu's local disk at /data/tts-gateway is the only place they exist).
|
# deliberate exception to "don't commit large binaries", made because this asset never
|
||||||
# Build CONTEXT must be that directory, not a checkout of this repo - see
|
# changes and there's no other artifact store in play). Build context is this directory.
|
||||||
# tts-gateway/README.md for the exact commands.
|
|
||||||
FROM busybox:1.36
|
FROM busybox:1.36
|
||||||
COPY G_790000.pth uma.json /models/
|
COPY G_790000.pth uma.json /models/
|
||||||
|
|||||||
BIN
tts-gateway/model/G_790000.pth
Normal file
BIN
tts-gateway/model/G_790000.pth
Normal file
Binary file not shown.
53
tts-gateway/model/uma.json
Normal file
53
tts-gateway/model/uma.json
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"train": {
|
||||||
|
"log_interval": 200,
|
||||||
|
"eval_interval": 1000,
|
||||||
|
"seed": 1234,
|
||||||
|
"epochs": 10000,
|
||||||
|
"learning_rate": 2e-4,
|
||||||
|
"betas": [0.8, 0.99],
|
||||||
|
"eps": 1e-9,
|
||||||
|
"batch_size": 12,
|
||||||
|
"fp16_run": true,
|
||||||
|
"lr_decay": 0.999875,
|
||||||
|
"segment_size": 8192,
|
||||||
|
"init_lr_ratio": 1,
|
||||||
|
"warmup_epochs": 0,
|
||||||
|
"c_mel": 45,
|
||||||
|
"c_kl": 1.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"training_files":"filelists/uma_text_train.txt.cleaned",
|
||||||
|
"validation_files":"filelists/uma_text_val.txt.cleaned",
|
||||||
|
"text_cleaners":["japanese_cleaners"],
|
||||||
|
"max_wav_value": 32768.0,
|
||||||
|
"sampling_rate": 22050,
|
||||||
|
"filter_length": 1024,
|
||||||
|
"hop_length": 256,
|
||||||
|
"win_length": 1024,
|
||||||
|
"n_mel_channels": 80,
|
||||||
|
"mel_fmin": 0.0,
|
||||||
|
"mel_fmax": null,
|
||||||
|
"add_blank": true,
|
||||||
|
"n_speakers": 92,
|
||||||
|
"cleaned_text": true
|
||||||
|
},
|
||||||
|
"model": {
|
||||||
|
"inter_channels": 192,
|
||||||
|
"hidden_channels": 192,
|
||||||
|
"filter_channels": 768,
|
||||||
|
"n_heads": 2,
|
||||||
|
"n_layers": 6,
|
||||||
|
"kernel_size": 3,
|
||||||
|
"p_dropout": 0.1,
|
||||||
|
"resblock": "1",
|
||||||
|
"resblock_kernel_sizes": [3,7,11],
|
||||||
|
"resblock_dilation_sizes": [[1,3,5], [1,3,5], [1,3,5]],
|
||||||
|
"upsample_rates": [8,8,2,2],
|
||||||
|
"upsample_initial_channel": 512,
|
||||||
|
"upsample_kernel_sizes": [16,16,4,4],
|
||||||
|
"n_layers_q": 3,
|
||||||
|
"use_spectral_norm": false,
|
||||||
|
"gin_channels": 256
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user