diff --git a/packages/types/src/providers/index.ts b/packages/types/src/providers/index.ts index bcb4a76d5d..c226efdcf6 100644 --- a/packages/types/src/providers/index.ts +++ b/packages/types/src/providers/index.ts @@ -40,6 +40,7 @@ import { litellmDefaultModelId } from "./lite-llm.js" import { mistralDefaultModelId } from "./mistral.js" import { moonshotDefaultModelId } from "./moonshot.js" import { openAiCodexDefaultModelId } from "./openai-codex.js" +import { openAiNativeDefaultModelId } from "./openai.js" import { openRouterDefaultModelId } from "./openrouter.js" import { poeDefaultModelId } from "./poe.js" import { qwenCodeDefaultModelId } from "./qwen-code.js" @@ -96,7 +97,7 @@ export function getProviderDefaultModelId( case "zai": return options?.isChina ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId case "openai-native": - return "gpt-4o" // Based on openai-native patterns + return openAiNativeDefaultModelId case "openai-codex": return openAiCodexDefaultModelId case "mistral": diff --git a/packages/types/src/providers/openai.ts b/packages/types/src/providers/openai.ts index 875e1759a3..600a94cd2e 100644 --- a/packages/types/src/providers/openai.ts +++ b/packages/types/src/providers/openai.ts @@ -3,9 +3,76 @@ import type { ModelInfo } from "../model.js" // https://openai.com/api/pricing/ export type OpenAiNativeModelId = keyof typeof openAiNativeModels -export const openAiNativeDefaultModelId: OpenAiNativeModelId = "gpt-5.1-codex-max" +export const openAiNativeDefaultModelId: OpenAiNativeModelId = "gpt-5.6-sol" export const openAiNativeModels = { + "gpt-5.6-sol": { + maxTokens: 128000, + contextWindow: 1_050_000, + includedTools: ["apply_patch"], + excludedTools: ["apply_diff", "write_to_file"], + supportsImages: true, + supportsPromptCache: true, + supportsReasoningEffort: ["none", "low", "medium", "high", "xhigh", "max"], + reasoningEffort: "high", + inputPrice: 5.0, + outputPrice: 30.0, + cacheReadsPrice: 0.5, + longContextPricing: { + thresholdTokens: 272_000, + inputPriceMultiplier: 2, + outputPriceMultiplier: 1.5, + appliesToServiceTiers: ["default", "flex"], + }, + supportsVerbosity: true, + supportsTemperature: false, + description: + "GPT-5.6 Sol: OpenAI's flagship model for frontier reasoning, complex coding, and multi-step agentic tasks", + }, + "gpt-5.6-terra": { + maxTokens: 128000, + contextWindow: 1_050_000, + includedTools: ["apply_patch"], + excludedTools: ["apply_diff", "write_to_file"], + supportsImages: true, + supportsPromptCache: true, + supportsReasoningEffort: ["none", "low", "medium", "high", "xhigh", "max"], + reasoningEffort: "medium", + inputPrice: 2.5, + outputPrice: 15.0, + cacheReadsPrice: 0.25, + longContextPricing: { + thresholdTokens: 272_000, + inputPriceMultiplier: 2, + outputPriceMultiplier: 1.5, + appliesToServiceTiers: ["default", "flex"], + }, + supportsVerbosity: true, + supportsTemperature: false, + description: "GPT-5.6 Terra: Balanced model for everyday coding, reasoning, and agentic tasks", + }, + "gpt-5.6-luna": { + maxTokens: 128000, + contextWindow: 1_050_000, + includedTools: ["apply_patch"], + excludedTools: ["apply_diff", "write_to_file"], + supportsImages: true, + supportsPromptCache: true, + supportsReasoningEffort: ["none", "low", "medium", "high", "xhigh", "max"], + reasoningEffort: "low", + inputPrice: 1.0, + outputPrice: 6.0, + cacheReadsPrice: 0.1, + longContextPricing: { + thresholdTokens: 272_000, + inputPriceMultiplier: 2, + outputPriceMultiplier: 1.5, + appliesToServiceTiers: ["default", "flex"], + }, + supportsVerbosity: true, + supportsTemperature: false, + description: "GPT-5.6 Luna: Fast, cost-efficient model optimized for speed and everyday use", + }, "gpt-5.1-codex-max": { maxTokens: 128000, contextWindow: 400000, diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index 2a3ec0afb2..397ca993cf 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -284,6 +284,24 @@ describe("OpenAiNativeHandler", () => { expect(modelInfo.info.reasoningEffort).toBe("medium") }) + it("should return GPT-5.6 Sol model info when selected", () => { + const solHandler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + }) + + const modelInfo = solHandler.getModel() + expect(modelInfo.id).toBe("gpt-5.6-sol") + expect(modelInfo.info.maxTokens).toBe(128000) + expect(modelInfo.info.contextWindow).toBe(1_050_000) + expect(modelInfo.info.supportsVerbosity).toBe(true) + expect(modelInfo.info.supportsReasoningEffort).toEqual(["none", "low", "medium", "high", "xhigh", "max"]) + expect(modelInfo.info.reasoningEffort).toBe("high") + expect(modelInfo.info.inputPrice).toBe(5.0) + expect(modelInfo.info.outputPrice).toBe(30.0) + expect(modelInfo.info.cacheReadsPrice).toBe(0.5) + }) + it("should return GPT-5.4 model info when selected", () => { const gpt54Handler = new OpenAiNativeHandler({ ...mockOptions, @@ -356,7 +374,7 @@ describe("OpenAiNativeHandler", () => { openAiNativeApiKey: "test-api-key", }) const modelInfo = handlerWithoutModel.getModel() - expect(modelInfo.id).toBe("gpt-5.1-codex-max") // Default model + expect(modelInfo.id).toBe("gpt-5.6-sol") // Default model expect(modelInfo.info).toBeDefined() }) }) @@ -805,6 +823,46 @@ describe("OpenAiNativeHandler", () => { ) }) + it("should support max reasoning effort for GPT-5.6 Sol", async () => { + // Mock fetch for Responses API + const mockFetch = vitest.fn().mockResolvedValue({ + ok: true, + body: new ReadableStream({ + start(controller) { + controller.enqueue( + new TextEncoder().encode( + 'data: {"type":"response.output_item.added","item":{"type":"text","text":"Max effort"}}\n\n', + ), + ) + controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) + controller.close() + }, + }), + }) + global.fetch = mockFetch as any + + // Mock SDK to fail + mockResponsesCreate.mockRejectedValue(new Error("SDK not available")) + + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + reasoningEffort: "max", + }) + + const stream = handler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + // drain + } + + expect(mockFetch).toHaveBeenCalledWith( + "https://api.openai.com/v1/responses", + expect.objectContaining({ + body: expect.stringContaining('"effort":"max"'), + }), + ) + }) + it("should omit reasoning when selection is 'disable'", async () => { // Mock fetch for Responses API const mockFetch = vitest.fn().mockResolvedValue({ diff --git a/src/api/providers/fetchers/__tests__/openrouter.spec.ts b/src/api/providers/fetchers/__tests__/openrouter.spec.ts index 268f83b92e..6f2349120c 100644 --- a/src/api/providers/fetchers/__tests__/openrouter.spec.ts +++ b/src/api/providers/fetchers/__tests__/openrouter.spec.ts @@ -471,6 +471,27 @@ describe("OpenRouter API", () => { expect(result.supportsReasoningEffort).toEqual(["none", "low", "medium", "high", "xhigh"]) }) + it("mirrors reasoning-effort capability array (incl. max) for openai/gpt-5.6-* ids", () => { + const mockModel = { + name: "GPT-5.6 Sol", + description: "Test model", + context_length: 1_050_000, + max_completion_tokens: 128000, + pricing: { prompt: "0.000005", completion: "0.00003", input_cache_read: "0.0000005" }, + } + + const result = parseOpenRouterModel({ + id: "openai/gpt-5.6-sol", + model: mockModel, + inputModality: ["text", "image"], + outputModality: ["text"], + maxTokens: 128000, + supportedParameters: ["reasoning", "max_tokens", "tools"], + }) + + expect(result.supportsReasoningEffort).toEqual(["none", "low", "medium", "high", "xhigh", "max"]) + }) + it("leaves supportsReasoningEffort as boolean for openai ids not in static defs", () => { const mockModel = { name: "GPT-5.5 Future",