Skip to content

fix(v1): redact ffmpeg argv + 400 on missing model for /v1/audio (#14 #18)#69

Merged
thinmintdev merged 4 commits into
mainfrom
fix/v1-audio-envelope-2026-05-21
May 21, 2026
Merged

fix(v1): redact ffmpeg argv + 400 on missing model for /v1/audio (#14 #18)#69
thinmintdev merged 4 commits into
mainfrom
fix/v1-audio-envelope-2026-05-21

Conversation

@thinmintdev
Copy link
Copy Markdown
Contributor

Closes findings #14 and #18 from tests/harness/FINDINGS.md.

What

  • test(comfyui): accept digest-pinned image_ref form #14POST /v1/audio/transcriptions with a non-audio body could surface the moonshine container's ffmpeg argv + tempfile path through the proxy when an out-of-tree / older upstream returned a raw CalledProcessError repr. The dispatcher forwards upstream bodies verbatim by design, so the audio route now scrubs the response for the leak markers (ffmpeg / CalledProcessError) and substitutes a clean 415 envelope with code=audio.unsupported_format. New UnsupportedMediaType subclass added to hal0.errors so future 415 call sites have an ergonomic on-ramp.

  • fix(installer): uninstall.sh dev mode + hal0-caddy unit cleanup #18POST /v1/audio/speech with a body missing the model field used to fall through the dispatcher's default-model + no-route path and surface a misleading 404. The route now raises BadRequest up front with code=request.missing_model so OpenAI clients see the real problem. Matching field added on /v1/audio/transcriptions for consistency (its existing 400 was on validation.invalid — rebadged).

Tests

New tests/api/test_v1_audio.py:

  • non-audio multipart upload via mocked-leaky upstream → 415 audio.unsupported_format, body free of ffmpeg / CalledProcessError / /tmp/
  • clean upstream 5xx body passes through untouched (scrubber narrowness regression guard)
  • /v1/audio/speech missing model → 400 request.missing_model
  • /v1/audio/speech whitespace-only model → 400 request.missing_model
  • happy-path STT round-trip via httpx.MockTransport (asserts multipart forwarded verbatim with boundary intact)
  • happy-path TTS round-trip via httpx.MockTransport (asserts binary audio bytes flow through)
  • sanity: scrubber leaves /v1/chat/completions alone

Existing tests:

  • Updated tests/api/test_v1_dispatch.py missing-model assertions to the new code (request.missing_model)
  • Added UnsupportedMediaType row to tests/api/test_typed_errors.py envelope round-trip parametrize table

Test plan

  • uv run ruff check src/hal0 tests — clean
  • uv run ruff format --check src/hal0 tests — clean
  • uv run pytest tests/api/test_v1_audio.py tests/api/test_v1_dispatch.py tests/api/test_typed_errors.py — 30/30 pass
  • full suite (excluding pre-existing failures): no new regressions

…18)

Two harness findings on the OpenAI-compatible audio routes:

- #14: POST /v1/audio/transcriptions with a non-audio body could leak
  the moonshine container's ffmpeg argv + tempfile path through the
  proxy when an out-of-tree / older upstream returned a raw
  CalledProcessError repr. The dispatcher forwards upstream bodies
  verbatim by design, so the audio route now scrubs the response for
  the leak markers ("ffmpeg" / "CalledProcessError") and substitutes a
  clean 415 envelope with code=audio.unsupported_format. New
  UnsupportedMediaType subclass added to hal0.errors so other 415 call
  sites have an ergonomic on-ramp.

- #18: POST /v1/audio/speech with a body missing the 'model' field
  previously fell through the dispatcher's default-model + no-route
  path and surfaced a misleading 404. The route now raises BadRequest
  up front with code=request.missing_model so OpenAI clients see the
  real problem (matching field added on /v1/audio/transcriptions for
  consistency). Existing tests' code assertion updated from
  validation.invalid to request.missing_model.

Tests
=====
- tests/api/test_v1_audio.py (new) covers:
  * non-audio multipart → 415 audio.unsupported_format, no "ffmpeg" or
    "CalledProcessError" substring in the response body
  * clean upstream 5xx body passes through untouched (scrubber narrowness)
  * /v1/audio/speech missing model → 400 request.missing_model
  * /v1/audio/speech empty/whitespace model → 400 request.missing_model
  * happy-path STT + TTS via httpx.MockTransport round-trip
  * sanity: scrubber leaves non-audio routes alone

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thinmintdev thinmintdev force-pushed the main branch 2 times, most recently from 0c4ce34 to 5458a0d Compare May 21, 2026 04:16
@thinmintdev thinmintdev merged commit 6402fea into main May 21, 2026
5 of 6 checks passed
@thinmintdev thinmintdev deleted the fix/v1-audio-envelope-2026-05-21 branch May 22, 2026 04:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant