From a9a9d884ce8ec7104c2b23ce6b3dc71a0d58966f Mon Sep 17 00:00:00 2001 From: SuYao Date: Mon, 30 Jun 2025 13:51:23 +0800 Subject: [PATCH] Fix/gemini (#7659) * refactor: update Gemini and OpenAI API clients for improved reasoning model handling - Replaced isGeminiReasoningModel with isSupportedThinkingTokenGeminiModel in GeminiAPIClient for better model validation. - Enhanced OpenAIAPIClient to support additional configurations for reasoning efforts and thinking budgets based on model type. - Introduced new thinking tags for Gemini models in ThinkingTagExtractionMiddleware. - Updated model checks in models.ts to streamline reasoning model identification. - Adjusted ThinkingButton component to differentiate between Gemini and Gemini Pro models based on regex checks. * refactor(GeminiAPIClient): streamline reasoning configuration handling - Simplified the logic for returning thinking configuration when reasoningEffort is undefined in GeminiAPIClient. - Updated ApiService to include enableReasoning flag for API calls, enhancing control over reasoning capabilities. * fix(OpenAIAPIClient): add support for non-flash Gemini models in reasoning configuration - Introduced a check for non-flash models in the OpenAIAPIClient to enhance reasoning configuration handling for supported Gemini models. - This change ensures that reasoning is correctly configured based on the model type, improving overall model validation. --- .../aiCore/clients/gemini/GeminiAPIClient.ts | 26 ++++++------ .../aiCore/clients/openai/OpenAIApiClient.ts | 40 ++++++++++++++++++- .../feat/ThinkingTagExtractionMiddleware.ts | 2 + src/renderer/src/config/models.ts | 6 ++- .../pages/home/Inputbar/ThinkingButton.tsx | 15 +++++-- src/renderer/src/services/ApiService.ts | 1 + src/renderer/src/types/sdk.ts | 1 + 7 files changed, 71 insertions(+), 20 deletions(-) diff --git a/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts b/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts index 549e931966..f37bcd3d30 100644 --- a/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts +++ b/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts @@ -22,8 +22,8 @@ import { GenericChunk } from '@renderer/aiCore/middleware/schemas' import { findTokenLimit, GEMINI_FLASH_MODEL_REGEX, - isGeminiReasoningModel, isGemmaModel, + isSupportedThinkingTokenGeminiModel, isVisionModel } from '@renderer/config/models' import { CacheService } from '@renderer/services/CacheService' @@ -393,29 +393,29 @@ export class GeminiAPIClient extends BaseApiClient< * @returns The reasoning effort */ private getBudgetToken(assistant: Assistant, model: Model) { - if (isGeminiReasoningModel(model)) { + if (isSupportedThinkingTokenGeminiModel(model)) { const reasoningEffort = assistant?.settings?.reasoning_effort // 如果thinking_budget是undefined,不思考 if (reasoningEffort === undefined) { - return { - thinkingConfig: { - includeThoughts: false, - ...(GEMINI_FLASH_MODEL_REGEX.test(model.id) ? { thinkingBudget: 0 } : {}) - } as ThinkingConfig - } + return GEMINI_FLASH_MODEL_REGEX.test(model.id) + ? { + thinkingConfig: { + thinkingBudget: 0 + } + } + : {} } - const effortRatio = EFFORT_RATIO[reasoningEffort] - - if (effortRatio > 1) { + if (reasoningEffort === 'auto') { return { thinkingConfig: { - includeThoughts: true + includeThoughts: true, + thinkingBudget: -1 } } } - + const effortRatio = EFFORT_RATIO[reasoningEffort] const { min, max } = findTokenLimit(model.id) || { min: 0, max: 0 } // 计算 budgetTokens,确保不低于 min const budget = Math.floor((max - min) * effortRatio + min) diff --git a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts index 6c6e524b53..9b72758c97 100644 --- a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts +++ b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts @@ -114,6 +114,9 @@ export class OpenAIAPIClient extends OpenAIBaseClient< if (!reasoningEffort) { if (model.provider === 'openrouter') { + if (isSupportedThinkingTokenGeminiModel(model) && !GEMINI_FLASH_MODEL_REGEX.test(model.id)) { + return {} + } return { reasoning: { enabled: false, exclude: true } } } if (isSupportedThinkingTokenQwenModel(model)) { @@ -126,7 +129,15 @@ export class OpenAIAPIClient extends OpenAIBaseClient< if (isSupportedThinkingTokenGeminiModel(model)) { if (GEMINI_FLASH_MODEL_REGEX.test(model.id)) { - return { reasoning_effort: 'none' } + return { + extra_body: { + google: { + thinking_config: { + thinking_budget: 0 + } + } + } + } } return {} } @@ -169,12 +180,37 @@ export class OpenAIAPIClient extends OpenAIBaseClient< } // OpenAI models - if (isSupportedReasoningEffortOpenAIModel(model) || isSupportedThinkingTokenGeminiModel(model)) { + if (isSupportedReasoningEffortOpenAIModel(model)) { return { reasoning_effort: reasoningEffort } } + if (isSupportedThinkingTokenGeminiModel(model)) { + if (reasoningEffort === 'auto') { + return { + extra_body: { + google: { + thinking_config: { + thinking_budget: -1, + include_thoughts: true + } + } + } + } + } + return { + extra_body: { + google: { + thinking_config: { + thinking_budget: budgetTokens, + include_thoughts: true + } + } + } + } + } + // Claude models if (isSupportedThinkingTokenClaudeModel(model)) { const maxTokens = assistant.settings?.maxTokens diff --git a/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts b/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts index 440de40045..fe2d51d8de 100644 --- a/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts +++ b/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts @@ -11,11 +11,13 @@ export const MIDDLEWARE_NAME = 'ThinkingTagExtractionMiddleware' // 不同模型的思考标签配置 const reasoningTags: TagConfig[] = [ { openingTag: '', closingTag: '', separator: '\n' }, + { openingTag: '', closingTag: '', separator: '\n' }, { openingTag: '###Thinking', closingTag: '###Response', separator: '\n' } ] const getAppropriateTag = (model?: Model): TagConfig => { if (model?.id?.includes('qwen3')) return reasoningTags[0] + if (model?.id?.includes('gemini-2.5')) return reasoningTags[1] // 可以在这里添加更多模型特定的标签配置 return reasoningTags[0] // 默认使用 标签 } diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index 3c2902a489..48560007ca 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -2509,14 +2509,16 @@ export function isGeminiReasoningModel(model?: Model): boolean { return true } - if (model.id.includes('gemini-2.5')) { + if (isSupportedThinkingTokenGeminiModel(model)) { return true } return false } -export const isSupportedThinkingTokenGeminiModel = isGeminiReasoningModel +export const isSupportedThinkingTokenGeminiModel = (model: Model): boolean => { + return model.id.includes('gemini-2.5') +} export function isQwenReasoningModel(model?: Model): boolean { if (!model) { diff --git a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx index 21db131cef..5bf57d9c9c 100644 --- a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx @@ -7,6 +7,7 @@ import { } from '@renderer/components/Icons/SVGIcon' import { useQuickPanel } from '@renderer/components/QuickPanel' import { + GEMINI_FLASH_MODEL_REGEX, isDoubaoThinkingAutoModel, isSupportedReasoningEffortGrokModel, isSupportedThinkingTokenDoubaoModel, @@ -37,13 +38,14 @@ const MODEL_SUPPORTED_OPTIONS: Record = { default: ['off', 'low', 'medium', 'high'], grok: ['off', 'low', 'high'], gemini: ['off', 'low', 'medium', 'high', 'auto'], + gemini_pro: ['low', 'medium', 'high', 'auto'], qwen: ['off', 'low', 'medium', 'high'], doubao: ['off', 'auto', 'high'] } // 选项转换映射表:当选项不支持时使用的替代选项 const OPTION_FALLBACK: Record = { - off: 'off', + off: 'low', // off -> low (for Gemini Pro models) low: 'high', medium: 'high', // medium -> high (for Grok models) high: 'high', @@ -57,6 +59,7 @@ const ThinkingButton: FC = ({ ref, model, assistant, ToolbarButton }): Re const isGrokModel = isSupportedReasoningEffortGrokModel(model) const isGeminiModel = isSupportedThinkingTokenGeminiModel(model) + const isGeminiFlashModel = GEMINI_FLASH_MODEL_REGEX.test(model.id) const isQwenModel = isSupportedThinkingTokenQwenModel(model) const isDoubaoModel = isSupportedThinkingTokenDoubaoModel(model) @@ -66,12 +69,18 @@ const ThinkingButton: FC = ({ ref, model, assistant, ToolbarButton }): Re // 确定当前模型支持的选项类型 const modelType = useMemo(() => { - if (isGeminiModel) return 'gemini' + if (isGeminiModel) { + if (isGeminiFlashModel) { + return 'gemini' + } else { + return 'gemini_pro' + } + } if (isGrokModel) return 'grok' if (isQwenModel) return 'qwen' if (isDoubaoModel) return 'doubao' return 'default' - }, [isGeminiModel, isGrokModel, isQwenModel, isDoubaoModel]) + }, [isGeminiModel, isGrokModel, isQwenModel, isDoubaoModel, isGeminiFlashModel]) // 获取当前模型支持的选项 const supportedOptions = useMemo(() => { diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index 4704a8bfd3..abc3db81b0 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -607,6 +607,7 @@ export async function checkApi(provider: Provider, model: Model): Promise messages: 'hi', assistant, streamOutput: true, + enableReasoning: false, shouldThrow: true } diff --git a/src/renderer/src/types/sdk.ts b/src/renderer/src/types/sdk.ts index 6505210b60..c7eeb9500c 100644 --- a/src/renderer/src/types/sdk.ts +++ b/src/renderer/src/types/sdk.ts @@ -53,6 +53,7 @@ export type ReasoningEffortOptionalParams = { enable_thinking?: boolean thinking_budget?: number enable_reasoning?: boolean + extra_body?: Record // Add any other potential reasoning-related keys here if they exist }