mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 14:42:20 +03:00
fix(sse): canonicalize alias provider ids before quota fetcher lookup (#10877)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- **fix(sse):** `getResetAwareProvider()` and the auto-combo quota lookup in `combo.ts` now canonicalize the provider id via `resolveProviderId()` before calling `getQuotaFetcher()`, so a fetcher registered under a provider's canonical id (e.g. `ollama-cloud`, `codex`) is found for combo targets stored under an alias spelling (e.g. `ollamacloud`, `cx`) instead of silently degrading reset-aware/reset-window/auto quota-aware routing to plain priority ordering (#10877)
|
||||
@@ -64,6 +64,7 @@ import { getHiddenModelsByProvider } from "@/models";
|
||||
import { resolveModelLockoutSettings } from "../../src/lib/resilience/modelLockoutSettings";
|
||||
import { fetchCodexQuota } from "./codexQuotaFetcher.ts";
|
||||
import { evaluateQuotaCutoff, getQuotaFetcher, type QuotaInfo } from "./quotaPreflight.ts";
|
||||
import { resolveProviderId } from "../../src/shared/constants/providers.ts";
|
||||
import * as semaphore from "./rateLimitSemaphore.ts";
|
||||
import { getCircuitBreaker } from "../../src/shared/utils/circuitBreaker";
|
||||
import { parseModel } from "./model.ts";
|
||||
@@ -491,7 +492,10 @@ export async function buildAutoCandidates(
|
||||
let quotaRemaining = 100;
|
||||
let quotaCutoffBlocked = false;
|
||||
let quotaCutoffReason: string | undefined;
|
||||
const fetcher = getQuotaFetcher(provider);
|
||||
// #10877: `provider` here may be a legacy/user-facing alias spelling
|
||||
// (target.provider/parseModel output); canonicalize before the fetcher
|
||||
// registry lookup so aliased combo members still hit quota-aware scoring.
|
||||
const fetcher = getQuotaFetcher(resolveProviderId(provider));
|
||||
const connection = target.connectionId ? connectionById.get(target.connectionId) : undefined;
|
||||
const authType = typeof connection?.authType === "string" ? connection.authType : null;
|
||||
const sessionAvailability =
|
||||
|
||||
@@ -14,6 +14,7 @@ import { isRecord } from "./comboData.ts";
|
||||
import type { SlaRoutingPolicy } from "../autoCombo/routerStrategy.ts";
|
||||
import { RESET_WINDOW_NAMES } from "./types.ts";
|
||||
import type { ResolvedComboTarget } from "./types.ts";
|
||||
import { resolveProviderId } from "../../../src/shared/constants/providers.ts";
|
||||
|
||||
const RESET_AWARE_SESSION_WINDOW_MS = 5 * 60 * 60 * 1000;
|
||||
const RESET_AWARE_WEEKLY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
||||
@@ -138,7 +139,11 @@ export function resolveSlaRoutingPolicy(
|
||||
|
||||
export function getResetAwareProvider(target: ResolvedComboTarget): string | null {
|
||||
const provider = (target.providerId || target.provider || "").toLowerCase();
|
||||
return provider || null;
|
||||
// #10877: combo targets can carry a legacy/user-facing alias spelling
|
||||
// (e.g. "ollamacloud", "cx") while quota fetchers register under the
|
||||
// canonical provider id (e.g. "ollama-cloud", "codex"). Canonicalize here
|
||||
// so getQuotaFetcher() lookups downstream (quotaStrategies.ts) find them.
|
||||
return provider ? resolveProviderId(provider) : null;
|
||||
}
|
||||
|
||||
function normalizeResetAt(value: unknown): string | null {
|
||||
|
||||
66
tests/unit/quota-scoring-alias-lookup-10877.test.ts
Normal file
66
tests/unit/quota-scoring-alias-lookup-10877.test.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { getResetAwareProvider } from "../../open-sse/services/combo/quotaScoring.ts";
|
||||
import { registerQuotaFetcher, getQuotaFetcher } from "../../open-sse/services/quotaPreflight.ts";
|
||||
import { resolveProviderId } from "../../src/shared/constants/providers.ts";
|
||||
import type { ResolvedComboTarget } from "../../open-sse/services/combo/types.ts";
|
||||
|
||||
function buildTarget(provider: string): ResolvedComboTarget {
|
||||
return {
|
||||
kind: "model",
|
||||
stepId: "s1",
|
||||
executionKey: "e1",
|
||||
modelStr: `${provider}/some-model`,
|
||||
provider,
|
||||
providerId: provider,
|
||||
connectionId: "conn-1",
|
||||
weight: 1,
|
||||
label: null,
|
||||
} as ResolvedComboTarget;
|
||||
}
|
||||
|
||||
test("#10877: getResetAwareProvider() canonicalizes an alias-spelled provider so the fetcher registered under the canonical id is found", () => {
|
||||
registerQuotaFetcher("ollama-cloud", async () => ({ ok: true }) as never);
|
||||
|
||||
const target = buildTarget("ollamacloud");
|
||||
const lookedUpProvider = getResetAwareProvider(target);
|
||||
|
||||
assert.equal(
|
||||
lookedUpProvider,
|
||||
resolveProviderId("ollamacloud"),
|
||||
"getResetAwareProvider() should return the canonical provider id, not the raw alias"
|
||||
);
|
||||
|
||||
const fetcher = getQuotaFetcher(lookedUpProvider!);
|
||||
assert.notEqual(
|
||||
fetcher,
|
||||
undefined,
|
||||
"a fetcher registered under the canonical provider id must be found for an alias-spelled combo target"
|
||||
);
|
||||
});
|
||||
|
||||
test("#10877: getResetAwareProvider() is a no-op (same cache key) for already-canonical provider ids", () => {
|
||||
registerQuotaFetcher("codex", async () => ({ ok: true }) as never);
|
||||
|
||||
const target = buildTarget("codex");
|
||||
const lookedUpProvider = getResetAwareProvider(target);
|
||||
|
||||
assert.equal(lookedUpProvider, "codex");
|
||||
assert.notEqual(getQuotaFetcher(lookedUpProvider!), undefined);
|
||||
});
|
||||
|
||||
test("#10877: getResetAwareProvider() returns null when neither providerId nor provider is set", () => {
|
||||
const target = {
|
||||
kind: "model",
|
||||
stepId: "s1",
|
||||
executionKey: "e1",
|
||||
modelStr: "unknown/model",
|
||||
provider: "",
|
||||
providerId: "",
|
||||
connectionId: "conn-1",
|
||||
weight: 1,
|
||||
label: null,
|
||||
} as ResolvedComboTarget;
|
||||
|
||||
assert.equal(getResetAwareProvider(target), null);
|
||||
});
|
||||
Reference in New Issue
Block a user