Audio surface: real Parakeet readiness probe + realtime-pipeline docs + smoke (closes #39, #40) - #41
Conversation
Work issues #39 (Parakeet STT 500s, CUDA-unknown) and #40 (deployed :8080 facade missing /v1/audio/transcriptions) backwards into one converged spec: model-gear takes ownership of the audio stack, wiring Parakeet STT + Magpie TTS + the vendored realtime facade into its compose template and `model fleet up`. Decisions captured: full-stack ownership (both /v1/audio/* routes), cheap model-ready Parakeet healthcheck, restart-clears-stale-CUDA-context runbook. Unblocks reachy-mini-cli's "hey reachy" wake-word. Includes the converged spec + the .devague frame state (evidence trail). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lan) Forward leg from the converged audio-ownership spec. Six file-disjoint, TDD-gated tasks covering all 24 coverage targets: t1 vendor Parakeet server + cheap model-ready probe t2 build Dockerfiles (facade + parakeet) t3 wire the audio stack into the compose template (sole compose owner) t4 bring it up via `model fleet up` t5 docs/realtime-pipeline.md + README Audio I/O + CUDA restart runbook t6 smoke/acceptance harness (openapi, WAV 200, healthcheck flip) Waves: [t1,t5] -> t2 -> t3 -> t4 -> t6 (serial after wave 0 by the compose chokepoint). Risks carried: CUDA root-cause unconfirmed, fleet gating on the GB10 budget, cheap-probe wording reconciliation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… smoke
Implements the three real gaps found after reconnaissance (t2/t3/t4 — the
audio overlay, Dockerfiles, fleet wiring — already existed in
templates/fleet/):
- t1: Parakeet /v1/health/ready now reflects REAL readiness (model loaded +
a trivial CUDA op) instead of returning {"status":"ready"}
unconditionally — the misleading-health bug behind #39. The pure decision
lives in stdlib-only model_gear/realtime/_readiness.py (CI-unit-tested,
8 cases), vendored into the Parakeet build context and COPY'd into the
image so the container imports it without the wheel. (decision c16: a
cheap probe, not a full transcription each interval.)
- t5: docs/realtime-pipeline.md — ownership of the live :8080 facade, fleet
bring-up, the prior drift (#39/#40), health/readiness, and the stale-CUDA
restart runbook.
- t6: scripts/audio-smoke.py — stdlib-only live smoke (openapi lists both
routes; WAV POST -> 200 {text}); reproduces #39's repro. Live run is
GPU-box-bound, not CI.
271 tests pass; black/isort/flake8 + markdownlint clean.
Refs #39 #40
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code Review by Qodo
Context used✅ Tickets:
🎫 Parakeet STT 500s with CUDA-unknown error (unblocks reachy-mini-cli wake-word) 🎫 Deployed realtime facade (:8080) missing /v1/audio/transcriptions route (unblocks reachy-mini-cli wake-word)✅ Compliance rules (platform):
35 rules 1.
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR Summary by QodoAudio surface: Parakeet readiness probe + realtime pipeline docs + smoke tests WalkthroughsDescription• Make Parakeet /v1/health/ready reflect model-loaded + CUDA-ready (503 otherwise). • Document :8080 audio facade ownership, fleet bring-up, and stale-CUDA restart runbook. • Add audio smoke script and CI tests for readiness; bump version to 0.20.0. Diagramgraph TD
client(["Client / OpenAI SDK"]) --> gateway["Fleet gateway (:8000)"] --> realtime["Realtime facade (:8080)"]
realtime -->|"/v1/audio/transcriptions"| stt["Parakeet STT (:9002)"]
realtime -->|"/v1/audio/speech"| tts["Magpie TTS (:9000)"]
dockerhc["Docker healthcheck"] --> readyep["GET /v1/health/ready"] --> stt --> readiness["evaluate_readiness()"]
High-Level AssessmentThe following are alternative approaches to this PR: 1. Install model-gear wheel in Parakeet image (no vendored _readiness.py)
2. Use a lightweight real inference probe (short fixed audio) for readiness
3. Expose explicit internal readiness state (startup completes) and skip CUDA op
Recommendation: Current approach is the best tradeoff for #39/#40: a cheap, deterministic readiness probe (model-loaded + trivial CUDA op) catches the stale-CUDA failure mode without turning the healthcheck into a periodic inference workload. The only structural follow-up worth considering is eliminating the duplicated vendored _readiness.py by installing just the needed module in the Parakeet image, if packaging/coupling stays manageable. File ChangesBug fix (3)
Tests (2)
Documentation (6)
Other (4)
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The devague-exported spec/plan H1 is the verbatim announcement sentence, which ends in a period (MD026/no-trailing-punctuation). These are generated planning artifacts like docs/superpowers/** — ignore them in lint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Scaffold _readiness.py via AUDIO_TEMPLATES so `model init --fleet --audio` writes it (Dockerfile.parakeet COPYs it — clean init would otherwise fail `docker compose build stt`). [Qodo #2, comment 3402397497] - Remove the inline fallback copy of the readiness decision from listen_server.py and add a drift test asserting the vendored twin matches the canonical model_gear/realtime/_readiness.py. [Qodo #4] - Log CUDA readiness-probe failures (type + message) before returning 503 so operators can tell driver-down / OOM / stale-context apart. [Qodo #3] - audio-smoke.py now exercises /v1/audio/speech and wires --stt-url to a direct-Parakeet check (was claimed-but-untested). [Qodo #5] - docs/realtime-pipeline.md uses $HOME/.model-gear, not ~/.model-gear (portability rule). [Qodo #1, comment 3402397490] 275 tests pass; black/isort/flake8 + markdownlint clean. Bump 0.20.0 -> 0.20.1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Fixed in 81de86a — replaced
|
|
Good catch — real bug. Fixed in 81de86a: added
|
|
Qodo review — all 5 findings addressed in 81de86a (4 bugs + 1 rule violation). The two inline threads have per-thread replies; the other three:
275 tests pass; black/isort/flake8 + markdownlint clean. Bumped 0.20.0 → 0.20.1.
|



What & why
Works #39 (Parakeet STT 500s with
CUDA error: unknown errorwhile reporting "healthy") and #40 (deployed:8080facade missing the REST/v1/audio/transcriptionsroute) — both blocking reachy-mini-cli's "hey reachy" wake-word.This PR carries the full devague chain: a converged spec (
docs/specs/…), a plan (docs/plans/…), and the implementation.Reconnaissance changed the scope
The issues implied the audio route existed nowhere deployable. Reconnaissance found the repo was ~70% already built:
templates/fleet/docker-compose.audio.yml(Magpietts+ Parakeetstt+ therealtimefacade on:8080+ a gateway/v1/audio/*route), both Dockerfiles, the vendored Parakeet server,model init --fleet --audio, andmodel fleet upauto-including the overlay — all already present. So this PR implements only the three real remaining gaps, rather than rebuilding what exists.The three real gaps (implemented)
templates/fleet/listen_server.py's/v1/health/readyreturned{"status":"ready"}unconditionally (process liveness only) — the exact reason a wedged CUDA context still showed Docker "healthy". It now reports ready only when the model is loaded and a trivial CUDA op succeeds (else503). A cheap probe by design — not a full transcription each interval. The pure decision lives in stdlib-onlymodel_gear/realtime/_readiness.py(CI-unit-tested, 8 cases, no torch/GPU), vendored into the build context andCOPY'd into the image so it resolves without the wheel.docs/realtime-pipeline.md(was missing despite being referenced frompyproject.toml, the audio overlay, and the app docstring): states model-gear owns the live:8080facade, themodel fleet upbring-up, the topology, the prior drift, and the stale-CUDA restart runbook.scripts/audio-smoke.py— stdlib-only live smoke: asserts/openapi.jsonlists both audio routes, then POSTs a WAV and asserts200 {text}. Reproduces Parakeet STT 500s with CUDA-unknown error (unblocks reachy-mini-cli wake-word) #39's repro.What is NOT closed by merging this — operational, on the box
The live acceptance is GPU/Docker/NGC-bound and cannot run in CI or a worktree:
docker restart model-gear-stt(or amodel fleet upcycle) to clear the stale CUDA context on the DGX Spark, thenscripts/audio-smoke.pyreturns200.model fleet up --apply(with the--audiooverlay) on the box so:8080serves the routes from model-gear's deployment, retiring the oldrealtime-apisibling container.Verification
271 passed(full suite, incl. 8 new readiness tests).black/isort/flake8clean;markdownlint0 errors on the new docs + CHANGELOG.afi cli doctor . --strictpasses.0.19.0 → 0.20.0.Did not touch
t2/t3/t4(Dockerfiles, audio overlay compose, fleet wiring) — verified pre-existing, left as-is.🤖 Generated with Claude Code