fix(providers): single source for provider order, resolve xao rank (#12790)

Validado numa worktree combinada com a onda de roteamento/free-tier desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-api-typecheck OK (288), check-file-size OK após rebaseline, 84/85 nos testes focados.

Duas cópias da mesma ordem de provider com um "keep in sync" implícito é dívida que cobra juros a cada provider novo. Uma definição com re-export nos dois lados resolve a classe.

O `xao/*` ordenando depois de todo provider conhecido em vez de junto do `xai-oauth` é um sintoma concreto de que a duplicação já estava divergindo.

**Integração:** `scripts/quality/run-all-gates.mjs` conflitou com o `check:pricing-freshness` que entrou pelo #12792 na mesma onda. Aditivo — os dois gates coexistem.
This commit is contained in:
Dizzle
2026-09-10 14:13:10 +02:00
committed by GitHub
parent 9795372ef1
commit d68c8c869e
12 changed files with 211 additions and 40 deletions

View File

@@ -1,5 +1,6 @@
import { NextResponse } from "next/server";
import {
getProviderConnectionFamilyIds,
isClaudeCodeCompatibleProvider,
isAnthropicCompatibleProvider,
isOpenAICompatibleProvider,
@@ -2343,7 +2344,7 @@ export async function GET(
const data = await response.json();
let pageModels = config.parseResponse(data);
if (provider === "alibaba" || provider === "alibaba-cn") {
if (getProviderConnectionFamilyIds("alibaba").includes(provider)) {
const { parseAlibabaModelStudioModelsForConnection } =
await import("./discovery/providerModelsConfig.ts");
pageModels = parseAlibabaModelStudioModelsForConnection(
@@ -2372,7 +2373,7 @@ export async function GET(
);
}
if (provider === "alibaba" || provider === "alibaba-cn") {
if (getProviderConnectionFamilyIds("alibaba").includes(provider)) {
const { shouldUseLiveAlibabaFreeModelDiscovery } =
await import("@omniroute/open-sse/services/alibabaFreeTier.ts");
const { scheduleAlibabaFreeTierProbeRefresh } =

View File

@@ -24,14 +24,7 @@
* effort-variant scoping, and Claude-mirror gating are untouched. Pure; no DB/IO.
*/
import { OAUTH_PROVIDERS, NOAUTH_PROVIDERS, APIKEY_PROVIDERS } from "@/shared/constants/providers";
/** Canonical provider precedence, keyed by provider id (not alias). Built once. */
const CANONICAL_PROVIDER_ORDER: readonly string[] = [
...Object.keys(OAUTH_PROVIDERS),
...Object.keys(NOAUTH_PROVIDERS),
...Object.keys(APIKEY_PROVIDERS),
];
import { COMBO_GROUP, groupSortPriority } from "@/shared/constants/canonicalProviderOrder";
/**
* Locale-independent code-unit comparator. UTF-16 code units put uppercase A-Z
@@ -41,9 +34,6 @@ function codeUnitCompare(a: string, b: string): number {
return a < b ? -1 : a > b ? 1 : 0;
}
/** Combo bucket key, distinct from any real provider id. */
const COMBO_GROUP = " combo";
/**
* Grouping key for a catalog row: "combo" for combo-owned rows, else owned_by
* (canonical identity). Rows with no usable owned_by fall back to the id-prefix;
@@ -59,16 +49,6 @@ function modelGroupKey(model: Record<string, unknown>): string {
return slash > 0 ? id.slice(0, slash) : id;
}
/**
* Sort priority for a group key: combo first, then registry precedence, then
* unknown groups (rank Infinity, ordered among themselves by code unit).
*/
function groupSortPriority(groupKey: string): number {
if (groupKey === COMBO_GROUP) return -1;
const idx = CANONICAL_PROVIDER_ORDER.indexOf(groupKey);
return idx >= 0 ? idx : Infinity;
}
/**
* Stable provider-grouped sort. Does not mutate the input. Deterministic for a
* given input array.