mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
duckduckgo-web returned 400 ERR_BAD_REQUEST on every request because the model
catalog advertised ids DuckDuckGo has retired from the free Duck.ai lineup
(gpt-4o-mini, gpt-5-mini, llama-4-scout, mistral-small-2501, o3-mini,
claude-3-5-haiku-20241022). duckchat/v1/chat validates `model` server-side and
rejects retired ids, and normalizeDuckDuckGoModel() defaulted to / passed through
gpt-4o-mini, so the retired id reached the wire verbatim.
Update all three id sources to the current free wire ids captured live from
duckchat/v1/models (2026-07-22): gpt-5.4-mini, gpt-5.4-nano, claude-haiku-4-5,
mistral-small-2603, tinfoil/gpt-oss-120b, tinfoil/gemma4-31b —
- executor: default gpt-4o-mini -> gpt-5.4-mini; legacy ids aliased to the
nearest current model via a lookup map; dropped the invalid gpt-5-mini
"minimal" reasoningEffort;
- freeModelCatalog.data.ts + providers/registry/duckduckgo-web: current ids.
Regression test duckduckgo-web-model-catalog-8000.test.ts asserts no retired id
ever reaches the wire and all three catalogs match the current set (RED on the
old default/passthrough + retired catalogs). Live 200 confirmation remains a
recommended VPS smoke per the plan-file.
73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
normalizeDuckDuckGoModel,
|
|
DUCKDUCKGO_DEFAULT_MODEL,
|
|
} from "../../open-sse/executors/duckduckgo-web.ts";
|
|
import { duckduckgo_webProvider } from "../../open-sse/config/providers/registry/duckduckgo-web/index.ts";
|
|
import { FREE_MODEL_BUDGETS } from "../../open-sse/config/freeModelCatalog.data.ts";
|
|
|
|
// #8000 — the current free Duck.ai lineup, wire ids captured live from
|
|
// duckchat/v1/models (2026-07-22). A retired/unknown model id is rejected by
|
|
// duckchat/v1/chat with 400 ERR_BAD_REQUEST, which is the exact reported symptom.
|
|
const CURRENT_FREE_IDS = new Set([
|
|
"gpt-5.4-mini",
|
|
"gpt-5.4-nano",
|
|
"claude-haiku-4-5",
|
|
"mistral-small-2603",
|
|
"tinfoil/gpt-oss-120b",
|
|
"tinfoil/gemma4-31b",
|
|
]);
|
|
|
|
// Ids OmniRoute historically advertised that Duck.ai has since retired.
|
|
const RETIRED_IDS = [
|
|
"gpt-4o-mini",
|
|
"gpt-5-mini",
|
|
"o3-mini",
|
|
"llama-4-scout",
|
|
"claude-3-5-haiku-20241022",
|
|
"mistral-small-2501",
|
|
];
|
|
|
|
test("#8000: default model is a current free wire id, not retired gpt-4o-mini", () => {
|
|
assert.equal(DUCKDUCKGO_DEFAULT_MODEL, "gpt-5.4-mini");
|
|
assert.notEqual(normalizeDuckDuckGoModel(undefined), "gpt-4o-mini");
|
|
assert.ok(CURRENT_FREE_IDS.has(normalizeDuckDuckGoModel(undefined)));
|
|
});
|
|
|
|
test("#8000: every retired id normalizes to a current wire id (never passes through)", () => {
|
|
for (const retired of RETIRED_IDS) {
|
|
const out = normalizeDuckDuckGoModel(retired);
|
|
assert.ok(CURRENT_FREE_IDS.has(out), `${retired} → ${out} must be a current free id`);
|
|
assert.notEqual(out, retired, `retired ${retired} must not reach the wire unchanged`);
|
|
}
|
|
// the `duckduckgo-web/` routing prefix is stripped before aliasing
|
|
assert.ok(CURRENT_FREE_IDS.has(normalizeDuckDuckGoModel("duckduckgo-web/gpt-4o-mini")));
|
|
assert.equal(normalizeDuckDuckGoModel("duckduckgo-web/gpt-5.4-nano"), "gpt-5.4-nano");
|
|
});
|
|
|
|
test("#8000: current wire ids pass through unchanged", () => {
|
|
for (const id of CURRENT_FREE_IDS) {
|
|
assert.equal(normalizeDuckDuckGoModel(id), id);
|
|
}
|
|
});
|
|
|
|
test("#8000: provider registry advertises exactly the current wire ids", () => {
|
|
const ids = duckduckgo_webProvider.models.map((m) => m.id);
|
|
assert.deepEqual(new Set(ids), CURRENT_FREE_IDS);
|
|
for (const retired of RETIRED_IDS) {
|
|
assert.ok(!ids.includes(retired), `registry must drop retired id ${retired}`);
|
|
}
|
|
});
|
|
|
|
test("#8000: free-model catalog advertises exactly the current wire ids", () => {
|
|
const ids = FREE_MODEL_BUDGETS.filter((e) => e.provider === "duckduckgo-web").map(
|
|
(e) => e.modelId
|
|
);
|
|
assert.deepEqual(new Set(ids), CURRENT_FREE_IDS);
|
|
for (const retired of RETIRED_IDS) {
|
|
assert.ok(!ids.includes(retired), `catalog must drop retired id ${retired}`);
|
|
}
|
|
});
|