Repository navigation
[AISAGE-707] feat(admin): configure preferred provider order in the gateway admin - #8507
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryReviewed the incremental commit that adds the Assumptions: Review Notes
Files Reviewed (4 files)
Previous Review Summaries (2 snapshots, latest commit 0d31c16)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 0d31c16)Status: No Issues Found | Recommendation: Merge Executive SummaryReviewed the incremental rewrite that replaces the single Assumptions: Review Notes
Files Reviewed (5 incremental + targeted context)
Previous review (commit 0f3393e)Status: No Issues Found | Recommendation: Merge Executive SummaryReviewed the additive Assumptions: Review Notes
Files Reviewed (15 files)
Reviewed by deepseek-flash-latest · Input: 88.1K · Output: 8.4K · Cached: 447.6K Review guidance: REVIEW.md from base branch |
…provider-order # Conflicts: # packages/db/src/migrations/meta/0292_snapshot.json # packages/db/src/migrations/meta/_journal.json
Summary
Adds a Provider Order tab to
/admin/gatewayfor editing the preferred inference provider order as JSON, using the same Monaco editor as Auto Free. Like the Auto Free, Preferred Models and Vercel model ID override PRs, the config is stored but not used yet:applyPreferredProviderand the catalog pricing inproviders/openrouter/index.tsstill use the built-in rules.0293_preferred_provider_orderadds a nullablepreferred_provider_orderjsonb column to theai_gateway_configsingleton. It is one additiveADD COLUMNwith no backfill.nullmeans nothing is stored. It started as0292in its own commit. Main later added0292_direct_byok_sync_state, so the merge commit regenerates it as0293withdrizzle-kit generate. The DDL is unchanged.PreferredProviderOrderConfigSchemain@kilocode/db/schema-types:{ "rules": [{ "includes": ["openai", "gpt"], "excludes": ["gpt-oss"], "providers": ["openai"] }] }includessubstring and none of the optionalexcludessubstrings. Rules are checked in order and the first match sets the order. If nothing matches, there is no preference.excludescovers cases likegptbut notgpt-osswithin a single rule./for an endpoint variant such asminimax/fp8. Each rule needs at least one include and at least one provider. There are at most 200 rules and 20 providers per rule. Duplicates withinincludes,excludesorprovidersare rejected, and so is a substring that is both included and excluded.ifchain ingetPreferredProviderOrderis nowPREFERRED_PROVIDER_ORDER_FALLBACK_CONFIGplusresolvePreferredProviderOrder(config, model), in the newproviders/preferred-provider-order.ts. The OpenAI rule (openaiorgpt, excludinggpt-oss) reproducesisOpenAiModelexactly, so behavior does not change: the existingapplyPreferredProvidertests pass without edits. The editor's defaults are exactly what the gateway runs today, and connecting the stored config later only means passing a different config.admin.preferredProviderOrder.getreturns{ config, fallback }.admin.preferredProviderOrder.setupserts only the new column, andnullclears it.includessubstrings (joined by "or"), anyexcludes("but not", in red), and the provider chain. A final "Any other model → No preference" row shows the fallthrough. The diagram follows the editor contents live. A badge says whether it shows the stored configuration, the built-in rules, or unsaved changes. If the JSON is invalid, the card asks you to fix it instead of drawing a diagram.findPreferredProviderOrderRuleIndexinpreferred-provider-order.tsbacks the highlight, andresolvePreferredProviderOrdernow uses it too.xlscreens the two cards sit side by side; on smaller screens they stack.Verification
anthropic/claude-sonnet-4.5highlights rule 2, and previewingopenai/gpt-oss-120bskips rule 1 (excluded) and highlights the fallthrough row.tsgo --noEmitpassed forapps/web,@kilocode/dband@kilocode/web-shared(both tsconfigs).oxlintandoxfmtare clean on the changed files, andgit diff --checkpasses.preferred-provider-order.test.ts(with new cases for the rule index),preferred-provider-order-router.test.ts,auto-free-config-router.test.tsandgateway-model-lists-router.test.tspassed.web-test (5/8)failure was a 5sbeforeAlltimeout inorganization-sales-demo-router.test.ts, which this PR does not touch. That suite passes locally, and the timeout does not appear in any of the 25 most recent failed runs of the workflow, so it was a one-off flake on a slow runner.Visual Changes
New "Provider Order" tab under Admin → Gateway, with the editor on the left and the current routing on the right.
gpt-ossis excluded)Reviewer Notes
ai_gateway_configcolumn and touchespage.tsx,admin-router.ts,schema-types.tsand the exact-row router tests. Whichever PR merges second has to regenerate its migration.Follow-up: connect the gateway to the stored config
readDbthrough a 60screateCachedFetch, the same pattern asgetVercelModelIdOverridesandgetUnavailableModelIds. UsePREFERRED_PROVIDER_ORDER_FALLBACK_CONFIGwhen the column isnull, and keep the last valid value when the stored config is invalid.getPreferredProviderOrderthen becomes async.applyPreferredProvideris already called from the asyncapplyProviderSpecificLogic. The catalog pricing loop inopenrouter/index.tsshould load the config once and callresolvePreferredProviderOrderfor each model.Further configuration suggestions
These are hardcoded next to the provider order today and would fit the same
ai_gateway_configpattern:getIgnoredProvidershardcodes "ignoreanthropicfor Claude unless non-trial enterprise or BYOK". This could be a second rule list,{ includes, excludes, ignore, unlessAnthropicAllowed }, or anignorefield on the same rules.allow_fallbacks,only,sort(price/throughput/latency),quantizationsormax_price. Then an incident, such as forcing Kimi onto Bedrock only, can be handled without a deploy.isClaudeModel,isKimiModel,isGlmModeland the others are substring checks spread across provider files, and they also control request transforms. One stored "family → substrings" table would keep provider order, ignores and transforms consistent when a new model name, for example a rebrand, does not match the old substring.match: "substring" | "prefix" | "regex"to each rule. Validate regexes on save and cap their length.organization_idson a rule, which wins over global rules, would cover that without custom code.provider.orderalso goes to Vercel throughopenRouterToVercelInferenceProviderId, which uses the hardcodedopenRouterToVercelInferenceProviderMapping. That mapping could sit next to the Vercel model ID overrides, so a new provider in the order does not also need a deploy.6a. Warn about unknown provider slugs. On save, flag slugs that are not in
OpenRouterInferenceProviderIdSchemaor in the synced endpoint tags. A warning rather than an error, because the enum can lag. A typo would otherwise drop that preference without any sign.kilo-exclusive-models.tsandcustom-pricing.constants.tsare still code. [AISAGE-707] feat(admin): configure custom model pricing in the gateway admin panel #7499 already starts on pricing.ai_gateway_configcolumn.