fix(providers): register BytePlus ModelArk so its API key validates (#3877) (#3935)

This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-15 19:49:51 -03:00
committed by GitHub
parent 15c8df4c78
commit 28c7ced2ec
5 changed files with 53 additions and 1 deletions

View File

@@ -27,6 +27,7 @@
### 🐛 Fixed
- **fix(providers): register BytePlus ModelArk so its API key can be added** — adding a BytePlus (`ark-…`) key reported "invalid". `byteplus` was present in the provider catalog (`APIKEY_PROVIDERS`) but **never registered in the routing registry**, so key validation fell through to `{ unsupported: true }` → HTTP 400 → the UI rendered every key as invalid (and the provider was unusable for inference). Added a registry entry modeled on the existing Volcengine Ark provider: OpenAI-compatible format, base `https://ark.ap-southeast.bytepluses.com/api/v3` (region `ap-southeast-1`), `Authorization: Bearer` auth, seeded with the catalog's advertised models (Seed 2.0, Kimi K2 Thinking, GLM 4.7, GPT-OSS-120B). ([#3877](https://github.com/diegosouzapw/OmniRoute/issues/3877) — thanks @nikohd12)
- **fix(providers): Nous Research key validation no longer fails on a stale probe model** — adding a valid Nous Research API key reported "invalid" even though the same key worked via the portal's copy-shell `curl`. The validation probe sent `model: "nousresearch/hermes-4-70b"`, which Nous does not serve, so the API returned `400` and the validator (which only treated `200`/`429` as success) reported the key invalid. The probe now uses the real `Hermes-4-70B` slug, and any non-auth 4xx (`400`/`404`/`422`) is treated as a valid key (the request shape was wrong, not the credentials) — mirroring the longcat/nvidia validators so a future model rename can't re-break key validation. ([#3881](https://github.com/diegosouzapw/OmniRoute/issues/3881) — thanks @FerLuisxd)
- **test(oauth): prove refresh_token preservation for the real gemini-cli / antigravity dispatch** — the #3679/#3766 regression test used a synthetic provider that routes through the generic `tokenUrl` path, so the fix was never proven for the actual Google-family providers, which dispatch through `refreshGoogleToken()` against the hardcoded `OAUTH_ENDPOINTS.google.token`. Added a test that drives `checkConnection` through the real `gemini-cli`/`antigravity` path (redirecting the Google token endpoint to a local server returning `invalid_grant`) and asserts the `refresh_token` is preserved (not nulled) — confirming these connections are not spuriously destroyed on a failed refresh. ([#3850](https://github.com/diegosouzapw/OmniRoute/issues/3850) — thanks @3xa228148)
- **fix(oauth): clear setup message for GitLab Duo instead of "Internal server error"** — adding a GitLab Duo connection without a registered OAuth client returned an opaque `Internal server error` at the Add Connection step. `buildAuthUrl` **threw** when `GITLAB_DUO_OAUTH_CLIENT_ID` was missing, and the route swallowed it into a generic 500. It now returns `null` (mirroring the Qoder provider) and the authorize route surfaces an actionable message: register an OAuth app at `https://gitlab.com/-/profile/applications` with redirect URI `http://localhost:20128/callback` and scopes `ai_features read_user`, then set `GITLAB_DUO_OAUTH_CLIENT_ID`. ([#3861](https://github.com/diegosouzapw/OmniRoute/issues/3861) — thanks @sidinsearch)

View File

@@ -10,9 +10,10 @@
"_rebaseline_2026_06_15_3871_empty_pool": "PR #3871 own growth: combo.ts 5203->5204 (+1 = guard expandAutoComboCandidatePool against an empty candidatePool array — Array.isArray(pool) && pool.length > 0 so [] falls through to active-connection expansion instead of early-returning). One-line correctness fix; not extractable.",
"_rebaseline_2026_06_15_3911_sse_role": "PR #3911 own growth: openai-responses.ts 878->903 (+25 = withAssistantRoleOnFirstDelta wrapper that synthesizes role=assistant on the first Responses->Chat delta so strict streaming clients (langchain/n8n) do not drop tool_call deltas). Cohesive translator fix; not extractable.",
"_rebaseline_2026_06_15_3881_nous_validator": "Issue #3881 own growth: validation.ts 4394->4407 (+13 = treat any non-auth 4xx (400/404/422) from the Nous Research probe as a valid key — the request shape/model was wrong, not the credentials — mirroring the longcat/nvidia validators, plus the corrected Hermes-4-70B probe model). Cohesive validator branch; not extractable.",
"_rebaseline_2026_06_15_3877_byteplus": "Issue #3877 own growth: providerRegistry.ts 4708->4730 (+22 = a byteplus (BytePlus ModelArk) registry entry — OpenAI-compatible, Ark base ap-southeast-1, Bearer, 4 seed models — modeled on the volcengine entry; byteplus was in APIKEY_PROVIDERS but unregistered here so validation returned {unsupported:true}). Cohesive provider registration; not extractable.",
"cap": 800,
"frozen": {
"open-sse/config/providerRegistry.ts": 4708,
"open-sse/config/providerRegistry.ts": 4730,
"open-sse/executors/antigravity.ts": 1649,
"open-sse/executors/base.ts": 1218,
"open-sse/executors/chatgpt-web.ts": 2870,

View File

@@ -4251,6 +4251,28 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
],
},
// #3877: byteplus was in the APIKEY_PROVIDERS catalog but never registered here, so key
// validation fell through to {unsupported:true} → the UI reported every key "invalid".
// BytePlus ModelArk is an OpenAI-compatible surface (region ap-southeast-1) authed with a
// Bearer ark-... key — same shape as the volcengine (Volcengine Ark) entry, different host.
byteplus: {
id: "byteplus",
alias: "bpm",
format: "openai",
executor: "default",
baseUrl: "https://ark.ap-southeast.bytepluses.com/api/v3/chat/completions",
modelsUrl: "https://ark.ap-southeast.bytepluses.com/api/v3/models",
authType: "apikey",
authHeader: "bearer",
defaultContextLength: 128000,
models: [
{ id: "seed-2.0", name: "Seed 2.0" },
{ id: "kimi-k2-thinking", name: "Kimi K2 Thinking", supportsReasoning: true },
{ id: "glm-4.7", name: "GLM 4.7" },
{ id: "gpt-oss-120b", name: "GPT-OSS-120B" },
],
},
bluesminds: {
id: "bluesminds",
alias: "bm",

View File

@@ -40,6 +40,7 @@ const CHAT_OPENAI_COMPAT_PROVIDER_IDS = [
"predibase",
"bytez",
"reka",
"byteplus",
];
test("chat-openai-compat providers are registered across provider metadata, registry and local catalog", () => {

View File

@@ -1802,6 +1802,33 @@ test("specialty validator accepts Nous Research credentials on chat completions"
assert.equal(nous.method, "nous_chat_completions");
});
test("BytePlus key validation reaches the Ark endpoint instead of 'not supported' (#3877)", async () => {
// #3877: byteplus was in APIKEY_PROVIDERS but never registered in the routing
// registry, so validation returned {unsupported:true} → UI showed "invalid" for any
// key. With the registry entry, a valid ark-... key probes the Ark /models endpoint
// with Bearer auth and validates.
let probedModelsUrl: string | null = null;
globalThis.fetch = async (url, init = {}) => {
const target = String(url);
if (target === "https://ark.ap-southeast.bytepluses.com/api/v3/models") {
probedModelsUrl = target;
const headers = toPlainHeaders(init.headers);
assert.equal(headers.Authorization, "Bearer ark-test-key");
return new Response(JSON.stringify({ data: [{ id: "kimi-k2-thinking" }] }), { status: 200 });
}
throw new Error(`unexpected fetch: ${target}`);
};
const result = await validateProviderApiKey({
provider: "byteplus",
apiKey: "ark-test-key",
});
assert.equal(result.unsupported, undefined, "byteplus must not be 'validation not supported'");
assert.equal(result.valid, true);
assert.equal(probedModelsUrl, "https://ark.ap-southeast.bytepluses.com/api/v3/models");
});
test("specialty validator rejects invalid Nous Research credentials", async () => {
globalThis.fetch = async (url, init = {}) => {
const target = String(url);