From b78932ff73c5255e5e7ad48c54415cefb1465b03 Mon Sep 17 00:00:00 2001 From: Hanbin Noh <282618027+hanbinnoh@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:57:45 +0900 Subject: [PATCH] fix(catalog): preserve nested reasoning efforts --- src/codex/catalog/provider-fetch.ts | 11 ++++++++++- tests/provider-model-discovery-contract.test.ts | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/codex/catalog/provider-fetch.ts b/src/codex/catalog/provider-fetch.ts index 04553cf92..665011d92 100644 --- a/src/codex/catalog/provider-fetch.ts +++ b/src/codex/catalog/provider-fetch.ts @@ -923,7 +923,16 @@ export function catalogHintsFromModelsApiItem(providerName: string, item: Provid item.max_context_length, ); const maxInputTokens = positiveSafeInteger(limits?.max_input_tokens, item.max_input_tokens); - const rawReasoningEfforts = capabilityRecord?.reasoning_effort ?? item.reasoning_efforts; + // Some OpenAI-compatible catalogs expose the selectable ladder under + // `reasoning_parameters.efforts` instead of the older `reasoning_efforts` key. + // Treat both as model metadata: otherwise a valid upstream capability disappears + // before client exporters (including omp) can advertise it. + const reasoningParameters = plainRecord(item.reasoning_parameters) + ?? plainRecord(metadata?.reasoning_parameters) + ?? plainRecord(capabilityRecord?.reasoning_parameters); + const rawReasoningEfforts = capabilityRecord?.reasoning_effort + ?? item.reasoning_efforts + ?? reasoningParameters?.efforts; const listedReasoningEfforts = normalizedStringList(rawReasoningEfforts, 8, 24); const reasoningEfforts = listedReasoningEfforts ? sanitizeCodexReasoningEfforts(listedReasoningEfforts) diff --git a/tests/provider-model-discovery-contract.test.ts b/tests/provider-model-discovery-contract.test.ts index fc1c4e8f4..e71bbd4e3 100644 --- a/tests/provider-model-discovery-contract.test.ts +++ b/tests/provider-model-discovery-contract.test.ts @@ -274,6 +274,13 @@ describe("registry-owned provider model discovery", () => { })).toEqual({}); }); + test("preserves nested reasoning_parameters effort ladders from OpenAI-compatible catalogs", () => { + expect(catalogHintsFromModelsApiItem("example", { + id: "reasoning-model", + reasoning_parameters: { efforts: ["low", "high", "max"] }, + })).toEqual({ reasoningEfforts: ["low", "high", "max"] }); + }); + test("drops untrusted metadata tokens containing control characters", () => { expect(catalogHintsFromModelsApiItem("example", { id: "controlled",