mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-09 00:12:15 +03:00
Follow-up to #11762/#11774, same bug class in combo's own model-lockout wiring: GitHub rejects several models (gpt-5.4, gpt-5.3-codex, etc.) with a 400 that's permanently unavailable for this account's Copilot integration, but nothing recorded a cross-request lockout — combo's #5249 in-request advance guard is correct but doesn't persist, so the same doomed model gets retried from scratch on every new request, indefinitely. Fix: on a model-scoped 400 (`isModelScoped400`), call `lockModelIfPerModelQuota(provider, connectionId, rawModel, "model_capacity", 1h)`. GitHub already has per-model-quota enabled, so only the rejected model locks — siblings keep working. `isModelLocked()` is already checked pre-dispatch, so no other wiring needed. Validated: 3/3 new tests + fixed a pre-existing test-isolation gap in combo-model-scoped-400-advance.test.ts (shared model name across sub-tests without clearing lockout state). Thanks!
51 lines
2.2 KiB
TypeScript
51 lines
2.2 KiB
TypeScript
/**
|
|
* Regression guard: a GitHub Copilot 400 "The requested model is not
|
|
* supported" / "not available for integrator ..." — permanent for THIS
|
|
* account/integration — must get locked out via lockModelIfPerModelQuota so
|
|
* future, separate requests skip the same dead model instead of retrying it
|
|
* on every single auto-combo request forever (observed: every request in
|
|
* production logs wasted several upstream 400 calls on the same GitHub
|
|
* models — gpt-5.4, gpt-5.5, gpt-5.6-luna, etc. — all day).
|
|
*
|
|
* Combo's existing #5249/#2101 guard already lets the current request keep
|
|
* rotating to the next target — that behavior is unchanged and untested
|
|
* here. This guards the NEW cross-request lockout side effect only.
|
|
*/
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const { isModelScoped400 } = await import("../../open-sse/services/combo/comboPredicates.ts");
|
|
const { lockModelIfPerModelQuota, isModelLocked, hasPerModelQuota } =
|
|
await import("../../open-sse/services/accountFallback.ts");
|
|
|
|
const GITHUB_NOT_SUPPORTED_400 = "[400]: The requested model is not supported.";
|
|
const GITHUB_INTEGRATOR_400 =
|
|
'[400]: The requested model is not available for integrator "vscode-chat". ' +
|
|
"Available models: [gpt-4.1 claude-fable-5]. Verify the correct Copilot-Integration-Id header is being sent.";
|
|
|
|
test("isModelScoped400 matches GitHub's two 'model not supported' phrasings", () => {
|
|
assert.equal(isModelScoped400(GITHUB_NOT_SUPPORTED_400), true);
|
|
assert.equal(isModelScoped400(GITHUB_INTEGRATOR_400), true);
|
|
});
|
|
|
|
test("github has per-model quota (locks the model, not the whole connection)", () => {
|
|
assert.equal(hasPerModelQuota("github"), true);
|
|
});
|
|
|
|
test("lockModelIfPerModelQuota locks a model-not-supported GitHub model for future requests", () => {
|
|
const connectionId = `github-${Date.now()}`;
|
|
|
|
const locked = lockModelIfPerModelQuota(
|
|
"github",
|
|
connectionId,
|
|
"gpt-5.4",
|
|
"model_capacity",
|
|
60 * 60 * 1000
|
|
);
|
|
|
|
assert.equal(locked, true);
|
|
assert.equal(isModelLocked("github", connectionId, "gpt-5.4"), true);
|
|
// A sibling model on the same connection must stay eligible.
|
|
assert.equal(isModelLocked("github", connectionId, "gpt-5.5"), false);
|
|
});
|