feat(alibaba): free-tier routing with live quota sync (#8893)

* feat(alibaba): add free-tier routing with console quota and builtin allowlist

Classify DashScope free vs paid models via console quota API, a hardcoded
operator allowlist fallback, and per-connection drained tracking. Wire wildcard
combo expansion, model refresh, combo exhaustion, and audit redaction for
Alibaba console credentials.

* fix(routing): reset forced connection pin and persist Alibaba free-tier drain

Drop session affinity pins when a forced connection is excluded after 429,
and record Alibaba free-tier exhaustion on upstream 403 so per-key drained
lists stay accurate without blocking sibling keys.

* fix(alibaba): prefer live quota sync over static free-tier allowlist

Stop unioning the builtin text allowlist when a console quota snapshot exists,
treat expired quotaValidityPeriod as not_capable, and add a dated JSON pack plus
sync-alibaba-allowlist script for operator refresh without code edits.

* docs(alibaba): document free-tier console path + allowlist env overrides

Adds the 4 ALIBABA_FREE_TIER_*_FE_PATH / ALIBABA_FREE_TIER_ALLOWLIST_PATH
env vars (referenced by alibabaFreeTierQuotaFetcher.ts and
alibabaFreeTierAllowlist.ts) to .env.example and
docs/reference/ENVIRONMENT.md so the env/docs contract check passes.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* refactor(open-sse): split alibabaFreeTierQuotaFetcher.ts under file-size cap

Extract pure parsing/classification/eligibility-filtering logic into
alibabaFreeTierQuotaClassify.ts and shared types/primitives into
alibabaFreeTierQuotaTypes.ts, leaving the HTTP/console-fetch flow in the
original file. Public API is unchanged (re-exported), behavior is identical.

Co-authored-by: AndrianBalanescu <AndrianBalanescu@users.noreply.github.com>

* fix: resolve typecheck errors in alibaba-free-tier routing

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: AndrianBalanescu <AndrianBalanescu@users.noreply.github.com>
Co-authored-by: AndrianBalanescu <andrian@balanescu.dev>
This commit is contained in:
Andrew B.
2026-08-11 02:25:23 -05:00
committed by GitHub
parent 9a99a39b33
commit 21fd0a94f8
33 changed files with 4878 additions and 31 deletions

View File

@@ -20,35 +20,18 @@ import {
} from "@omniroute/open-sse/config/providers/registry/kimi/coding/runtime.ts";
import { ALIBABA_MODEL_STUDIO_MODELS } from "@omniroute/open-sse/config/providers/registry/alibaba/index.ts";
import { QWEN_CLOUD_TEXT_MODELS } from "@omniroute/open-sse/config/providers/registry/qwen-cloud/index.ts";
import { filterAlibabaFreeEligibleModels } from "@omniroute/open-sse/services/alibabaFreeTierDiscovery.ts";
import { shouldUseLiveAlibabaFreeModelDiscovery } from "@omniroute/open-sse/services/alibabaFreeTier.ts";
import { isDashscopeTextModelId } from "@omniroute/open-sse/services/dashscopeTextModels.ts";
import { extractZaiToken } from "@omniroute/open-sse/executors/zai-web.ts";
import { normalizeOpenAiLikeModelsResponse } from "./normalizers";
const DASHSCOPE_TEXT_MODEL_PREFIXES = [
"qwen",
"qwq-",
"deepseek-",
"glm-",
"kimi-",
"minimax-",
] as const;
// DashScope's OpenAI-compatible /models response contains only the standard
// id/object/owned_by fields for Alibaba and Qwen Cloud, so there is no upstream
// modality field to filter on. Keep known text-generation families and reject IDs
// whose tokenized names identify media, speech, embedding, reranking, or vision-only lines.
const DASHSCOPE_NON_TEXT_MODEL_TOKEN =
/(?:^|[-_.\/])(?:asr|audio|captioner|embedding|image|livetranslate|omni|ocr|realtime|rerank|s2s|speech|tts|video|vl)(?:$|[-_.\/])/i;
const QWEN_CLOUD_TEXT_MODEL_IDS = new Set(QWEN_CLOUD_TEXT_MODELS.map((model) => model.id));
const ALIBABA_MODEL_STUDIO_MODEL_IDS = new Set(
ALIBABA_MODEL_STUDIO_MODELS.map((model) => model.id)
);
export function isDashscopeTextModelId(value: unknown): value is string {
if (typeof value !== "string") return false;
const modelId = value.trim().toLowerCase();
if (!modelId || DASHSCOPE_NON_TEXT_MODEL_TOKEN.test(modelId)) return false;
return DASHSCOPE_TEXT_MODEL_PREFIXES.some((prefix) => modelId.startsWith(prefix));
}
export { isDashscopeTextModelId };
export function parseDashscopeTextModels(data: any): any[] {
const models = Array.isArray(data?.data)
@@ -83,6 +66,23 @@ export function parseAlibabaModelStudioModels(data: any): any[] {
);
}
export function parseAlibabaModelStudioModelsForConnection(
data: any,
providerSpecificData?: Record<string, unknown> | null
): any[] {
if (shouldUseLiveAlibabaFreeModelDiscovery(providerSpecificData)) {
const models = parseDashscopeTextModels(data);
const eligibleIds = new Set(
filterAlibabaFreeEligibleModels(
models.map((model: { id?: string }) => model.id).filter(Boolean) as string[],
providerSpecificData
)
);
return models.filter((model: { id?: string }) => model.id && eligibleIds.has(model.id));
}
return parseAlibabaModelStudioModels(data);
}
export function parseQwenCloudTextModels(data: any): any[] {
return parseCuratedDashscopeModels(data, QWEN_CLOUD_TEXT_MODELS, QWEN_CLOUD_TEXT_MODEL_IDS);
}

View File

@@ -2322,7 +2322,15 @@ export async function GET(
}
const data = await response.json();
const pageModels = config.parseResponse(data);
let pageModels = config.parseResponse(data);
if (provider === "alibaba" || provider === "alibaba-cn") {
const { parseAlibabaModelStudioModelsForConnection } =
await import("./discovery/providerModelsConfig.ts");
pageModels = parseAlibabaModelStudioModelsForConnection(
data,
connection.providerSpecificData as Record<string, unknown> | null | undefined
);
}
allModels = allModels.concat(pageModels);
const nextPageToken = data.nextPageToken;
@@ -2344,6 +2352,45 @@ export async function GET(
);
}
if (provider === "alibaba" || provider === "alibaba-cn") {
const { shouldUseLiveAlibabaFreeModelDiscovery } =
await import("@omniroute/open-sse/services/alibabaFreeTier.ts");
const { scheduleAlibabaFreeTierProbeRefresh } =
await import("@omniroute/open-sse/services/alibabaFreeTierDiscovery.ts");
const { scheduleAlibabaFreeTierQuotaRefresh, hasAlibabaConsoleFreeTierAuth } =
await import("@omniroute/open-sse/services/alibabaFreeTierQuotaFetcher.ts");
const { resolveAlibabaProviderBaseUrl } =
await import("@/shared/constants/alibabaProviderRegions.ts");
const providerSpecificData = connection.providerSpecificData as Record<
string,
unknown
> | null;
if (shouldUseLiveAlibabaFreeModelDiscovery(providerSpecificData)) {
if (hasAlibabaConsoleFreeTierAuth(providerSpecificData)) {
scheduleAlibabaFreeTierQuotaRefresh(provider, {
id: connectionId,
providerSpecificData,
});
} else {
const baseUrl = resolveAlibabaProviderBaseUrl(
provider,
providerSpecificData,
paginationBaseUrl.replace(/\/models$/, "")
);
scheduleAlibabaFreeTierProbeRefresh(
provider,
{
id: connectionId,
apiKey: token,
providerSpecificData,
},
allModels,
`${baseUrl.replace(/\/$/, "")}/chat/completions`
);
}
}
}
return buildApiDiscoveryResponse(allModels);
} catch (error) {
if (error instanceof SafeOutboundFetchError && error.code === "URL_GUARD_BLOCKED") {