fix(realtime): resolve virtual-model aliases on realtime and audio endpoints#514
Conversation
…dpoints Aliases 404'd on every realtime and audio endpoint while resolving fine on chat, responses, and embeddings: POST /v1/realtime/calls?model=<alias> -> 404 model not found POST /v1/realtime/client_secrets (session.model) -> 404 GET /v1/realtime?model=<alias> -> 404 POST /v1/audio/speech -> 404 POST /v1/audio/transcriptions -> 404 Two causes. realtimeService and audioService never received the virtual-model resolver (h.modelResolver), so prepare() only type-asserted the registry-only selectorResolver on the Router, which canonicalizes concrete ids and leaves an alias untouched. And even the parsed selector was discarded: the raw request model was forwarded to the router, which then failed the provider lookup. These endpoints resolve their model in the service layer because OperationRealtime and the audio operations are absent from the workflow middleware's resolveWorkflow switch, so ensureRequestModelResolution never runs for them. Both services now resolve through the shared resolveServiceModel helper, which runs the same two-step gateway.ResolveExecutionSelector (alias table, then provider registry) used by chat/responses/embeddings, and forward the resolved concrete selector upstream. The prior unit test could not catch this: it injected a mock whose ResolveModel faked the alias resolution the production Router never performs, and asserted the router received the raw alias under a comment claiming the opposite. Replaced with regression tests that drive a real virtualmodels.Service through a registry-only provider double for all three realtime routes plus speech and transcription; each fails if the resolver is unwired or the raw model is forwarded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAudio and realtime services now resolve model aliases to concrete selectors via a new ChangesAlias Resolution Wiring
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant Service as audioService/realtimeService
participant resolveServiceModel
participant Gateway as gateway.ResolveExecutionSelector
participant Router as Provider Router
Client->>Service: request with alias model
Service->>resolveServiceModel: resolve(requested selector)
resolveServiceModel->>Gateway: ResolveExecutionSelector(ctx, provider, resolver)
Gateway-->>resolveServiceModel: resolved core.ModelSelector
resolveServiceModel-->>Service: resolved selector
Service->>Service: store selector on route, rewrite req.Model/Provider
Service->>Router: dispatch/target lookup using resolved model/provider
Router-->>Client: response
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Problem
Virtual-model aliases 404 on every realtime and audio endpoint, while resolving correctly on chat, responses, and embeddings. Commit 2c3ab68 (#500) and
CLAUDE.mdboth claim aliases work on these routes — they don't.Reproduced against live OpenAI (alias →
openai/gpt-realtime, target present in/v1/models):POST /v1/realtime/calls?model=…POST /v1/realtime/client_secrets(session.model)GET /v1/realtime?model=…(websocket)POST /v1/audio/speechPOST /v1/audio/transcriptionsPOST /v1/chat/completions(control)Root cause
Two independent defects, both required to reach the bug:
realtimeServiceandaudioServicecopiedprovider,modelAuthorizer,budgetChecker,rateLimiter— but noth.modelResolver(thevirtualmodels.Service).prepare()type-asserted the registry-onlyselectorResolveron the Router, which canonicalizes concrete ids and returns an alias unchanged with no error.router.Realtime*Target/CreateSpeech/CreateTranscription, which then 404'd in the provider lookup.These endpoints resolve their model in the service layer because
OperationRealtimeand the audio operations are absent from the workflow middleware'sresolveWorkflowswitch, soensureRequestModelResolution(the chat path's route to the alias resolver) never runs for them.Fix
Both services now resolve through a shared
resolveServiceModelhelper that runs the same two-stepgateway.ResolveExecutionSelector(alias table → provider registry) used by chat/responses/embeddings, and forward the resolved concrete selector upstream. Wiring added in both service constructors.Why the existing test missed it
realtime_webrtc_service_test.goinjected a mock whoseResolveModelfaked the alias resolution the production Router never performs, and asserted the router received the raw alias ("voice-alias") under a comment claiming the opposite. That assertion is corrected, and new regression tests inrealtime_audio_alias_resolution_test.godrive a realvirtualmodels.Servicethrough a registry-only provider double across all three realtime routes plus speech and transcription. Each new test fails if the resolver is unwired or the raw model is forwarded (both verified by reverting each half independently).Verification
internal/server,internal/providers,internal/gateway,internal/virtualmodels: green;make test-race+make lintpass in pre-commit.session.modelrewritten; calls 201 withLocation; websocket 101; speech 200; transcription 200).Found during pre-release QA of the current release branch.
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests