From 8c532ecbdc65df33700b2bbbd199b76754f22e92 Mon Sep 17 00:00:00 2001 From: Logan Yang Date: Fri, 8 Aug 2025 09:30:46 -0700 Subject: [PATCH] Add GPT5 models (#1698) * Add GPT5 models * Refactor BUILTIN_CHAT_MODELS: remove some models * Fix temperature again --- package-lock.json | 90 ++++++++--- package.json | 2 +- src/LLMProviders/chatModelManager.ts | 148 ++++++++++++------ src/aiParams.ts | 4 + src/constants.ts | 64 ++++---- src/settings/model.ts | 4 + .../v2/components/ModelEditDialog.tsx | 107 +++++++++++++ src/utils.ts | 38 +++++ 8 files changed, 346 insertions(+), 111 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3651ad34..bd59260d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@langchain/google-genai": "^0.1.6", "@langchain/groq": "^0.1.2", "@langchain/mistralai": "^0.2.0", - "@langchain/openai": "^0.5.6", + "@langchain/openai": "^0.6.6", "@langchain/xai": "^0.0.2", "@orama/orama": "^3.0.0-rc-2", "@radix-ui/react-checkbox": "^1.1.3", @@ -4369,9 +4369,9 @@ } }, "node_modules/@langchain/core": { - "version": "0.3.55", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.55.tgz", - "integrity": "sha512-SojY2ugpT6t9eYfFB9Ysvyhhyh+KJTGXs50hdHUE9tAEQWp3WAwoxe4djwJnOZ6fSpWYdpFt2UT2ksHVDy2vXA==", + "version": "0.3.68", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.68.tgz", + "integrity": "sha512-dWPT1h9ObG1TK9uivFTk/pgBULZ6/tBmq8czGUjZjR+1xh9jB4tm/D5FY6o5FklXcEpnAI9peNq2x17Kl9wbMg==", "license": "MIT", "dependencies": { "@cfworker/json-schema": "^4.0.2", @@ -4379,12 +4379,12 @@ "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", - "langsmith": "^0.3.16", + "langsmith": "^0.3.46", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", - "zod": "^3.22.4", + "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" }, "engines": { @@ -4417,6 +4417,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4432,6 +4433,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4443,9 +4445,10 @@ } }, "node_modules/@langchain/core/node_modules/langsmith": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.20.tgz", - "integrity": "sha512-zwVQos6tjcksCTfdM67QKq7yyED4GmQiZw/sJ6UCMYZxlvTMMg3PeQ9tOePXAWNWoJygOnH+EwGXr7gYOOETDg==", + "version": "0.3.54", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.54.tgz", + "integrity": "sha512-+yMM0pFU172QLq/PIUdaI0Yn+M6CjSSFEQh2R+0B1IvtmVuB72R8if8eLnQ8LdqgvN7jHTjTqJmc/EZVJqv+QA==", + "license": "MIT", "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", @@ -4456,9 +4459,21 @@ "uuid": "^10.0.0" }, "peerDependencies": { + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, "openai": { "optional": true } @@ -4472,6 +4487,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -4620,20 +4636,41 @@ } }, "node_modules/@langchain/openai": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.5.6.tgz", - "integrity": "sha512-zN0iyJthPNmcefIBVybZwcTBgcqu/ElJFov42ZntxEncK4heOMAE9lkq9LQ5CaPU/SgrduibrM1oL57+tLUtaA==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.6.6.tgz", + "integrity": "sha512-0fxSg290WTCTEM0PECDGfst2QYUiULKhzyydaOPLMQ5pvWHjJkzBudx+CyHkeQ8DvGXysJteSmZzAMjRCj4duQ==", + "license": "MIT", "dependencies": { "js-tiktoken": "^1.0.12", - "openai": "^4.93.0", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.3" + "openai": "^5.12.1", + "zod": "^3.25.32" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@langchain/core": ">=0.3.39 <0.4.0" + "@langchain/core": ">=0.3.68 <0.4.0" + } + }, + "node_modules/@langchain/openai/node_modules/openai": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.12.1.tgz", + "integrity": "sha512-26s536j4Fi7P3iUma1S9H33WRrw0Qu8pJ2nYJHffrlKHPU0JK4d0r3NcMgqEcAeTdNLGYNyoFsqN4g4YE9vutg==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } } }, "node_modules/@langchain/textsplitters": { @@ -10719,9 +10756,10 @@ "dev": true }, "node_modules/console-table-printer": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.1.tgz", - "integrity": "sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==", + "version": "2.14.6", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.14.6.tgz", + "integrity": "sha512-MCBl5HNVaFuuHW6FGbL/4fB7N/ormCy+tQ+sxTrF6QtSbSNETvPuOVbkJBhzDgYhvjWGrTma4eYJa37ZuoQsPw==", + "license": "MIT", "dependencies": { "simple-wcswidth": "^1.0.1" } @@ -19885,9 +19923,10 @@ "peer": true }, "node_modules/simple-wcswidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", - "integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", + "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", + "license": "MIT" }, "node_modules/sisteransi": { "version": "1.0.5", @@ -21679,9 +21718,10 @@ } }, "node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index a484cd56..6e130346 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "@langchain/google-genai": "^0.1.6", "@langchain/groq": "^0.1.2", "@langchain/mistralai": "^0.2.0", - "@langchain/openai": "^0.5.6", + "@langchain/openai": "^0.6.6", "@langchain/xai": "^0.0.2", "@orama/orama": "^3.0.0-rc-2", "@radix-ui/react-checkbox": "^1.1.3", diff --git a/src/LLMProviders/chatModelManager.ts b/src/LLMProviders/chatModelManager.ts index 17b6b3d6..74c78969 100644 --- a/src/LLMProviders/chatModelManager.ts +++ b/src/LLMProviders/chatModelManager.ts @@ -8,7 +8,13 @@ import { import { getDecryptedKey } from "@/encryptionService"; import { logError, logInfo } from "@/logger"; import { getModelKeyFromModel, getSettings, subscribeToSettingsChange } from "@/settings/model"; -import { err2String, isOSeriesModel, safeFetch, withSuppressedTokenWarnings } from "@/utils"; +import { + err2String, + getModelInfo, + ModelInfo, + safeFetch, + withSuppressedTokenWarnings, +} from "@/utils"; import { HarmBlockThreshold, HarmCategory } from "@google/generative-ai"; import { ChatAnthropic } from "@langchain/anthropic"; import { ChatCohere } from "@langchain/cohere"; @@ -89,43 +95,65 @@ export default class ChatModelManager { return ChatModelManager.instance; } + private static readonly REASONING_MODEL_TEMPERATURE = 1; + + /** + * Determines the appropriate temperature for a model + * @returns temperature value or undefined if temperature should not be set + */ + private getTemperatureForModel( + modelInfo: ModelInfo, + customModel: CustomModel, + settings: any + ): number | undefined { + // Thinking-enabled models don't accept temperature + if (modelInfo.isThinkingEnabled) { + return undefined; + } + + // O-series and GPT-5 models require temperature = 1 + if (modelInfo.isOSeries || modelInfo.isGPT5) { + return ChatModelManager.REASONING_MODEL_TEMPERATURE; + } + + // All other models use configured temperature + return customModel.temperature ?? settings.temperature; + } + private async getModelConfig(customModel: CustomModel): Promise { const settings = getSettings(); const modelName = customModel.name; - const isOSeries = isOSeriesModel(modelName); - const isThinkingEnabled = - modelName.startsWith("claude-3-7-sonnet") || modelName.startsWith("claude-sonnet-4"); + const modelInfo = getModelInfo(modelName); + const { isThinkingEnabled } = modelInfo; - // Base config without temperature when thinking is enabled - const baseConfig: Omit = { + // Base config - temperature will be handled by provider-specific methods + const baseConfig: Omit = { modelName: modelName, streaming: customModel.stream ?? true, maxRetries: 3, maxConcurrency: 3, enableCors: customModel.enableCors, + // Add temperature for normal models (will be overridden by special configs if needed) + ...(!isThinkingEnabled && { temperature: customModel.temperature ?? settings.temperature }), }; - // Add temperature only if thinking is not enabled - if (!isThinkingEnabled) { - (baseConfig as any).temperature = customModel.temperature ?? settings.temperature; - } - const providerConfig: { [K in keyof ChatProviderConstructMap]: ConstructorParameters[0]; } = { [ChatModelProviders.OPENAI]: { modelName: modelName, - openAIApiKey: await getDecryptedKey(customModel.apiKey || settings.openAIApiKey), + apiKey: await getDecryptedKey(customModel.apiKey || settings.openAIApiKey), configuration: { baseURL: customModel.baseUrl, fetch: customModel.enableCors ? safeFetch : undefined, organization: await getDecryptedKey(customModel.openAIOrgId || settings.openAIOrgId), }, - ...this.handleOpenAIExtraArgs( - isOSeries, + ...this.getOpenAISpecialConfig( + modelName, customModel.maxTokens ?? settings.maxTokens, - customModel.temperature ?? settings.temperature + customModel.temperature ?? settings.temperature, + customModel ), }, [ChatModelProviders.ANTHROPIC]: { @@ -146,7 +174,7 @@ export default class ChatModelManager { [ChatModelProviders.AZURE_OPENAI]: { modelName: customModel.azureOpenAIApiDeploymentName || settings.azureOpenAIApiDeploymentName, - openAIApiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), + apiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), configuration: { baseURL: customModel.baseUrl || @@ -160,10 +188,11 @@ export default class ChatModelManager { }, fetch: customModel.enableCors ? safeFetch : undefined, }, - ...this.handleOpenAIExtraArgs( - isOSeries, + ...this.getOpenAISpecialConfig( + modelName, customModel.maxTokens ?? settings.maxTokens, - customModel.temperature ?? settings.temperature + customModel.temperature ?? settings.temperature, + customModel ), }, [ChatModelProviders.COHEREAI]: { @@ -200,7 +229,7 @@ export default class ChatModelManager { }, [ChatModelProviders.OPENROUTERAI]: { modelName: modelName, - openAIApiKey: await getDecryptedKey(customModel.apiKey || settings.openRouterAiApiKey), + apiKey: await getDecryptedKey(customModel.apiKey || settings.openRouterAiApiKey), configuration: { baseURL: customModel.baseUrl || "https://openrouter.ai/api/v1", fetch: customModel.enableCors ? safeFetch : undefined, @@ -225,7 +254,7 @@ export default class ChatModelManager { }, [ChatModelProviders.LM_STUDIO]: { modelName: modelName, - openAIApiKey: customModel.apiKey || "default-key", + apiKey: customModel.apiKey || "default-key", configuration: { baseURL: customModel.baseUrl || "http://localhost:1234/v1", fetch: customModel.enableCors ? safeFetch : undefined, @@ -233,21 +262,22 @@ export default class ChatModelManager { }, [ChatModelProviders.OPENAI_FORMAT]: { modelName: modelName, - openAIApiKey: await getDecryptedKey(customModel.apiKey || settings.openAIApiKey), + apiKey: await getDecryptedKey(customModel.apiKey || settings.openAIApiKey), configuration: { baseURL: customModel.baseUrl, fetch: customModel.enableCors ? safeFetch : undefined, defaultHeaders: { "dangerously-allow-browser": "true" }, }, - ...this.handleOpenAIExtraArgs( - isOSeries, + ...this.getOpenAISpecialConfig( + modelName, customModel.maxTokens ?? settings.maxTokens, - customModel.temperature ?? settings.temperature + customModel.temperature ?? settings.temperature, + customModel ), }, [ChatModelProviders.COPILOT_PLUS]: { modelName: modelName, - openAIApiKey: await getDecryptedKey(settings.plusLicenseKey), + apiKey: await getDecryptedKey(settings.plusLicenseKey), configuration: { baseURL: BREVILABS_API_BASE_URL, fetch: customModel.enableCors ? safeFetch : undefined, @@ -277,15 +307,14 @@ export default class ChatModelManager { customModel ); + // LangChain 0.6.6 handles token configuration for special models internally const tokenConfig = isThinkingEnabled ? { maxTokens: customModel.maxTokens ?? settings.maxTokens, } - : this.handleOpenAIExtraArgs( - isOSeries, - customModel.maxTokens ?? settings.maxTokens, - customModel.temperature ?? settings.temperature - ); + : { + maxTokens: customModel.maxTokens ?? settings.maxTokens, + }; const finalConfig = { ...baseConfig, @@ -294,28 +323,47 @@ export default class ChatModelManager { ...tokenConfig, }; - // Final safety check to ensure no temperature when thinking is enabled - if (isThinkingEnabled) { - delete finalConfig.temperature; - } - return finalConfig as ModelConfig; } - private handleOpenAIExtraArgs( - isOSeriesModel: boolean, + /** + * Adds special configuration for OpenAI models that support reasoning + * LangChain 0.6.6+ handles most of the token/temperature logic internally + */ + private getOpenAISpecialConfig( + modelName: string, maxTokens: number, - temperature: number | undefined + _temperature: number | undefined, + customModel?: CustomModel ) { - const config = isOSeriesModel - ? { - maxCompletionTokens: maxTokens, - temperature: temperature === undefined ? undefined : 1, - } - : { - maxTokens: maxTokens, - temperature: temperature, - }; + const settings = getSettings(); + const modelInfo = getModelInfo(modelName); + const resolvedTemperature = this.getTemperatureForModel( + modelInfo, + customModel || ({} as CustomModel), + settings + ); + + const config: any = { + maxTokens, + temperature: resolvedTemperature, + }; + + // Add reasoning parameters for O-series and GPT-5 models + // LangChain 0.6.6 will handle the endpoint routing and parameter conversion + if (modelInfo.isOSeries || modelInfo.isGPT5) { + config.reasoning = { + effort: customModel?.reasoningEffort || settings.reasoningEffort || "low", + }; + + // Add verbosity for GPT-5 models (when API supports it) + if (modelInfo.isGPT5) { + const verbosityValue = customModel?.verbosity || settings.verbosity || "medium"; + config.verbosity = verbosityValue; + config.modelKwargs = { verbosity: verbosityValue }; + } + } + return config; } @@ -504,8 +552,8 @@ export default class ChatModelManager { const modelConfig = await this.getModelConfig(modelToTest); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { streaming, maxTokens, maxCompletionTokens, ...pingConfig } = modelConfig; - const isOSeries = isOSeriesModel(modelToTest.name); - const tokenConfig = this.handleOpenAIExtraArgs(isOSeries, 30, modelConfig.temperature); + // For ping, just use minimal config + const tokenConfig = { maxTokens: 30 }; const testModel = new (this.getProviderConstructor(modelToTest))({ ...pingConfig, ...tokenConfig, diff --git a/src/aiParams.ts b/src/aiParams.ts index 77771bef..56bfddbd 100644 --- a/src/aiParams.ts +++ b/src/aiParams.ts @@ -124,6 +124,10 @@ export interface CustomModel { azureOpenAIApiDeploymentName?: string; azureOpenAIApiVersion?: string; azureOpenAIApiEmbeddingDeploymentName?: string; + + // OpenAI GPT-5 and O-series specific fields + reasoningEffort?: "minimal" | "low" | "medium" | "high"; + verbosity?: "low" | "medium" | "high"; } export function setModelKey(modelKey: string) { diff --git a/src/constants.ts b/src/constants.ts index d4a8f674..7b450207 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -122,6 +122,9 @@ export const DEFAULT_MODEL_SETTING = { export enum ChatModels { COPILOT_PLUS_FLASH = "copilot-plus-flash", + GPT_5 = "gpt-5", + GPT_5_mini = "gpt-5-mini", + GPT_5_nano = "gpt-5-nano", GPT_41 = "gpt-4.1", GPT_41_mini = "gpt-4.1-mini", GPT_41_nano = "gpt-4.1-nano", @@ -216,6 +219,30 @@ export const BUILTIN_CHAT_MODELS: CustomModel[] = [ projectEnabled: true, capabilities: [ModelCapability.VISION], }, + { + name: ChatModels.GPT_5, + provider: ChatModelProviders.OPENAI, + enabled: true, + isBuiltIn: true, + core: true, + capabilities: [ModelCapability.VISION], + }, + { + name: ChatModels.GPT_5_mini, + provider: ChatModelProviders.OPENAI, + enabled: true, + isBuiltIn: true, + core: true, + capabilities: [ModelCapability.VISION], + }, + { + name: ChatModels.GPT_5_nano, + provider: ChatModelProviders.OPENAI, + enabled: true, + isBuiltIn: true, + core: true, + capabilities: [ModelCapability.VISION], + }, { name: ChatModels.GPT_41, provider: ChatModelProviders.OPENAI, @@ -239,17 +266,8 @@ export const BUILTIN_CHAT_MODELS: CustomModel[] = [ provider: ChatModelProviders.OPENAI, enabled: true, isBuiltIn: true, - core: true, capabilities: [ModelCapability.VISION], }, - { - name: ChatModels.O4_mini, - provider: ChatModelProviders.OPENAI, - enabled: true, - isBuiltIn: true, - core: true, - capabilities: [ModelCapability.REASONING], - }, { name: ChatModels.CLAUDE_4_SONNET, provider: ChatModelProviders.ANTHROPIC, @@ -264,20 +282,6 @@ export const BUILTIN_CHAT_MODELS: CustomModel[] = [ isBuiltIn: true, capabilities: [ModelCapability.VISION, ModelCapability.REASONING], }, - { - name: ChatModels.CLAUDE_3_5_SONNET, - provider: ChatModelProviders.ANTHROPIC, - enabled: true, - isBuiltIn: true, - core: true, - capabilities: [ModelCapability.VISION], - }, - { - name: ChatModels.CLAUDE_3_5_HAIKU, - provider: ChatModelProviders.ANTHROPIC, - enabled: true, - isBuiltIn: true, - }, { name: ChatModels.GROK3, provider: ChatModelProviders.XAI, @@ -314,18 +318,6 @@ export const BUILTIN_CHAT_MODELS: CustomModel[] = [ projectEnabled: true, capabilities: [ModelCapability.VISION], }, - { - name: ChatModels.COMMAND_R, - provider: ChatModelProviders.COHEREAI, - enabled: true, - isBuiltIn: true, - }, - { - name: ChatModels.COMMAND_R_PLUS, - provider: ChatModelProviders.COHEREAI, - enabled: true, - isBuiltIn: true, - }, { name: ChatModels.AZURE_OPENAI, provider: ChatModelProviders.AZURE_OPENAI, @@ -733,6 +725,8 @@ export const DEFAULT_SETTINGS: CopilotSettings = { "writeToFile", "replaceInFile", ], + reasoningEffort: "low", + verbosity: "medium", }; export const EVENT_NAMES = { diff --git a/src/settings/model.ts b/src/settings/model.ts index bacf2977..86533b53 100644 --- a/src/settings/model.ts +++ b/src/settings/model.ts @@ -114,6 +114,10 @@ export interface CopilotSettings { suggestedDefaultCommands: boolean; autonomousAgentMaxIterations: number; autonomousAgentEnabledToolIds: string[]; + /** Default reasoning effort for models that support it (GPT-5, O-series, etc.) */ + reasoningEffort: "minimal" | "low" | "medium" | "high"; + /** Default verbosity level for models that support it */ + verbosity: "low" | "medium" | "high"; } export const settingsStore = createStore(); diff --git a/src/settings/v2/components/ModelEditDialog.tsx b/src/settings/v2/components/ModelEditDialog.tsx index 941b2219..d33deb21 100644 --- a/src/settings/v2/components/ModelEditDialog.tsx +++ b/src/settings/v2/components/ModelEditDialog.tsx @@ -4,6 +4,13 @@ import { FormField } from "@/components/ui/form-field"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { PasswordInput } from "@/components/ui/password-input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { SettingSlider } from "@/components/ui/setting-slider"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { @@ -372,6 +379,106 @@ export const ModelEditModalContent: React.FC = ({ step={0.05} /> + + {/* Reasoning Effort and Verbosity for GPT-5 and O-series models */} + {localModel.provider === "openai" && + (localModel.name.startsWith("gpt-5") || + localModel.name.startsWith("o1") || + localModel.name.startsWith("o3") || + localModel.name.startsWith("o4")) && ( + <> + + Reasoning Effort + + + + + + +
+

+ Controls the amount of reasoning effort the model uses. Higher + effort provides more thorough reasoning but takes longer. Note: + thinking tokens are not available yet! +

+
    +
  • + Minimal: Fastest responses, minimal reasoning (GPT-5 only) +
  • +
  • Low: Faster responses, basic reasoning (default)
  • +
  • Medium: Balanced performance
  • +
  • High: Thorough reasoning, slower responses
  • +
+
+
+
+
+ + } + > + +
+ + {/* Verbosity only for GPT-5 models */} + {localModel.name.startsWith("gpt-5") && ( + + Verbosity + + + + + + +
+

Controls the length and detail of the model responses.

+
    +
  • Low: Concise, brief responses
  • +
  • Medium: Balanced detail
  • +
  • High: Detailed, comprehensive responses
  • +
+
+
+
+
+ + } + > + +
+ )} + + )} )} diff --git a/src/utils.ts b/src/utils.ts index 4307011b..82997cc8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -784,6 +784,8 @@ export async function checkLatestVersion(): Promise<{ } } +// Note: LangChain 0.6.6+ handles O-series and GPT-5 models automatically +// These functions are kept for backward compatibility and specific checks export function isOSeriesModel(model: BaseChatModel | string): boolean { if (typeof model === "string") { return model.startsWith("o1") || model.startsWith("o3") || model.startsWith("o4"); @@ -794,6 +796,42 @@ export function isOSeriesModel(model: BaseChatModel | string): boolean { return modelName.startsWith("o1") || modelName.startsWith("o3") || modelName.startsWith("o4"); } +export function isGPT5Model(model: BaseChatModel | string): boolean { + if (typeof model === "string") { + return model.startsWith("gpt-5"); + } + + // For BaseChatModel instances + const modelName = (model as any).modelName || (model as any).model || ""; + return modelName.startsWith("gpt-5"); +} + +/** + * Utility for determining model characteristics + * Note: Most of this is handled by LangChain 0.6.6+ internally + */ +export interface ModelInfo { + isOSeries: boolean; + isGPT5: boolean; + isThinkingEnabled: boolean; +} + +export function getModelInfo(model: BaseChatModel | string): ModelInfo { + const modelName = + typeof model === "string" ? model : (model as any).modelName || (model as any).model || ""; + + const isOSeries = isOSeriesModel(modelName); + const isGPT5 = isGPT5Model(modelName); + const isThinkingEnabled = + modelName.startsWith("claude-3-7-sonnet") || modelName.startsWith("claude-sonnet-4"); + + return { + isOSeries, + isGPT5, + isThinkingEnabled, + }; +} + export function getMessageRole( model: BaseChatModel | string, defaultRole: "system" | "human" = "system"