From 7a0da13676256201273171ba8681a0606f2b5639 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Tue, 26 Aug 2025 22:41:10 +0800 Subject: [PATCH] fix(qwen3): fix qwen3 thinking control by soft command (#9568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(qwen3): 修复Qwen3模型思考模式处理逻辑 重构processPostsuffixQwen3Model函数,简化后缀处理逻辑 添加nvidia到不支持思考模式的提供商列表 移除postsuffix参数 --- .../aiCore/clients/openai/OpenAIApiClient.ts | 3 +- src/renderer/src/config/providers.ts | 6 +- .../src/services/ModelMessageService.ts | 56 +++++++------------ 3 files changed, 27 insertions(+), 38 deletions(-) diff --git a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts index 089d1af799..c8a4ff82d0 100644 --- a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts +++ b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts @@ -621,11 +621,10 @@ export class OpenAIAPIClient extends OpenAIBaseClient< const lastUserMsg = userMessages.findLast((m) => m.role === 'user') if (lastUserMsg) { if (isSupportedThinkingTokenQwenModel(model) && !isSupportEnableThinkingProvider(this.provider)) { - const postsuffix = '/no_think' const qwenThinkModeEnabled = assistant.settings?.qwenThinkMode === true const currentContent = lastUserMsg.content - lastUserMsg.content = processPostsuffixQwen3Model(currentContent, postsuffix, qwenThinkModeEnabled) as any + lastUserMsg.content = processPostsuffixQwen3Model(currentContent, qwenThinkModeEnabled) } if (this.provider.id === SystemProviderIds.poe) { // 如果以后 poe 支持 reasoning_effort 参数了,可以删掉这部分 diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index a8c0383108..ac73f47cc7 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -1276,7 +1276,11 @@ export const isSupportStreamOptionsProvider = (provider: Provider) => { ) } -const NOT_SUPPORT_QWEN3_ENABLE_THINKING_PROVIDER = ['ollama', 'lmstudio'] as const satisfies SystemProviderId[] +const NOT_SUPPORT_QWEN3_ENABLE_THINKING_PROVIDER = [ + 'ollama', + 'lmstudio', + 'nvidia' +] as const satisfies SystemProviderId[] /** * 判断提供商是否支持使用 enable_thinking 参数来控制 Qwen3 等模型的思考。 Only for OpenAI Chat Completions API. diff --git a/src/renderer/src/services/ModelMessageService.ts b/src/renderer/src/services/ModelMessageService.ts index b48543ea19..40609fa39c 100644 --- a/src/renderer/src/services/ModelMessageService.ts +++ b/src/renderer/src/services/ModelMessageService.ts @@ -1,5 +1,6 @@ import { Model } from '@renderer/types' -import { ChatCompletionContentPart, ChatCompletionContentPartText, ChatCompletionMessageParam } from 'openai/resources' +import { findLast } from 'lodash' +import { ChatCompletionContentPart, ChatCompletionMessageParam } from 'openai/resources' export function processReqMessages( model: Model, @@ -43,60 +44,45 @@ function interleaveUserAndAssistantMessages(messages: ChatCompletionMessageParam // Process postsuffix for Qwen3 model export function processPostsuffixQwen3Model( - // content 類型:string | ChatCompletionContentPart[] | null - content: string | ChatCompletionContentPart[] | null, - postsuffix: string, + // content 類型:string | ChatCompletionContentPart[] + content: string | ChatCompletionContentPart[], qwenThinkModeEnabled: boolean -): string | ChatCompletionContentPart[] | null { +): string | ChatCompletionContentPart[] { + const noThinkSuffix = '/no_think' + const thinkSuffix = '/think' if (typeof content === 'string') { if (qwenThinkModeEnabled) { - // 思考模式启用,移除 postsuffix - if (content.endsWith(postsuffix)) { - return content.substring(0, content.length - postsuffix.length).trimEnd() + if (!content.endsWith(thinkSuffix)) { + return content + ' ' + thinkSuffix } } else { - // 思考模式未启用,添加 postsuffix - if (!content.endsWith(postsuffix)) { - return content + ' ' + postsuffix + if (!content.endsWith(noThinkSuffix)) { + return content + ' ' + noThinkSuffix } } } else if (Array.isArray(content)) { - let lastTextPartIndex = -1 - for (let i = content.length - 1; i >= 0; i--) { - if (content[i].type === 'text') { - lastTextPartIndex = i - break - } - } + const lastTextPart = findLast(content, (part) => part.type === 'text') - if (lastTextPartIndex !== -1) { - const textPart = content[lastTextPartIndex] as ChatCompletionContentPartText + if (lastTextPart) { if (qwenThinkModeEnabled) { - // 思考模式启用,移除 postsuffix - if (textPart.text.endsWith(postsuffix)) { - textPart.text = textPart.text.substring(0, textPart.text.length - postsuffix.length).trimEnd() - // 可選:如果 textPart.text 變為空,可以考慮是否移除該 part + if (!lastTextPart.text.endsWith(thinkSuffix)) { + lastTextPart.text += thinkSuffix } } else { - // 思考模式未启用,添加 postsuffix - if (!textPart.text.endsWith(postsuffix)) { - textPart.text += postsuffix + if (!lastTextPart.text.endsWith(noThinkSuffix)) { + lastTextPart.text += noThinkSuffix } } } else { // 數組中沒有文本部分 - if (!qwenThinkModeEnabled) { + if (qwenThinkModeEnabled) { // 思考模式未啓用,需要添加 postsuffix // 如果沒有文本部分,則添加一個新的文本部分 - content.push({ type: 'text', text: postsuffix }) + content.push({ type: 'text', text: thinkSuffix }) + } else { + content.push({ type: 'text', text: noThinkSuffix }) } } - } else { - // currentContent 是 null - if (!qwenThinkModeEnabled) { - // 思考模式未启用,需要添加 postsuffix - return content - } } return content }