refactor: consolidate provider metadata into a single-source registry#41
Merged
Conversation
…rive choices from registry
…gistry The union was a hand-written duplicate of the registry's behavior.subscription flags. The registry now exports the subscriptionProviderKeys carrier array and the type derived from it; subscription.ts re-exports the type, and a unit assertion pins the carrier to the flags so type and data cannot drift.
The picker's single empty-state message pointed every cause at the Providers page, which is misleading when the selected provider simply doesn't support the route's endpoint. The filter chain is now staged (unselected -> provider -> endpoint -> search) so the empty state can name its cause: provider lacks the endpoint, no provider serves it, search miss, all candidates selected, or genuinely no models.
A provider sync can load dozens of models into the Add Model picker, which rendered them all in one unbounded table. The picker now pages client-side (10 per page) with the same list-pagination footer used by Activity; page resets on endpoint/provider/search changes and on open, and the footer only appears past one page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
All static provider metadata now lives in one file —
packages/config/src/provider-registry.ts— and every other layer derives from it. Previously the same facts were independently declared in (at least) five places: the runtime descriptor table, the Console provider templates, hardcoded openai/anthropic branches in route policies, the streaming dialect'ssupportsPathSuffix, and endpoint path literals scattered across subscription/adapter/connectivity/model-list code, plus the OAuth configs.The registry holds, per provider key (15 known keys):
ProviderDescriptorchat_completions/responses/messages), structurally separated frommodels/v1/models/codex/models), which is internal (model sync) and never exposed to usersAll values were transcribed verbatim from the donor files; no capability facts were changed.
Consumers rewired (signatures preserved via thin shims)
packages/provider: descriptor, dialect (supportsPathSuffixnow registry-driven for every provider, not just the two subscription ones), oauth config, subscription URL builders, adapter/connectivity/model-list path literalspackages/db: provider templates (derived),ProviderType,listProviderRouteEndpointProtocols(hardcoded branches deleted)packages/domain:routeEndpointProtocolsre-exported from the registry (canonical definition moved)packages/gateway-runtime: streaming path suffixesapps/console: direct-create choices derived from the registry; duplicate protocol label map removedBehavior changes (intentional)
Testing
tests/features/provider-registry.unit.case.ts: field-for-field assertions against donor values (independent witness, not a mirror), endpoints never containmodels, unknown-key permissive policy, subscription key listingpnpm run verify: 350/350 tests greenpnpm run verify:features: all 15 features re-verified, zero regressionsprovider-metadata-registrywired intofeature_list.jsonNo database schema changes.
Follow-up (cbd17d3)
SubscriptionProviderKeywas still a hand-written two-value union inpackages/provider/src/subscription.ts— a type-level duplicate of the registry'sbehavior.subscriptionflags that the compiler could not keep in sync (theisSubscriptionProviderKeytype predicate is trusted, not verified). The registry now exports asubscriptionProviderKeyscarrier array with the type derived from it;subscription.tsre-exports the type, and a new unit assertion pins the carrier to thebehavior.subscriptionflags so the type and the data go red in CI if they ever drift. Re-verified:pnpm run verify(351/351) andverify:features(15/15) green.Follow-up (32abe8c)
The route dialog's model picker had a single empty-state message that always pointed at the Providers page — misleading when the real cause is that the selected provider doesn't support the route's endpoint (now that endpoint compatibility filters per provider via the registry). The filter chain is staged (unselected → provider → endpoint → search) so the empty state names its cause: "OpenAI doesn't support the Messages endpoint", "None of your providers support …", search miss with a compatible-model count, all candidates already selected, or genuinely no Provider Models. E2E now exercises the provider-mismatch branch in a real browser;
verify(351/351) andverify:features(15/15) green.Follow-up (32c4565)
The Add Model picker rendered every synced model in one unbounded table (a single provider sync can load dozens). It now pages client-side at 10 rows per page, reusing the
list-paginationfooter styles from the Activity page; the page resets on endpoint/provider/search changes and when the picker opens, and the footer only renders past one page. Covered by a new E2E case (23 seeded models: page walking, disabled edge buttons, filter reset, footer hidden for short result sets, no overflow at 1280/390).verify(351/351) andverify:features(15/15) green.