mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 22:52:08 +08:00
fix: add verbosity parameter support for GPT-5 models across legacy and modern AI SDK (#11281)
* Initial plan * feat: add verbosity parameter support for GPT-5 models in OpenAIAPIClient Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com> * fix: ensure gpt-5-pro always uses 'high' verbosity Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com> * refactor: move verbosity configuration to config/models as suggested Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com> * refactor: encapsulate verbosity logic in getVerbosity method Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com> * feat: add support for verbosity and reasoning options for GPT-5 Pro and GPT-5.1 models * fix comment * build: add @ai-sdk/google dependency Add the @ai-sdk/google package to support Google AI SDK integration * build: add @ai-sdk/anthropic dependency * refactor(aiCore): update reasoning params handling for AI providers - Add type imports for provider options - Handle 'none' reasoning effort consistently across providers - Improve type safety by using Pick with provider options - Standardize disabled reasoning config for all providers * fix: adjust none effort ratio from 0 to 0.01 Prevent potential division by zero errors by ensuring none effort ratio has a small positive value * feat(reasoning): add support for GPT-5.1 series models Handle 'none' reasoning effort for GPT-5.1 models and add model type check * Update src/renderer/src/aiCore/utils/reasoning.ts --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com> Co-authored-by: suyao <sy20010504@gmail.com> Co-authored-by: icarus <eurfelux@gmail.com>
This commit is contained in:
parent
2511113b62
commit
11fb730b4d
@ -108,8 +108,10 @@
|
|||||||
"@agentic/searxng": "^7.3.3",
|
"@agentic/searxng": "^7.3.3",
|
||||||
"@agentic/tavily": "^7.3.3",
|
"@agentic/tavily": "^7.3.3",
|
||||||
"@ai-sdk/amazon-bedrock": "^3.0.53",
|
"@ai-sdk/amazon-bedrock": "^3.0.53",
|
||||||
|
"@ai-sdk/anthropic": "^2.0.44",
|
||||||
"@ai-sdk/cerebras": "^1.0.31",
|
"@ai-sdk/cerebras": "^1.0.31",
|
||||||
"@ai-sdk/gateway": "^2.0.9",
|
"@ai-sdk/gateway": "^2.0.9",
|
||||||
|
"@ai-sdk/google": "^2.0.32",
|
||||||
"@ai-sdk/google-vertex": "^3.0.62",
|
"@ai-sdk/google-vertex": "^3.0.62",
|
||||||
"@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.8#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.8-d4d0aaac93.patch",
|
"@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.8#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.8-d4d0aaac93.patch",
|
||||||
"@ai-sdk/mistral": "^2.0.23",
|
"@ai-sdk/mistral": "^2.0.23",
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import {
|
import {
|
||||||
|
getModelSupportedVerbosity,
|
||||||
isFunctionCallingModel,
|
isFunctionCallingModel,
|
||||||
isNotSupportTemperatureAndTopP,
|
isNotSupportTemperatureAndTopP,
|
||||||
isOpenAIModel,
|
isOpenAIModel,
|
||||||
@ -242,12 +243,18 @@ export abstract class BaseApiClient<
|
|||||||
return serviceTierSetting
|
return serviceTierSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getVerbosity(): OpenAIVerbosity {
|
protected getVerbosity(model?: Model): OpenAIVerbosity {
|
||||||
try {
|
try {
|
||||||
const state = window.store?.getState()
|
const state = window.store?.getState()
|
||||||
const verbosity = state?.settings?.openAI?.verbosity
|
const verbosity = state?.settings?.openAI?.verbosity
|
||||||
|
|
||||||
if (verbosity && ['low', 'medium', 'high'].includes(verbosity)) {
|
if (verbosity && ['low', 'medium', 'high'].includes(verbosity)) {
|
||||||
|
// If model is provided, check if the verbosity is supported by the model
|
||||||
|
if (model) {
|
||||||
|
const supportedVerbosity = getModelSupportedVerbosity(model)
|
||||||
|
// Use user's verbosity if supported, otherwise use the first supported option
|
||||||
|
return supportedVerbosity.includes(verbosity) ? verbosity : supportedVerbosity[0]
|
||||||
|
}
|
||||||
return verbosity
|
return verbosity
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import {
|
|||||||
isSupportedThinkingTokenModel,
|
isSupportedThinkingTokenModel,
|
||||||
isSupportedThinkingTokenQwenModel,
|
isSupportedThinkingTokenQwenModel,
|
||||||
isSupportedThinkingTokenZhipuModel,
|
isSupportedThinkingTokenZhipuModel,
|
||||||
|
isSupportVerbosityModel,
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
MODEL_SUPPORTED_REASONING_EFFORT,
|
MODEL_SUPPORTED_REASONING_EFFORT,
|
||||||
ZHIPU_RESULT_TOKENS
|
ZHIPU_RESULT_TOKENS
|
||||||
@ -733,6 +734,13 @@ export class OpenAIAPIClient extends OpenAIBaseClient<
|
|||||||
...modalities,
|
...modalities,
|
||||||
// groq 有不同的 service tier 配置,不符合 openai 接口类型
|
// groq 有不同的 service tier 配置,不符合 openai 接口类型
|
||||||
service_tier: this.getServiceTier(model) as OpenAIServiceTier,
|
service_tier: this.getServiceTier(model) as OpenAIServiceTier,
|
||||||
|
...(isSupportVerbosityModel(model)
|
||||||
|
? {
|
||||||
|
text: {
|
||||||
|
verbosity: this.getVerbosity(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
...this.getProviderSpecificParameters(assistant, model),
|
...this.getProviderSpecificParameters(assistant, model),
|
||||||
...reasoningEffort,
|
...reasoningEffort,
|
||||||
...getOpenAIWebSearchParams(model, enableWebSearch),
|
...getOpenAIWebSearchParams(model, enableWebSearch),
|
||||||
|
|||||||
@ -520,7 +520,7 @@ export class OpenAIResponseAPIClient extends OpenAIBaseClient<
|
|||||||
...(isSupportVerbosityModel(model)
|
...(isSupportVerbosityModel(model)
|
||||||
? {
|
? {
|
||||||
text: {
|
text: {
|
||||||
verbosity: this.getVerbosity()
|
verbosity: this.getVerbosity(model)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
import { baseProviderIdSchema, customProviderIdSchema } from '@cherrystudio/ai-core/provider'
|
import { baseProviderIdSchema, customProviderIdSchema } from '@cherrystudio/ai-core/provider'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import { isOpenAIModel, isQwenMTModel, isSupportFlexServiceTierModel } from '@renderer/config/models'
|
import {
|
||||||
|
getModelSupportedVerbosity,
|
||||||
|
isOpenAIModel,
|
||||||
|
isQwenMTModel,
|
||||||
|
isSupportFlexServiceTierModel,
|
||||||
|
isSupportVerbosityModel
|
||||||
|
} from '@renderer/config/models'
|
||||||
import { isSupportServiceTierProvider } from '@renderer/config/providers'
|
import { isSupportServiceTierProvider } from '@renderer/config/providers'
|
||||||
import { mapLanguageToQwenMTModel } from '@renderer/config/translate'
|
import { mapLanguageToQwenMTModel } from '@renderer/config/translate'
|
||||||
import type { Assistant, Model, Provider } from '@renderer/types'
|
import type { Assistant, Model, Provider } from '@renderer/types'
|
||||||
@ -191,6 +197,23 @@ function buildOpenAIProviderOptions(
|
|||||||
...reasoningParams
|
...reasoningParams
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSupportVerbosityModel(model)) {
|
||||||
|
const state = window.store?.getState()
|
||||||
|
const userVerbosity = state?.settings?.openAI?.verbosity
|
||||||
|
|
||||||
|
if (userVerbosity && ['low', 'medium', 'high'].includes(userVerbosity)) {
|
||||||
|
const supportedVerbosity = getModelSupportedVerbosity(model)
|
||||||
|
// Use user's verbosity if supported, otherwise use the first supported option
|
||||||
|
const verbosity = supportedVerbosity.includes(userVerbosity) ? userVerbosity : supportedVerbosity[0]
|
||||||
|
|
||||||
|
providerOptions = {
|
||||||
|
...providerOptions,
|
||||||
|
textVerbosity: verbosity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return providerOptions
|
return providerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import type { BedrockProviderOptions } from '@ai-sdk/amazon-bedrock'
|
||||||
|
import type { AnthropicProviderOptions } from '@ai-sdk/anthropic'
|
||||||
|
import type { GoogleGenerativeAIProviderOptions } from '@ai-sdk/google'
|
||||||
|
import type { XaiProviderOptions } from '@ai-sdk/xai'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import { DEFAULT_MAX_TOKENS } from '@renderer/config/constant'
|
import { DEFAULT_MAX_TOKENS } from '@renderer/config/constant'
|
||||||
import {
|
import {
|
||||||
@ -7,6 +11,7 @@ import {
|
|||||||
isDeepSeekHybridInferenceModel,
|
isDeepSeekHybridInferenceModel,
|
||||||
isDoubaoSeedAfter251015,
|
isDoubaoSeedAfter251015,
|
||||||
isDoubaoThinkingAutoModel,
|
isDoubaoThinkingAutoModel,
|
||||||
|
isGPT51SeriesModel,
|
||||||
isGrok4FastReasoningModel,
|
isGrok4FastReasoningModel,
|
||||||
isGrokReasoningModel,
|
isGrokReasoningModel,
|
||||||
isOpenAIDeepResearchModel,
|
isOpenAIDeepResearchModel,
|
||||||
@ -56,13 +61,20 @@ export function getReasoningEffort(assistant: Assistant, model: Model): Reasonin
|
|||||||
}
|
}
|
||||||
const reasoningEffort = assistant?.settings?.reasoning_effort
|
const reasoningEffort = assistant?.settings?.reasoning_effort
|
||||||
|
|
||||||
if (!reasoningEffort) {
|
// Handle undefined and 'none' reasoningEffort.
|
||||||
|
// TODO: They should be separated.
|
||||||
|
if (!reasoningEffort || reasoningEffort === 'none') {
|
||||||
// openrouter: use reasoning
|
// openrouter: use reasoning
|
||||||
if (model.provider === SystemProviderIds.openrouter) {
|
if (model.provider === SystemProviderIds.openrouter) {
|
||||||
// Don't disable reasoning for Gemini models that support thinking tokens
|
// Don't disable reasoning for Gemini models that support thinking tokens
|
||||||
if (isSupportedThinkingTokenGeminiModel(model) && !GEMINI_FLASH_MODEL_REGEX.test(model.id)) {
|
if (isSupportedThinkingTokenGeminiModel(model) && !GEMINI_FLASH_MODEL_REGEX.test(model.id)) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
// 'none' is not an available value for effort for now.
|
||||||
|
// I think they should resolve this issue soon, so I'll just go ahead and use this value.
|
||||||
|
if (isGPT51SeriesModel(model) && reasoningEffort === 'none') {
|
||||||
|
return { reasoning: { effort: 'none' } }
|
||||||
|
}
|
||||||
// Don't disable reasoning for models that require it
|
// Don't disable reasoning for models that require it
|
||||||
if (
|
if (
|
||||||
isGrokReasoningModel(model) ||
|
isGrokReasoningModel(model) ||
|
||||||
@ -117,6 +129,13 @@ export function getReasoningEffort(assistant: Assistant, model: Model): Reasonin
|
|||||||
return { thinking: { type: 'disabled' } }
|
return { thinking: { type: 'disabled' } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specially for GPT-5.1. Suppose this is a OpenAI Compatible provider
|
||||||
|
if (isGPT51SeriesModel(model) && reasoningEffort === 'none') {
|
||||||
|
return {
|
||||||
|
reasoningEffort: 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,7 +390,7 @@ export function getOpenAIReasoningParams(assistant: Assistant, model: Model): Re
|
|||||||
|
|
||||||
export function getAnthropicThinkingBudget(assistant: Assistant, model: Model): number {
|
export function getAnthropicThinkingBudget(assistant: Assistant, model: Model): number {
|
||||||
const { maxTokens, reasoning_effort: reasoningEffort } = getAssistantSettings(assistant)
|
const { maxTokens, reasoning_effort: reasoningEffort } = getAssistantSettings(assistant)
|
||||||
if (reasoningEffort === undefined) {
|
if (reasoningEffort === undefined || reasoningEffort === 'none') {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
const effortRatio = EFFORT_RATIO[reasoningEffort]
|
const effortRatio = EFFORT_RATIO[reasoningEffort]
|
||||||
@ -393,14 +412,17 @@ export function getAnthropicThinkingBudget(assistant: Assistant, model: Model):
|
|||||||
* 获取 Anthropic 推理参数
|
* 获取 Anthropic 推理参数
|
||||||
* 从 AnthropicAPIClient 中提取的逻辑
|
* 从 AnthropicAPIClient 中提取的逻辑
|
||||||
*/
|
*/
|
||||||
export function getAnthropicReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
export function getAnthropicReasoningParams(
|
||||||
|
assistant: Assistant,
|
||||||
|
model: Model
|
||||||
|
): Pick<AnthropicProviderOptions, 'thinking'> {
|
||||||
if (!isReasoningModel(model)) {
|
if (!isReasoningModel(model)) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reasoningEffort = assistant?.settings?.reasoning_effort
|
const reasoningEffort = assistant?.settings?.reasoning_effort
|
||||||
|
|
||||||
if (reasoningEffort === undefined) {
|
if (reasoningEffort === undefined || reasoningEffort === 'none') {
|
||||||
return {
|
return {
|
||||||
thinking: {
|
thinking: {
|
||||||
type: 'disabled'
|
type: 'disabled'
|
||||||
@ -429,7 +451,10 @@ export function getAnthropicReasoningParams(assistant: Assistant, model: Model):
|
|||||||
* 注意:Gemini/GCP 端点所使用的 thinkingBudget 等参数应该按照驼峰命名法传递
|
* 注意:Gemini/GCP 端点所使用的 thinkingBudget 等参数应该按照驼峰命名法传递
|
||||||
* 而在 Google 官方提供的 OpenAI 兼容端点中则使用蛇形命名法 thinking_budget
|
* 而在 Google 官方提供的 OpenAI 兼容端点中则使用蛇形命名法 thinking_budget
|
||||||
*/
|
*/
|
||||||
export function getGeminiReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
export function getGeminiReasoningParams(
|
||||||
|
assistant: Assistant,
|
||||||
|
model: Model
|
||||||
|
): Pick<GoogleGenerativeAIProviderOptions, 'thinkingConfig'> {
|
||||||
if (!isReasoningModel(model)) {
|
if (!isReasoningModel(model)) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
@ -438,7 +463,7 @@ export function getGeminiReasoningParams(assistant: Assistant, model: Model): Re
|
|||||||
|
|
||||||
// Gemini 推理参数
|
// Gemini 推理参数
|
||||||
if (isSupportedThinkingTokenGeminiModel(model)) {
|
if (isSupportedThinkingTokenGeminiModel(model)) {
|
||||||
if (reasoningEffort === undefined) {
|
if (reasoningEffort === undefined || reasoningEffort === 'none') {
|
||||||
return {
|
return {
|
||||||
thinkingConfig: {
|
thinkingConfig: {
|
||||||
includeThoughts: false,
|
includeThoughts: false,
|
||||||
@ -478,27 +503,35 @@ export function getGeminiReasoningParams(assistant: Assistant, model: Model): Re
|
|||||||
* @param model - The model being used
|
* @param model - The model being used
|
||||||
* @returns XAI-specific reasoning parameters
|
* @returns XAI-specific reasoning parameters
|
||||||
*/
|
*/
|
||||||
export function getXAIReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
export function getXAIReasoningParams(assistant: Assistant, model: Model): Pick<XaiProviderOptions, 'reasoningEffort'> {
|
||||||
if (!isSupportedReasoningEffortGrokModel(model)) {
|
if (!isSupportedReasoningEffortGrokModel(model)) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { reasoning_effort: reasoningEffort } = getAssistantSettings(assistant)
|
const { reasoning_effort: reasoningEffort } = getAssistantSettings(assistant)
|
||||||
|
|
||||||
if (!reasoningEffort) {
|
if (!reasoningEffort || reasoningEffort === 'none') {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For XAI provider Grok models, use reasoningEffort parameter directly
|
switch (reasoningEffort) {
|
||||||
return {
|
case 'auto':
|
||||||
reasoningEffort
|
case 'minimal':
|
||||||
|
case 'medium':
|
||||||
|
return { reasoningEffort: 'low' }
|
||||||
|
case 'low':
|
||||||
|
case 'high':
|
||||||
|
return { reasoningEffort }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Bedrock reasoning parameters
|
* Get Bedrock reasoning parameters
|
||||||
*/
|
*/
|
||||||
export function getBedrockReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
export function getBedrockReasoningParams(
|
||||||
|
assistant: Assistant,
|
||||||
|
model: Model
|
||||||
|
): Pick<BedrockProviderOptions, 'reasoningConfig'> {
|
||||||
if (!isReasoningModel(model)) {
|
if (!isReasoningModel(model)) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
@ -509,6 +542,14 @@ export function getBedrockReasoningParams(assistant: Assistant, model: Model): R
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reasoningEffort === 'none') {
|
||||||
|
return {
|
||||||
|
reasoningConfig: {
|
||||||
|
type: 'disabled'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only apply thinking budget for Claude reasoning models
|
// Only apply thinking budget for Claude reasoning models
|
||||||
if (!isSupportedThinkingTokenClaudeModel(model)) {
|
if (!isSupportedThinkingTokenClaudeModel(model)) {
|
||||||
return {}
|
return {}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import type {
|
|||||||
import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils'
|
import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils'
|
||||||
|
|
||||||
import { isEmbeddingModel, isRerankModel } from './embedding'
|
import { isEmbeddingModel, isRerankModel } from './embedding'
|
||||||
import { isGPT5SeriesModel, isGPT51SeriesModel } from './utils'
|
import { isGPT5ProModel, isGPT5SeriesModel, isGPT51SeriesModel } from './utils'
|
||||||
import { isTextToImageModel } from './vision'
|
import { isTextToImageModel } from './vision'
|
||||||
import { GEMINI_FLASH_MODEL_REGEX, isOpenAIDeepResearchModel } from './websearch'
|
import { GEMINI_FLASH_MODEL_REGEX, isOpenAIDeepResearchModel } from './websearch'
|
||||||
|
|
||||||
@ -26,6 +26,7 @@ export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = {
|
|||||||
gpt5_codex: ['low', 'medium', 'high'] as const,
|
gpt5_codex: ['low', 'medium', 'high'] as const,
|
||||||
gpt5_1: ['none', 'low', 'medium', 'high'] as const,
|
gpt5_1: ['none', 'low', 'medium', 'high'] as const,
|
||||||
gpt5_1_codex: ['none', 'medium', 'high'] as const,
|
gpt5_1_codex: ['none', 'medium', 'high'] as const,
|
||||||
|
gpt5pro: ['high'] as const,
|
||||||
grok: ['low', 'high'] as const,
|
grok: ['low', 'high'] as const,
|
||||||
grok4_fast: ['auto'] as const,
|
grok4_fast: ['auto'] as const,
|
||||||
gemini: ['low', 'medium', 'high', 'auto'] as const,
|
gemini: ['low', 'medium', 'high', 'auto'] as const,
|
||||||
@ -47,6 +48,7 @@ export const MODEL_SUPPORTED_OPTIONS: ThinkingOptionConfig = {
|
|||||||
o: MODEL_SUPPORTED_REASONING_EFFORT.o,
|
o: MODEL_SUPPORTED_REASONING_EFFORT.o,
|
||||||
openai_deep_research: MODEL_SUPPORTED_REASONING_EFFORT.openai_deep_research,
|
openai_deep_research: MODEL_SUPPORTED_REASONING_EFFORT.openai_deep_research,
|
||||||
gpt5: [...MODEL_SUPPORTED_REASONING_EFFORT.gpt5] as const,
|
gpt5: [...MODEL_SUPPORTED_REASONING_EFFORT.gpt5] as const,
|
||||||
|
gpt5pro: MODEL_SUPPORTED_REASONING_EFFORT.gpt5pro,
|
||||||
gpt5_codex: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_codex,
|
gpt5_codex: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_codex,
|
||||||
gpt5_1: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_1,
|
gpt5_1: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_1,
|
||||||
gpt5_1_codex: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_1_codex,
|
gpt5_1_codex: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_1_codex,
|
||||||
@ -90,6 +92,9 @@ const _getThinkModelType = (model: Model): ThinkingModelType => {
|
|||||||
thinkingModelType = 'gpt5_codex'
|
thinkingModelType = 'gpt5_codex'
|
||||||
} else {
|
} else {
|
||||||
thinkingModelType = 'gpt5'
|
thinkingModelType = 'gpt5'
|
||||||
|
if (isGPT5ProModel(model)) {
|
||||||
|
thinkingModelType = 'gpt5pro'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (isSupportedReasoningEffortOpenAIModel(model)) {
|
} else if (isSupportedReasoningEffortOpenAIModel(model)) {
|
||||||
thinkingModelType = 'o'
|
thinkingModelType = 'o'
|
||||||
|
|||||||
@ -240,6 +240,21 @@ export const isGPT51SeriesModel = (model: Model) => {
|
|||||||
return modelId.includes('gpt-5.1')
|
return modelId.includes('gpt-5.1')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GPT-5 verbosity configuration
|
||||||
|
// gpt-5-pro only supports 'high', other GPT-5 models support all levels
|
||||||
|
export const MODEL_SUPPORTED_VERBOSITY: Record<string, ('low' | 'medium' | 'high')[]> = {
|
||||||
|
'gpt-5-pro': ['high'],
|
||||||
|
default: ['low', 'medium', 'high']
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getModelSupportedVerbosity = (model: Model): ('low' | 'medium' | 'high')[] => {
|
||||||
|
const modelId = getLowerBaseModelName(model.id)
|
||||||
|
if (modelId.includes('gpt-5-pro')) {
|
||||||
|
return MODEL_SUPPORTED_VERBOSITY['gpt-5-pro']
|
||||||
|
}
|
||||||
|
return MODEL_SUPPORTED_VERBOSITY.default
|
||||||
|
}
|
||||||
|
|
||||||
export const isGeminiModel = (model: Model) => {
|
export const isGeminiModel = (model: Model) => {
|
||||||
const modelId = getLowerBaseModelName(model.id)
|
const modelId = getLowerBaseModelName(model.id)
|
||||||
return modelId.includes('gemini')
|
return modelId.includes('gemini')
|
||||||
@ -256,3 +271,8 @@ export const ZHIPU_RESULT_TOKENS = ['<|begin_of_box|>', '<|end_of_box|>'] as con
|
|||||||
export const agentModelFilter = (model: Model): boolean => {
|
export const agentModelFilter = (model: Model): boolean => {
|
||||||
return !isEmbeddingModel(model) && !isRerankModel(model) && !isTextToImageModel(model)
|
return !isEmbeddingModel(model) && !isRerankModel(model) && !isTextToImageModel(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isGPT5ProModel = (model: Model) => {
|
||||||
|
const modelId = getLowerBaseModelName(model.id)
|
||||||
|
return modelId.includes('gpt-5-pro')
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Selector from '@renderer/components/Selector'
|
import Selector from '@renderer/components/Selector'
|
||||||
import {
|
import {
|
||||||
|
getModelSupportedVerbosity,
|
||||||
isSupportedReasoningEffortOpenAIModel,
|
isSupportedReasoningEffortOpenAIModel,
|
||||||
isSupportFlexServiceTierModel,
|
isSupportFlexServiceTierModel,
|
||||||
isSupportVerbosityModel
|
isSupportVerbosityModel
|
||||||
@ -80,20 +81,24 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const verbosityOptions = [
|
const verbosityOptions = useMemo(() => {
|
||||||
{
|
const allOptions = [
|
||||||
value: 'low',
|
{
|
||||||
label: t('settings.openai.verbosity.low')
|
value: 'low',
|
||||||
},
|
label: t('settings.openai.verbosity.low')
|
||||||
{
|
},
|
||||||
value: 'medium',
|
{
|
||||||
label: t('settings.openai.verbosity.medium')
|
value: 'medium',
|
||||||
},
|
label: t('settings.openai.verbosity.medium')
|
||||||
{
|
},
|
||||||
value: 'high',
|
{
|
||||||
label: t('settings.openai.verbosity.high')
|
value: 'high',
|
||||||
}
|
label: t('settings.openai.verbosity.high')
|
||||||
]
|
}
|
||||||
|
]
|
||||||
|
const supportedVerbosityLevels = getModelSupportedVerbosity(model)
|
||||||
|
return allOptions.filter((option) => supportedVerbosityLevels.includes(option.value as any))
|
||||||
|
}, [model, t])
|
||||||
|
|
||||||
const serviceTierOptions = useMemo(() => {
|
const serviceTierOptions = useMemo(() => {
|
||||||
let baseOptions: { value: ServiceTier; label: string }[]
|
let baseOptions: { value: ServiceTier; label: string }[]
|
||||||
@ -155,6 +160,15 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
|||||||
}
|
}
|
||||||
}, [provider.id, serviceTierMode, serviceTierOptions, setServiceTierMode])
|
}, [provider.id, serviceTierMode, serviceTierOptions, setServiceTierMode])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (verbosity && !verbosityOptions.some((option) => option.value === verbosity)) {
|
||||||
|
const supportedVerbosityLevels = getModelSupportedVerbosity(model)
|
||||||
|
// Default to the highest supported verbosity level
|
||||||
|
const defaultVerbosity = supportedVerbosityLevels[supportedVerbosityLevels.length - 1]
|
||||||
|
setVerbosity(defaultVerbosity)
|
||||||
|
}
|
||||||
|
}, [model, verbosity, verbosityOptions, setVerbosity])
|
||||||
|
|
||||||
if (!isOpenAIReasoning && !isSupportServiceTier && !isSupportVerbosity) {
|
if (!isOpenAIReasoning && !isSupportServiceTier && !isSupportVerbosity) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,6 +86,7 @@ const ThinkModelTypes = [
|
|||||||
'gpt5_1',
|
'gpt5_1',
|
||||||
'gpt5_codex',
|
'gpt5_codex',
|
||||||
'gpt5_1_codex',
|
'gpt5_1_codex',
|
||||||
|
'gpt5pro',
|
||||||
'grok',
|
'grok',
|
||||||
'grok4_fast',
|
'grok4_fast',
|
||||||
'gemini',
|
'gemini',
|
||||||
@ -113,7 +114,7 @@ export function isThinkModelType(type: string): type is ThinkingModelType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EFFORT_RATIO: EffortRatio = {
|
export const EFFORT_RATIO: EffortRatio = {
|
||||||
none: 0,
|
none: 0.01,
|
||||||
minimal: 0.05,
|
minimal: 0.05,
|
||||||
low: 0.05,
|
low: 0.05,
|
||||||
medium: 0.5,
|
medium: 0.5,
|
||||||
|
|||||||
@ -126,6 +126,10 @@ export type OpenAIExtraBody = {
|
|||||||
source_lang: 'auto'
|
source_lang: 'auto'
|
||||||
target_lang: string
|
target_lang: string
|
||||||
}
|
}
|
||||||
|
// for gpt-5 series models verbosity control
|
||||||
|
text?: {
|
||||||
|
verbosity?: 'low' | 'medium' | 'high'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// image is for openrouter. audio is ignored for now
|
// image is for openrouter. audio is ignored for now
|
||||||
export type OpenAIModality = OpenAI.ChatCompletionModality | 'image'
|
export type OpenAIModality = OpenAI.ChatCompletionModality | 'image'
|
||||||
|
|||||||
16
yarn.lock
16
yarn.lock
@ -102,7 +102,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/anthropic@npm:2.0.44":
|
"@ai-sdk/anthropic@npm:2.0.44, @ai-sdk/anthropic@npm:^2.0.44":
|
||||||
version: 2.0.44
|
version: 2.0.44
|
||||||
resolution: "@ai-sdk/anthropic@npm:2.0.44"
|
resolution: "@ai-sdk/anthropic@npm:2.0.44"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -206,6 +206,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@ai-sdk/google@npm:^2.0.32":
|
||||||
|
version: 2.0.32
|
||||||
|
resolution: "@ai-sdk/google@npm:2.0.32"
|
||||||
|
dependencies:
|
||||||
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
|
"@ai-sdk/provider-utils": "npm:3.0.17"
|
||||||
|
peerDependencies:
|
||||||
|
zod: ^3.25.76 || ^4.1.8
|
||||||
|
checksum: 10c0/052de16f1f66188e126168c8a9cc903448104528c7e44d6867bbf555c9067b9d6d44a4c4e0e014838156ba39095cb417f1b76363eb65212ca4d005f3651e58d2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.31#~/.yarn/patches/@ai-sdk-google-npm-2.0.31-b0de047210.patch":
|
"@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.31#~/.yarn/patches/@ai-sdk-google-npm-2.0.31-b0de047210.patch":
|
||||||
version: 2.0.31
|
version: 2.0.31
|
||||||
resolution: "@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.31#~/.yarn/patches/@ai-sdk-google-npm-2.0.31-b0de047210.patch::version=2.0.31&hash=9f3835"
|
resolution: "@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.31#~/.yarn/patches/@ai-sdk-google-npm-2.0.31-b0de047210.patch::version=2.0.31&hash=9f3835"
|
||||||
@ -9891,8 +9903,10 @@ __metadata:
|
|||||||
"@agentic/searxng": "npm:^7.3.3"
|
"@agentic/searxng": "npm:^7.3.3"
|
||||||
"@agentic/tavily": "npm:^7.3.3"
|
"@agentic/tavily": "npm:^7.3.3"
|
||||||
"@ai-sdk/amazon-bedrock": "npm:^3.0.53"
|
"@ai-sdk/amazon-bedrock": "npm:^3.0.53"
|
||||||
|
"@ai-sdk/anthropic": "npm:^2.0.44"
|
||||||
"@ai-sdk/cerebras": "npm:^1.0.31"
|
"@ai-sdk/cerebras": "npm:^1.0.31"
|
||||||
"@ai-sdk/gateway": "npm:^2.0.9"
|
"@ai-sdk/gateway": "npm:^2.0.9"
|
||||||
|
"@ai-sdk/google": "npm:^2.0.32"
|
||||||
"@ai-sdk/google-vertex": "npm:^3.0.62"
|
"@ai-sdk/google-vertex": "npm:^3.0.62"
|
||||||
"@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.8#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.8-d4d0aaac93.patch"
|
"@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.8#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.8-d4d0aaac93.patch"
|
||||||
"@ai-sdk/mistral": "npm:^2.0.23"
|
"@ai-sdk/mistral": "npm:^2.0.23"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user