import { ChatDataAdapter, SSEChunk } from './chatDataAdapter' import { MsgItem } from '@/uni_modules/uni-ai-x/types' /** * SSE数据处理器 * 专门用于处理Server-Sent Events流式数据 */ export class SSEDataProcessor { // 存储累积的SSE数据块 private sseChunks: SSEChunk[] = [] // 当前会话ID private chatId: string = '' // 消息映射表,用于累积文本内容 private messageMap = new Map() constructor(chatId: string) { this.chatId = chatId } /** * 处理单个SSE数据块 * @param chunk SSE数据块 * @returns 处理后的MsgItem数组 */ processChunk(chunk: SSEChunk): MsgItem[] { // 添加到累积列表 this.sseChunks.push(chunk) if (!ChatDataAdapter.isRenderableSSEChunk(chunk)) { return this.getAllMessages() } // 处理数据块 this.processMessage(chunk) // 返回当前所有消息 return this.getAllMessages() } /** * 处理消息累积 * @param chunk SSE数据块 */ private processMessage(chunk: SSEChunk): void { if (!ChatDataAdapter.isRenderableSSEChunk(chunk)) { return } const messageId = chunk.data.id const text = chunk.data.text || '' const isFinished = chunk.data.finished // console.log(`Processing message ${messageId}: text="${text}", finished=${isFinished}`) if (!this.messageMap.has(messageId)) { // 创建新消息 // console.log(`Creating new message for ID: ${messageId}`) const msgItem = ChatDataAdapter.convertSSEChunkToMsgItem(chunk, this.chatId) this.messageMap.set(messageId, msgItem) // console.log(`New message created:`, msgItem) } else { // 累积文本内容,使用更智能的去重逻辑 // console.log(`Updating existing message for ID: ${messageId}`) const existingMsg = this.messageMap.get(messageId)! // console.log(`Previous body: "${existingMsg.body}"`) // 智能去重:检查新文本是否是现有内容的子串或重复 if (this.shouldAppendText(existingMsg.body, text)) { existingMsg.body += text // console.log(`New body: "${existingMsg.body}"`) } else { // console.log(`Text skipped (duplicate or redundant): "${text}"`) } // 更新状态 if (isFinished) { existingMsg.state = 3 // 完成状态 console.log(`Message ${messageId} marked as finished`) } } // console.log(`Current message map size: ${this.messageMap.size}`) // this.messageMap.forEach((msg, id) => { // console.log(`Message ${id}: body="${msg.body}", state=${msg.state}`) // }) } /** * 智能判断是否应该追加文本 * @param existingBody 现有文本内容 * @param newText 新文本内容 * @returns 是否应该追加 */ private shouldAppendText(existingBody: string, newText: string): boolean { // 如果新文本为空,不追加 if (!newText || newText.trim().length === 0) { return false } // 如果现有内容已经包含新文本,不追加 if (existingBody.includes(newText)) { return false } // 如果新文本是现有内容的子串,不追加 if (newText.length < existingBody.length && existingBody.includes(newText)) { return false } // 检查是否是重复的标点符号或空格 const lastChar = existingBody.charAt(existingBody.length - 1) const firstChar = newText.charAt(0) // 避免重复的标点符号 if (lastChar === firstChar && '.,!?;:'.includes(lastChar)) { return false } // 避免重复的空格 if (lastChar === ' ' && firstChar === ' ') { return false } return true } /** * 获取所有消息 * @returns MsgItem数组 */ getAllMessages(): MsgItem[] { const result: MsgItem[] = [] // console.log(`Getting all messages from map (size: ${this.messageMap.size})`) this.messageMap.forEach((value, key) => { // console.log(`Adding message ${key}:`, value) result.push(value) }) // console.log(`Returning ${result.length} messages:`, result) return result } /** * 获取指定ID的消息 * @param messageId 消息ID * @returns MsgItem或null */ getMessage(messageId: string): MsgItem | null { return this.messageMap.get(messageId) || null } /** * 清除所有数据 */ clear(): void { this.sseChunks = [] this.messageMap.clear() } /** * 获取累积的SSE数据块 * @returns SSE数据块数组 */ getSSEChunks(): SSEChunk[] { return [...this.sseChunks] } /** * 检查是否有未完成的消息 * @returns 是否有未完成的消息 */ hasIncompleteMessages(): boolean { let hasIncomplete = false this.messageMap.forEach((msg) => { if (msg.state === 2) { // 未完成状态 hasIncomplete = true } }) return hasIncomplete } /** * 获取未完成的消息数量 * @returns 未完成消息数量 */ getIncompleteMessageCount(): number { let count = 0 this.messageMap.forEach((msg) => { if (msg.state === 2) { // 未完成状态 count++ } }) return count } } /** * 解析SSE数据字符串 * @param sseString SSE原始字符串 * @returns SSEChunk数组 */ export function parseSSEString(sseString: string): SSEChunk[] { const chunks: SSEChunk[] = [] // 按行分割 const lines = sseString.split('\n') for (const line of lines) { const trimmedLine = line.trim() // 跳过空行和注释行 if (!trimmedLine || trimmedLine.startsWith(':')) { continue } // 检查是否是data行 if (trimmedLine.startsWith('data:')) { const dataContent = trimmedLine.substring(5).trim() // 跳过[DONE]标记 if (dataContent === '[DONE]') { continue } try { // 解析JSON数据 const chunk: SSEChunk = JSON.parse(dataContent) chunks.push(chunk) } catch (error) { console.error('解析SSE数据失败:', error, '原始数据:', dataContent) } } } return chunks } /** * 处理完整的SSE响应 * @param sseString 完整的SSE响应字符串 * @param chatId 聊天ID * @returns 处理后的MsgItem数组 */ export function processCompleteSSEResponse(sseString: string, chatId: string): MsgItem[] { const chunks = parseSSEString(sseString) const processor = new SSEDataProcessor(chatId) chunks.forEach(chunk => { processor.processChunk(chunk) }) return processor.getAllMessages() } /** * 创建SSE数据处理器实例 * @param chatId 聊天ID * @returns SSEDataProcessor实例 */ export function createSSEProcessor(chatId: string): SSEDataProcessor { return new SSEDataProcessor(chatId) } // 重新导出SSEChunk类型 export { SSEChunk } from './chatDataAdapter'