mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 06:02:14 +03:00
* fix(ci): route the Gemini Web b64_json download error through sanitizeErrorMessage() open-sse/handlers/imageGeneration/providers/geminiWeb.ts embedded a raw err.message in the b64_json download-failure response, tripping check:error-helper (Hard Rule #12) on release/v3.8.50. Refs #9985. * fix(tests): drain test-drift base-reds left by #10603/#10537 and a stale qwen-web catalog id Several base-reds on release/v3.8.50 (#9985) share one root cause: a legitimate product change landed without updating the test asserting the old behavior. - tests/unit/glm-provider-model-import-route.test.ts (12 tests) and tests/unit/model-sync-route.test.ts (2 tests) predate #10603, which made upstream model sync opt-in (isAutoFetchModelsEnabled() now requires providerSpecificData.autoFetchModels === true) and made manual custom-model overrides survive a sync instead of being demoted. Updated both files to opt in / assert the new preserve-manual-overrides behavior, with a comment citing #10603. - tests/unit/antigravity-model-aliases.test.ts predates #10537, which retired the collapsed 'gemini-3.7-flash' alias (upstream 'gemini-3.7-flash-tiered') in favor of the three directly-callable tiered ids. Dropped the retired id from EXPECTED_FLASH_TIERS. - open-sse/config/freeModelCatalog.data.ts: the qwen-web free-catalog entry still listed the retired 'qwen3.8-max-preview' id instead of the current 'qwen3.8-max' (open-sse/config/providers/registry/qwen/web/index.ts and the executor's compat alias both confirm 'qwen3.8-max' is canonical). Real data drift, not test drift. - src/i18n/messages/zh-TW.json: providers.autoFetchModelsTooltip (added by #10603) used the mainland term 緩存 instead of the zh-TW glossary-canonical 快取, tripping the i18n-glossary-consistency-check base-red. - src/lib/oauth/providers/zed-hosted.ts: removed an unused default export (the named export already covers every consumer) — shaves one symbol off the check:dead-code ratchet (419 -> 418; baseline 415, 3 still outstanding). Refs #9985. --------- Co-authored-by: Markus Hartung <mail@hartmark.se>
This commit is contained in:
committed by
GitHub
parent
65e1960029
commit
da0088df99
@@ -0,0 +1,3 @@
|
||||
- **fix(ci):** route `open-sse/handlers/imageGeneration/providers/geminiWeb.ts`'s b64_json
|
||||
download-failure message through `sanitizeErrorMessage()` instead of embedding a raw
|
||||
`err.message`, clearing the `check:error-helper` base-red on `release/v3.8.50` (#9985).
|
||||
@@ -0,0 +1,12 @@
|
||||
- **fix(tests):** drain several base-reds on `release/v3.8.50` (#9985) that were all instances
|
||||
of the same pattern — a legitimate product change landed without updating the test that
|
||||
asserted the old behavior: `tests/unit/glm-provider-model-import-route.test.ts` (12 tests)
|
||||
and `tests/unit/model-sync-route.test.ts` (2 tests) predate #10603's "upstream model sync is
|
||||
opt-in and manual overrides are preserved" change; `tests/unit/antigravity-model-aliases.test.ts`
|
||||
predated #10537 retiring the collapsed `gemini-3.7-flash` alias in favor of its three tiered
|
||||
ids. Also fixes a real data drift in `open-sse/config/freeModelCatalog.data.ts` (the `qwen-web`
|
||||
free-catalog entry still pointed at the retired `qwen3.8-max-preview` id instead of the
|
||||
current `qwen3.8-max`), corrects the zh-TW `providers.autoFetchModelsTooltip` string to the
|
||||
glossary-canonical 快取 instead of 緩存, and removes an unused default export from
|
||||
`src/lib/oauth/providers/zed-hosted.ts` (the named export already covers every consumer) to
|
||||
shave one symbol off the `check:dead-code` ratchet regression.
|
||||
@@ -361,7 +361,7 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [
|
||||
{ provider: "qoder", modelId: "deepseek-v4-pro", displayName: "DeepSeek-V4-Pro", monthlyTokens: 0, creditTokens: 1000000, freeType: "one-time-initial", poolKey: "qoder", tos: "caution" },
|
||||
{ provider: "qoder", modelId: "deepseek-v4-flash", displayName: "DeepSeek-V4-Flash", monthlyTokens: 0, creditTokens: 1000000, freeType: "one-time-initial", poolKey: "qoder", tos: "caution" },
|
||||
{ provider: "qoder", modelId: "minimax-m3", displayName: "MiniMax-M3", monthlyTokens: 0, creditTokens: 1000000, freeType: "one-time-initial", poolKey: "qoder", tos: "caution" },
|
||||
{ provider: "qwen-web", modelId: "qwen3.8-max-preview", displayName: "Qwen3.8 Max Preview", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" },
|
||||
{ provider: "qwen-web", modelId: "qwen3.8-max", displayName: "Qwen3.8 Max", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" },
|
||||
{ provider: "qwen-web", modelId: "qwen3.7-max", displayName: "Qwen3.7 Max", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" },
|
||||
{ provider: "qwen-web", modelId: "qwen3.7-plus", displayName: "Qwen3.7 Plus", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" },
|
||||
{ provider: "qwen-web", modelId: "qwen3.6-plus", displayName: "Qwen3.6 Plus", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" },
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import { GeminiWebExecutor } from "../../../executors/gemini-web.ts";
|
||||
import { fetchRemoteImage } from "@/shared/network/remoteImageFetch";
|
||||
import { saveImageErrorResult, saveImageSuccessResult } from "../../imageGeneration.ts";
|
||||
import { sanitizeErrorMessage } from "../../../utils/error.ts";
|
||||
|
||||
/** Each image is one gemini.google.com turn (~30-60s). Cap like chatgpt-web. */
|
||||
const GEMINI_WEB_IMAGE_N_MAX = 4;
|
||||
@@ -210,7 +211,7 @@ export async function handleGeminiWebImageGeneration({
|
||||
model,
|
||||
status: 502,
|
||||
startTime,
|
||||
error: `Gemini Web generated an image but OmniRoute could not download it for b64_json conversion: ${err instanceof Error ? err.message : String(err)}`,
|
||||
error: `Gemini Web generated an image but OmniRoute could not download it for b64_json conversion: ${sanitizeErrorMessage(err instanceof Error ? err.message : String(err))}`,
|
||||
requestBody,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6350,7 +6350,7 @@
|
||||
"autoFetchModels": "自動獲取上游模型",
|
||||
"autoFetchModelsDisabled": "上游模型自動獲取已禁用",
|
||||
"autoFetchModelsEnabled": "上游模型自動獲取已啟用",
|
||||
"autoFetchModelsTooltip": "在需要時獲取並緩存上游模型",
|
||||
"autoFetchModelsTooltip": "在需要時獲取並快取上游模型",
|
||||
"autoFetchModelsToggleFailed": "無法切換上游模型自動獲取",
|
||||
"autoFetchModelsPartialFailure": "某些連接已更新,但上游模型自動獲取並未在所有地方更改",
|
||||
"overridesUpstreamModel": "覆蓋上游",
|
||||
|
||||
@@ -126,5 +126,3 @@ export const zedHosted = {
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export default zedHosted;
|
||||
|
||||
@@ -17,8 +17,11 @@ function getPublicModel(id: string) {
|
||||
return ANTIGRAVITY_PUBLIC_MODELS.find((model) => model.id === id) as any;
|
||||
}
|
||||
|
||||
// #10537 retired the single-alias `gemini-3.7-flash` (which mapped to the upstream
|
||||
// `gemini-3.7-flash-tiered`) in favor of three directly-callable tiered public models —
|
||||
// the suffixed ids now work upstream without the collapsing alias. Keep this list in sync
|
||||
// with ANTIGRAVITY_PUBLIC_MODELS/ANTIGRAVITY_MODEL_ALIASES instead of the retired bare id.
|
||||
const EXPECTED_FLASH_TIERS = [
|
||||
["gemini-3.7-flash", "Gemini 3.7 Flash"],
|
||||
["gemini-3.7-flash-high", "Gemini 3.7 Flash (High)"],
|
||||
["gemini-3.7-flash-medium", "Gemini 3.7 Flash (Medium)"],
|
||||
["gemini-3.7-flash-low", "Gemini 3.7 Flash (Low)"],
|
||||
|
||||
@@ -4,6 +4,12 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
// #10603 made upstream model sync opt-in (isAutoFetchModelsEnabled() now requires
|
||||
// providerSpecificData.autoFetchModels === true) so remote discovery doesn't overwrite
|
||||
// manual catalog overrides by default. Every connection below sets it explicitly so the
|
||||
// mocked `fetch` in each test actually gets called — without it, the route short-circuits
|
||||
// to the local/cached catalog before ever reaching the network call these tests assert on.
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-glm-models-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
@@ -29,7 +35,7 @@ test("GLM import uses international coding endpoint when apiRegion is internatio
|
||||
authType: "apikey",
|
||||
name: "glm-intl",
|
||||
apiKey: "glm-key",
|
||||
providerSpecificData: { apiRegion: "international" },
|
||||
providerSpecificData: { apiRegion: "international", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -88,7 +94,7 @@ test("GLM import normalizes custom coding models URLs without duplicating endpoi
|
||||
authType: "apikey",
|
||||
name: `glm-custom-${index}`,
|
||||
apiKey: testCase.apiKey,
|
||||
providerSpecificData: { baseUrl: testCase.baseUrl },
|
||||
providerSpecificData: { baseUrl: testCase.baseUrl, autoFetchModels: true },
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -129,7 +135,7 @@ test("GLM import falls back to Anthropic model discovery when coding discovery f
|
||||
authType: "apikey",
|
||||
name: "glm-discovery-fallback",
|
||||
apiKey: "glm-key",
|
||||
providerSpecificData: { apiRegion: "international" },
|
||||
providerSpecificData: { apiRegion: "international", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -178,7 +184,7 @@ test("GLM import preserves auth failures instead of falling back across transpor
|
||||
authType: "apikey",
|
||||
name: "glm-auth-fail",
|
||||
apiKey: "bad-key",
|
||||
providerSpecificData: { apiRegion: "international" },
|
||||
providerSpecificData: { apiRegion: "international", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -207,7 +213,7 @@ test("GLMT import shares the GLM coding models endpoint and surfaces provider me
|
||||
authType: "apikey",
|
||||
name: "glmt-intl",
|
||||
apiKey: "glmt-key",
|
||||
providerSpecificData: { apiRegion: "international" },
|
||||
providerSpecificData: { apiRegion: "international", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -241,7 +247,7 @@ test("GLM import uses China coding endpoint when apiRegion is china", async () =
|
||||
authType: "apikey",
|
||||
name: "glm-cn",
|
||||
apiKey: "glm-cn-key",
|
||||
providerSpecificData: { apiRegion: "china" },
|
||||
providerSpecificData: { apiRegion: "china", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -272,7 +278,7 @@ test("GLM China provider import uses the specialized GLM discovery path", async
|
||||
authType: "apikey",
|
||||
name: "glm-cn-provider",
|
||||
apiKey: "glm-cn-key",
|
||||
providerSpecificData: {},
|
||||
providerSpecificData: { autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -304,7 +310,7 @@ test("GLM import defaults to international endpoint when apiRegion is missing",
|
||||
authType: "apikey",
|
||||
name: "glm-default",
|
||||
apiKey: "glm-key",
|
||||
providerSpecificData: {},
|
||||
providerSpecificData: { autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -331,7 +337,7 @@ test("GLM import defaults to international endpoint when apiRegion is invalid",
|
||||
authType: "apikey",
|
||||
name: "glm-bogus",
|
||||
apiKey: "glm-key",
|
||||
providerSpecificData: { apiRegion: "bogus" },
|
||||
providerSpecificData: { apiRegion: "bogus", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -359,7 +365,7 @@ test("GLM import prefers apiKey over accessToken and sends only Authorization Be
|
||||
name: "glm-both-tokens",
|
||||
apiKey: "glm-api-key",
|
||||
accessToken: "glm-access-token",
|
||||
providerSpecificData: { apiRegion: "international" },
|
||||
providerSpecificData: { apiRegion: "international", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -387,7 +393,7 @@ test("GLM import falls back to accessToken when apiKey is absent", async () => {
|
||||
authType: "apikey",
|
||||
name: "glm-access-only",
|
||||
accessToken: "glm-access-token",
|
||||
providerSpecificData: { apiRegion: "international" },
|
||||
providerSpecificData: { apiRegion: "international", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -414,7 +420,7 @@ test("GLM import falls back to the local catalog on upstream non-OK status codes
|
||||
authType: "apikey",
|
||||
name: "glm-error",
|
||||
apiKey: "glm-key",
|
||||
providerSpecificData: { apiRegion: "international" },
|
||||
providerSpecificData: { apiRegion: "international", autoFetchModels: true },
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
@@ -471,6 +471,10 @@ test("model sync route writes synced available models for non-Gemini providers t
|
||||
]);
|
||||
});
|
||||
|
||||
// #10603 ("make upstream model sync opt-in and preserve manual overrides") also applies to
|
||||
// import/merge mode: a manual custom-model row sharing an id with a discovered model is kept
|
||||
// as the user-owned overlay instead of being demoted/removed. See the comment above the
|
||||
// "reports synced managed models separately from preserved manual models" test.
|
||||
test("model sync route import mode merges discovered models without deleting manual models", async () => {
|
||||
await resetStorage();
|
||||
|
||||
@@ -512,10 +516,13 @@ test("model sync route import mode merges discovered models without deleting man
|
||||
assert.equal(body.updatedCount, 0);
|
||||
assert.equal(body.syncedAliases, 1);
|
||||
assert.deepEqual(body.modelChanges, { added: 1, removed: 0, updated: 0, total: 1 });
|
||||
assert.deepEqual(body.customModelChanges, { added: 0, removed: 1, updated: 0, total: 1 });
|
||||
assert.deepEqual(body.customModelChanges, { added: 0, removed: 0, updated: 0, total: 0 });
|
||||
assert.deepEqual(
|
||||
body.models.map((model) => ({ id: model.id, source: model.source })),
|
||||
[{ id: "manual-only", source: "manual" }]
|
||||
[
|
||||
{ id: "manual-only", source: "manual" },
|
||||
{ id: "router-v4", source: "manual" },
|
||||
]
|
||||
);
|
||||
assert.deepEqual(
|
||||
body.importedModels.map((model) => ({ id: model.id, source: model.source })),
|
||||
@@ -820,6 +827,13 @@ test("model sync route forwards cookies, filters built-ins, and syncs aliases fo
|
||||
assert.equal(logs[0].account, "External Sync");
|
||||
});
|
||||
|
||||
// #10603 ("make upstream model sync opt-in and preserve manual overrides") changed
|
||||
// importManagedModels() so a manually configured custom-model row that shares an id with
|
||||
// a synced upstream model is no longer demoted/removed — it stays as the user-owned
|
||||
// metadata overlay and is merged over the synced base at read time (see the comment in
|
||||
// src/lib/providerModels/managedModelImport.ts). Only rows already tagged as an
|
||||
// imported/auto-sync source get pruned. This test predates that change; its expectations
|
||||
// below reflect the current preserve-manual-overrides behavior.
|
||||
test("model sync route reports synced managed models separately from preserved manual models", async () => {
|
||||
await resetStorage();
|
||||
|
||||
@@ -858,10 +872,13 @@ test("model sync route reports synced managed models separately from preserved m
|
||||
assert.equal(body.availableModelsCount, 2);
|
||||
assert.equal(body.importedCount, 1);
|
||||
assert.equal(body.updatedCount, 0);
|
||||
assert.deepEqual(body.customModelChanges, { added: 0, removed: 1, updated: 0, total: 1 });
|
||||
assert.deepEqual(body.customModelChanges, { added: 0, removed: 0, updated: 0, total: 0 });
|
||||
assert.deepEqual(
|
||||
body.models.map((model) => ({ id: model.id, source: model.source })),
|
||||
[{ id: "manual-only", source: "manual" }]
|
||||
[
|
||||
{ id: "manual-only", source: "manual" },
|
||||
{ id: "router-v4", source: "manual" },
|
||||
]
|
||||
);
|
||||
assert.deepEqual(
|
||||
(await modelsDb.getSyncedAvailableModels("openrouter")).map((model) => ({
|
||||
|
||||
Reference in New Issue
Block a user