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).
This commit is contained in:
Jonathan Bailey
2026-08-13 03:53:07 -07:00
committed by GitHub
parent 06f41cda63
commit f5629d2166
3 changed files with 59 additions and 0 deletions

View File

@@ -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

View File

@@ -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).

View File

@@ -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);
}
});