refactor: update reasoning plugins and enhance performance

- Replaced `smoothReasoningPlugin` with `reasoningTimePlugin` to improve reasoning time tracking.
- Commented out the unused `textPlugin` in the plugin list for better clarity.
- Adjusted delay settings in both `smoothReasoningPlugin` and `textPlugin` for optimized processing.
- Enhanced logging in reasoning plugins for better debugging and performance insights.
This commit is contained in:
MyPrototypeWhat 2025-07-01 15:28:06 +08:00
parent cf5ed8e858
commit 182ab6092c
5 changed files with 146 additions and 22 deletions

View File

@ -28,8 +28,7 @@ import AiSdkToChunkAdapter from './AiSdkToChunkAdapter'
import LegacyAiProvider from './index'
import { AiSdkMiddlewareConfig, buildAiSdkMiddlewares } from './middleware/aisdk/AiSdkMiddlewareBuilder'
import { CompletionsResult } from './middleware/schemas'
import smoothReasoningPlugin from './plugins/smoothReasoningPlugin'
import textPlugin from './plugins/textPlugin'
import reasoningTimePlugin from './plugins/reasoningTimePlugin'
import { getAiSdkProviderId } from './provider/factory'
/**
@ -119,11 +118,11 @@ export default class ModernAiProvider {
private buildPlugins(middlewareConfig: AiSdkMiddlewareConfig) {
const plugins: AiPlugin[] = []
// 1. 总是添加通用插件
plugins.push(textPlugin)
// plugins.push(textPlugin)
// 2. 推理模型时添加推理插件
if (middlewareConfig.enableReasoning) {
plugins.push(smoothReasoningPlugin)
plugins.push(reasoningTimePlugin)
}
// 3. 启用Prompt工具调用时添加工具插件
@ -151,7 +150,6 @@ export default class ModernAiProvider {
})
)
}
console.log(
'最终插件列表:',
plugins.map((p) => p.name)

View File

@ -0,0 +1,67 @@
import { definePlugin } from '@cherrystudio/ai-core'
export default definePlugin({
name: 'reasoningTimePlugin',
transformStream: () => () => {
// === 时间跟踪状态 ===
let thinkingStartTime = 0
let hasStartedThinking = false
let accumulatedThinkingContent = ''
return new TransformStream({
transform(chunk, controller) {
if (chunk.type !== 'reasoning') {
// === 处理 reasoning 结束 ===
if (hasStartedThinking) {
console.log(`[ReasoningPlugin] Ending reasoning.`)
// 生成 reasoning-signature
controller.enqueue({
type: 'reasoning-signature',
text: accumulatedThinkingContent,
thinking_millsec: performance.now() - thinkingStartTime
})
// 重置状态
accumulatedThinkingContent = ''
hasStartedThinking = false
thinkingStartTime = 0
}
controller.enqueue(chunk)
return
}
// === 处理 reasoning 类型 ===
// 1. 时间跟踪逻辑
if (!hasStartedThinking) {
hasStartedThinking = true
thinkingStartTime = performance.now()
console.log(`[ReasoningPlugin] Starting reasoning session`)
}
accumulatedThinkingContent += chunk.textDelta
// 2. 直接透传 chunk并附加上时间
console.log(`[ReasoningPlugin] Forwarding reasoning chunk: "${chunk.textDelta}"`)
controller.enqueue({
...chunk,
thinking_millsec: performance.now() - thinkingStartTime
})
},
// === flush 处理流结束时仍在reasoning状态的场景 ===
flush(controller) {
if (hasStartedThinking) {
console.log(`[ReasoningPlugin] Final flush for reasoning-signature.`)
controller.enqueue({
type: 'reasoning-signature',
text: accumulatedThinkingContent,
thinking_millsec: performance.now() - thinkingStartTime
})
}
}
})
}
})

View File

@ -1,7 +1,9 @@
// 可能会废弃在流上做delay还是有问题
import { definePlugin } from '@cherrystudio/ai-core'
const chunkingRegex = /([\u4E00-\u9FFF])|\S+\s+/
const delayInMs = 20
const delayInMs = 50
export default definePlugin({
name: 'reasoningPlugin',
@ -15,12 +17,32 @@ export default definePlugin({
let hasStartedThinking = false
let accumulatedThinkingContent = ''
// === 日志计数器 ===
let chunkCount = 0
let delayCount = 0
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const detectChunk = (buffer: string) => {
const match = chunkingRegex.exec(buffer)
if (!match) return null
return buffer.slice(0, match.index) + match?.[0]
// 收集所有当前可匹配的chunks
const collectMatches = (inputBuffer: string) => {
const matches: string[] = []
let tempBuffer = inputBuffer
let match
// 重置regex状态
chunkingRegex.lastIndex = 0
while ((match = chunkingRegex.exec(tempBuffer)) !== null) {
matches.push(match[0])
tempBuffer = tempBuffer.slice(match.index + match[0].length)
// 重置regex以从头开始匹配剩余内容
chunkingRegex.lastIndex = 0
}
return {
matches,
remaining: tempBuffer
}
}
return new TransformStream({
@ -28,8 +50,13 @@ export default definePlugin({
if (chunk.type !== 'reasoning') {
// === 处理 reasoning 结束 ===
if (hasStartedThinking && accumulatedThinkingContent) {
console.log(
`[ReasoningPlugin] Ending reasoning. Final stats: chunks=${chunkCount}, delays=${delayCount}, efficiency=${(chunkCount / Math.max(delayCount, 1)).toFixed(2)}x`
)
// 先输出剩余的 buffer
if (buffer.length > 0) {
console.log(`[ReasoningPlugin] Flushing remaining buffer: "${buffer}"`)
controller.enqueue({
type: 'reasoning',
textDelta: buffer,
@ -49,6 +76,8 @@ export default definePlugin({
accumulatedThinkingContent = ''
hasStartedThinking = false
thinkingStartTime = 0
chunkCount = 0
delayCount = 0
}
controller.enqueue(chunk)
@ -61,28 +90,56 @@ export default definePlugin({
if (!hasStartedThinking) {
hasStartedThinking = true
thinkingStartTime = performance.now()
console.log(`[ReasoningPlugin] Starting reasoning session`)
}
accumulatedThinkingContent += chunk.textDelta
// 2. Smooth 处理逻辑
// 2. 动态Smooth处理逻辑
const beforeBuffer = buffer
buffer += chunk.textDelta
let match
while ((match = detectChunk(buffer)) != null) {
controller.enqueue({
type: 'reasoning',
textDelta: match,
thinking_millsec: performance.now() - thinkingStartTime
})
buffer = buffer.slice(match.length)
console.log(`[ReasoningPlugin] Received chunk: "${chunk.textDelta}", buffer: "${beforeBuffer}" → "${buffer}"`)
// 收集所有当前可以匹配的chunks
const { matches, remaining } = collectMatches(buffer)
if (matches.length > 0) {
console.log(
`[ReasoningPlugin] Collected ${matches.length} matches: [${matches.map((m) => `"${m}"`).join(', ')}], remaining: "${remaining}"`
)
// 批量输出所有匹配的chunks
for (const matchText of matches) {
controller.enqueue({
type: 'reasoning',
textDelta: matchText,
thinking_millsec: performance.now() - thinkingStartTime
})
chunkCount++
}
// 更新buffer为剩余内容
buffer = remaining
// 只等待一次而不是每个chunk都等待
delayCount++
console.log(
`[ReasoningPlugin] Delaying ${delayInMs}ms (delay #${delayCount}, efficiency: ${(chunkCount / delayCount).toFixed(2)} chunks/delay)`
)
const delayStart = performance.now()
await delay(delayInMs)
const actualDelay = performance.now() - delayStart
console.log(`[ReasoningPlugin] Delay completed: expected=${delayInMs}ms, actual=${actualDelay.toFixed(1)}ms`)
} else {
console.log(`[ReasoningPlugin] No matches found, keeping in buffer: "${buffer}"`)
}
// 如果没有匹配保留在buffer中等待下次数据
},
// === flush 处理剩余 buffer ===
flush(controller) {
if (buffer.length > 0) {
console.log(`[ReasoningPlugin] Final flush: "${buffer}"`)
controller.enqueue({
type: 'reasoning',
textDelta: buffer,

View File

@ -1,10 +1,12 @@
// 可能会废弃在流上做delay还是有问题
import { definePlugin, smoothStream } from '@cherrystudio/ai-core'
export default definePlugin({
name: 'textPlugin',
transformStream: () =>
smoothStream({
delayInMs: 20,
delayInMs: 50,
// 中文3个字符一个chunk,英文一个单词一个chunk
chunking: /([\u4E00-\u9FFF]{3})|\S+\s+/
})

View File

@ -148,7 +148,7 @@ const getBlockThrottler = (id: string) => {
}
const rafId = requestAnimationFrame(() => {
// store.dispatch(updateOneBlock({ id, changes: blockUpdate }))
store.dispatch(updateOneBlock({ id, changes: blockUpdate }))
blockUpdateRafs.delete(id)
})
@ -167,7 +167,7 @@ const getBlockThrottler = (id: string) => {
*/
const throttledBlockUpdate = (id: string, blockUpdate: any) => {
const throttler = getBlockThrottler(id)
store.dispatch(updateOneBlock({ id, changes: blockUpdate }))
// store.dispatch(updateOneBlock({ id, changes: blockUpdate }))
throttler(blockUpdate)
}