From e70174817e904a747df1a24d41ac82b0a7ab9f55 Mon Sep 17 00:00:00 2001 From: suyao Date: Fri, 20 Jun 2025 16:31:16 +0800 Subject: [PATCH] refactor: update AiSdkToChunkAdapter and middleware for improved chunk handling - Modified convertAndEmitChunk method to handle new chunk types and streamline processing. - Adjusted thinkingTimeMiddleware to remove unnecessary parameters and enhance clarity. - Updated middleware integration in AiSdkMiddlewareBuilder for better middleware management. --- .../src/aiCore/AiSdkToChunkAdapter.ts | 22 ++++++++++++++----- src/renderer/src/aiCore/index_new.ts | 5 +---- .../aisdk/AiSdkMiddlewareBuilder.ts | 2 +- .../aisdk/ThinkingTimeMiddleware.ts | 19 +++++++--------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/renderer/src/aiCore/AiSdkToChunkAdapter.ts b/src/renderer/src/aiCore/AiSdkToChunkAdapter.ts index ae6ba25987..9eef6981dd 100644 --- a/src/renderer/src/aiCore/AiSdkToChunkAdapter.ts +++ b/src/renderer/src/aiCore/AiSdkToChunkAdapter.ts @@ -68,7 +68,7 @@ export class AiSdkToChunkAdapter { * 转换 AI SDK chunk 为 Cherry Studio chunk 并调用回调 * @param chunk AI SDK 的 chunk 数据 */ - private convertAndEmitChunk(chunk: TextStreamPart, final: { text: string; reasoning_content: string }) { + private convertAndEmitChunk(chunk: any, final: { text: string; reasoning_content: string }) { console.log('AI SDK chunk type:', chunk.type, chunk) switch (chunk.type) { // === 文本相关事件 === @@ -80,12 +80,24 @@ export class AiSdkToChunkAdapter { }) break - case 'reasoning-signature': - // 不再需要处理,中间件会发出 THINKING_COMPLETE + case 'reasoning': + this.onChunk({ + type: ChunkType.THINKING_DELTA, + text: chunk.textDelta || '' + }) break - case 'redacted-reasoning': - // 不再需要处理 + this.onChunk({ + type: ChunkType.THINKING_DELTA, + text: chunk.data || '' + }) + break + case 'reasoning-signature': + this.onChunk({ + type: ChunkType.THINKING_COMPLETE, + text: chunk.text || '', + thinking_millsec: chunk.thinking_millsec || 0 + }) break // === 工具调用相关事件 === diff --git a/src/renderer/src/aiCore/index_new.ts b/src/renderer/src/aiCore/index_new.ts index 2622c55335..37dd4fdeca 100644 --- a/src/renderer/src/aiCore/index_new.ts +++ b/src/renderer/src/aiCore/index_new.ts @@ -19,7 +19,6 @@ import { } from '@cherrystudio/ai-core' import { isDedicatedImageGenerationModel } from '@renderer/config/models' import type { GenerateImageParams, Model, Provider } from '@renderer/types' -import { Chunk } from '@renderer/types/chunk' // 引入适配器 import AiSdkToChunkAdapter from './AiSdkToChunkAdapter' @@ -121,15 +120,13 @@ export default class ModernAiProvider { public async completions( modelId: string, params: StreamTextParams, - middlewareConfig: AiSdkMiddlewareConfig, - onChunk?: (chunk: Chunk) => void + middlewareConfig: AiSdkMiddlewareConfig ): Promise { // const model = params.assistant.model // 检查是否应该使用现代化客户端 // if (this.modernClient && model && isModernSdkSupported(this.provider, model)) { // try { - console.log('completions', modelId, params, onChunk) return await this.modernCompletions(modelId, params, middlewareConfig) // } catch (error) { // console.warn('Modern client failed, falling back to legacy:', error) diff --git a/src/renderer/src/aiCore/middleware/aisdk/AiSdkMiddlewareBuilder.ts b/src/renderer/src/aiCore/middleware/aisdk/AiSdkMiddlewareBuilder.ts index d309a1112d..f9303f3950 100644 --- a/src/renderer/src/aiCore/middleware/aisdk/AiSdkMiddlewareBuilder.ts +++ b/src/renderer/src/aiCore/middleware/aisdk/AiSdkMiddlewareBuilder.ts @@ -100,7 +100,7 @@ export function buildAiSdkMiddlewares(config: AiSdkMiddlewareConfig): NamedAiSdk if (config.onChunk && config.model && isReasoningModel(config.model)) { builder.add({ name: 'thinking-time', - aiSdkMiddlewares: [thinkingTimeMiddleware(config.onChunk)] + aiSdkMiddlewares: [thinkingTimeMiddleware()] }) } diff --git a/src/renderer/src/aiCore/middleware/aisdk/ThinkingTimeMiddleware.ts b/src/renderer/src/aiCore/middleware/aisdk/ThinkingTimeMiddleware.ts index 66db710874..39fbef6d74 100644 --- a/src/renderer/src/aiCore/middleware/aisdk/ThinkingTimeMiddleware.ts +++ b/src/renderer/src/aiCore/middleware/aisdk/ThinkingTimeMiddleware.ts @@ -1,5 +1,5 @@ import { LanguageModelV1Middleware, LanguageModelV1StreamPart } from '@cherrystudio/ai-core' -import { Chunk, ChunkType, ThinkingCompleteChunk } from '@renderer/types/chunk' +import { ChunkType, ThinkingCompleteChunk } from '@renderer/types/chunk' /** * 一个用于统计 LLM "思考时间"(Time to First Token)的 AI SDK 中间件。 @@ -9,15 +9,16 @@ import { Chunk, ChunkType, ThinkingCompleteChunk } from '@renderer/types/chunk' * 2. 它会创建一个新的 `TransformStream` 来代理原始的流。 * 3. 当第一个数据块 (chunk) 从原始流中到达时,记录结束时间。 * 4. 计算两者之差,即为 "思考时间" + * 这里只处理了thinking_complete */ -export default function thinkingTimeMiddleware(onChunkReceived: (chunk: Chunk) => void): LanguageModelV1Middleware { +export default function thinkingTimeMiddleware(): LanguageModelV1Middleware { return { wrapStream: async ({ doStream }) => { let hasThinkingContent = false let thinkingStartTime = 0 let accumulatedThinkingContent = '' const { stream, ...reset } = await doStream() - const transformStream = new TransformStream({ + const transformStream = new TransformStream({ transform(chunk, controller) { if (chunk.type === 'reasoning' || chunk.type === 'redacted-reasoning') { if (!hasThinkingContent) { @@ -25,19 +26,15 @@ export default function thinkingTimeMiddleware(onChunkReceived: (chunk: Chunk) = thinkingStartTime = Date.now() } accumulatedThinkingContent += chunk.textDelta || '' - onChunkReceived({ - type: ChunkType.THINKING_DELTA, - text: chunk.textDelta || '' - }) } else { if (hasThinkingContent && thinkingStartTime > 0) { const thinkingTime = Date.now() - thinkingStartTime - const thinkingCompleteChunk: ThinkingCompleteChunk = { - type: ChunkType.THINKING_COMPLETE, + const thinkingCompleteChunk = { + type: 'reasoning-signature', text: accumulatedThinkingContent, thinking_millsec: thinkingTime } - onChunkReceived(thinkingCompleteChunk) + controller.enqueue(thinkingCompleteChunk) hasThinkingContent = false thinkingStartTime = 0 accumulatedThinkingContent = '' @@ -55,7 +52,7 @@ export default function thinkingTimeMiddleware(onChunkReceived: (chunk: Chunk) = text: accumulatedThinkingContent, thinking_millsec: thinkingTime } - onChunkReceived(thinkingCompleteChunk) + controller.enqueue(thinkingCompleteChunk) } controller.terminate() }