From 2a9bf4722ede040fd5c9abbe441ad034fc3ef145 Mon Sep 17 00:00:00 2001 From: sungl <81428141+Sun-GLiang@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:21:43 +0800 Subject: [PATCH 1/2] fix(models): add GPT-6 Sol and Luna metadata Generated-by: Codex (GPT-6) --- .../core/src/__tests__/model-catalog.test.ts | 37 ++++++++++++++++++ .../core/src/__tests__/model-metadata.test.ts | 6 ++- packages/core/src/model-metadata.ts | 38 ++++++++++++++++++- .../__tests__/model-factory-thinking.test.ts | 22 +++++++++++ 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/packages/core/src/__tests__/model-catalog.test.ts b/packages/core/src/__tests__/model-catalog.test.ts index eaf3d3c563..27675f1991 100644 --- a/packages/core/src/__tests__/model-catalog.test.ts +++ b/packages/core/src/__tests__/model-catalog.test.ts @@ -62,6 +62,43 @@ test('catalog transport preserves independent limits and their unmodified defaul assert.equal(decoded.defaultInputLimit, 32000); }); +test('GPT-6 Sol and Luna use GPT labels and expose supported thinking levels', () => { + const models = [{ id: 'gpt-6-sol' }, { id: 'gpt-6-luna' }]; + for (const providerType of ['openai', 'openai-codex'] as const) { + const entries = buildModelCatalogEntries({ providerType, models, modelSource: 'fetched' }); + assert.deepEqual( + entries.map(({ id, displayName, thinkingLevels }) => ({ id, displayName, thinkingLevels })), + [ + { + id: 'gpt-6-sol', + displayName: 'GPT-6 Sol', + thinkingLevels: [ + ...(providerType === 'openai' ? ['off'] : []), + 'low', + 'medium', + 'high', + 'xhigh', + 'max', + ], + }, + { + id: 'gpt-6-luna', + displayName: 'GPT-6 Luna', + thinkingLevels: [ + ...(providerType === 'openai' ? ['off'] : []), + 'low', + 'medium', + 'high', + 'xhigh', + 'max', + ], + }, + ], + providerType, + ); + } +}); + test('a live inventory annotates a model it omits and preserves higher-priority failures', () => { const input = { providerType: 'zai-coding-plan' as const, diff --git a/packages/core/src/__tests__/model-metadata.test.ts b/packages/core/src/__tests__/model-metadata.test.ts index ec0e403128..2e8239947e 100644 --- a/packages/core/src/__tests__/model-metadata.test.ts +++ b/packages/core/src/__tests__/model-metadata.test.ts @@ -156,11 +156,13 @@ describe('model-metadata vision capability', () => { }); describe('openAiAdapterApiProtocol', () => { - it('routes a normalized gpt-5 family to the Responses wire', () => { + it('routes normalized GPT-5 and GPT-6 families to the Responses wire', () => { assert.equal(openAiAdapterApiProtocol(' GPT-5.6-sol '), 'openai-responses'); + assert.equal(openAiAdapterApiProtocol('gpt-6-sol'), 'openai-responses'); + assert.equal(openAiAdapterApiProtocol('gpt-6-luna'), 'openai-responses'); }); - it('keeps a non-gpt-5 OpenAI model on the Chat Completions wire', () => { + it('keeps an older OpenAI model on the Chat Completions wire', () => { assert.equal(openAiAdapterApiProtocol('gpt-4o'), 'openai-chat'); }); diff --git a/packages/core/src/model-metadata.ts b/packages/core/src/model-metadata.ts index 3a66da7549..db3d596b1c 100644 --- a/packages/core/src/model-metadata.ts +++ b/packages/core/src/model-metadata.ts @@ -189,7 +189,7 @@ export function openAiAdapterApiProtocol( (providerType === 'opencode-go' && id === 'muse-spark-1.2-contributor') || ((providerType === 'alibaba-token-plan-cn' || providerType === 'alibaba-token-plan') && id === 'qwen3.8-max') || - /^gpt-5/i.test(id) || + /^gpt-[56]/i.test(id) || ((providerType === 'xai' || providerType === 'xai-oauth') && id === 'grok-4.5') ? 'openai-responses' : 'openai-chat'; @@ -267,6 +267,31 @@ const GOOGLE_MODEL_OVERRIDES: Record = { }, }; +// These models are in the live models.dev OpenAI catalog but not yet in the +// bundled snapshot. Keep the public API's `none` effort separate from Codex +// OAuth. The Codex model list also advertises `ultra` for Sol with automatic +// task delegation. The OpenAI Responses SDK used by Maka's OAuth path +// discards it for GPT-6, so do not offer it until the request path can send +// and handle it. +const OPENAI_GPT6_THINKING_OPTIONS: ThinkingOptions = { + efforts: ['none', 'low', 'medium', 'high', 'xhigh', 'max'], +}; + +const OPENAI_GPT6_MODEL_OVERRIDES: Record = { + 'gpt-6-sol': { + displayName: 'GPT-6 Sol', + thinkingOptions: OPENAI_GPT6_THINKING_OPTIONS, + }, + 'gpt-6-luna': { + displayName: 'GPT-6 Luna', + thinkingOptions: OPENAI_GPT6_THINKING_OPTIONS, + }, +}; + +const CODEX_GPT6_THINKING_OPTIONS: ThinkingOptions = { + efforts: ['low', 'medium', 'high', 'xhigh', 'max'], +}; + // The OAuth path pins its own context windows over whatever the public // catalog says. Base facts come from the active table, falling back to the // shipped snapshot so a model upstream stops listing keeps a display name. @@ -284,6 +309,16 @@ function withoutInputLimit(metadata: ModelMetadata | undefined): ModelMetadata | function openAiOAuthModelMetadata(active: ModelsDevMetadata): Record { return { + 'gpt-6-sol': { + ...openAiOAuthBase(active, 'gpt-6-sol'), + ...OPENAI_GPT6_MODEL_OVERRIDES['gpt-6-sol'], + thinkingOptions: CODEX_GPT6_THINKING_OPTIONS, + }, + 'gpt-6-luna': { + ...openAiOAuthBase(active, 'gpt-6-luna'), + ...OPENAI_GPT6_MODEL_OVERRIDES['gpt-6-luna'], + thinkingOptions: CODEX_GPT6_THINKING_OPTIONS, + }, 'gpt-5.6-sol': { ...openAiOAuthBase(active, 'gpt-5.6-sol'), contextWindow: 372_000, @@ -488,6 +523,7 @@ const COMMAND_CODE_MODEL_METADATA: Record = { function buildStaticModelMetadata(active: ModelsDevMetadata): ModelsDevMetadata { return { anthropic: ANTHROPIC_MODEL_OVERRIDES, + openai: OPENAI_GPT6_MODEL_OVERRIDES, 'claude-subscription': claudeSubscriptionModelMetadata(active), // The Command Code Provider-API plan rides the same effort table. commandcode: COMMAND_CODE_MODEL_METADATA, diff --git a/packages/runtime/src/__tests__/model-factory-thinking.test.ts b/packages/runtime/src/__tests__/model-factory-thinking.test.ts index 9370b154cf..4ce90a8994 100644 --- a/packages/runtime/src/__tests__/model-factory-thinking.test.ts +++ b/packages/runtime/src/__tests__/model-factory-thinking.test.ts @@ -214,6 +214,28 @@ describe('buildProviderOptions: thinking level', () => { }); }); + test('GPT-6 Sol and Luna send selected reasoning effort on API and Codex paths', () => { + for (const modelId of ['gpt-6-sol', 'gpt-6-luna']) { + assert.deepEqual(buildProviderOptions(conn('openai'), modelId, 'max'), { + openai: { + store: false, + reasoningSummary: 'auto', + reasoningEffort: 'max', + parallelToolCalls: true, + }, + }); + assert.deepEqual(buildProviderOptions(conn('openai-codex'), modelId, 'max'), { + openai: { + store: false, + textVerbosity: 'medium', + reasoningSummary: 'auto', + reasoningEffort: 'max', + parallelToolCalls: true, + }, + }); + } + }); + test('openai-codex (gpt-5.5) preserves store:false / textVerbosity and merges reasoningEffort', () => { assert.deepEqual(buildProviderOptions(conn('openai-codex'), 'gpt-5.5'), { openai: { From da27e0c6b10e78c711910e3abf8fe436a87c365e Mon Sep 17 00:00:00 2001 From: sungl <81428141+Sun-GLiang@users.noreply.github.com> Date: Wed, 23 Sep 2026 22:12:36 +0800 Subject: [PATCH 2/2] fix(models): omit unsupported GPT-6 off effort Generated-by: Codex (GPT-6) --- .../core/src/__tests__/model-catalog.test.ts | 18 +------- packages/core/src/model-metadata.ts | 17 +++----- .../__tests__/responses-wire-contract.test.ts | 42 +++++++++++++++++++ 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/packages/core/src/__tests__/model-catalog.test.ts b/packages/core/src/__tests__/model-catalog.test.ts index 27675f1991..3ce417e688 100644 --- a/packages/core/src/__tests__/model-catalog.test.ts +++ b/packages/core/src/__tests__/model-catalog.test.ts @@ -72,26 +72,12 @@ test('GPT-6 Sol and Luna use GPT labels and expose supported thinking levels', ( { id: 'gpt-6-sol', displayName: 'GPT-6 Sol', - thinkingLevels: [ - ...(providerType === 'openai' ? ['off'] : []), - 'low', - 'medium', - 'high', - 'xhigh', - 'max', - ], + thinkingLevels: ['low', 'medium', 'high', 'xhigh', 'max'], }, { id: 'gpt-6-luna', displayName: 'GPT-6 Luna', - thinkingLevels: [ - ...(providerType === 'openai' ? ['off'] : []), - 'low', - 'medium', - 'high', - 'xhigh', - 'max', - ], + thinkingLevels: ['low', 'medium', 'high', 'xhigh', 'max'], }, ], providerType, diff --git a/packages/core/src/model-metadata.ts b/packages/core/src/model-metadata.ts index db3d596b1c..2b5476c9de 100644 --- a/packages/core/src/model-metadata.ts +++ b/packages/core/src/model-metadata.ts @@ -268,13 +268,12 @@ const GOOGLE_MODEL_OVERRIDES: Record = { }; // These models are in the live models.dev OpenAI catalog but not yet in the -// bundled snapshot. Keep the public API's `none` effort separate from Codex -// OAuth. The Codex model list also advertises `ultra` for Sol with automatic -// task delegation. The OpenAI Responses SDK used by Maka's OAuth path -// discards it for GPT-6, so do not offer it until the request path can send -// and handle it. +// bundled snapshot. The OpenAI Responses SDK accepts only these five GPT-6 +// efforts on both API and Codex OAuth paths. It discards `none` and the Codex +// model list's `ultra` for Sol, so do not offer them until the request path +// can send and handle them. const OPENAI_GPT6_THINKING_OPTIONS: ThinkingOptions = { - efforts: ['none', 'low', 'medium', 'high', 'xhigh', 'max'], + efforts: ['low', 'medium', 'high', 'xhigh', 'max'], }; const OPENAI_GPT6_MODEL_OVERRIDES: Record = { @@ -288,10 +287,6 @@ const OPENAI_GPT6_MODEL_OVERRIDES: Record = { }, }; -const CODEX_GPT6_THINKING_OPTIONS: ThinkingOptions = { - efforts: ['low', 'medium', 'high', 'xhigh', 'max'], -}; - // The OAuth path pins its own context windows over whatever the public // catalog says. Base facts come from the active table, falling back to the // shipped snapshot so a model upstream stops listing keeps a display name. @@ -312,12 +307,10 @@ function openAiOAuthModelMetadata(active: ModelsDevMetadata): Record): Record { + test('GPT-6 catalog thinking levels reach Responses for API and Codex OAuth', async () => { + for (const providerType of ['openai', 'openai-codex'] as const) { + for (const modelId of ['gpt-6-sol', 'gpt-6-luna']) { + const [entry] = buildModelCatalogEntries({ + providerType, + models: [{ id: modelId }], + modelSource: 'fetched', + }); + assert.ok(entry); + assert.deepEqual(entry.thinkingLevels, ['low', 'medium', 'high', 'xhigh', 'max']); + + const requests: Record[] = []; + const fetch = (async (_url: string | URL | Request, init?: RequestInit) => { + requests.push(JSON.parse(String(init?.body)) as Record); + return Response.json({ + id: 'response-gpt-6', + object: 'response', + status: 'completed', + output: [], + usage: { input_tokens: 1, output_tokens: 1 }, + }); + }) as typeof globalThis.fetch; + const connection = conn(providerType); + const model = getAIModel({ connection, apiKey: 'test-token', modelId, fetch }); + + for (const level of entry.thinkingLevels) { + await model.doGenerate({ + prompt: [{ role: 'user', content: [{ type: 'text', text: 'ping' }] }], + providerOptions: buildProviderOptions(connection, modelId, level), + }); + } + + assert.deepEqual( + requests.map((body) => (body.reasoning as { effort?: string } | undefined)?.effort), + entry.thinkingLevels, + `${providerType}/${modelId}`, + ); + } + } + }); + test('does not route Maka tool_search history through OpenAI native tool_search validation', async () => { const connection = conn('openai-codex', 'codex-subscription'); connection.defaultModel = 'gpt-5.6-sol';