diff --git a/changelog.d/fixes/12800-cliproxyapi-unknown-provider.md b/changelog.d/fixes/12800-cliproxyapi-unknown-provider.md new file mode 100644 index 0000000000..cc0a6e2f21 --- /dev/null +++ b/changelog.d/fixes/12800-cliproxyapi-unknown-provider.md @@ -0,0 +1 @@ +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) diff --git a/open-sse/services/accountFallback.ts b/open-sse/services/accountFallback.ts index 1d9e79cbb1..5d947d429a 100644 --- a/open-sse/services/accountFallback.ts +++ b/open-sse/services/accountFallback.ts @@ -376,7 +376,7 @@ export const MODEL_ACCESS_DENIED_PATTERNS = [ /\bunsupported\s+model\b/i, /\baccess.*denied.*model\b/i, /\bmodel.*access.*denied\b/i, - /\bplease select a different model\b/i, + /\bplease select a different model\b/i, /\bunknown\s+provider\s+for\s+model\b/i, // "...access to the requested model" / "model ... access" — bounded lookahead // (no nested quantifiers) so it stays ReDoS-safe while requiring BOTH an // access/permission word and "model" so a pure auth error never matches. @@ -416,7 +416,7 @@ const PROVIDER_MODEL_UNSUPPORTED_PATTERNS = [ /\bmodel\b[\s\S]{0,80}?\b(?:does\s+not\s+support|doesn't\s+support|unsupported)\b/i, /\b(?:does\s+not\s+support|doesn't\s+support|unsupported)\b[\s\S]{0,80}?\bmodel\b/i, /\bunsupported\s+model\b/i, - /\bplease select a different model\b/i, + /\bplease select a different model\b/i, /\bunknown\s+provider\s+for\s+model\b/i, ]; /** diff --git a/tests/unit/cliproxyapi-unknown-provider-400-12800.test.ts b/tests/unit/cliproxyapi-unknown-provider-400-12800.test.ts new file mode 100644 index 0000000000..f0871e581b --- /dev/null +++ b/tests/unit/cliproxyapi-unknown-provider-400-12800.test.ts @@ -0,0 +1,26 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; + +import { + MODEL_ACCESS_DENIED_PATTERNS, + isProviderModelUnsupported400, +} from "../../open-sse/services/accountFallback.ts"; + +const CLIPROXYAPI_ERROR_TEXT = "unknown provider for model Qwen/Qwen3.6-27B-TEE"; + +describe("#12800 — CLIProxyAPI 'unknown provider for model X' classification", () => { + it("MODEL_ACCESS_DENIED_PATTERNS should recognize it as a model-access-denied 400", () => { + const matches = MODEL_ACCESS_DENIED_PATTERNS.some((p) => p.test(CLIPROXYAPI_ERROR_TEXT)); + assert.equal(matches, true); + }); + + it("isProviderModelUnsupported400() should recognize it as provider-wide unsupported", () => { + const result = isProviderModelUnsupported400(400, CLIPROXYAPI_ERROR_TEXT); + assert.equal(result, true); + }); + + it("should not match a genuine auth/credential error", () => { + const authText = "invalid api key for model Qwen/Qwen3.6-27B-TEE"; + assert.equal(isProviderModelUnsupported400(400, authText), false); + }); +});