From 0b43281359612c44a24c62592ab4078ed2839a80 Mon Sep 17 00:00:00 2001 From: StealthyCoder123 Date: Sat, 31 Oct 2026 00:36:59 +0000 Subject: [PATCH] fix(codex): restore reasoning options for catalog-only models --- .../src/provider/CodexChatGptModels.test.ts | 53 ++++++++++++++++++- .../server/src/provider/CodexChatGptModels.ts | 29 ++++++---- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/apps/server/src/provider/CodexChatGptModels.test.ts b/apps/server/src/provider/CodexChatGptModels.test.ts index 9533a3c64d00a..34017812ed9cf 100644 --- a/apps/server/src/provider/CodexChatGptModels.test.ts +++ b/apps/server/src/provider/CodexChatGptModels.test.ts @@ -49,7 +49,7 @@ it.effect( ], ); assert.deepEqual(a[1]!.capabilities, native[0]!.capabilities); - assert.isNull(a[0]!.capabilities); + assert.deepEqual(a[0]!.capabilities, native[0]!.capabilities); const b = yield* chatGptModels("account-b", native).pipe( Effect.provideService(HttpClient.HttpClient, http), ); @@ -72,3 +72,54 @@ it.effect("does not present a cached catalog as account entitlements when discov assert.strictEqual(error._tag, "ChatGptCatalogError"); }), ); + +it.effect("borrows catalog-only reasoning capabilities by variant, then by native order", () => + Effect.gen(function* () { + const http = HttpClient.make((request) => + Effect.succeed( + HttpClientResponse.fromWeb( + request, + new Response( + JSON.stringify({ + models: [ + { slug: "gpt-6.1-sol", display_name: "Sol", visibility: "list" }, + { slug: "gpt-6.1-terra", display_name: "Terra", visibility: "list" }, + { slug: "gpt-6-astra", display_name: "Astra", visibility: "list" }, + ], + }), + ), + ), + ), + ); + const reasoning = (currentValue: string) => ({ + optionDescriptors: [ + { + id: "reasoningEffort", + label: "Reasoning", + type: "select" as const, + options: ["low", "medium", "high", "xhigh", "max", "ultra"].map((id) => ({ + id, + label: id, + isDefault: id === currentValue, + })), + currentValue, + }, + ], + }); + const native = [ + { slug: "gpt-6-astra", name: "Astra", isCustom: false, capabilities: null }, + { slug: "gpt-6-luna", name: "Luna", isCustom: false, capabilities: reasoning("low") }, + { slug: "gpt-6-sol", name: "Sol", isCustom: false, capabilities: reasoning("medium") }, + ]; + const models = yield* chatGptModels("account-a", native).pipe( + Effect.provideService(HttpClient.HttpClient, http), + ); + assert.deepEqual(models[0]!.capabilities, native[2]!.capabilities); + assert.deepEqual(models[1]!.capabilities, native[1]!.capabilities); + assert.isNull(models[2]!.capabilities); + const withoutCapabilities = yield* chatGptModels("account-a", [native[0]!]).pipe( + Effect.provideService(HttpClient.HttpClient, http), + ); + assert.isTrue(withoutCapabilities.every((model) => model.capabilities === null)); + }), +); diff --git a/apps/server/src/provider/CodexChatGptModels.ts b/apps/server/src/provider/CodexChatGptModels.ts index f3490b6cf737e..a152bbda0252c 100644 --- a/apps/server/src/provider/CodexChatGptModels.ts +++ b/apps/server/src/provider/CodexChatGptModels.ts @@ -30,17 +30,24 @@ export const chatGptModels = Effect.fn("chatGptModels")(function* ( ); if (response.status !== 200) return yield* new ChatGptCatalogError({ status: response.status }); const catalog = yield* response.json.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Catalog))); + const fallbackCapabilities = + nativeModels.find((model) => model.capabilities)?.capabilities ?? null; return catalog.models .filter((model) => model.visibility === "list") - .map( - (model) => - ({ - ...nativeModels.find((native) => native.slug === model.slug), - capabilities: - nativeModels.find((native) => native.slug === model.slug)?.capabilities ?? null, - slug: model.slug, - name: model.display_name, - isCustom: false, - }) satisfies ServerProviderModel, - ); + .map((model) => { + const native = nativeModels.find((native) => native.slug === model.slug); + const variant = model.slug.split("-").at(-1); + const capabilities = native + ? native.capabilities + : (nativeModels.find( + (candidate) => candidate.slug.endsWith(`-${variant}`) && candidate.capabilities, + )?.capabilities ?? fallbackCapabilities); + return { + ...native, + capabilities, + slug: model.slug, + name: model.display_name, + isCustom: false, + } satisfies ServerProviderModel; + }); }, Effect.timeout("15 seconds"));