已开启
feat: deepsearch前端对话流程优化 #66
chenjingheng创建于 1月28日
feat: deepsearch前端对话流程优化 #66
已开启
共 2 个文件变更+253-151
| @@ -50,11 +50,13 @@ export class DeepsearchSSEHandler { | |||
| 50 | private store: StoreDependencies; | 50 | private store: StoreDependencies; |
| 51 | private streamCache: StreamCache; | 51 | private streamCache: StreamCache; |
| 52 | private conversationId: string; | 52 | private conversationId: string; |
| 53 | + private messageFindCache: Map<string, Message | null>; // 新增:消息查找缓存 | ||
| 53 | 54 | ||
| 54 | constructor(store: StoreDependencies, streamCache: StreamCache, conversationId: string) { | 55 | constructor(store: StoreDependencies, streamCache: StreamCache, conversationId: string) { |
| 55 | this.store = store; | 56 | this.store = store; |
| 56 | this.streamCache = streamCache; | 57 | this.streamCache = streamCache; |
| 57 | this.conversationId = conversationId; | 58 | this.conversationId = conversationId; |
| 59 | + this.messageFindCache = new Map(); // 初始化缓存 | ||
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | /** | 62 | /** |
| @@ -143,8 +145,10 @@ export class DeepsearchSSEHandler { | |||
| 143 | const lastMessageItems = this.store.getCurrentMessageItems(); | 145 | const lastMessageItems = this.store.getCurrentMessageItems(); |
| 144 | if (lastMessageItems && sectionIdx !== undefined && planIdx !== undefined) { | 146 | if (lastMessageItems && sectionIdx !== undefined && planIdx !== undefined) { |
| 145 | 147 | ||
| 146 | - const sectionTask = this.findTaskInMessages(lastMessageItems.messagesIds, msg => | 148 | + const sectionTask = this.findTaskInMessages( |
| 147 | - msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx | 149 | + lastMessageItems.messagesIds, |
| 150 | + msg => msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx, | ||
| 151 | + `section_${sectionIdx}` // 添加缓存key | ||
| 148 | ); | 152 | ); |
| 149 | 153 | ||
| 150 | // 【步骤1】更新上一个 planTask (task_1_x_(n-1)) | 154 | // 【步骤1】更新上一个 planTask (task_1_x_(n-1)) |
| @@ -204,8 +208,10 @@ export class DeepsearchSSEHandler { | |||
| 204 | const lastMessageItems = this.store.getCurrentMessageItems(); | 208 | const lastMessageItems = this.store.getCurrentMessageItems(); |
| 205 | if (!lastMessageItems) return; | 209 | if (!lastMessageItems) return; |
| 206 | 210 | ||
| 207 | - const sectionTask = this.findTaskInMessages(lastMessageItems.messagesIds, msg => | 211 | + const sectionTask = this.findTaskInMessages( |
| 208 | - msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx | 212 | + lastMessageItems.messagesIds, |
| 213 | + msg => msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx, | ||
| 214 | + `section_${sectionIdx}` // 添加缓存key | ||
| 209 | ); | 215 | ); |
| 210 | 216 | ||
| 211 | if (sectionTask) { | 217 | if (sectionTask) { |
| @@ -303,8 +309,10 @@ export class DeepsearchSSEHandler { | |||
| 303 | const lastMessageItems = this.store.getCurrentMessageItems(); | 309 | const lastMessageItems = this.store.getCurrentMessageItems(); |
| 304 | if (!lastMessageItems) return; | 310 | if (!lastMessageItems) return; |
| 305 | 311 | ||
| 306 | - const sectionTask = this.findTaskInMessages(lastMessageItems.messagesIds, msg => | 312 | + const sectionTask = this.findTaskInMessages( |
| 307 | - msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx | 313 | + lastMessageItems.messagesIds, |
| 314 | + msg => msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx, | ||
| 315 | + `section_${sectionIdx}` // 添加缓存key | ||
| 308 | ); | 316 | ); |
| 309 | 317 | ||
| 310 | if (sectionTask) { | 318 | if (sectionTask) { |
| @@ -339,7 +347,11 @@ export class DeepsearchSSEHandler { | |||
| 339 | const streamKey = this.generateStreamKey(sseData.agent, sectionIdx, planIdx, stepIdx); | 347 | const streamKey = this.generateStreamKey(sseData.agent, sectionIdx, planIdx, stepIdx); |
| 340 | const lastMessageItems = this.store.getCurrentMessageItems(); | 348 | const lastMessageItems = this.store.getCurrentMessageItems(); |
| 341 | 349 | ||
| 342 | - if (!lastMessageItems || getMessageItemsIsUser(lastMessageItems)) return; | 350 | + if (!lastMessageItems || getMessageItemsIsUser(lastMessageItems)) { |
| 351 | + // 清除缓存 | ||
| 352 | + this.messageFindCache.clear(); | ||
| 353 | + return; | ||
| 354 | + } | ||
| 343 | 355 | ||
| 344 | // outline 完成 | 356 | // outline 完成 |
| 345 | if (sseData.agent === 'outline') { | 357 | if (sseData.agent === 'outline') { |
| @@ -532,8 +544,10 @@ export class DeepsearchSSEHandler { | |||
| 532 | // sub_reporter 完成 | 544 | // sub_reporter 完成 |
| 533 | if (sseData.agent === 'sub_reporter' && sectionIdx !== undefined && sectionIdx > 0) { | 545 | if (sseData.agent === 'sub_reporter' && sectionIdx !== undefined && sectionIdx > 0) { |
| 534 | const cachedContent = this.getCacheContent(streamKey); | 546 | const cachedContent = this.getCacheContent(streamKey); |
| 535 | - const sectionTask = this.findTaskInMessages(lastMessageItems.messagesIds, msg => | 547 | + const sectionTask = this.findTaskInMessages( |
| 536 | - msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx | 548 | + lastMessageItems.messagesIds, |
| 549 | + msg => msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx, | ||
| 550 | + `section_${sectionIdx}` // 添加缓存key | ||
| 537 | ); | 551 | ); |
| 538 | 552 | ||
| 539 | if (sectionTask) { | 553 | if (sectionTask) { |
| @@ -599,8 +613,10 @@ export class DeepsearchSSEHandler { | |||
| 599 | 613 | ||
| 600 | // 检查是否包含最终结果(不是简单的 SECTION END 或 ALL END) | 614 | // 检查是否包含最终结果(不是简单的 SECTION END 或 ALL END) |
| 601 | if (content && typeof content === 'object' && (content.response_content || content.exception_info)) { | 615 | if (content && typeof content === 'object' && (content.response_content || content.exception_info)) { |
| 602 | - const outlineTask = this.findTaskInMessages(lastMessageItems.messagesIds, msg => | 616 | + const outlineTask = this.findTaskInMessages( |
| 603 | - msg.type === MessageType.TASK && msg.sectionIdx === 0 | 617 | + lastMessageItems.messagesIds, |
| 618 | + msg => msg.type === MessageType.TASK && msg.sectionIdx === 0, | ||
| 619 | + 'outline_root' // 添加缓存key | ||
| 604 | ); | 620 | ); |
| 605 | 621 | ||
| 606 | if (outlineTask) { | 622 | if (outlineTask) { |
| @@ -634,6 +650,9 @@ export class DeepsearchSSEHandler { | |||
| 634 | isStreaming: false, | 650 | isStreaming: false, |
| 635 | }); | 651 | }); |
| 636 | } | 652 | } |
| 653 | + | ||
| 654 | + // 处理完成后清除缓存 | ||
| 655 | + this.messageFindCache.clear(); | ||
| 637 | } | 656 | } |
| 638 | 657 | ||
| 639 | /** | 658 | /** |
| @@ -648,8 +667,10 @@ export class DeepsearchSSEHandler { | |||
| 648 | if (['collector_info_retrieval', 'collector_summary'].includes(sseData.agent) && | 667 | if (['collector_info_retrieval', 'collector_summary'].includes(sseData.agent) && |
| 649 | sectionIdx !== undefined && planIdx !== undefined && stepIdx !== undefined) { | 668 | sectionIdx !== undefined && planIdx !== undefined && stepIdx !== undefined) { |
| 650 | 669 | ||
| 651 | - const sectionTask = this.findTaskInMessages(lastMessageItems.messagesIds, msg => | 670 | + const sectionTask = this.findTaskInMessages( |
| 652 | - msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx | 671 | + lastMessageItems.messagesIds, |
| 672 | + msg => msg.type === MessageType.TASK && msg.sectionIdx === sectionIdx, | ||
| 673 | + `section_${sectionIdx}` // 添加缓存key | ||
| 653 | ); | 674 | ); |
| 654 | 675 | ||
| 655 | if (!sectionTask) { | 676 | if (!sectionTask) { |
| @@ -835,9 +856,9 @@ export class DeepsearchSSEHandler { | |||
| 835 | }); | 856 | }); |
| 836 | 857 | ||
| 837 | // 3. 更新 task_1 (outline_task): status = COMPLETED | 858 | // 3. 更新 task_1 (outline_task): status = COMPLETED |
| 838 | - updateMessage(lastMessageItems.id, outlineTask.id, { | 859 | + // updateMessage(lastMessageItems.id, outlineTask.id, { |
| 839 | - status: TaskStatus.COMPLETED, | 860 | + // status: TaskStatus.COMPLETED, |
| 840 | - }); | 861 | + // }); |
| 841 | } | 862 | } |
| 842 | } | 863 | } |
| 843 | 864 | ||
| @@ -1068,21 +1089,38 @@ export class DeepsearchSSEHandler { | |||
| 1068 | } | 1089 | } |
| 1069 | 1090 | ||
| 1070 | /** | 1091 | /** |
| 1071 | - * 在消息列表中递归查找任务 | 1092 | + * 在消息列表中递归查找任务(带缓存) |
| 1072 | */ | 1093 | */ |
| 1073 | - private findTaskInMessages(messageIds: string[], predicate: (msg: Message) => boolean): Message | null { | 1094 | + private findTaskInMessages(messageIds: string[], predicate: (msg: Message) => boolean, cacheKey?: string): Message | null { |
| 1095 | + // 如果有缓存key,先检查缓存 | ||
| 1096 | + if (cacheKey && this.messageFindCache.has(cacheKey)) { | ||
| 1097 | + return this.messageFindCache.get(cacheKey)!; | ||
| 1098 | + } | ||
| 1099 | + | ||
| 1100 | + // 递归查找 | ||
| 1074 | for (const messageId of messageIds) { | 1101 | for (const messageId of messageIds) { |
| 1075 | const msg = this.store.getMessageById(messageId); | 1102 | const msg = this.store.getMessageById(messageId); |
| 1076 | if (!msg) continue; | 1103 | if (!msg) continue; |
| 1077 | 1104 | ||
| 1078 | if (predicate(msg)) { | 1105 | if (predicate(msg)) { |
| 1106 | + // 缓存结果 | ||
| 1107 | + if (cacheKey) { | ||
| 1108 | + this.messageFindCache.set(cacheKey, msg); | ||
| 1109 | + } | ||
| 1079 | return msg; | 1110 | return msg; |
| 1080 | } | 1111 | } |
| 1081 | if (msg.childMessageIds && msg.childMessageIds.length > 0) { | 1112 | if (msg.childMessageIds && msg.childMessageIds.length > 0) { |
| 1082 | const found = this.findTaskInMessages(msg.childMessageIds, predicate); | 1113 | const found = this.findTaskInMessages(msg.childMessageIds, predicate); |
| 1083 | - if (found) return found; | 1114 | + if (found) { |
| 1115 | + // 缓存结果 | ||
| 1116 | + if (cacheKey) { | ||
| 1117 | + this.messageFindCache.set(cacheKey, found); | ||
| 1118 | + } | ||
| 1119 | + return found; | ||
| 1120 | + } | ||
| 1084 | } | 1121 | } |
| 1085 | } | 1122 | } |
| 1123 | + | ||
| 1086 | return null; | 1124 | return null; |
| 1087 | } | 1125 | } |
| 1088 | 1126 | ||
| @@ -1130,9 +1168,9 @@ export class DeepsearchSSEHandler { | |||
| 1130 | let count = 0; | 1168 | let count = 0; |
| 1131 | 1169 | ||
| 1132 | const markRecursively = (message: Message) => { | 1170 | const markRecursively = (message: Message) => { |
| 1133 | - // 只标记非 COMPLETED/CANCELLED(用户手动停止)/UNKNOWN 的消息 | 1171 | + // 只标记非 COMPLETED/CANCELLED(用户手动停止)/UNKNOWN / FAILED 的消息 |
| 1134 | - if (message.status !== TaskStatus.COMPLETED && message.status !== TaskStatus.CANCELLED && | 1172 | + if (message.status !== TaskStatus.COMPLETED && message.status !== TaskStatus.CANCELLED && |
| 1135 | - (/*markAsCompleted ||*/ message.status !== TaskStatus.UNKNOWN)) { | 1173 | + message.status !== TaskStatus.FAILED && (/*markAsCompleted ||*/ message.status !== TaskStatus.UNKNOWN)) { |
| 1136 | updateMessage(messageItems.id, message.id, { | 1174 | updateMessage(messageItems.id, message.id, { |
| 1137 | status: targetStatus, | 1175 | status: targetStatus, |
| 1138 | }); | 1176 | }); |
| @@ -828,12 +828,15 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 828 | 828 | ||
| 829 | if (!hasChanges) return state; | 829 | if (!hasChanges) return state; |
| 830 | 830 | ||
| 831 | - const newMessageItemsMap = new Map(state.messageItemsMap); | 831 | + // 创建新的MessageItems对象 |
| 832 | - newMessageItemsMap.set(messageItemsId, { | 832 | + const updatedMessageItems: MessageItems = { |
| 833 | ...messageItems, | 833 | ...messageItems, |
| 834 | ...updates, | 834 | ...updates, |
| 835 | updatedAt: Date.now(), | 835 | updatedAt: Date.now(), |
| 836 | - }); | 836 | + }; |
| 837 | + | ||
| 838 | + const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 839 | + newMessageItemsMap.set(messageItemsId, updatedMessageItems); | ||
| 837 | 840 | ||
| 838 | return { | 841 | return { |
| 839 | messageItemsMap: newMessageItemsMap, | 842 | messageItemsMap: newMessageItemsMap, |
| @@ -1050,34 +1053,41 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1050 | 1053 | ||
| 1051 | updateMessage: (messageItemsId: string, messageId: string, updates: Partial<Message>) => { | 1054 | updateMessage: (messageItemsId: string, messageId: string, updates: Partial<Message>) => { |
| 1052 | set((state) => { | 1055 | set((state) => { |
| 1053 | - // 更新 messagesMap | ||
| 1054 | const existingMessage = state.messagesMap.get(messageId); | 1056 | const existingMessage = state.messagesMap.get(messageId); |
| 1055 | if (!existingMessage) { | 1057 | if (!existingMessage) { |
| 1056 | console.warn('[updateMessage] Message not found:', messageId); | 1058 | console.warn('[updateMessage] Message not found:', messageId); |
| 1057 | return state; | 1059 | return state; |
| 1058 | } | 1060 | } |
| 1059 | 1061 | ||
| 1060 | - const newMessagesMap = new Map(state.messagesMap); | 1062 | + // 创建新的Message对象 |
| 1061 | const updatedMessage: Message = { | 1063 | const updatedMessage: Message = { |
| 1062 | ...existingMessage, | 1064 | ...existingMessage, |
| 1063 | ...updates, | 1065 | ...updates, |
| 1064 | updatedAt: Date.now(), | 1066 | updatedAt: Date.now(), |
| 1065 | }; | 1067 | }; |
| 1068 | + | ||
| 1069 | + const newMessagesMap = new Map(state.messagesMap); | ||
| 1066 | newMessagesMap.set(messageId, updatedMessage); | 1070 | newMessagesMap.set(messageId, updatedMessage); |
| 1067 | 1071 | ||
| 1068 | - // 更新 messageItemsMap 的时间戳 | 1072 | + // 更新 messageItems 时间戳 |
| 1069 | - const newMessageItemsMap = new Map(state.messageItemsMap); | 1073 | + const messageItems = state.messageItemsMap.get(messageItemsId); |
| 1070 | - const messageItems = newMessageItemsMap.get(messageItemsId); | ||
| 1071 | if (messageItems) { | 1074 | if (messageItems) { |
| 1072 | - newMessageItemsMap.set(messageItemsId, { | 1075 | + const updatedMessageItems: MessageItems = { |
| 1073 | ...messageItems, | 1076 | ...messageItems, |
| 1074 | updatedAt: Date.now(), | 1077 | updatedAt: Date.now(), |
| 1075 | - }); | 1078 | + }; |
| 1079 | + | ||
| 1080 | + const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1081 | + newMessageItemsMap.set(messageItemsId, updatedMessageItems); | ||
| 1082 | + | ||
| 1083 | + return { | ||
| 1084 | + messagesMap: newMessagesMap, | ||
| 1085 | + messageItemsMap: newMessageItemsMap, | ||
| 1086 | + }; | ||
| 1076 | } | 1087 | } |
| 1077 | 1088 | ||
| 1078 | return { | 1089 | return { |
| 1079 | messagesMap: newMessagesMap, | 1090 | messagesMap: newMessagesMap, |
| 1080 | - messageItemsMap: newMessageItemsMap, | ||
| 1081 | }; | 1091 | }; |
| 1082 | }); | 1092 | }); |
| 1083 | }, | 1093 | }, |
| @@ -1088,29 +1098,31 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1088 | if (!messageItems) return state; | 1098 | if (!messageItems) return state; |
| 1089 | 1099 | ||
| 1090 | const newMessagesMap = new Map(state.messagesMap); | 1100 | const newMessagesMap = new Map(state.messagesMap); |
| 1091 | - const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1092 | 1101 | ||
| 1093 | // 递归删除子消息 | 1102 | // 递归删除子消息 |
| 1094 | deleteMessageRecursively(messageId, newMessagesMap, state.messagesMap); | 1103 | deleteMessageRecursively(messageId, newMessagesMap, state.messagesMap); |
| 1095 | 1104 | ||
| 1096 | - // 从 messageItems 的 messagesIds 中移除 | 1105 | + // 更新 messageItems |
| 1097 | - const newMessagesIds = messageItems.messagesIds.filter(id => id !== messageId); | 1106 | + const updatedMessageItems: MessageItems = { |
| 1098 | - newMessageItemsMap.set(messageItemsId, { | ||
| 1099 | ...messageItems, | 1107 | ...messageItems, |
| 1100 | - messagesIds: newMessagesIds, | 1108 | + messagesIds: messageItems.messagesIds.filter(id => id !== messageId), |
| 1101 | updatedAt: Date.now(), | 1109 | updatedAt: Date.now(), |
| 1102 | - }); | 1110 | + }; |
| 1111 | + | ||
| 1112 | + const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1113 | + newMessageItemsMap.set(messageItemsId, updatedMessageItems); | ||
| 1103 | 1114 | ||
| 1104 | // 从父消息的 childMessageIds 中移除 | 1115 | // 从父消息的 childMessageIds 中移除 |
| 1105 | const message = state.messagesMap.get(messageId); | 1116 | const message = state.messagesMap.get(messageId); |
| 1106 | if (message?.parentMessageId) { | 1117 | if (message?.parentMessageId) { |
| 1107 | const parentMessage = newMessagesMap.get(message.parentMessageId); | 1118 | const parentMessage = newMessagesMap.get(message.parentMessageId); |
| 1108 | if (parentMessage?.childMessageIds) { | 1119 | if (parentMessage?.childMessageIds) { |
| 1109 | - newMessagesMap.set(message.parentMessageId, { | 1120 | + const updatedParent: Message = { |
| 1110 | ...parentMessage, | 1121 | ...parentMessage, |
| 1111 | childMessageIds: parentMessage.childMessageIds.filter(id => id !== messageId), | 1122 | childMessageIds: parentMessage.childMessageIds.filter(id => id !== messageId), |
| 1112 | updatedAt: Date.now(), | 1123 | updatedAt: Date.now(), |
| 1113 | - }); | 1124 | + }; |
| 1125 | + newMessagesMap.set(message.parentMessageId, updatedParent); | ||
| 1114 | } | 1126 | } |
| 1115 | } | 1127 | } |
| 1116 | 1128 | ||
| @@ -1131,27 +1143,36 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1131 | 1143 | ||
| 1132 | if (!content || content.length === 0) return state; | 1144 | if (!content || content.length === 0) return state; |
| 1133 | 1145 | ||
| 1134 | - const newMessagesMap = new Map(state.messagesMap); | 1146 | + // 创建新的Message对象 |
| 1135 | const currentContent = typeof existingMessage.content === 'string' ? existingMessage.content : ''; | 1147 | const currentContent = typeof existingMessage.content === 'string' ? existingMessage.content : ''; |
| 1136 | - newMessagesMap.set(messageId, { | 1148 | + const updatedMessage: Message = { |
| 1137 | ...existingMessage, | 1149 | ...existingMessage, |
| 1138 | content: currentContent + content, | 1150 | content: currentContent + content, |
| 1139 | updatedAt: Date.now(), | 1151 | updatedAt: Date.now(), |
| 1140 | - }); | 1152 | + }; |
| 1153 | + | ||
| 1154 | + const newMessagesMap = new Map(state.messagesMap); | ||
| 1155 | + newMessagesMap.set(messageId, updatedMessage); | ||
| 1141 | 1156 | ||
| 1142 | // 更新 messageItems 时间戳 | 1157 | // 更新 messageItems 时间戳 |
| 1143 | - const newMessageItemsMap = new Map(state.messageItemsMap); | 1158 | + const messageItems = state.messageItemsMap.get(messageItemsId); |
| 1144 | - const messageItems = newMessageItemsMap.get(messageItemsId); | ||
| 1145 | if (messageItems) { | 1159 | if (messageItems) { |
| 1146 | - newMessageItemsMap.set(messageItemsId, { | 1160 | + const updatedMessageItems: MessageItems = { |
| 1147 | ...messageItems, | 1161 | ...messageItems, |
| 1148 | updatedAt: Date.now(), | 1162 | updatedAt: Date.now(), |
| 1149 | - }); | 1163 | + }; |
| 1164 | + | ||
| 1165 | + const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1166 | + newMessageItemsMap.set(messageItemsId, updatedMessageItems); | ||
| 1167 | + | ||
| 1168 | + return { | ||
| 1169 | + messagesMap: newMessagesMap, | ||
| 1170 | + messageItemsMap: newMessageItemsMap, | ||
| 1171 | + }; | ||
| 1150 | } | 1172 | } |
| 1151 | 1173 | ||
| 1152 | return { | 1174 | return { |
| 1153 | messagesMap: newMessagesMap, | 1175 | messagesMap: newMessagesMap, |
| 1154 | - messageItemsMap: newMessageItemsMap, | ||
| 1155 | }; | 1176 | }; |
| 1156 | }); | 1177 | }); |
| 1157 | }, | 1178 | }, |
| @@ -1246,75 +1267,93 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1246 | // 标记正在处理队列 | 1267 | // 标记正在处理队列 |
| 1247 | set({ sseProcessingQueue: true }); | 1268 | set({ sseProcessingQueue: true }); |
| 1248 | 1269 | ||
| 1249 | - // 获取队列中的第一个事件 | 1270 | + // 使用requestAnimationFrame批量处理 |
| 1250 | - const event = state.sseEventQueue[0]; | 1271 | + const BATCH_SIZE = 10; // 每批处理10个事件 |
| 1251 | 1272 | ||
| 1252 | - // 获取当前 MessageItems | 1273 | + const processBatch = () => { |
| 1253 | - const getCurrentMessageItems = () => { | 1274 | + const currentState = get(); |
| 1254 | - const currentList = get().getCurrentMessageItems(); | 1275 | + const eventsToProcess = currentState.sseEventQueue.slice(0, BATCH_SIZE); |
| 1255 | - return currentList[currentList.length - 1]; | ||
| 1256 | - }; | ||
| 1257 | 1276 | ||
| 1258 | - // 动态导入 deepsearch 处理器 | 1277 | + if (eventsToProcess.length === 0) { |
| 1259 | - import('./handlers/deepsearchSSEHandler').then(({ DeepsearchSSEHandler }) => { | 1278 | + set({ sseProcessingQueue: false }); |
| 1260 | - const handler = new DeepsearchSSEHandler( | 1279 | + return; |
| 1261 | - { | ||
| 1262 | - getCurrentMessageItems: getCurrentMessageItems, | ||
| 1263 | - addSystemMessage: get().addSystemMessage, | ||
| 1264 | - addMessageAsChild: get().addMessageAsChild, | ||
| 1265 | - updateMessage: get().updateMessage, | ||
| 1266 | - deleteMessage: get().deleteMessage, | ||
| 1267 | - updateMessageItems: get().updateMessageItems, | ||
| 1268 | - appendMessageContent: get().appendMessageContent, | ||
| 1269 | - getMessageById: get().getMessageById, | ||
| 1270 | - getMessageTree: get().getMessageTree, | ||
| 1271 | - getChildMessages: get().getChildMessages, | ||
| 1272 | - getMessageItemsIsUser: get().getMessageItemsIsUser, | ||
| 1273 | - }, | ||
| 1274 | - { | ||
| 1275 | - get: (key: string) => get().sseStreamCache.get(key), | ||
| 1276 | - set: (key: string, chunks: string[]) => { | ||
| 1277 | - set((state) => { | ||
| 1278 | - const newCache = new Map(state.sseStreamCache); | ||
| 1279 | - newCache.set(key, chunks); | ||
| 1280 | - return { sseStreamCache: newCache }; | ||
| 1281 | - }); | ||
| 1282 | - }, | ||
| 1283 | - delete: (key: string) => { | ||
| 1284 | - set((state) => { | ||
| 1285 | - const newCache = new Map(state.sseStreamCache); | ||
| 1286 | - newCache.delete(key); | ||
| 1287 | - return { sseStreamCache: newCache }; | ||
| 1288 | - }); | ||
| 1289 | - }, | ||
| 1290 | - }, | ||
| 1291 | - event.conversationId | ||
| 1292 | - ); | ||
| 1293 | - // 使用类型断言,因为 useConversationStore 不应该知道具体的 SSE 数据结构 | ||
| 1294 | - handler.handleSSEMessage(event.sseData as any); | ||
| 1295 | - | ||
| 1296 | - // 从队列中移除已处理的事件 | ||
| 1297 | - set((state) => ({ | ||
| 1298 | - sseEventQueue: state.sseEventQueue.slice(1), | ||
| 1299 | - })); | ||
| 1300 | - | ||
| 1301 | - // 继续处理下一个事件 | ||
| 1302 | - get().processSSEQueue(); | ||
| 1303 | - }).catch((error) => { | ||
| 1304 | - console.error('[processSSEQueue] Failed to load deepsearch handler:', error); | ||
| 1305 | - // 降级处理:创建简单的文本消息 | ||
| 1306 | - // 使用类型断言访问 SSE 数据属性 | ||
| 1307 | - const sseData = event.sseData as any; | ||
| 1308 | - if (sseData.event === 'start' || sseData.event === 'message') { | ||
| 1309 | - get().addSystemMessage(event.conversationId, MessageType.REPORT, sseData.content || ''); | ||
| 1310 | } | 1280 | } |
| 1311 | 1281 | ||
| 1312 | - // 从队列中移除失败的事件,继续处理下一个 | 1282 | + // 获取当前 MessageItems |
| 1313 | - set((state) => ({ | 1283 | + const getCurrentMessageItems = () => { |
| 1314 | - sseEventQueue: state.sseEventQueue.slice(1), | 1284 | + const currentList = get().getCurrentMessageItems(); |
| 1315 | - })); | 1285 | + return currentList[currentList.length - 1]; |
| 1316 | - get().processSSEQueue(); | 1286 | + }; |
| 1317 | - }); | 1287 | + |
| 1288 | + // 动态导入 deepsearch 处理器 | ||
| 1289 | + import('./handlers/deepsearchSSEHandler').then(({ DeepsearchSSEHandler }) => { | ||
| 1290 | + const handler = new DeepsearchSSEHandler( | ||
| 1291 | + { | ||
| 1292 | + getCurrentMessageItems: getCurrentMessageItems, | ||
| 1293 | + addSystemMessage: get().addSystemMessage, | ||
| 1294 | + addMessageAsChild: get().addMessageAsChild, | ||
| 1295 | + updateMessage: get().updateMessage, | ||
| 1296 | + deleteMessage: get().deleteMessage, | ||
| 1297 | + updateMessageItems: get().updateMessageItems, | ||
| 1298 | + appendMessageContent: get().appendMessageContent, | ||
| 1299 | + getMessageById: get().getMessageById, | ||
| 1300 | + getMessageTree: get().getMessageTree, | ||
| 1301 | + getChildMessages: get().getChildMessages, | ||
| 1302 | + getMessageItemsIsUser: get().getMessageItemsIsUser, | ||
| 1303 | + }, | ||
| 1304 | + { | ||
| 1305 | + get: (key: string) => get().sseStreamCache.get(key), | ||
| 1306 | + set: (key: string, chunks: string[]) => { | ||
| 1307 | + set((state) => { | ||
| 1308 | + const newCache = new Map(state.sseStreamCache); | ||
| 1309 | + newCache.set(key, chunks); | ||
| 1310 | + return { sseStreamCache: newCache }; | ||
| 1311 | + }); | ||
| 1312 | + }, | ||
| 1313 | + delete: (key: string) => { | ||
| 1314 | + set((state) => { | ||
| 1315 | + const newCache = new Map(state.sseStreamCache); | ||
| 1316 | + newCache.delete(key); | ||
| 1317 | + return { sseStreamCache: newCache }; | ||
| 1318 | + }); | ||
| 1319 | + }, | ||
| 1320 | + }, | ||
| 1321 | + eventsToProcess[0].conversationId | ||
| 1322 | + ); | ||
| 1323 | + | ||
| 1324 | + // 批量处理事件 | ||
| 1325 | + eventsToProcess.forEach(event => { | ||
| 1326 | + // 使用类型断言,因为 useConversationStore 不应该知道具体的 SSE 数据结构 | ||
| 1327 | + handler.handleSSEMessage(event.sseData as any); | ||
| 1328 | + }); | ||
| 1329 | + | ||
| 1330 | + // 从队列中移除已处理的事件 | ||
| 1331 | + set((state) => ({ | ||
| 1332 | + sseEventQueue: state.sseEventQueue.slice(eventsToProcess.length), | ||
| 1333 | + })); | ||
| 1334 | + | ||
| 1335 | + // 继续处理下一批 | ||
| 1336 | + requestAnimationFrame(processBatch); | ||
| 1337 | + }).catch((error) => { | ||
| 1338 | + console.error('[processSSEQueue] Failed to load deepsearch handler:', error); | ||
| 1339 | + | ||
| 1340 | + // 降级处理:批量创建简单的文本消息 | ||
| 1341 | + eventsToProcess.forEach(event => { | ||
| 1342 | + const sseData = event.sseData as any; | ||
| 1343 | + if (sseData.event === 'start' || sseData.event === 'message') { | ||
| 1344 | + get().addSystemMessage(event.conversationId, MessageType.REPORT, sseData.content || ''); | ||
| 1345 | + } | ||
| 1346 | + }); | ||
| 1347 | + | ||
| 1348 | + // 从队列中移除失败的事件,继续处理下一批 | ||
| 1349 | + set((state) => ({ | ||
| 1350 | + sseEventQueue: state.sseEventQueue.slice(eventsToProcess.length), | ||
| 1351 | + })); | ||
| 1352 | + requestAnimationFrame(processBatch); | ||
| 1353 | + }); | ||
| 1354 | + }; | ||
| 1355 | + | ||
| 1356 | + requestAnimationFrame(processBatch); | ||
| 1318 | }, | 1357 | }, |
| 1319 | 1358 | ||
| 1320 | // ========== 状态管理 ========== | 1359 | // ========== 状态管理 ========== |
| @@ -1565,21 +1604,43 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1565 | const state = get(); | 1604 | const state = get(); |
| 1566 | const WARNING_THRESHOLD = 50 * 1024 * 1024; // 50MB in bytes | 1605 | const WARNING_THRESHOLD = 50 * 1024 * 1024; // 50MB in bytes |
| 1567 | 1606 | ||
| 1568 | - // 计算 Map 结构的大小 | 1607 | + // 使用更轻量的估算方法 |
| 1569 | - const calculateMapSize = <K, V>(map: Map<K, V>): number => { | 1608 | + const estimateSize = (obj: any): number => { |
| 1570 | - let size = 0; | 1609 | + if (obj === null || obj === undefined) return 0; |
| 1571 | - map.forEach((value, key) => { | 1610 | + if (typeof obj === 'string') return obj.length * 2; // UTF-16 |
| 1572 | - // 使用 JSON.stringify 估算大小,然后用 Blob 转换为字节数 | 1611 | + if (typeof obj === 'number') return 8; |
| 1573 | - const serialized = JSON.stringify({ key, value }); | 1612 | + if (typeof obj === 'boolean') return 4; |
| 1574 | - size += new Blob([serialized]).size; | 1613 | + if (obj instanceof Date) return 24; |
| 1575 | - }); | 1614 | + |
| 1576 | - return size; | 1615 | + if (Array.isArray(obj)) { |
| 1616 | + return obj.reduce((sum, item) => sum + estimateSize(item), 0) + 16; // 数组开销 | ||
| 1617 | + } | ||
| 1618 | + | ||
| 1619 | + if (obj instanceof Map) { | ||
| 1620 | + let size = 16; // Map开销 | ||
| 1621 | + obj.forEach((value, key) => { | ||
| 1622 | + size += estimateSize(key) + estimateSize(value) + 16; // entry开销 | ||
| 1623 | + }); | ||
| 1624 | + return size; | ||
| 1625 | + } | ||
| 1626 | + | ||
| 1627 | + if (typeof obj === 'object') { | ||
| 1628 | + let size = 16; // 对象开销 | ||
| 1629 | + for (const key in obj) { | ||
| 1630 | + if (obj.hasOwnProperty(key)) { | ||
| 1631 | + size += estimateSize(key) + estimateSize(obj[key]) + 16; | ||
| 1632 | + } | ||
| 1633 | + } | ||
| 1634 | + return size; | ||
| 1635 | + } | ||
| 1636 | + | ||
| 1637 | + return 0; | ||
| 1577 | }; | 1638 | }; |
| 1578 | 1639 | ||
| 1579 | // 计算各个 Map 的大小 | 1640 | // 计算各个 Map 的大小 |
| 1580 | - const conversationsSize = calculateMapSize(state.conversationsMap); | 1641 | + const conversationsSize = estimateSize(state.conversationsMap); |
| 1581 | - const messageItemsSize = calculateMapSize(state.messageItemsMap); | 1642 | + const messageItemsSize = estimateSize(state.messageItemsMap); |
| 1582 | - const messagesSize = calculateMapSize(state.messagesMap); | 1643 | + const messagesSize = estimateSize(state.messagesMap); |
| 1583 | 1644 | ||
| 1584 | const totalSize = conversationsSize + messageItemsSize + messagesSize; | 1645 | const totalSize = conversationsSize + messageItemsSize + messagesSize; |
| 1585 | const totalSizeInMB = totalSize / (1024 * 1024); | 1646 | const totalSizeInMB = totalSize / (1024 * 1024); |
| @@ -1795,21 +1856,20 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1795 | } | 1856 | } |
| 1796 | 1857 | ||
| 1797 | set((state) => { | 1858 | set((state) => { |
| 1798 | - const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1799 | - const newMessagesMap = new Map(state.messagesMap); | ||
| 1800 | - const newConversationsMap = new Map(state.conversationsMap); | ||
| 1801 | - | ||
| 1802 | // 恢复 messageItems | 1859 | // 恢复 messageItems |
| 1860 | + const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1803 | doc.messageItems.forEach((items) => { | 1861 | doc.messageItems.forEach((items) => { |
| 1804 | newMessageItemsMap.set(items.id, items); | 1862 | newMessageItemsMap.set(items.id, items); |
| 1805 | }); | 1863 | }); |
| 1806 | 1864 | ||
| 1807 | // 恢复 messages | 1865 | // 恢复 messages |
| 1866 | + const newMessagesMap = new Map(state.messagesMap); | ||
| 1808 | Object.entries(doc.messages).forEach(([msgId, msg]) => { | 1867 | Object.entries(doc.messages).forEach(([msgId, msg]) => { |
| 1809 | newMessagesMap.set(msgId, msg); | 1868 | newMessagesMap.set(msgId, msg); |
| 1810 | }); | 1869 | }); |
| 1811 | 1870 | ||
| 1812 | // 更新 conversation 的 messageItemsIds | 1871 | // 更新 conversation 的 messageItemsIds |
| 1872 | + const newConversationsMap = new Map(state.conversationsMap); | ||
| 1813 | const existingConversation = newConversationsMap.get(conversationId); | 1873 | const existingConversation = newConversationsMap.get(conversationId); |
| 1814 | if (existingConversation) { | 1874 | if (existingConversation) { |
| 1815 | newConversationsMap.set(conversationId, { | 1875 | newConversationsMap.set(conversationId, { |
| @@ -1819,9 +1879,9 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1819 | } | 1879 | } |
| 1820 | 1880 | ||
| 1821 | return { | 1881 | return { |
| 1822 | - conversationsMap: newConversationsMap, | ||
| 1823 | messageItemsMap: newMessageItemsMap, | 1882 | messageItemsMap: newMessageItemsMap, |
| 1824 | messagesMap: newMessagesMap, | 1883 | messagesMap: newMessagesMap, |
| 1884 | + conversationsMap: newConversationsMap, | ||
| 1825 | }; | 1885 | }; |
| 1826 | }); | 1886 | }); |
| 1827 | } catch (error) { | 1887 | } catch (error) { |
| @@ -1848,56 +1908,60 @@ export const useConversationStore = create<ConversationStore>((set, get) => ({ | |||
| 1848 | } | 1908 | } |
| 1849 | 1909 | ||
| 1850 | set((state) => { | 1910 | set((state) => { |
| 1851 | - const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1852 | - | ||
| 1853 | // 删除当前对话的所有 messageItems | 1911 | // 删除当前对话的所有 messageItems |
| 1912 | + const newMessageItemsMap = new Map(state.messageItemsMap); | ||
| 1854 | conversation.messageItemsIds.forEach((itemsId) => { | 1913 | conversation.messageItemsIds.forEach((itemsId) => { |
| 1855 | newMessageItemsMap.delete(itemsId); | 1914 | newMessageItemsMap.delete(itemsId); |
| 1856 | }); | 1915 | }); |
| 1857 | 1916 | ||
| 1858 | - // 注意:messagesMap 中的消息需要递归删除 | 1917 | + // 删除属于当前对话的所有 messages |
| 1859 | - // 为了简化,我们清空并重新构建只包含非当前对话的消息 | 1918 | + // 先收集要删除的 messageId,因为不能在遍历Map时删除 |
| 1860 | - const newFilteredMessagesMap = new Map<string, Message>(); | 1919 | + const messageIdsToDelete: string[] = []; |
| 1861 | state.messagesMap.forEach((msg, msgId) => { | 1920 | state.messagesMap.forEach((msg, msgId) => { |
| 1862 | - // 只保留不属于当前对话的消息 | 1921 | + if (msg.conversationId === currentConversationId) { |
| 1863 | - if (msg.conversationId !== currentConversationId) { | 1922 | + messageIdsToDelete.push(msgId); |
| 1864 | - newFilteredMessagesMap.set(msgId, msg); | ||
| 1865 | } | 1923 | } |
| 1866 | }); | 1924 | }); |
| 1867 | 1925 | ||
| 1926 | + // 删除收集到的消息 | ||
| 1927 | + const newMessagesMap = new Map(state.messagesMap); | ||
| 1928 | + messageIdsToDelete.forEach(msgId => { | ||
| 1929 | + newMessagesMap.delete(msgId); | ||
| 1930 | + }); | ||
| 1931 | + | ||
| 1868 | return { | 1932 | return { |
| 1869 | messageItemsMap: newMessageItemsMap, | 1933 | messageItemsMap: newMessageItemsMap, |
| 1870 | - messagesMap: newFilteredMessagesMap, | 1934 | + messagesMap: newMessagesMap, |
| 1871 | }; | 1935 | }; |
| 1872 | }); | 1936 | }); |
| 1873 | }, | 1937 | }, |
| 1874 | })); | 1938 | })); |
| 1875 | - | ||
| 1876 | // ===== 监听 IndexDB 删除事件,同步删除内存中的对话 ===== | 1939 | // ===== 监听 IndexDB 删除事件,同步删除内存中的对话 ===== |
| 1877 | if (typeof window !== 'undefined') { | 1940 | if (typeof window !== 'undefined') { |
| 1878 | conversationEventEmitter.on('conversation-deleted', async (event: any) => { | 1941 | conversationEventEmitter.on('conversation-deleted', async (event: any) => { |
| 1879 | - const { conversationId } = event | 1942 | + const { conversationId } = event; |
| 1880 | 1943 | ||
| 1881 | // 从内存中删除对话 | 1944 | // 从内存中删除对话 |
| 1882 | useConversationStore.setState((state) => { | 1945 | useConversationStore.setState((state) => { |
| 1883 | - const newConversationsMap = new Map(state.conversationsMap) | 1946 | + // 删除conversationsMap中的条目 |
| 1884 | - const newConversationsList = state.conversationsList.filter((id: string) => id !== conversationId) | 1947 | + const newConversationsMap = new Map(state.conversationsMap); |
| 1948 | + newConversationsMap.delete(conversationId); | ||
| 1949 | + | ||
| 1950 | + // 从conversationsList中移除 | ||
| 1951 | + const newConversationsList = state.conversationsList.filter(id => id !== conversationId); | ||
| 1885 | 1952 | ||
| 1886 | // 如果删除的是当前对话,清空当前对话ID | 1953 | // 如果删除的是当前对话,清空当前对话ID |
| 1887 | const newCurrentConversationId = state.currentConversationId === conversationId | 1954 | const newCurrentConversationId = state.currentConversationId === conversationId |
| 1888 | ? null | 1955 | ? null |
| 1889 | - : state.currentConversationId | 1956 | + : state.currentConversationId; |
| 1890 | - | ||
| 1891 | - // 从内存中删除对话基本信息 | ||
| 1892 | - newConversationsMap.delete(conversationId) | ||
| 1893 | 1957 | ||
| 1894 | return { | 1958 | return { |
| 1895 | conversationsMap: newConversationsMap, | 1959 | conversationsMap: newConversationsMap, |
| 1896 | conversationsList: newConversationsList, | 1960 | conversationsList: newConversationsList, |
| 1897 | currentConversationId: newCurrentConversationId, | 1961 | currentConversationId: newCurrentConversationId, |
| 1898 | - } | 1962 | + }; |
| 1899 | - }) | 1963 | + }); |
| 1900 | - }) | 1964 | + }); |
| 1901 | } | 1965 | } |
| 1902 | 1966 | ||
| 1903 | // 开发环境下暴露 store 到 window 对象,方便调试 | 1967 | // 开发环境下暴露 store 到 window 对象,方便调试 |