EVAL — Hybrid routing synthesis (#931), 2026-06-10¶
Issue: #931 (synthesize #928 / #929 / #930 into a routing decision)
Branch: feat/autoresearch-batch-3-championships
Status: Synthesis-in-progress. Locks in the framing the user articulated during the batch ("we have options, not all users have DGX — we keep options and explore") and pins the per-profile decisions on what the underlying evals concluded.
TL;DR¶
Three production profile shapes, three different best-of-class recommendations. Not "X wins, kill the others" — instead "here's the operating point each profile occupies, what we measured, and where the tradeoffs land."
| Profile shape | Whisper | Diarize | Summary | Net change from before this PR |
|---|---|---|---|---|
local.yaml (laptop, no DGX) |
openai-whisper / MPS | pyannote / MPS | hermes3:8b (Ollama on CPU) | None — confirmed |
cloud_with_dgx_* (DGX-equipped) |
openai-whisper / :8002 — promoted after temperature-bug fix this PR |
pyannote / :8001 |
Ollama qwen3.5:35b on :11434 |
Transcription default flips to DGX :8002 (was: MPS/cloud per pre-Cell-C draft) |
cloud_* (no DGX, cloud-OK) |
cloud Whisper API | Gemini speech (current — unmeasured this batch) | gemini-2.5-flash-lite | None — not re-litigated |
After late-batch findings landed, two defaults DO flip:
cloud_with_dgx_*transcription →whisper-openaiondgx:8002(after the temperature-schedule fix this PR).- vLLM autoresearch service default →
26.05-py3 + Qwen3.6-35B-A3B(after Cell C confirmed parity with Ollama on the same model family).
The local.yaml and cloud_* shapes stay as-is — evidence base
under each profile improved, no default change needed.
What each underlying eval concluded¶
Diarization (#930)¶
3-way: pyannote on Apple MPS vs NVIDIA CUDA (DGX) vs pure CPU.
- MPS and CUDA are essentially tied at ~23 s for a ~5-min episode (~13× realtime). Same model, same numerics-up-to-noise.
- Pure CPU is ~17× slower than either GPU path (~415 s).
- Speaker-count is contaminated by single-voice v2 TTS (
#934); can't draw quality conclusions from this fixture set until distinct voices land.
Net effect on routing: pyannote in-process is fine for any profile that runs on a GPU host (Apple Silicon counts). Route to DGX when the host has no GPU, OR when you want to keep load off the laptop.
Full report: docs/guides/eval-reports/EVAL_DIARIZATION_DGX_VS_CLOUD_2026_06.md
Summary (#928, with methodology caveat)¶
Head-to-head: Ollama qwen3.5:35b vs vLLM DeepSeek-R1-Distill-32B.
- Ollama 5.00 / 5.00 / 5.00 / 5.00 = 5.00 mean (Sonnet) / 4.90 (GPT-5.4 cross-check, 100% agreement, no contested flags)
- vLLM R1-Distill 4.80 / 3.60 / 2.20 / 2.40 = 3.25 mean (Sonnet) / 3.70 (GPT-5.4, 85% agreement, no contested flags)
- R1-Distill emits reasoning prose mid-summary (e.g., "Okay, so I need to summarize this podcast episode…"). That's a prompt- engineering gap, not a model defect. Fixable in a follow-up; not load-bearing for this PR.
Cell C (proper isolation, done in this PR): vLLM-served
Qwen3.6-35B-A3B (bf16) on nvcr.io/nvidia/vllm:26.05-py3 tied
with Ollama-served qwen3.5:35b (Q4_K_M) within scoring noise —
Sonnet 4.6 mean 4.90 vs 5.00, GPT-5.4 cross-check 4.90 vs 4.95.
The 1.75-point gap in the parent eval was the model choice
(Qwen3.6 vs R1-Distill), not the serving stack.
Net effect on routing: keep Ollama qwen3.5:35b as the
cloud_with_dgx_* summary default. The reason is now operational
(Ollama is simpler to manage), not quality (both stacks are
indistinguishable when serving the same model family).
Full report: docs/guides/eval-reports/EVAL_SUMMARY_DGX_LOCAL_2026_06.md
Transcription (#929 — done; 4-way, post-fix)¶
4-way: openai-whisper on Apple MPS vs DGX :8002 (fixed) vs pure
CPU vs DGX faster-whisper on :8000 (speaches image).
- MPS (laptop): WER 0.096 mean, 1.6× realtime (warm 2.6-3.8×). Clean local default.
- Pure CPU (M4 Pro, 5 episodes): WER 0.137 mean, 2.34× realtime. Quality 30% behind MPS, still production-viable. MPS is only ~1.4× faster than CPU on this workload, not 17× — the diarization extrapolation didn't hold for openai-whisper.
- DGX openai-whisper on
:8002, AFTER the bug fix this PR: WER 0.102 mean (within 6% of MPS, inside scoring noise), 4.56× realtime (~3× faster than MPS, ~2× faster than CPU). New production winner for DGX-equipped profiles. - DGX faster-whisper (speaches image,
:8000): still broken — empty output on 4/5 episodes, hallucinations on the 5th. Separate bug from the openai-whisper one (lives in speaches container config). Filed as follow-up; NOT a production candidate.
The temperature-schedule bug: pre-fix, DGX openai-whisper was
producing WER 3.20 mean (hallucinations with 2-9× extra words). The
synthesis-pre-Cell-C version of this report recommended NOT routing
to DGX whisper because of those numbers. Root cause was a
config bug in infra/dgx/whisper-server/app.py (forced
temperature=0.0 scalar disabled openai-whisper's built-in
fallback schedule, which is what saves long audio from
autoregressive runaway). Fixed in this PR. Re-run confirmed clean.
DGX-over-Tailscale hang pattern: The post-fix sweep hit intermittent HTTP-response hangs (server returns 200 OK, body stuck mid-transit over Tailscale). Worked around per-episode for this eval. Filed as #956 — applies to every DGX consumer (whisper, pyannote, vLLM, future agents), not just whisper.
Net effect on routing:
- Laptop runs (
local.yaml) → MPS openai-whisper. No change. - DGX-equipped runs (
cloud_with_dgx_*) → route transcription towhisper-openaion:8002(the fixed container). Quality matches MPS within noise, ~3× faster realtime. This flips the earlier (pre-Cell-C) recommendation that suggested keeping transcription off the DGX. - Cloud Whisper API stays the
cloud_*default; not retested this batch.
Full report: docs/guides/eval-reports/EVAL_TRANSCRIPTION_3WAY_2026_06.md
Recommended profile defaults (after this batch)¶
local.yaml (laptop / privacy / airgapped) — no change¶
- Transcription: openai-whisper on MPS auto-select (already the case)
- Diarization: pyannote in-process on MPS auto-select (already the case)
- Summary: hermes3:8b on Ollama (#949 finale verdict, already the case)
Validated: this profile delivers usable end-to-end performance on the operator's MacBook without any DGX dependency. Numbers: ~1.6× realtime whisper (5-ep mean; warm runs 2.6-3.8×), ~13× realtime pyannote, ~50× realtime hermes3:8b summary. Bonus finding this batch: openai-whisper is also 1.5× realtime on pure CPU on M4 Pro, so MPS isn't load-bearing — the fallback path stays production-viable.
cloud_with_dgx_* (DGX-equipped, cost-conscious) — transcription flips to DGX after the temperature-bug fix¶
- Transcription: openai-whisper at
dgx:8002(the fixed container, this PR). WER 0.102 mean (within noise of MPS 0.096), 4.56× realtime mean — the fastest GPU-accelerated path available.speaches/faster-whisperatdgx:8000is NOT the path; that container has a separate unresolved bug (#952 remains gated on the speaches root-cause). - Diarization: pyannote at
dgx:8001(#926) - Summary: Ollama qwen3.5:35b at
dgx:11434
Operational caveats:
- The earlier "GPU contention with vLLM degrades whisper" claim
was wrong. The DGX whisper container produced byte-identical
bad output regardless of vLLM running — it was a config bug in
our
app.py(forcedtemperature=0.0disabled openai-whisper's fallback schedule). Fix is in this PR. After the fix, no contention sensitivity observed. - DGX-over-Tailscale HTTP hangs (
#956): long-blocking calls from the laptop to the DGX occasionally lose the response mid-transit (server returns 200 OK, body stuck). Reproducible. Affects every DGX client, not just whisper. Production consumers should design around this — either async job pattern, or the shared timeout/retry/keepalive layer landed in#956. For the eval, the workaround was per-call timeout combined with per-episode invocation.
cloud_* (no DGX, cloud-OK) — NOT retested this batch¶
- Transcription: cloud Whisper API
- Diarization:
speaker_detector_provider: gemini(current — unmeasured this batch since no Gemini speech provider exists in the repo; filed) - Summary: gemini-2.5-flash-lite (per
#816)
Out of scope for this PR. The cloud paths were validated previously and this batch didn't relitigate them. They stay the documented choice for users without local GPU.
What this PR DOES change (after the late-batch findings)¶
cloud_with_dgx_*transcription default: flips towhisper-openaiondgx:8002(the fixed container). Earlier drafts of this report had kept transcription off the DGX because of the temperature-bug results.- vLLM autoresearch default: flips from
25.11-py3 + DeepSeek-R1-Distill-32Bto26.05-py3 + Qwen3.6-35B-A3B + --max-num-seqs 128. The Cell C comparison (this PR) showed Qwen3.6-served-by-vLLM ties Ollama- qwen3.5:35b within scoring noise. R1-Distill kept as a one-line revert (docker-compose.yml.r1-distill.bakpreserved on the DGX).
What this PR does NOT change¶
local.yamlprofile defaults — confirmed unchanged.cloud_*profiles — not relitigated; cloud Whisper API + Gemini flash-lite +speaker_detector_provider: geministay the documented choice.- Ollama qwen3.5:35b remains the summary default for
cloud_with_dgx_*(Cell C confirmed: Ollama and vLLM tie when serving the same model family; Ollama is operationally simpler). - The faster-whisper container (
dgx:8000, speaches image) stays out of the production routing — separate bug, separate fix.#952remains the engine-comparison gate, blocked on that fix.
Follow-ups (filed)¶
| # | What | Why |
|---|---|---|
#946 (in flight) |
Whisper client resilience: duration-scaled timeout + single-flight | Should now consume #956's shared resilience layer instead of bespoke patterns |
#952 |
Validate faster-whisper WER vs openai-whisper on real podcasts | Blocked on speaches compute_type=default root-cause (the empty-output bug surfaced this batch) |
#953 |
openai-whisper DGX service (deployed earlier, temperature-schedule bug fixed this PR) | The deploy was correct; the API contract had a bad default. App.py fix in this PR. |
#956 (filed this batch) |
DGX-over-Tailscale client resilience (shared timeout/retry/keepalive layer for every DGX consumer) | Long-blocking HTTP over Tailscale loses responses mid-transit; hit during the post-fix #929 sweep |
#954 (filed today) |
Diarization client resilience analog to #946 |
Same operational gap surfaced during this batch |
#934 |
Distinct voices in v2 fixtures | Required to verdict pyannote speaker-count accuracy |
| #928 Cell D / E | Quant isolation (R1-Distill on Ollama Q4 GGUF, and Ollama-Qwen3.6 at bf16) | Cell C resolved the serving-stack question; remaining isolated variable is precision |
| Gemini speaker provider deploy | Wire cloud_*'s declared speaker_detector_provider: gemini so #930 can run the 3rd candidate |
Unblocks full 3-candidate diarization championship |
| R1 reasoning-suppressed prompt | Modify the summary prompt to strip <think> and require summary-only output |
Determines whether R1-Distill is competitive when not reasoning-as-output |
Honest framing for the operator¶
This batch moved the evidence base forward more than it changed recommendations. The DGX is now properly diagnosed for whisper contention; the vLLM serving stack is live for future autoresearch without redeploys; the summary champion is validated; the laptop diarization story is documented. Where defaults stay put, it's because the evidence either confirmed the current choice or wasn't strong enough to flip it.
The "we keep options and explore" stance the user articulated mid- batch is reflected in this synthesis: every profile shape has a documented operating point with measured numbers, and no recommendation says "kill the alternatives." That's the right shape for a research output.
Artifacts¶
docs/guides/eval-reports/EVAL_DIARIZATION_DGX_VS_CLOUD_2026_06.mddocs/guides/eval-reports/EVAL_SUMMARY_DGX_LOCAL_2026_06.mddocs/guides/eval-reports/EVAL_TRANSCRIPTION_3WAY_2026_06.mdscripts/eval/score/diarization_dgx_vs_cloud_v1.pyscripts/eval/score/whisper_dgx_vs_cloud_v1.py(withLOCAL_*_DEVICEenv overrides)scripts/eval/score/summary_vllm_predict_v1.pyinfra/dgx/whisper-server/(#953 deploy)infra/dgx/vllm-autoresearch/(#928 prereq deploy; moved to https://github.com/chipi/agentic-ai-homelab/ on 2026-06-12)
References¶
- Issues: #931 (this synthesis), #928, #929, #930
- Parent epic: #927 (DGX-vs-cloud autoresearch programme)
- Prior local-LLM finale: #932 / #949
- Related infra: #953 (openai-whisper), #948 (speaches investigation), #946 (whisper client resilience), #954 (diarization client resilience), #952 (faster-whisper validation), #934 (distinct-voice fixtures)
2026-06-14 addendum — any-vLLM-vs-transcription overlap is now a hard rule¶
The 2026-06-14 #963 re-run, measured against the operator's coder-next
vLLM stack (Qwen/Qwen3-Coder-Next-FP8 at gpu-memory-utilization=0.75;
see agentic-ai-homelab/infra/vllm/coder-next/) as a stand-in for the
project's planned-but-not-deployed vllm-autoresearch turned up a
failure mode that the 2026-06-11 re-run didn't see:
- SC3 (active vLLM serving) caused one of five episodes
(
p03_e01) to collapse to WER 1.000 at an 18× slowdown — a catastrophic single-episode failure that survived as a successful HTTP response (so the resilience layer can't catch it). - The other four episodes degraded gracefully (latency 2–3.7× slower, WER +0–6pp). Mean SC3 latency 2.0× realtime vs 4.4× idle.
- vLLM's own tail latency blew out to 335s during the same window.
Net effect on the routing recommendation. The
cloud_with_dgx_primary profile defaults stay the right shape —
quality is fine when whisper + any vLLM don't overlap. But the
operator-side rule "do not overlap any active vLLM serving
(coder-next, autoresearch, or other future stacks) with
transcription windows" is now a load-bearing piece of the
hybrid-routing decision, not advisory. The prior draft framed this as
autoresearch-specific; corrected here because the measurement target
was coder-next.
The PROD_RUNBOOK §"Provider model selection — DGX vs cloud per stage" section codifies this rule and points back to the contention report for the underlying evidence.
Also corrected this re-run: vLLM gpu-memory-utilization=0.75
is now the GB10 floor (0.92 OOM-crashes the host because of the
unified CPU+GPU pool). Compose default is now
VLLM_GPU_MEM_UTIL:-0.75 upstream in the homelab repo (coder-next; the
autoresearch stack shipped 2026-06-14 at homelab
infra/vllm/autoresearch/ and inherits the same cap). The 2026-06-11
SC3 numbers were attributed at writing time to Qwen3.6-35B-A3B; today's
numbers ran under 0.75 against Qwen3-Coder-Next-FP8 — same GB10 GPU,
same contention shape, attribution clarified.
Open follow-ups filed (do not block this synthesis):
- #996 — Characterize the catastrophic-tail failure rate (N≥20
episodes) against the autoresearch vLLM. As of 2026-06-14 the
autoresearch stack is live at
:8003on the DGX (homelab repoinfra/vllm/autoresearch/, modelQwen/Qwen3-30B-A3B-Instruct-2507), so this is now actionable rather than blocked. N=1 today; the operational rule covers the risk but the actual rate is unknown. Homelab issues#1(MoE config),#2(vLLM image bump),#3(model size drift) document the residual caveats to record when #996 numbers land. - #997 — Benchmark Gemini speaker-detector quality vs pyannote on
the v2 fixture bed. The 3rd-candidate gap in #930 (Gemini provider
was undeployed when #930 ran; #962 shipped it but the quality
comparison was never run). Doesn't affect
cloud_with_dgx_primaryrouting — only validates thecloud_*profile'sspeaker_detector_provider: geminiassertion.