From 0149cfbd213ecbde3e27c388a262ebfafb1051ab Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Sat, 19 Jul 2025 23:23:53 +0800 Subject: [PATCH] fix: get empty response when using MCP by functional method (#8296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 添加日志记录以调试中间件处理流程 在多个中间件中添加日志记录以跟踪chunk处理流程 在AiProvider中添加日志记录以调试中间件移除逻辑 * fix(openai): 修复tool_call chunk被跳过的问题 添加对choice.delta.content为null情况的处理 同时添加日志输出用于调试chunk数据 * fix(openai): 修复流式响应中空内容判断逻辑 * fix(openai): 修复流式响应中tool_calls内容判断逻辑 --- .../src/aiCore/clients/openai/OpenAIApiClient.ts | 2 ++ src/renderer/src/aiCore/index.ts | 11 +++++++++++ .../aiCore/middleware/core/McpToolChunkMiddleware.ts | 1 + .../src/aiCore/middleware/core/TextChunkMiddleware.ts | 1 + .../feat/ThinkingTagExtractionMiddleware.ts | 1 + .../middleware/feat/ToolUseExtractionMiddleware.ts | 1 + src/renderer/src/services/ApiService.ts | 1 + 7 files changed, 18 insertions(+) diff --git a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts index f7b88aeda6..fc2994d4cc 100644 --- a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts +++ b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts @@ -692,6 +692,7 @@ export class OpenAIAPIClient extends OpenAIBaseClient< return (context: ResponseChunkTransformerContext) => ({ async transform(chunk: OpenAISdkRawChunk, controller: TransformStreamDefaultController) { // 持续更新usage信息 + logger.silly('chunk', chunk) if (chunk.usage) { lastUsageInfo = { prompt_tokens: chunk.usage.prompt_tokens || 0, @@ -714,6 +715,7 @@ export class OpenAIAPIClient extends OpenAIBaseClient< choice.delta && Object.keys(choice.delta).length > 0 && (!('content' in choice.delta) || + (choice.delta.tool_calls && choice.delta.tool_calls.length > 0) || (typeof choice.delta.content === 'string' && choice.delta.content !== '') || (typeof (choice.delta as any).reasoning_content === 'string' && (choice.delta as any).reasoning_content !== '') || diff --git a/src/renderer/src/aiCore/index.ts b/src/renderer/src/aiCore/index.ts index 3ff0bc5b85..7e79cb0fc5 100644 --- a/src/renderer/src/aiCore/index.ts +++ b/src/renderer/src/aiCore/index.ts @@ -77,34 +77,45 @@ export default class AiProvider { .add(MiddlewareRegistry[ImageGenerationMiddlewareName]) } else { // Existing logic for other models + logger.silly('Builder Params', params) if (!params.enableReasoning) { // 这里注释掉不会影响正常的关闭思考,可忽略不计的性能下降 // builder.remove(ThinkingTagExtractionMiddlewareName) builder.remove(ThinkChunkMiddlewareName) + logger.silly('ThinkChunkMiddleware is removed') } // 注意:用client判断会导致typescript类型收窄 if (!(this.apiClient instanceof OpenAIAPIClient) && !(this.apiClient instanceof OpenAIResponseAPIClient)) { + logger.silly('ThinkingTagExtractionMiddleware is removed') builder.remove(ThinkingTagExtractionMiddlewareName) } if (!(this.apiClient instanceof AnthropicAPIClient) && !(this.apiClient instanceof OpenAIResponseAPIClient)) { + logger.silly('RawStreamListenerMiddleware is removed') builder.remove(RawStreamListenerMiddlewareName) } if (!params.enableWebSearch) { + logger.silly('WebSearchMiddleware is removed') builder.remove(WebSearchMiddlewareName) } if (!params.mcpTools?.length) { builder.remove(ToolUseExtractionMiddlewareName) + logger.silly('ToolUseExtractionMiddleware is removed') builder.remove(McpToolChunkMiddlewareName) + logger.silly('McpToolChunkMiddleware is removed') } if (isEnabledToolUse(params.assistant) && isFunctionCallingModel(model)) { builder.remove(ToolUseExtractionMiddlewareName) + logger.silly('ToolUseExtractionMiddleware is removed') } if (params.callType !== 'chat') { + logger.silly('AbortHandlerMiddleware is removed') builder.remove(AbortHandlerMiddlewareName) } if (params.callType === 'test') { builder.remove(ErrorHandlerMiddlewareName) + logger.silly('ErrorHandlerMiddleware is removed') builder.remove(FinalChunkConsumerMiddlewareName) + logger.silly('FinalChunkConsumerMiddleware is removed') } } diff --git a/src/renderer/src/aiCore/middleware/core/McpToolChunkMiddleware.ts b/src/renderer/src/aiCore/middleware/core/McpToolChunkMiddleware.ts index a14ce323b8..50609131e3 100644 --- a/src/renderer/src/aiCore/middleware/core/McpToolChunkMiddleware.ts +++ b/src/renderer/src/aiCore/middleware/core/McpToolChunkMiddleware.ts @@ -100,6 +100,7 @@ function createToolHandlingTransform( async transform(chunk: GenericChunk, controller) { try { // 处理MCP工具进展chunk + logger.silly('chunk', chunk) if (chunk.type === ChunkType.MCP_TOOL_CREATED) { const createdChunk = chunk as MCPToolCreatedChunk diff --git a/src/renderer/src/aiCore/middleware/core/TextChunkMiddleware.ts b/src/renderer/src/aiCore/middleware/core/TextChunkMiddleware.ts index 03035f257c..82e8a7cdaa 100644 --- a/src/renderer/src/aiCore/middleware/core/TextChunkMiddleware.ts +++ b/src/renderer/src/aiCore/middleware/core/TextChunkMiddleware.ts @@ -43,6 +43,7 @@ export const TextChunkMiddleware: CompletionsMiddleware = const enhancedTextStream = resultFromUpstream.pipeThrough( new TransformStream({ transform(chunk: GenericChunk, controller) { + logger.silly('chunk', chunk) if (chunk.type === ChunkType.TEXT_DELTA) { accumulatedTextContent += chunk.text diff --git a/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts b/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts index 46a4ec3e85..9d7baccb8f 100644 --- a/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts +++ b/src/renderer/src/aiCore/middleware/feat/ThinkingTagExtractionMiddleware.ts @@ -72,6 +72,7 @@ export const ThinkingTagExtractionMiddleware: CompletionsMiddleware = const processedStream = resultFromUpstream.pipeThrough( new TransformStream({ transform(chunk: GenericChunk, controller) { + logger.silly('chunk', chunk) if (chunk.type === ChunkType.TEXT_DELTA) { const textChunk = chunk as TextDeltaChunk diff --git a/src/renderer/src/aiCore/middleware/feat/ToolUseExtractionMiddleware.ts b/src/renderer/src/aiCore/middleware/feat/ToolUseExtractionMiddleware.ts index 933f4b8060..2f361b2a25 100644 --- a/src/renderer/src/aiCore/middleware/feat/ToolUseExtractionMiddleware.ts +++ b/src/renderer/src/aiCore/middleware/feat/ToolUseExtractionMiddleware.ts @@ -69,6 +69,7 @@ function createToolUseExtractionTransform( async transform(chunk: GenericChunk, controller) { try { // 处理文本内容,检测工具使用标签 + logger.silly('chunk', chunk) if (chunk.type === ChunkType.TEXT_DELTA) { const textChunk = chunk as TextDeltaChunk diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index 399fe9111c..501f9b7152 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -398,6 +398,7 @@ export async function fetchChatCompletion({ filterEmptyMessages(filterContextMessages(takeRight(filteredMessages, contextCount + 2))) // 取原来几个provider的最大值 ) + // FIXME: qwen3即使关闭思考仍然会导致enableReasoning的结果为true const enableReasoning = ((isSupportedThinkingTokenModel(model) || isSupportedReasoningEffortModel(model)) && assistant.settings?.reasoning_effort !== undefined) ||