From 89f8643426d5cbc95c0fbb82ae46563bf5e05fa1 Mon Sep 17 00:00:00 2001 From: du5t Date: Thu, 23 Jul 2026 23:29:30 +0900 Subject: [PATCH] Pin remaining unbounded deps, drop stale JS model-list fallback, add smoke test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pyannote.audio was still `>=3.1` — the same unbounded-constraint pattern that silently broke qwen3 (transformers>=4.45.0 resolving to a version without qwen3_asr support on a later rebuild). Confirmed it had already drifted once this session (4.0.4 -> 4.0.7). Pinned to the exact version the current diarization code (token=, .speaker_diarization) is verified against. asr.js's config-fetch failure path fabricated a hardcoded backend/model list that could silently drift from the server's real one (this is exactly how the wrong Qwen3-ASR-2B/8B model IDs stuck around). Replaced it with a visible error instead of a second source of truth. Added scripts/smoke_test.sh: hits every backend's /transcribe or /synthesize directly with a synthetic clip. Every regression found this session (wrong model IDs, pyannote API drift, cuDNN path conflict) would have shown up here immediately instead of waiting for a user to hit it. Co-Authored-By: Claude Sonnet 5 --- app/ui/assets/js/asr.js | 17 ++++++-------- envs/faster_whisper.txt | 2 +- envs/qwen3.txt | 2 +- scripts/smoke_test.sh | 50 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 12 deletions(-) create mode 100755 scripts/smoke_test.sh diff --git a/app/ui/assets/js/asr.js b/app/ui/assets/js/asr.js index 1b6dd63..038bf99 100644 --- a/app/ui/assets/js/asr.js +++ b/app/ui/assets/js/asr.js @@ -7,16 +7,13 @@ async function loadConfig() { const r = await fetch('/asr/config'); SERVER_CONFIG = await r.json(); } catch (e) { - SERVER_CONFIG = { - default_backend: 'faster-whisper', - default_model: 'large-v3', - default_language: 'ko', - backends: { - 'faster-whisper': { models: ['tiny','base','small','medium','large-v3','large-v2','turbo'] }, - 'qwen3': { models: ['Qwen/Qwen3-ASR-0.6B-hf','Qwen/Qwen3-ASR-1.7B-hf'] }, - 'vibevoice': { models: ['microsoft/VibeVoice-ASR'] }, - }, - }; + // No hardcoded fallback list here on purpose: the server's /asr/config is + // the single source of truth for backend/model names. A duplicated guess + // here would silently drift out of sync with it (this has already bitten + // us once — see qwen3 model IDs). Surface the failure instead. + SERVER_CONFIG = null; + const el = document.getElementById('file-status'); + if (el) el.textContent = '설정을 불러오지 못했습니다. 페이지를 새로고침해주세요.'; } initAsrUI(); } diff --git a/envs/faster_whisper.txt b/envs/faster_whisper.txt index 4e7c858..5a6aafd 100644 --- a/envs/faster_whisper.txt +++ b/envs/faster_whisper.txt @@ -2,5 +2,5 @@ fastapi==0.115.0 uvicorn[standard]==0.30.6 python-multipart==0.0.9 faster-whisper==1.1.1 -pyannote.audio>=3.1 +pyannote.audio==4.0.7 numpy diff --git a/envs/qwen3.txt b/envs/qwen3.txt index 317caf0..b3e853b 100644 --- a/envs/qwen3.txt +++ b/envs/qwen3.txt @@ -2,7 +2,7 @@ fastapi==0.115.0 uvicorn[standard]==0.30.6 python-multipart==0.0.9 -transformers>=4.45.0 +transformers==5.14.1 accelerate>=0.30.0 librosa>=0.10.0 soundfile>=0.12.0 diff --git a/scripts/smoke_test.sh b/scripts/smoke_test.sh new file mode 100755 index 0000000..08c7df4 --- /dev/null +++ b/scripts/smoke_test.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Minimal post-deploy smoke test. Hits every ASR/TTS worker's own endpoint +# directly (bypassing the gateway's OIDC auth) with a synthetic test clip, to +# catch startup/dependency/API-compat regressions before a user has to find +# them (qwen3's wrong model IDs, pyannote's use_auth_token/token rename, and +# the cuDNN LD_LIBRARY_PATH conflict all would have shown up here as an +# immediate FAIL instead of a silent break discovered later). +# +# Run this after every `bash build.sh` + redeploy. +set -uo pipefail + +CONTAINER="${SPEECH_CONTAINER:-speech}" +TMP_WAV="/tmp/smoke_test_tone.wav" +FAIL=0 + +pass() { printf ' OK %s\n' "$1"; } +fail() { printf 'FAIL %s: %s\n' "$1" "$2"; FAIL=1; } + +echo "=== generating synthetic test clip ===" +podman exec "$CONTAINER" ffmpeg -y -f lavfi -i "sine=frequency=440:duration=3" -ar 16000 -ac 1 "$TMP_WAV" >/dev/null 2>&1 + +echo "=== faster-whisper (8001) ===" +resp=$(podman exec "$CONTAINER" curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8001/transcribe \ + -F "file=@${TMP_WAV};type=audio/wav" -F "model=tiny" -F "language=ko") +[ "$resp" = "200" ] && pass "faster-whisper" || fail "faster-whisper" "HTTP $resp" + +echo "=== qwen3 (8004) ===" +resp=$(podman exec "$CONTAINER" curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8004/transcribe \ + -F "file=@${TMP_WAV};type=audio/wav" -F "model=Qwen/Qwen3-ASR-0.6B-hf" -F "language=ko") +[ "$resp" = "200" ] && pass "qwen3" || fail "qwen3" "HTTP $resp" + +echo "=== vibevoice (8006) ===" +resp=$(podman exec "$CONTAINER" curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8006/transcribe \ + -F "file=@${TMP_WAV};type=audio/wav" -F "model=microsoft/VibeVoice-ASR") +[ "$resp" = "200" ] && pass "vibevoice" || fail "vibevoice" "HTTP $resp" + +echo "=== xtts (8005) ===" +resp=$(podman exec "$CONTAINER" curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8005/synthesize \ + -F "text=스모크 테스트입니다." -F "language=ko" -F "speaker_wav=${TMP_WAV}") +[ "$resp" = "200" ] && pass "xtts" || fail "xtts" "HTTP $resp" + +podman exec "$CONTAINER" rm -f "$TMP_WAV" + +echo +if [ "$FAIL" = "0" ]; then + echo "all backends OK" +else + echo "one or more backends FAILED — check: podman logs $CONTAINER" +fi +exit "$FAIL"