fix(models): route auto-discovered providers by wire protocol (#41)#43
Conversation
- replace npmToLLMProvider map with npmToWireProtocol (openai/anthropic/google) - add createAutoRoutedGoogleProvider so @ai-sdk/google proxies work (fixes opencode/gemini-* failing with "no LLM provider mapping") - strip the genai-injected v1beta segment for proxies whose base URL already carries a version (e.g. opencode's /zen/v1) - preserve openai-compat fallback and clearer error for unroutable providers - document auto-routing in README and providers docs; update CreateProvider godoc - add regression tests for wire routing and version-path rewriting Fixes #41
|
Connected to Huly®: KIT-44 |
📝 WalkthroughWalkthroughThis PR implements wire-protocol-based auto-routing for LLM providers, replacing direct npm-to-LLM-provider mappings with a wire-protocol abstraction. It adds Google Gemini support with a custom HTTP transport that normalizes proxy base paths, updates registry LLM support checks, and includes comprehensive tests and documentation. ChangesWire-Protocol Auto-Routing
Sequence DiagramsequenceDiagram
participant AutoRoute as autoRouteProvider
participant NPMMap as npmToWireProtocol
participant Dispatch as Protocol Dispatcher
participant OpenAI as createAutoRoutedOpenAIProvider
participant Anthropic as createAutoRoutedAnthropicProvider
participant Google as createAutoRoutedGoogleProvider
participant Compat as createAutoRoutedOpenAICompatProvider
AutoRoute->>NPMMap: Select npm, map to wireProtocol
NPMMap-->>AutoRoute: wireProtocol enum
AutoRoute->>Dispatch: Dispatch by protocol
alt OpenAI wire
Dispatch->>OpenAI: Create OpenAI provider
else Anthropic wire
Dispatch->>Anthropic: Create Anthropic provider
else Google wire
Dispatch->>Google: Create Google/Gemini provider
else Unknown npm + API URL
Dispatch->>Compat: Fall back to OpenAI-compatible
else Unknown npm, no API URL
Dispatch-->>AutoRoute: Error: unknown npm
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/models/autoroute_test.go (1)
17-42: ⚡ Quick winMake the mapping regression guard exact.
This test checks the expected entries and a few forbidden ones, but it still passes if some other unintended package is added to
npmToWireProtocol. Adding a size check here would make the contract precise and catch the same class of drift this test is meant to prevent.Suggested change
want := map[string]wireProtocol{ "`@ai-sdk/openai`": wireOpenAI, "`@ai-sdk/openai-compatible`": wireOpenAI, "`@ai-sdk/anthropic`": wireAnthropic, "`@ai-sdk/google`": wireGoogle, } + if len(npmToWireProtocol) != len(want) { + t.Fatalf("npmToWireProtocol has %d entries, want %d", len(npmToWireProtocol), len(want)) + } for npm, wire := range want { if got := npmToWireProtocol[npm]; got != wire { t.Errorf("npmToWireProtocol[%q] = %d, want %d", npm, got, wire) } } - - // Bundle packages must NOT be in the table (regression guard against the - // old npmToLLMProvider map that listed 10 entries but only handled 3). - for _, npm := range []string{ - "`@ai-sdk/google-vertex`", - "`@ai-sdk/google-vertex/anthropic`", - "`@ai-sdk/amazon-bedrock`", - "`@ai-sdk/azure`", - "`@openrouter/ai-sdk-provider`", - "`@ai-sdk/vercel`", - } { - if _, ok := npmToWireProtocol[npm]; ok { - t.Errorf("npmToWireProtocol unexpectedly contains bundle package %q", npm) - } - }🤖 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 `@internal/models/autoroute_test.go` around lines 17 - 42, The test should assert the exact size of the npmToWireProtocol map to prevent unexpected entries; after verifying the specific expected keys (wireOpenAI, wireAnthropic, wireGoogle) and ensuring the forbidden bundle keys are absent, add a length check like: compare len(npmToWireProtocol) to the number of expected entries (len(want)) and fail the test if they differ so the test fails when any extra mapping is added.
🤖 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.
Nitpick comments:
In `@internal/models/autoroute_test.go`:
- Around line 17-42: The test should assert the exact size of the
npmToWireProtocol map to prevent unexpected entries; after verifying the
specific expected keys (wireOpenAI, wireAnthropic, wireGoogle) and ensuring the
forbidden bundle keys are absent, add a length check like: compare
len(npmToWireProtocol) to the number of expected entries (len(want)) and fail
the test if they differ so the test fails when any extra mapping is added.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 556e5eb4-9f36-4858-9855-5ce749e35ba0
📒 Files selected for processing (6)
README.mdinternal/models/autoroute_test.gointernal/models/modelsdb.gointernal/models/providers.gointernal/models/registry.gowww/pages/providers.md
Description
autoRouteProviderpreviously routed models.dev providers through a flat npm→LLM-provider-name map (npmToLLMProvider) that listed 10 entries but only handled 3 in itsswitch. Any model whose resolved npm package fell outsideanthropic/openai/openaicompathit a deaddefaultbranch and failed withunsupported provider: … has no LLM provider mapping. This broke every Gemini model proxied through aggregators like opencode, whosegemini-*models carry a per-model@ai-sdk/googleoverride.This change refactors auto-routing to dispatch on wire protocol (OpenAI / Anthropic / Google) — matching fantasy's actual architecture of three native protocols plus thin wrappers — using the provider's
apiURL from models.dev as the base. Providers with anapiURL but an unrecognized npm package fall back to the OpenAI-compatible wire, preserving prior behaviour.It also fixes a second-order bug surfaced by the new Google path: the underlying genai SDK always injects its own
v1betaversion segment, so a proxy base URL that already carries a version (e.g. opencode's/zen/v1) produced a doubled/zen/v1/v1beta/...path that 404'd. A small URL-rewriting transport (mirroring the existingcodexTransportpattern) strips the redundant segment only for versioned proxy base URLs; the nativegoogle/path is untouched.Before:
kit -m opencode/gemini-3.5-flash "say hi"→ERROR … @ai-sdk/google has no LLM provider mappingAfter: routes through the Google wire and returns a real Gemini response.
Fixes #41
Type of Change
Checklist
go test -race ./internal/models/...,golangci-lint run)Additional Information
Files changed:
internal/models/modelsdb.go— replacenpmToLLMProviderwith awireProtocoltype + 4-entrynpmToWireProtocolmap (bundle packages intentionally omitted; they have native top-level cases)internal/models/providers.go— switchautoRouteProvideron wire protocol; addcreateAutoRoutedGoogleProviderand thegeminiProxyTransportversion-path fix; updateCreateProvidergodocinternal/models/registry.go—isProviderLLMSupportedconsultsnpmToWireProtocolinternal/models/autoroute_test.go— new regression tests for wire routing, error paths, and version-segment rewritingREADME.md,www/pages/providers.md— document auto-routing by wire protocolBackwards compatibility: All previously-working flows are preserved — the
openai/anthropic/openaicompatcases collapse cleanly into their wire protocols, the unknown-npm→openai-compat fallback is retained, and native top-level provider cases are untouched. Verified end-to-end via CLI and the TUI/modelpicker.Summary by CodeRabbit
provider/modelformat.