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.
This commit is contained in:
suyao 2025-06-20 16:31:16 +08:00 committed by MyPrototypeWhat
parent c5cb443de0
commit e70174817e
4 changed files with 27 additions and 21 deletions

View File

@ -68,7 +68,7 @@ export class AiSdkToChunkAdapter {
* AI SDK chunk Cherry Studio chunk
* @param chunk AI SDK chunk
*/
private convertAndEmitChunk(chunk: TextStreamPart<any>, 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
// === 工具调用相关事件 ===

View File

@ -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<CompletionsResult> {
// 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)

View File

@ -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()]
})
}

View File

@ -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<LanguageModelV1StreamPart, LanguageModelV1StreamPart>({
const transformStream = new TransformStream<LanguageModelV1StreamPart, any>({
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()
}