feat(providers): live model lists for OpenRouter and OpenGateway - #860
feat(providers): live model lists for OpenRouter and OpenGateway#860euxaristia wants to merge 3 commits into
Conversation
Prefer each aggregator public /v1/models catalog over the dead OpenGateway models.json path and models.dev-only OpenRouter mapping so the picker shows current routes with context windows and free labels. Refs Gitlawb#859
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughOpenRouter and OpenGateway now fetch public live model lists, parse expanded metadata, and integrate live-only models into discovery. URL derivation uses OpenAI-compatible ChangesLive provider catalogs
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant DiscoverCatalog
participant FetchRemote
participant PublicCatalog
participant ModelParser
participant MergeCatalogModels
DiscoverCatalog->>FetchRemote: request public live catalog
FetchRemote->>PublicCatalog: GET /v1/models
PublicCatalog-->>FetchRemote: model payload
FetchRemote->>ModelParser: parse models and metadata
ModelParser-->>FetchRemote: filtered live models
FetchRemote-->>DiscoverCatalog: return live catalog
DiscoverCatalog->>MergeCatalogModels: merge live and curated models
MergeCatalogModels-->>DiscoverCatalog: return discovered models
Possibly related issues
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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.
Inline comments:
In `@internal/providermodelcatalog/remote.go`:
- Around line 207-230: The remoteModel pricing representation must also accept
OpenRouter’s string-valued pricing.prompt and pricing.completion fields. Add
uniquely tagged pricing fields to remoteModel, then update toModel to parse and
map them into InputCost and OutputCost while preserving the existing
cost.input/cost.output mapping as fallback where appropriate.
In `@internal/providermodeldiscovery/discovery_test.go`:
- Around line 55-84: Fix the sort-order assertion in
TestParseModelsResponseCapturesContextAndFree by directly comparing the ordered
model IDs as a joined list against the expected alphabetical sequence. Remove
the nested conditional and ensure the assertion fails when either model appears
at the wrong index.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: b6a21cdf-551f-42c7-af9e-92f2d6eab0d7
📒 Files selected for processing (5)
internal/providermodelcatalog/logic_test.gointernal/providermodelcatalog/remote.gointernal/providermodelcatalog/remote_test.gointernal/providermodeldiscovery/discovery.gointernal/providermodeldiscovery/discovery_test.go
…ertions Parse OpenRouter prompt and completion string pricing fields into InputCost and OutputCost, and directly assert alphabetical model sort order in discovery tests. Refs Gitlawb#859
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Thanks for this — the bug is real. I confirmed https://opengateway.gitlawb.com/zero/models.json returns 404 while /v1/models returns 200 unauthenticated, so the catalog really was pointing at nothing, and pulling auto back into the picker is the right fix.
Two things before it goes in.
Pricing lands a factor of a million out. internal/providermodelcatalog/remote.go:455 — parsePricingString returns OpenRouter's and OpenGateway's pricing.prompt verbatim, but those fields are USD per token, while Model.InputCost/OutputCost everywhere else in the codebase (models.dev cost.input) is USD per million tokens. providerWizardModelMeta renders it with $%g/%g (internal/tui/provider_wizard_models.go:44-46), so running the real live payloads through the parser and the picker formatter gives:
this branch: anthropic/claude-opus-4.8 | 1000000 ctx · tools · reasoning · $5e-06/2.5e-05
main: openai/gpt-4.1 | 1048576 ctx · tools · $2/8
That's all 303 OpenRouter models and the 10 priced OpenGateway models showing scientific notation a millionth of the real price, in the same list as correctly-scaled per-million prices from every other provider.
return val * 1e6 fixes it. Worth knowing that when I applied it the only failing test was remote_test.go:202, which currently pins the wrong unit (want 0.000003/0.000015) — so the test agrees with the bug today. Bumping it to 3/15 matches what models.dev reports for the same model and the suite goes green.
OpenRouter lost its independent fallback. With models.dev out of the openrouter path (remote.go:35-48), both the catalog fetch and the live probe now hit openrouter.ai, so if that host is unreachable the picker errors out entirely instead of degrading. Two httptest servers, openrouter returning 502 and models.dev healthy: main returns 1 model and a nil error, this branch returns 0 models and models endpoint returned 502 Bad Gateway. Falling back to FetchModelsDev when the live fetch fails would keep the old resilience.
Two notes, no action needed. For these two providers fetchCatalogModels and Discover now GET the same /models URL, so opening the picker once makes two identical requests — about 0.5MB twice on OpenRouter, uncached; one could reuse the other's body. And five OpenRouter models publish pricing.prompt: "-1" as their variable/BYOK marker, which parsePricingString maps to 0 and renders as no price. Harmless, worth a comment so it doesn't read as a parse failure later.
Things I went looking for specifically and found clean, since this is a provider-catalog change:
- No attribution or affiliate headers. The only headers this diff sends are
Accept: application/jsonandUser-Agent: zero-cli, both pre-existing. NoHTTP-Referer, noX-Title, no app tag, no query parameter the user didn't configure. - No credential on the public catalog fetch.
FetchOpenRouter/FetchOpenGatewaygo throughfetchJSON, which sends no auth — recording the requests with a key configured shows the catalog fetch carrying an emptyAuthorization; only the live probe sends the key. Redaction still holds: a 401 whose body echoes the header comes back asbad key: Bearer [REDACTED]. - Trust anchors. Both hardcoded URLs are reachable unauthenticated and are the obvious hosts — the project's own gateway and the official OpenRouter API. No new third party introduced.
- Sensible limits: 4MiB cap against a real 529KB payload, 10s client timeout, 8s context in the picker, and discovery still degrades to the catalog rather than erroring the command.
go build ./..., go vet and gofmt -l are clean on the touched packages, and internal/tui and internal/providercatalog pass. Everything above is in one pass — nothing queued behind it.
Convert OpenRouter/OpenGateway per-token pricing to per-million for Model costs, and fall back to models.dev when the live OpenRouter fetch fails so the picker still degrades.
|
Addressed both review blockers:
|
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Approving. Re-checked on b6c1832d rather than taking the summary's word for it, and both blockers are properly closed.
Pricing. return val * 1e6 is right, and more importantly the test now pins the unit instead of agreeing with the bug. I deleted the scale and TestParseOpenRouterCatalogMapsLiveMetadata goes red with claude costs = 0.000003/0.000015, want 3/15 (per-million, matching models.dev) — so the thing that made this bug invisible before is now the thing that catches it. Good call updating remote_test.go:203 to the real unit rather than leaving it pinned to the old value.
Fallback. FetchModelsDev(ctx, "openrouter", options) on live-fetch failure restores the resilience that was lost, so a down openrouter.ai degrades to models.dev instead of erroring the picker.
internal/providermodelcatalog and internal/providermodeldiscovery are both green here.
Thanks for the "-1" BYOK comment too — that was a throwaway note in my review and you picked it up anyway. It'll stop the next reader mistaking a deliberate marker for a parse failure.
Summary
OpenRouter and OpenGateway now load their public live model lists the way cairn-code does, so the picker stays current without the broken static OpenGateway catalog or a models.dev-only OpenRouter path.
Changes
internal/providermodelcatalogGET {base}/v1/models(replacing dead/zero/models.json)GET {base}/modelsinstead of models.dev aloneinternal/providermodeldiscovery/modelsresponsesTests
Test plan
go test ./internal/providermodelcatalog/ ./internal/providermodeldiscovery/ -count=1go vet ./internal/providermodelcatalog/ ./internal/providermodeldiscovery/gofmt -lclean on touched packagesgo test ./internal/providercatalog/ ./internal/tui/ -count=1go run ./cmd/zero-release build/modelwith OpenGateway key unset → live routes includingautoload fromGET /v1/models/modelwith OpenRouter key set → full live coding list with context windowsFixes #859
Summary by CodeRabbit
New Features
Bug Fixes