Fix verbose_json incompatibility with OpenAI transcribe models#218
Conversation
OpenAI's gpt-4o-transcribe and gpt-4o-mini-transcribe model family only accepts "json" or "text" as response_format — sending "verbose_json" returns a 400 unsupported_value error and the setup test fails. Make transcriptionResponseFormat model-aware: models whose name contains "transcribe" get "json"; all others (Groq whisper-large-v3, etc.) keep "verbose_json" so the hallucination filter's no_speech_prob segments remain available. The hallucination filter already degrades gracefully when segments are absent, so there is no second change needed. Also add an explicit 400 case to friendlyHTTPMessage so users see "Check your model name and Base URL in Settings" rather than the generic fallback, which is actionable for this exact failure mode. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthrough
ChangesTranscription Service Updates
Estimated code review effort: 2 (Simple) | ~8 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
this sounds a bit too wide |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/TranscriptionService.swift (1)
219-220: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse
\(provider)interpolation for consistency with other cases.The HTTP 400 message uses the literal word "Provider" while every other case interpolates the actual host via
\(provider). This loses provider-specific context that would help users distinguish between, e.g., OpenAI and Groq rejections.Proposed fix
- return "Provider rejected the request (HTTP 400). Check your model name and Base URL in Settings." + return "\(provider) rejected the request (HTTP 400). Check your model name and Base URL in Settings."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/TranscriptionService.swift` around lines 219 - 220, Update the HTTP 400 branch in the relevant error-message switch to interpolate the existing provider value as \(provider), matching the other cases while preserving the guidance about the model name and Base URL.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@Sources/TranscriptionService.swift`:
- Around line 219-220: Update the HTTP 400 branch in the relevant error-message
switch to interpolate the existing provider value as \(provider), matching the
other cases while preserving the guidance about the model name and Base URL.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d150879b-7db8-4cdd-9233-253ba4094570
📒 Files selected for processing (1)
Sources/TranscriptionService.swift
What happened
When setting up FreeFlow with an OpenAI
*-transcribemodel (e.g.gpt-4o-mini-transcribe), the setup test fails immediately and the onboarding screen shows a raw API error body:Two separate problems compound here: the wrong
response_formatvalue is being sent, and HTTP 400 had no friendly case in the error handler so the raw body surfaced.The diagnosis
TranscriptionServicehard-codesresponse_format = "verbose_json". OpenAI's Whisper-based models (whisper-1) accept this. OpenAI's newer*-transcribemodel family explicitly rejects it — their API only accepts"json"or"text".verbose_jsonis needed for Groq/Whisper users because it returns per-segmentno_speech_probvalues, which the hallucination filter relies on. Switching to"json"globally would break that. But"json"is the right default for any model whose name contains"transcribe".The hallucination filter already degrades gracefully when segments are absent (
parseTranscriptchecks forjson["segments"]and returnsfalse— skipping the filter — when none are present), so"json"responses from transcribe models flow through cleanly.The fix
1. Model-aware
response_format— changed from a stored constant to a computed property:This preserves
verbose_jsonfor Groq and Whisper users while routing any*-transcribemodel to thejsonformat the provider expects.2. Explicit HTTP 400 case — added to
friendlyHTTPMessage:Previously 400 fell through to the generic default. The new message points directly at the two most common 400 causes: wrong model name and wrong Base URL.
Files changed
Sources/TranscriptionService.swift— two changes: computedtranscriptionResponseFormat,case 400infriendlyHTTPMessageBehavior unchanged
whisper-large-v3, etc.): still receiveverbose_json, hallucination filter works as before."transcribe"in its name: unchanged.Verified
gpt-4o-mini-transcribepath: reasoned through —response_formatbecomes"json", API accepts the request, transcript returned; hallucination filter skips gracefully with no segments."whisper-large-v3"does not contain"transcribe", format stays"verbose_json", no change.Notes / Considered
"json"for all models? The hallucination filter needsno_speech_probfromverbose_jsonsegments. Removing it for Whisper users would silently stop filtering common hallucinations on silence.contains("transcribe")check assumes OpenAI keeps"transcribe"in future model names. If a provider uses a different naming scheme, this falls back toverbose_jsonand they would see the same 400 again. A settings override forresponse_formatwould be a more robust long-term fix, but that is a separate change.Summary by CodeRabbit
Bug Fixes
Improvements