mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-10 15:49:29 +08:00
Refactor/reasoning time (#10393)
This commit is contained in:
parent
bb0ec0a3ec
commit
06ab2822be
@ -163,14 +163,13 @@ export class AiSdkToChunkAdapter {
|
|||||||
final.reasoningContent += chunk.text || ''
|
final.reasoningContent += chunk.text || ''
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.THINKING_DELTA,
|
type: ChunkType.THINKING_DELTA,
|
||||||
text: final.reasoningContent || '',
|
text: final.reasoningContent || ''
|
||||||
thinking_millsec: (chunk.providerMetadata?.metadata?.thinking_millsec as number) || 0
|
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'reasoning-end':
|
case 'reasoning-end':
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.THINKING_COMPLETE,
|
type: ChunkType.THINKING_COMPLETE,
|
||||||
text: (chunk.providerMetadata?.metadata?.thinking_content as string) || final.reasoningContent
|
text: final.reasoningContent || ''
|
||||||
})
|
})
|
||||||
final.reasoningContent = ''
|
final.reasoningContent = ''
|
||||||
break
|
break
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { getEnableDeveloperMode } from '@renderer/hooks/useSettings'
|
|||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
|
|
||||||
import { AiSdkMiddlewareConfig } from '../middleware/AiSdkMiddlewareBuilder'
|
import { AiSdkMiddlewareConfig } from '../middleware/AiSdkMiddlewareBuilder'
|
||||||
import reasoningTimePlugin from './reasoningTimePlugin'
|
|
||||||
import { searchOrchestrationPlugin } from './searchOrchestrationPlugin'
|
import { searchOrchestrationPlugin } from './searchOrchestrationPlugin'
|
||||||
import { createTelemetryPlugin } from './telemetryPlugin'
|
import { createTelemetryPlugin } from './telemetryPlugin'
|
||||||
|
|
||||||
@ -39,9 +38,9 @@ export function buildPlugins(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 推理模型时添加推理插件
|
// 3. 推理模型时添加推理插件
|
||||||
if (middlewareConfig.enableReasoning) {
|
// if (middlewareConfig.enableReasoning) {
|
||||||
plugins.push(reasoningTimePlugin)
|
// plugins.push(reasoningTimePlugin)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 4. 启用Prompt工具调用时添加工具插件
|
// 4. 启用Prompt工具调用时添加工具插件
|
||||||
if (middlewareConfig.isPromptToolUse) {
|
if (middlewareConfig.isPromptToolUse) {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useSettings } from '@renderer/hooks/useSettings'
|
|||||||
import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue'
|
import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue'
|
||||||
import { MessageBlockStatus, type ThinkingMessageBlock } from '@renderer/types/newMessage'
|
import { MessageBlockStatus, type ThinkingMessageBlock } from '@renderer/types/newMessage'
|
||||||
import { Collapse, message as antdMessage, Tooltip } from 'antd'
|
import { Collapse, message as antdMessage, Tooltip } from 'antd'
|
||||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@ -105,30 +105,37 @@ const ThinkingBlock: React.FC<Props> = ({ block }) => {
|
|||||||
const ThinkingTimeSeconds = memo(
|
const ThinkingTimeSeconds = memo(
|
||||||
({ blockThinkingTime, isThinking }: { blockThinkingTime: number; isThinking: boolean }) => {
|
({ blockThinkingTime, isThinking }: { blockThinkingTime: number; isThinking: boolean }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
// const [thinkingTime, setThinkingTime] = useState(blockThinkingTime || 0)
|
const [displayTime, setDisplayTime] = useState(blockThinkingTime)
|
||||||
|
|
||||||
// FIXME: 这里统计的和请求处统计的有一定误差
|
const timer = useRef<NodeJS.Timeout | null>(null)
|
||||||
// useEffect(() => {
|
|
||||||
// let timer: NodeJS.Timeout | null = null
|
|
||||||
// if (isThinking) {
|
|
||||||
// timer = setInterval(() => {
|
|
||||||
// setThinkingTime((prev) => prev + 100)
|
|
||||||
// }, 100)
|
|
||||||
// } else if (timer) {
|
|
||||||
// // 立即清除计时器
|
|
||||||
// clearInterval(timer)
|
|
||||||
// timer = null
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return () => {
|
useEffect(() => {
|
||||||
// if (timer) {
|
if (isThinking) {
|
||||||
// clearInterval(timer)
|
if (!timer.current) {
|
||||||
// timer = null
|
timer.current = setInterval(() => {
|
||||||
// }
|
setDisplayTime((prev) => prev + 100)
|
||||||
// }
|
}, 100)
|
||||||
// }, [isThinking])
|
}
|
||||||
|
} else {
|
||||||
|
if (timer.current) {
|
||||||
|
clearInterval(timer.current)
|
||||||
|
timer.current = null
|
||||||
|
}
|
||||||
|
setDisplayTime(blockThinkingTime)
|
||||||
|
}
|
||||||
|
|
||||||
const thinkingTimeSeconds = useMemo(() => (blockThinkingTime / 1000).toFixed(1), [blockThinkingTime])
|
return () => {
|
||||||
|
if (timer.current) {
|
||||||
|
clearInterval(timer.current)
|
||||||
|
timer.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isThinking, blockThinkingTime])
|
||||||
|
|
||||||
|
const thinkingTimeSeconds = useMemo(
|
||||||
|
() => ((displayTime < 1000 ? 100 : displayTime) / 1000).toFixed(1),
|
||||||
|
[displayTime]
|
||||||
|
)
|
||||||
|
|
||||||
return isThinking
|
return isThinking
|
||||||
? t('chat.thinking', {
|
? t('chat.thinking', {
|
||||||
|
|||||||
@ -235,13 +235,12 @@ describe('ThinkingBlock', () => {
|
|||||||
renderThinkingBlock(thinkingBlock)
|
renderThinkingBlock(thinkingBlock)
|
||||||
|
|
||||||
const activeTimeText = getThinkingTimeText()
|
const activeTimeText = getThinkingTimeText()
|
||||||
expect(activeTimeText).toHaveTextContent('1.0s')
|
|
||||||
expect(activeTimeText).toHaveTextContent('Thinking...')
|
expect(activeTimeText).toHaveTextContent('Thinking...')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle extreme thinking times correctly', () => {
|
it('should handle extreme thinking times correctly', () => {
|
||||||
const testCases = [
|
const testCases = [
|
||||||
{ thinking_millsec: 0, expectedTime: '0.0s' },
|
{ thinking_millsec: 0, expectedTime: '0.1s' }, // New logic: values < 1000ms display as 0.1s
|
||||||
{ thinking_millsec: 86400000, expectedTime: '86400.0s' }, // 1 day
|
{ thinking_millsec: 86400000, expectedTime: '86400.0s' }, // 1 day
|
||||||
{ thinking_millsec: 259200000, expectedTime: '259200.0s' } // 3 days
|
{ thinking_millsec: 259200000, expectedTime: '259200.0s' } // 3 days
|
||||||
]
|
]
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export const createThinkingCallbacks = (deps: ThinkingCallbacksDependencies) =>
|
|||||||
|
|
||||||
// 内部维护的状态
|
// 内部维护的状态
|
||||||
let thinkingBlockId: string | null = null
|
let thinkingBlockId: string | null = null
|
||||||
let _thinking_millsec = 0
|
let thinking_millsec_now: number = 0
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onThinkingStart: async () => {
|
onThinkingStart: async () => {
|
||||||
@ -24,27 +24,27 @@ export const createThinkingCallbacks = (deps: ThinkingCallbacksDependencies) =>
|
|||||||
type: MessageBlockType.THINKING,
|
type: MessageBlockType.THINKING,
|
||||||
content: '',
|
content: '',
|
||||||
status: MessageBlockStatus.STREAMING,
|
status: MessageBlockStatus.STREAMING,
|
||||||
thinking_millsec: _thinking_millsec
|
thinking_millsec: 0
|
||||||
}
|
}
|
||||||
thinkingBlockId = blockManager.initialPlaceholderBlockId!
|
thinkingBlockId = blockManager.initialPlaceholderBlockId!
|
||||||
blockManager.smartBlockUpdate(thinkingBlockId, changes, MessageBlockType.THINKING, true)
|
blockManager.smartBlockUpdate(thinkingBlockId, changes, MessageBlockType.THINKING, true)
|
||||||
} else if (!thinkingBlockId) {
|
} else if (!thinkingBlockId) {
|
||||||
const newBlock = createThinkingBlock(assistantMsgId, '', {
|
const newBlock = createThinkingBlock(assistantMsgId, '', {
|
||||||
status: MessageBlockStatus.STREAMING,
|
status: MessageBlockStatus.STREAMING,
|
||||||
thinking_millsec: _thinking_millsec
|
thinking_millsec: 0
|
||||||
})
|
})
|
||||||
thinkingBlockId = newBlock.id
|
thinkingBlockId = newBlock.id
|
||||||
await blockManager.handleBlockTransition(newBlock, MessageBlockType.THINKING)
|
await blockManager.handleBlockTransition(newBlock, MessageBlockType.THINKING)
|
||||||
}
|
}
|
||||||
|
thinking_millsec_now = performance.now()
|
||||||
},
|
},
|
||||||
|
|
||||||
onThinkingChunk: async (text: string, thinking_millsec?: number) => {
|
onThinkingChunk: async (text: string) => {
|
||||||
_thinking_millsec = thinking_millsec || 0
|
|
||||||
if (thinkingBlockId) {
|
if (thinkingBlockId) {
|
||||||
const blockChanges: Partial<MessageBlock> = {
|
const blockChanges: Partial<MessageBlock> = {
|
||||||
content: text,
|
content: text,
|
||||||
status: MessageBlockStatus.STREAMING,
|
status: MessageBlockStatus.STREAMING
|
||||||
thinking_millsec: _thinking_millsec
|
// thinking_millsec: performance.now() - thinking_millsec_now
|
||||||
}
|
}
|
||||||
blockManager.smartBlockUpdate(thinkingBlockId, blockChanges, MessageBlockType.THINKING)
|
blockManager.smartBlockUpdate(thinkingBlockId, blockChanges, MessageBlockType.THINKING)
|
||||||
}
|
}
|
||||||
@ -52,14 +52,15 @@ export const createThinkingCallbacks = (deps: ThinkingCallbacksDependencies) =>
|
|||||||
|
|
||||||
onThinkingComplete: (finalText: string) => {
|
onThinkingComplete: (finalText: string) => {
|
||||||
if (thinkingBlockId) {
|
if (thinkingBlockId) {
|
||||||
|
const now = performance.now()
|
||||||
const changes: Partial<MessageBlock> = {
|
const changes: Partial<MessageBlock> = {
|
||||||
content: finalText,
|
content: finalText,
|
||||||
status: MessageBlockStatus.SUCCESS,
|
status: MessageBlockStatus.SUCCESS,
|
||||||
thinking_millsec: _thinking_millsec
|
thinking_millsec: now - thinking_millsec_now
|
||||||
}
|
}
|
||||||
blockManager.smartBlockUpdate(thinkingBlockId, changes, MessageBlockType.THINKING, true)
|
blockManager.smartBlockUpdate(thinkingBlockId, changes, MessageBlockType.THINKING, true)
|
||||||
thinkingBlockId = null
|
thinkingBlockId = null
|
||||||
_thinking_millsec = 0
|
thinking_millsec_now = 0
|
||||||
} else {
|
} else {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`[onThinkingComplete] Received thinking.complete but last block was not THINKING (was ${blockManager.lastBlockType}) or lastBlockId is null.`
|
`[onThinkingComplete] Received thinking.complete but last block was not THINKING (was ${blockManager.lastBlockType}) or lastBlockId is null.`
|
||||||
|
|||||||
@ -425,7 +425,10 @@ describe('streamCallback Integration Tests', () => {
|
|||||||
expect(thinkingBlock).toBeDefined()
|
expect(thinkingBlock).toBeDefined()
|
||||||
expect(thinkingBlock?.content).toBe('Final thoughts')
|
expect(thinkingBlock?.content).toBe('Final thoughts')
|
||||||
expect(thinkingBlock?.status).toBe(MessageBlockStatus.SUCCESS)
|
expect(thinkingBlock?.status).toBe(MessageBlockStatus.SUCCESS)
|
||||||
expect((thinkingBlock as any)?.thinking_millsec).toBe(3000)
|
// thinking_millsec 现在是本地计算的,只验证它存在且是一个合理的数字
|
||||||
|
expect((thinkingBlock as any)?.thinking_millsec).toBeDefined()
|
||||||
|
expect(typeof (thinkingBlock as any)?.thinking_millsec).toBe('number')
|
||||||
|
expect((thinkingBlock as any)?.thinking_millsec).toBeGreaterThanOrEqual(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle tool call flow', async () => {
|
it('should handle tool call flow', async () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user