test(e2e): TokenSpeed Qwen3-ASR audio transcription - #1912
Conversation
Add an e2e test for POST /v1/audio/transcriptions against a TokenSpeed Qwen3-ASR worker, driven through the standard api_client (OpenAI SDK) / model fixtures like the other chat_completions e2e tests. Covers a whole-file transcription, the text response_format, and 400 rejection of an unsupported language. Runs in the existing e2e-1gpu-chat (tokenspeed) lane via the engine marker. Refs #1905 Signed-off-by: Simo Lin <linsimo.mark@gmail.com>
📝 WalkthroughWalkthroughAdds end-to-end TokenSpeed coverage for Qwen3-ASR audio transcription, including JSON and plain-text responses plus unsupported-language rejection. ChangesTokenSpeed transcription coverage
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant E2ETest
participant OpenAIClient
participant TokenSpeedWorker
E2ETest->>OpenAIClient: submit audio transcription request
OpenAIClient->>TokenSpeedWorker: send model, WAV, and options
TokenSpeedWorker-->>OpenAIClient: return text or HTTP 400
OpenAIClient-->>E2ETest: expose response or BadRequestError
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces end-to-end tests for audio transcription using the TokenSpeed Qwen3-ASR worker. The feedback suggests explicitly passing the filename and MIME type as a tuple to the transcription client to ensure reliable multipart request formatting, and adding assertions on the error message in the unsupported language test to verify the failure reason.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| with AUDIO_WAV.open("rb") as audio: | ||
| result = api_client.audio.transcriptions.create( | ||
| model=model, | ||
| file=audio, | ||
| language="en", | ||
| temperature=0.0, | ||
| ) |
There was a problem hiding this comment.
Passing a raw file object to api_client.audio.transcriptions.create relies on the client library correctly guessing the filename and MIME type from the file descriptor. To prevent potential issues with MIME type detection or missing filenames in the multipart payload, it is safer to explicitly pass a tuple containing the filename, file object, and content type.
| with AUDIO_WAV.open("rb") as audio: | |
| result = api_client.audio.transcriptions.create( | |
| model=model, | |
| file=audio, | |
| language="en", | |
| temperature=0.0, | |
| ) | |
| with AUDIO_WAV.open("rb") as audio: | |
| result = api_client.audio.transcriptions.create( | |
| model=model, | |
| file=(AUDIO_WAV.name, audio, "audio/wav"), | |
| language="en", | |
| temperature=0.0, | |
| ) |
| with AUDIO_WAV.open("rb") as audio: | ||
| result = api_client.audio.transcriptions.create( | ||
| model=model, | ||
| file=audio, | ||
| response_format="text", | ||
| ) |
There was a problem hiding this comment.
Explicitly pass the filename and MIME type as a tuple to ensure the multipart request is correctly formatted and the backend can properly identify the audio format.
| with AUDIO_WAV.open("rb") as audio: | |
| result = api_client.audio.transcriptions.create( | |
| model=model, | |
| file=audio, | |
| response_format="text", | |
| ) | |
| with AUDIO_WAV.open("rb") as audio: | |
| result = api_client.audio.transcriptions.create( | |
| model=model, | |
| file=(AUDIO_WAV.name, audio, "audio/wav"), | |
| response_format="text", | |
| ) |
| with AUDIO_WAV.open("rb") as audio: | ||
| with pytest.raises(openai.BadRequestError): | ||
| api_client.audio.transcriptions.create( | ||
| model=model, | ||
| file=audio, | ||
| language="zz", | ||
| ) |
There was a problem hiding this comment.
In addition to passing the explicit file tuple, it is highly recommended to assert on the error message returned by the API. This ensures that the test fails if a BadRequestError is raised for an unrelated reason (e.g., a malformed request structure or invalid model name) rather than the expected unsupported language error.
| with AUDIO_WAV.open("rb") as audio: | |
| with pytest.raises(openai.BadRequestError): | |
| api_client.audio.transcriptions.create( | |
| model=model, | |
| file=audio, | |
| language="zz", | |
| ) | |
| with AUDIO_WAV.open("rb") as audio: | |
| with pytest.raises(openai.BadRequestError) as exc_info: | |
| api_client.audio.transcriptions.create( | |
| model=model, | |
| file=(AUDIO_WAV.name, audio, "audio/wav"), | |
| language="zz", | |
| ) | |
| assert "language" in str(exc_info.value).lower() |
|
This pull request has been automatically marked as stale because it has not had any activity within 14 days. It will be automatically closed if no further activity occurs within 16 days. Leave a comment if you feel this pull request should remain open. Thank you! |
Description
Problem
#1905 added Qwen3-ASR audio transcription served via TokenSpeed but shipped no e2e coverage for
POST /v1/audio/transcriptions. (Supersedes the closed #1909, which reinvented the request layer with rawhttpx.)Solution
Add an e2e test driven through the standard
api_client(OpenAI SDK) +modelfixtures — the same idiom astest_multimodal.py/test_enable_thinking.py— callingapi_client.audio.transcriptions.create(...). Covers a whole-file transcription,response_format="text", and 400 rejection of an unsupportedlanguage.Changes
e2e_test/chat_completions/test_transcription_tokenspeed.py(new):TestTokenSpeedTranscription, markers@engine("tokenspeed") @gpu(1) @e2e @model("Qwen/Qwen3-ASR-1.7B"),setup_backend=["grpc"].Test Plan
Runs in the existing
e2e-1gpu-chat (tokenspeed)lane. Locallypy_compile/ruff/mypyclean. NOTE: CI currently fails at worker startup — the tokenspeed engine's transformers does not recognize theqwen3_asrarchitecture (Qwen3-ASR is wired for vLLM via an arch override); this is an engine-support gap, not a test defect, and is under discussion.Checklist
cargo +nightly fmtpassescargo clippy --all-targets --all-features -- -D warningspasses