mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-27 21:01:32 +08:00
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:
parent
c5cb443de0
commit
e70174817e
@ -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
|
||||
|
||||
// === 工具调用相关事件 ===
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user