mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 04:12:17 +03:00
fix(providers): declare Agnes official thinking effort tiers (#13655)
Declares the accepted thinking-effort tiers per Agnes chat model (2.0/2.5: none/low/medium/high/max; 3.0 adds minimal/xhigh), so the generic declared-tier clamp maps `xhigh`/`off` onto values the upstream accepts instead of forwarding them verbatim and collecting a 400. Validated as a combined board first (this PR merged with the 11 siblings of the same batch on the release tip): eslint on every changed file with the suppressions file, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 275 passing / 0 failing focused node:test cases across the 28 test files the batch touches. Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run. Thanks @HouMinXi!
This commit is contained in:
1
changelog.d/fixes/agnes-thinking-effort-tiers.md
Normal file
1
changelog.d/fixes/agnes-thinking-effort-tiers.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(providers): **declare Agnes chat models' live `reasoning_effort` vocabulary so catalog/builder/sanitizer stop inventing aliases the API 400s.** 2.0/2.5 accept `none/low/medium/high/max`; 3.0 also accepts `minimal` and `xhigh`. `off`/`ultra` still clamp off the wire.
|
||||
@@ -1,5 +1,22 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
|
||||
// Official Agnes chat contract from live /v1/chat/completions probes
|
||||
// (2026-09-14, apihub.agnes-ai.com). 2.0/2.5 accept none/low/medium/high/max
|
||||
// and 400 on xhigh/off/ultra/minimal. 3.0 additionally accepts minimal and
|
||||
// xhigh. HuggingFace's Agnes-3.0-Flash card lists four of these
|
||||
// (none/low/medium/high); live 3.0 also takes minimal and xhigh, so the
|
||||
// registry follows the live API rather than the shorter card.
|
||||
export const AGNES_FLASH_THINKING_EFFORTS = ["none", "low", "medium", "high", "max"] as const;
|
||||
export const AGNES_30_THINKING_EFFORTS = [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh",
|
||||
"max",
|
||||
] as const;
|
||||
|
||||
export const agnesProvider: RegistryEntry = {
|
||||
id: "agnes",
|
||||
format: "openai",
|
||||
@@ -15,6 +32,7 @@ export const agnesProvider: RegistryEntry = {
|
||||
contextLength: 262144,
|
||||
maxOutputTokens: 65536,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: [...AGNES_FLASH_THINKING_EFFORTS],
|
||||
supportsVision: true,
|
||||
toolCalling: true,
|
||||
},
|
||||
@@ -24,18 +42,20 @@ export const agnesProvider: RegistryEntry = {
|
||||
contextLength: 524288,
|
||||
maxOutputTokens: 65536,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: [...AGNES_FLASH_THINKING_EFFORTS],
|
||||
supportsVision: true,
|
||||
toolCalling: true,
|
||||
interleavedField: "reasoning_content",
|
||||
},
|
||||
// Wiki (2026-09-10): agnes-3.0-flash is 512k context / 65,536
|
||||
// output, same window as 2.5-flash. Live /v1/models lists it.
|
||||
{
|
||||
// Wiki (2026-09-10) lists agnes-3.0-flash at 512K context / 65,536 max
|
||||
// output, same window as 2.5 Flash. Live GET /v1/models includes it.
|
||||
id: "agnes-3.0-flash",
|
||||
name: "Agnes 3.0 Flash",
|
||||
contextLength: 524288,
|
||||
maxOutputTokens: 65536,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: [...AGNES_30_THINKING_EFFORTS],
|
||||
supportsVision: true,
|
||||
toolCalling: true,
|
||||
interleavedField: "reasoning_content",
|
||||
|
||||
@@ -9,13 +9,18 @@ process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
|
||||
const { VIDEO_PROVIDER_IDS } = await import("../../src/shared/constants/providers.ts");
|
||||
const { REGISTRY: providerRegistry } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const { REGISTRY: providerRegistry, getRegistryModelThinkingEfforts } =
|
||||
await import("../../open-sse/config/providerRegistry.ts");
|
||||
const { IMAGE_PROVIDERS, getAllImageModels } =
|
||||
await import("../../open-sse/config/imageRegistry.ts");
|
||||
const { VIDEO_PROVIDERS, getAllVideoModels } =
|
||||
await import("../../open-sse/config/videoRegistry.ts");
|
||||
const { FREE_MODEL_BUDGETS } = await import("../../open-sse/config/freeModelCatalog.ts");
|
||||
const { DefaultExecutor } = await import("../../open-sse/executors/default.ts");
|
||||
const { sanitizeReasoningEffortForProvider } =
|
||||
await import("../../open-sse/executors/base/reasoningEffort.ts");
|
||||
const { getThinkingCapabilityFields } =
|
||||
await import("../../src/app/api/v1/models/catalogHelpers.ts");
|
||||
const { handleImageGeneration } = await import("../../open-sse/handlers/imageGeneration.ts");
|
||||
const { handleVideoGeneration } = await import("../../open-sse/handlers/videoGeneration.ts");
|
||||
const { resolveChatCoreTargetFormat } =
|
||||
@@ -87,6 +92,7 @@ test("agnes ships the current public chat models with the correct capabilities",
|
||||
assert.equal(flash20.contextLength, 262144);
|
||||
assert.equal(flash20.maxOutputTokens, 65536);
|
||||
assert.equal(flash20.supportsReasoning, true);
|
||||
assert.deepEqual(flash20.supportedThinkingEfforts, ["none", "low", "medium", "high", "max"]);
|
||||
assert.equal(flash20.supportsVision, true);
|
||||
assert.equal(flash20.toolCalling, true);
|
||||
|
||||
@@ -94,26 +100,97 @@ test("agnes ships the current public chat models with the correct capabilities",
|
||||
assert.ok(flash25, "agnes-2.5-flash must be defined");
|
||||
assert.equal(flash25.contextLength, 524288);
|
||||
assert.equal(flash25.maxOutputTokens, 65536);
|
||||
assert.equal(flash25.supportsReasoning, true);
|
||||
assert.deepEqual(flash25.supportedThinkingEfforts, ["none", "low", "medium", "high", "max"]);
|
||||
|
||||
const flash30 = entry.models.find((m) => m.id === "agnes-3.0-flash");
|
||||
assert.ok(flash30, "agnes-3.0-flash must be defined");
|
||||
assert.equal(flash30.contextLength, 524288);
|
||||
assert.equal(flash30.maxOutputTokens, 65536);
|
||||
assert.equal(flash30.supportsReasoning, true);
|
||||
assert.deepEqual(flash30.supportedThinkingEfforts, [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh",
|
||||
"max",
|
||||
]);
|
||||
assert.equal(flash30.supportsVision, true);
|
||||
assert.equal(flash30.toolCalling, true);
|
||||
assert.equal(flash30.interleavedField, "reasoning_content");
|
||||
});
|
||||
|
||||
test("agnes chat models advertise official thinking vocabulary", () => {
|
||||
for (const id of ["agnes-2.0-flash", "agnes-2.5-flash"]) {
|
||||
assert.deepEqual(getRegistryModelThinkingEfforts("agnes", id), [
|
||||
"none",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"max",
|
||||
]);
|
||||
}
|
||||
assert.deepEqual(getRegistryModelThinkingEfforts("agnes", "agnes-3.0-flash"), [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh",
|
||||
"max",
|
||||
]);
|
||||
});
|
||||
|
||||
test("agnes catalog effort_tiers match declared vocabulary, not six-tier fallback", () => {
|
||||
const efforts = getRegistryModelThinkingEfforts("agnes", "agnes-3.0-flash");
|
||||
assert.ok(efforts && efforts.length > 0);
|
||||
assert.deepEqual(
|
||||
getThinkingCapabilityFields("agnes", "agnes-3.0-flash", true, efforts, !efforts.length),
|
||||
{
|
||||
thinking: true,
|
||||
supportsThinking: true,
|
||||
effort_tiers: ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("agnes sanitizer keeps official tiers and clamps undocumented ones", () => {
|
||||
const clamp = (model: string, effort: string) =>
|
||||
(
|
||||
sanitizeReasoningEffortForProvider({ reasoning_effort: effort }, "agnes", model) as {
|
||||
reasoning_effort?: string;
|
||||
}
|
||||
).reasoning_effort;
|
||||
|
||||
assert.equal(clamp("agnes-3.0-flash", "none"), "none");
|
||||
assert.equal(clamp("agnes-3.0-flash", "minimal"), "minimal");
|
||||
assert.equal(clamp("agnes-3.0-flash", "low"), "low");
|
||||
assert.equal(clamp("agnes-3.0-flash", "high"), "high");
|
||||
assert.equal(clamp("agnes-3.0-flash", "xhigh"), "xhigh");
|
||||
assert.equal(clamp("agnes-3.0-flash", "max"), "max");
|
||||
assert.equal(clamp("agnes-3.0-flash", "ultra"), "max");
|
||||
assert.equal(clamp("agnes-3.0-flash", "off"), "none");
|
||||
|
||||
// 2.0/2.5 reject xhigh (HTTP 400); clamp up to the next accepted tier (max).
|
||||
assert.equal(clamp("agnes-2.0-flash", "xhigh"), "max");
|
||||
assert.equal(clamp("agnes-2.5-flash", "xhigh"), "max");
|
||||
assert.equal(clamp("agnes-2.0-flash", "max"), "max");
|
||||
assert.equal(clamp("agnes-2.0-flash", "off"), "none");
|
||||
assert.equal(clamp("agnes-2.0-flash", "minimal"), "low");
|
||||
assert.equal(clamp("agnes-2.5-flash", "minimal"), "low");
|
||||
assert.equal(clamp("agnes-2.5-flash", "off"), "none");
|
||||
});
|
||||
|
||||
test("agnes registry advertises the live OpenAI-style /models endpoint", () => {
|
||||
const entry = providerRegistry.agnes;
|
||||
assert.equal(entry.modelsUrl, AGNES_MODELS_URL);
|
||||
});
|
||||
|
||||
test("agnes is classified for live OpenAI-style /models discovery", async () => {
|
||||
const { isNamedOpenAIStyleProvider } = await import(
|
||||
"../../src/app/api/providers/[id]/models/discovery/providerSets.ts"
|
||||
);
|
||||
const { isNamedOpenAIStyleProvider } =
|
||||
await import("../../src/app/api/providers/[id]/models/discovery/providerSets.ts");
|
||||
assert.equal(isNamedOpenAIStyleProvider("agnes"), true);
|
||||
});
|
||||
|
||||
@@ -125,9 +202,8 @@ test("agnes honors per-connection CN base URL override", () => {
|
||||
});
|
||||
|
||||
test("agnes base-URL field is always-on so CN keys can point at api.agnes-ai.cn", async () => {
|
||||
const helpers = await import(
|
||||
"../../src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts"
|
||||
);
|
||||
const helpers =
|
||||
await import("../../src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts");
|
||||
assert.equal(helpers.isBaseUrlConfigurableProvider("agnes"), true);
|
||||
assert.equal(helpers.getProviderBaseUrlDefault("agnes"), "https://apihub.agnes-ai.com/v1");
|
||||
assert.equal(helpers.getProviderBaseUrlPlaceholder("agnes"), AGNES_CN_BASE_URL);
|
||||
|
||||
Reference in New Issue
Block a user