From f5629d2166d002e694868c01322206cfda2095ce Mon Sep 17 00:00:00 2001 From: Jonathan Bailey <127773378+excessivechaos@users.noreply.github.com> Date: Thu, 13 Aug 2026 03:53:07 -0700 Subject: [PATCH] fix(discovery): parse reasoning tiers nested under metadata.reasoning.supported_efforts (#10138) neuralwatt's /v1/models wraps capabilities and reasoning under a metadata object (metadata.reasoning.supported_efforts + metadata.capabilities .reasoning_effort), one level deeper than the shapes detectSupported ThinkingEfforts recognized. Synced openai-compatible rows therefore carried no supportedThinkingEfforts and no effort aliases were advertised. Recognize the metadata-nested shape with the same schema and validation as the top-level #7694 reasoning.supported_efforts, placed right after it in precedence so a top-level declaration still wins when both are present. Covered by three regression tests (parse, precedence, malformed-degradation). --- .../fixes/discovery-metadata-effort-tiers.md | 1 + src/lib/providerModels/modelDiscovery.ts | 21 +++++++++++ .../model-discovery-reasoning-levels.test.ts | 37 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 changelog.d/fixes/discovery-metadata-effort-tiers.md diff --git a/changelog.d/fixes/discovery-metadata-effort-tiers.md b/changelog.d/fixes/discovery-metadata-effort-tiers.md new file mode 100644 index 0000000000..3744a2063f --- /dev/null +++ b/changelog.d/fixes/discovery-metadata-effort-tiers.md @@ -0,0 +1 @@ +- fix(discovery): parse upstream reasoning tiers nested under metadata.reasoning.supported_efforts (neuralwatt /v1/models shape) so synced openai-compatible models advertise effort aliases diff --git a/src/lib/providerModels/modelDiscovery.ts b/src/lib/providerModels/modelDiscovery.ts index fa8ab8cce0..609f3f9111 100644 --- a/src/lib/providerModels/modelDiscovery.ts +++ b/src/lib/providerModels/modelDiscovery.ts @@ -153,6 +153,27 @@ export function detectSupportedThinkingEfforts(record: JsonRecord): string[] | u } } + // neuralwatt-style upstreams wrap the same tier data one level deeper under + // `metadata.reasoning.supported_efforts` (their /v1/models nests capabilities + // and reasoning under a `metadata` object). Same semantics and validation as + // the top-level #7694 shape; placed right after it so a top-level declaration + // still wins when both are present. + const metadataRecord = asRecord(record.metadata); + const metadataParsed = reasoningSupportedEffortsSchema.safeParse(metadataRecord.reasoning); + if (metadataParsed.success && metadataParsed.data) { + const rawEfforts = metadataParsed.data.supported_efforts; + if (Array.isArray(rawEfforts)) { + const efforts = Array.from( + new Set( + rawEfforts + .filter((effort): effort is string => typeof effort === "string" && effort.length > 0) + .map(normalizeSupportedEffort) + ) + ); + if (efforts.length > 0) return efforts; + } + } + // #9160: fall back to `capabilities.effort_tiers` before the legacy fields. // OmniRoute's own catalog surfaces effort tiers inside `capabilities.effort_tiers`, // which the existing `parseEffortList` already handles (string arrays). diff --git a/tests/unit/model-discovery-reasoning-levels.test.ts b/tests/unit/model-discovery-reasoning-levels.test.ts index d811167398..01846d3882 100644 --- a/tests/unit/model-discovery-reasoning-levels.test.ts +++ b/tests/unit/model-discovery-reasoning-levels.test.ts @@ -176,3 +176,40 @@ test("client_version opt-in without an explicit version still defaults off (no g }); assert.ok(!url.includes("client_version")); }); + +test("metadata.reasoning.supported_efforts (neuralwatt shape) is parsed into supportedThinkingEfforts", () => { + const [model] = normalizeDiscoveredModels([ + { + id: "neuralwatt/glm-5.2", + metadata: { + reasoning: { supported_efforts: ["max", "high", "none"] }, + capabilities: { reasoning_effort: true }, + }, + }, + ]); + // max canonicalizes to xhigh through the shared discovery normalization, + // matching every other tier-array source. + assert.deepEqual(model.supportedThinkingEfforts, ["xhigh", "high", "none"]); +}); + +test("metadata.reasoning.supported_efforts does not override a top-level declared tier list", () => { + const [model] = normalizeDiscoveredModels([ + { + id: "both-shapes", + reasoning: { supported_efforts: ["medium"] }, + metadata: { reasoning: { supported_efforts: ["max", "none"] } }, + }, + ]); + assert.deepEqual(model.supportedThinkingEfforts, ["medium"]); +}); + +test("malformed metadata.reasoning shape degrades to undefined, does not throw", () => { + const models = normalizeDiscoveredModels([ + { id: "bad-metadata-1", metadata: { reasoning: { supported_efforts: "not-an-array" } } }, + { id: "bad-metadata-2", metadata: { reasoning: 42 } }, + { id: "bad-metadata-3", metadata: null }, + ]); + for (const model of models) { + assert.equal("supportedThinkingEfforts" in model, false); + } +});