/** * 流式状态显示组件 * 用于显示RAG检索过程的实时状态 */ class StreamStatusDisplay { constructor(container) { this.container = container; this.statusElements = {}; this.retrievalHistory = []; // 记录检索历史 this.statusHistory = []; // 记录所有状态历史 this.maxDisplayItems = 3; // 最多显示3条状态 this.eventQueue = []; // 事件队列 this.isProcessing = false; // 是否正在处理队列 this.minDisplayTime = 500; // 每个状态最少显示500ms this.init(); } init() { // 创建状态显示区域 this.statusArea = document.createElement('div'); this.statusArea.className = 'stream-status-area'; this.statusArea.style.cssText = ` padding: 12px 16px; background: linear-gradient(135deg, #f6f9fc 0%, #f0f4f8 100%); border-radius: 12px; margin-bottom: 16px; font-size: 14px; color: #475569; min-height: 48px; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); display: flex; align-items: center; gap: 12px; position: relative; overflow: hidden; `; // 添加动画背景 const animBg = document.createElement('div'); animBg.style.cssText = ` position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(99, 102, 241, 0.1), transparent); animation: shimmer 2s infinite; `; this.statusArea.appendChild(animBg); // 添加CSS动画 if (!document.getElementById('stream-status-styles')) { const style = document.createElement('style'); style.id = 'stream-status-styles'; style.textContent = ` @keyframes shimmer { 0% { left: -100%; } 100% { left: 100%; } } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .status-icon { font-size: 20px; animation: pulse 1.5s ease-in-out infinite; } .status-spinner { animation: rotate 1s linear infinite; } `; document.head.appendChild(style); } // 状态内容容器 this.contentArea = document.createElement('div'); this.contentArea.style.cssText = ` position: relative; z-index: 1; flex: 1; display: flex; align-items: center; gap: 12px; `; this.statusArea.appendChild(this.contentArea); this.container.appendChild(this.statusArea); } updateStatus(event) { // 清除淡出定时器 if (this.fadeTimeout) { clearTimeout(this.fadeTimeout); } switch (event.type) { case 'cache_hit': case 'cache': if (event.cached) { this.showCacheHit(); } else { this.showCacheMiss(); } break; case 'cache_miss': this.showCacheMiss(); break; case 'starting': this.showStatus('🔍 开始分析查询...', '#6366f1'); break; case 'complexity_check': const complexity = event.data; if (complexity) { if (complexity.is_complex) { this.showStatus( `📊 复杂查询 (${complexity.level || '高'}) - 置信度: ${(complexity.confidence * 100).toFixed(1)}%`, '#f59e0b' ); } else { this.showStatus( `📊 简单查询 - 置信度: ${(complexity.confidence * 100).toFixed(1)}%`, '#10b981' ); } } break; case 'documents': const docs = event.data; if (docs) { // 记录检索历史 this.retrievalHistory.push({ count: docs.count, new_docs: docs.new_docs || 0, is_incremental: docs.is_incremental || false, retrieval_type: docs.retrieval_type || '检索', retrieval_reason: docs.retrieval_reason || '' }); // 根据是否是增量检索显示不同信息 let message = ''; if (docs.is_incremental) { // 增量检索(第二次及以后) const reason = docs.retrieval_reason || '继续检索'; message = `📚 ${reason}:新增 ${docs.new_docs || 0} 篇文档(总计 ${docs.count} 篇)`; this.showStatus(message, '#8b5cf6'); // 紫色表示增量 } else { // 初始检索 message = `📚 已检索 ${docs.count} 篇文档`; if (docs.retrieval_type) { message = `📚 ${docs.retrieval_type}:${docs.count} 篇文档`; } this.showStatus(message, '#6366f1'); // 蓝色表示初始 } // 显示检索历史汇总 if (this.retrievalHistory.length > 1) { this.showRetrievalSummary(); } // 显示来源文档 if (docs.sources && docs.sources.length > 0) { this.addSources(docs.sources); } } break; case 'sufficiency_check': const sufficient = event.data; if (sufficient) { // 置信度可能是数字或未定义 const confidence = sufficient.confidence !== undefined ? sufficient.confidence : 0.5; if (sufficient.is_sufficient) { this.showStatus( `✅ 信息充分 (置信度: ${(confidence * 100).toFixed(1)}%)`, '#10b981' ); } else { this.showStatus( `⚠️ 信息不足,继续检索... (置信度: ${(confidence * 100).toFixed(1)}%)`, '#f59e0b' ); } } break; case 'sub_queries': const queries = event.data; if (queries && queries.length > 0) { this.showStatus(`🔄 生成了 ${queries.length} 个子查询`, '#8b5cf6'); this.showSubQueries(queries); } break; case 'iteration': const iter = event.data; if (iter) { // 只显示当前轮次,不显示最大轮次 this.showStatus(`🔄 第 ${iter.current} 轮迭代`, '#6366f1'); } break; case 'generating': // 不要在这里调用scheduleFadeOut this.showStatus('✨ 正在生成答案...', '#f59e0b'); // 使用黄色表示进行中 break; case 'cached': this.showStatus('💾 结果已缓存', '#10b981'); this.scheduleFadeOut(); break; case 'complete': this.showStatus('✅ 答案生成完成', '#10b981'); // 答案生成完成后,2秒后自动消失 setTimeout(() => { this.fadeOutAndHide(); }, 2000); break; case 'cancelled': // 清除之前的所有状态 this.statusHistory = []; // 显示取消状态 this.showStatus('⚠️ 任务已停止', '#ef4444'); // 1.5秒后淡出 setTimeout(() => { this.fadeOutAndHide(); }, 1500); break; default: if (event.message) { this.showStatus(event.message, '#6366f1'); } } } showStatus(message, color = '#6366f1') { // 添加到历史记录 this.statusHistory.push({ message: message, color: color, timestamp: Date.now() }); // 使用独立的渲染方法 this.renderStatuses(); } showCacheHit() { this.statusArea.style.background = 'linear-gradient(135deg, #dcfce7 0%, #d1fae5 100%)'; this.statusArea.style.borderLeft = '3px solid #10b981'; this.contentArea.innerHTML = `
`; } showCacheMiss() { this.statusArea.style.background = 'linear-gradient(135deg, #fef3c7 0%, #fde68a 100%)'; this.statusArea.style.borderLeft = '3px solid #f59e0b'; this.contentArea.innerHTML = ` `; } showSubQueries(queries) { const maxDisplay = 3; const displayQueries = queries.slice(0, maxDisplay); const remaining = queries.length - maxDisplay; // 构建子查询列表HTML const queriesList = displayQueries.map((q, i) => `${i + 1}. ${q.length > 30 ? q.substring(0, 30) + '...' : q}` ).join('