-
Notifications
You must be signed in to change notification settings - Fork 625
refactor(chat): streamline message handling and error notification logic #974
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -592,34 +592,32 @@ export const useChatStore = defineStore('chat', () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 处理普通内容 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (msg.content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const lastContentBlock = curMsg.content[curMsg.content.length - 1] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastContentBlock && lastContentBlock.type === 'content') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastContentBlock.content += msg.content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 打字机音效,与实际数据流同步 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| playTypewriterSound() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastContentBlock) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastContentBlock.status = 'success' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastContentBlock) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastContentBlock.status = 'success' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastContentBlock.type === 'content') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastContentBlock.content += msg.content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curMsg.content.push({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'content', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: msg.content, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: 'loading', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamp: Date.now() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 如果是新块的第一个字符,也播放声音 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| playTypewriterSound() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+595
to
607
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Content chunks are dropped when the previous block isn’t As soon as a text chunk arrives while the last block is a tool call/action/etc., this branch marks that block - const lastContentBlock = curMsg.content[curMsg.content.length - 1]
- if (lastContentBlock) {
- lastContentBlock.status = 'success'
- if (lastContentBlock.type === 'content') {
- lastContentBlock.content += msg.content
- }
- } else {
+ const lastContentBlock = curMsg.content[curMsg.content.length - 1]
+ if (lastContentBlock?.type === 'content') {
+ lastContentBlock.content += msg.content
+ } else {
+ finalizeLastBlock()
curMsg.content.push({
type: 'content',
content: msg.content,
status: 'loading',
timestamp: Date.now()
})
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 打字机音效,与实际数据流同步 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| playTypewriterSound() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 处理推理内容 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (msg.reasoning_content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const lastReasoningBlock = curMsg.content[curMsg.content.length - 1] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastReasoningBlock && lastReasoningBlock.type === 'reasoning_content') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastReasoningBlock.content += msg.reasoning_content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastReasoningBlock) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastReasoningBlock.status = 'success' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastReasoningBlock) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastReasoningBlock.status = 'success' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastReasoningBlock.type === 'reasoning_content') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastReasoningBlock.content += msg.reasoning_content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curMsg.content.push({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'reasoning_content', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: msg.reasoning_content, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
612
to
623
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The refactor applies the same pattern to Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -727,63 +725,62 @@ export const useChatStore = defineStore('chat', () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 从缓存中获取消息 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cached = getGeneratingMessagesCache().get(msg.eventId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cached) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (getActiveThreadId() === getActiveThreadId()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updatedMessage = await threadP.getMessage(msg.eventId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const enrichedMessage = await enrichMessageWithExtra(updatedMessage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (enrichedMessage.is_variant && enrichedMessage.parentId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 处理变体消息的错误状态 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parentMsgIndex = getMessages().findIndex((m) => m.id === enrichedMessage.parentId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (parentMsgIndex !== -1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parentMsg = getMessages()[parentMsgIndex] as AssistantMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!parentMsg.variants) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parentMsg.variants = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const variantIndex = parentMsg.variants.findIndex((v) => v.id === enrichedMessage.id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (variantIndex !== -1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parentMsg.variants[variantIndex] = enrichedMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parentMsg.variants.push(enrichedMessage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getMessages()[parentMsgIndex] = { ...parentMsg } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updatedMessage = await threadP.getMessage(msg.eventId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const enrichedMessage = await enrichMessageWithExtra(updatedMessage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (enrichedMessage.is_variant && enrichedMessage.parentId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 处理变体消息的错误状态 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parentMsgIndex = getMessages().findIndex((m) => m.id === enrichedMessage.parentId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (parentMsgIndex !== -1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parentMsg = getMessages()[parentMsgIndex] as AssistantMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!parentMsg.variants) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parentMsg.variants = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 非变体消息的原有错误处理逻辑 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const messageIndex = getMessages().findIndex((m) => m.id === msg.eventId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (messageIndex !== -1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getMessages()[messageIndex] = enrichedMessage as AssistantMessage | UserMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const variantIndex = parentMsg.variants.findIndex((v) => v.id === enrichedMessage.id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (variantIndex !== -1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parentMsg.variants[variantIndex] = enrichedMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parentMsg.variants.push(enrichedMessage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getMessages()[parentMsgIndex] = { ...parentMsg } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const wid = window.api.getWindowId() || 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 检查窗口是否聚焦,如果未聚焦则发送错误通知 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isFocused = await windowP.isMainWindowFocused(wid) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isFocused) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 获取错误信息 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let errorMessage = t('chat.notify.generationError') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (enrichedMessage && (enrichedMessage as AssistantMessage).content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const assistantMsg = enrichedMessage as AssistantMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 查找错误信息块 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const block of assistantMsg.content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (block.status === 'error' && block.content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorMessage = block.content.substring(0, 20) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (block.content.length > 20) errorMessage += '...' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 非变体消息的原有错误处理逻辑 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const messageIndex = getMessages().findIndex((m) => m.id === msg.eventId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (messageIndex !== -1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getMessages()[messageIndex] = enrichedMessage as AssistantMessage | UserMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const wid = window.api.getWindowId() || 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 检查窗口是否聚焦,如果未聚焦则发送错误通知 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isFocused = await windowP.isMainWindowFocused(wid) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isFocused) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 获取错误信息 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let errorMessage = t('chat.notify.generationError') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (enrichedMessage && (enrichedMessage as AssistantMessage).content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const assistantMsg = enrichedMessage as AssistantMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 查找错误信息块 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const block of assistantMsg.content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (block.status === 'error' && block.content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorMessage = block.content.substring(0, 20) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (block.content.length > 20) errorMessage += '...' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 发送错误通知 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await notificationP.showNotification({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: `error-${msg.eventId}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: t('chat.notify.generationError'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: errorMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Failed to load error message:', error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 发送错误通知 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await notificationP.showNotification({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: `error-${msg.eventId}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: t('chat.notify.generationError'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: errorMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Failed to load error message:', error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getGeneratingMessagesCache().delete(msg.eventId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| generatingThreadIds.value.delete(cached.threadId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 设置会话的workingStatus为error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -921,7 +918,7 @@ export const useChatStore = defineStore('chat', () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cache = getGeneratingMessagesCache() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const generatingMessage = Array.from(cache.entries()).find( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ([, cached]) => cached.threadId === threadId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) as string[] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (generatingMessage) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [messageId] = generatingMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await threadP.stopMessageGeneration(messageId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the streaming handler, when
msg.contentarrives and the last block exists but is not of typecontent, the new code marks that block assuccessand returns without pushing a new text block. Previously theelsebranch covered both "no block" and "non‑content block" so the plain response text was appended. With the current logic, any stream that first emits a tool/action/reasoning block and then regular content will drop the textual response entirely while still playing the typing sound. Users will observe empty assistant replies whenever content follows a different block type.Useful? React with 👍 / 👎.