diff --git a/deepsearcher/agent/deep_search.py b/deepsearcher/agent/deep_search.py index 860c225..3b6afc1 100644 --- a/deepsearcher/agent/deep_search.py +++ b/deepsearcher/agent/deep_search.py @@ -92,14 +92,13 @@ SUMMARY_PROMPT = """ 如果检索到的信息不足以回答问题,你应该使用你的知识来进行扩展补充。 注意,不要逐个回答问题,而是应该综合所有问题和信息,生成一个完整的回答。 同时,你应该根据提供的信息生成文内引用"[^index]"(markdown文内引用)。 -如果你自己提供的信息需要使用"[^0]"引用,即你提供的信息使用固定index=0。 -来自的引用序号从[^index]从index=1开始,来源需要与前文中的"href"一致 -不需要对每个分配一个引用,而是相同共用一个引用。 +来自的引用序号从[^index]从index=1开始,来源需要与前文中的"id"一致。 +不需要对每个分配一个引用,而是相同共用引用,并确保每一个都被引用。 另外,如果回答的内容文内引用需要引用多个,请添加多个[^index]到句尾。 -"XGBoost是非常强大的集成学习模型。[^1]但是XGBoost的缺点是计算复杂度高,需要大量的计算资源。[^0]" +"XGBoost是非常强大的集成学习模型。[^1]但是XGBoost的缺点是计算复杂度高,需要大量的计算资源。[^2]" @@ -152,10 +151,6 @@ class DeepSearch(BaseAgent): self.vector_db = vector_db self.max_iter = max_iter self.route_collection = route_collection - self.all_collections = [ - collection_info.collection_name - for collection_info in self.vector_db.list_collections(dim=embedding_model.dimension) - ] self.text_window_splitter = text_window_splitter def invoke(self, query: str, dim: int, **kwargs) -> list[str]: @@ -227,7 +222,10 @@ class DeepSearch(BaseAgent): query=query, dim=self.embedding_model.dimension ) else: - selected_collections = self.all_collections + selected_collections = [ + collection_info.collection_name + for collection_info in self.vector_db.list_collections(dim=self.embedding_model.dimension) + ] all_retrieved_results = [] query_vector = self.embedding_model.embed_query(query) @@ -394,7 +392,7 @@ class DeepSearch(BaseAgent): if not all_retrieved_results or len(all_retrieved_results) == 0: send_info(f"'{original_query}'没能找到更多信息!") return "", [] - chunks, refs = self._format_chunks(all_retrieved_results) + chunks, refs = self._format_chunks(all_retrieved_results, with_chunk_id=False) send_info(f"正在总结 {len(all_retrieved_results)} 个查找到的文档片段") summary_prompt = SUMMARY_PROMPT.format( original_query=original_query, @@ -406,13 +404,13 @@ class DeepSearch(BaseAgent): send_answer(response) return response, all_retrieved_results - def _format_chunks(self, retrieved_results: list[RetrievalResult]) -> tuple[str, str]: + def _format_chunks(self, retrieved_results: list[RetrievalResult], with_chunk_id: bool = True) -> tuple[str, str]: # 以referecen为key,把chunk放到字典中 ref_dict = defaultdict(list) for result in retrieved_results: ref_dict[result.reference].append(result.text) formated_chunks = [] - formated_refs = ["\n\n[^0]: AI 生成\n"] + formated_refs = ["\n\n"] chunk_count = 0 for i, reference in enumerate(ref_dict): formated_chunk = "".join( @@ -421,6 +419,11 @@ class DeepSearch(BaseAgent): f"" + f"\n{chunk}\n" + f"\n" + ) + if with_chunk_id else ( + f"" + + f"\n{chunk}\n" + + f"\n" ) for j, chunk in enumerate(ref_dict[reference]) ] @@ -428,7 +431,7 @@ class DeepSearch(BaseAgent): print(formated_chunk) formated_chunks.append(formated_chunk) chunk_count += len(ref_dict[reference]) - formated_refs.append(f"[^{i + 1}]: " + str(reference) + "\n") + formated_refs.append(f"[{i + 1}]: " + str(reference) + "\n") formated_chunks = "".join(formated_chunks) formated_refs = "".join(formated_refs) return formated_chunks, formated_refs diff --git a/deepsearcher/templates/static/js/app.js b/deepsearcher/templates/static/js/app.js index 65ed29e..7d1a3ef 100644 --- a/deepsearcher/templates/static/js/app.js +++ b/deepsearcher/templates/static/js/app.js @@ -4,481 +4,481 @@ let isStreaming = false; // 工具函数:显示状态信息 function showStatus(elementId, message, type) { - const statusElement = document.getElementById(elementId); - - // 清除之前的类型类 - statusElement.classList.remove( - 'status-success', - 'status-error', - 'status-loading' - ); - - // 添加新的类型类 - if (type === 'success') { - statusElement.classList.add('status-success'); - statusElement.innerHTML = message; - } else if (type === 'error') { - statusElement.classList.add('status-error'); - statusElement.innerHTML = message; - } else if (type === 'loading') { - statusElement.classList.add('status-loading'); - statusElement.innerHTML = `
${message}`; - } - - statusElement.classList.add('visible'); + const statusElement = document.getElementById(elementId); + + // 清除之前的类型类 + statusElement.classList.remove( + 'status-success', + 'status-error', + 'status-loading' + ); + + // 添加新的类型类 + if (type === 'success') { + statusElement.classList.add('status-success'); + statusElement.innerHTML = message; + } else if (type === 'error') { + statusElement.classList.add('status-error'); + statusElement.innerHTML = message; + } else if (type === 'loading') { + statusElement.classList.add('status-loading'); + statusElement.innerHTML = `
${message}`; + } + + statusElement.classList.add('visible'); } // 工具函数:显示消息流 function displayMessages(messages) { - const container = document.getElementById('messageContainer'); - container.innerHTML = ''; + const container = document.getElementById('messageContainer'); + container.innerHTML = ''; - messages.forEach((message) => { - addMessageToContainer(message); - }); + messages.forEach((message) => { + addMessageToContainer(message); + }); - // 滚动到底部 - container.scrollTop = container.scrollHeight; + // 滚动到底部 + container.scrollTop = container.scrollHeight; } // 工具函数:添加单个消息到容器 function addMessageToContainer(message) { - console.log('Adding message to container:', message); - - const container = document.getElementById('messageContainer'); - if (!container) { - console.error('Message container not found!'); - return; - } - - const messageElement = document.createElement('div'); - messageElement.className = `message message-${message.type}`; - - const contentElement = document.createElement('div'); - contentElement.textContent = message.content; - - messageElement.appendChild(contentElement); - - // 只有在有有效时间戳时才显示时间 - if (message.timestamp && !isNaN(message.timestamp)) { - const date = new Date(message.timestamp * 1000); - if (!isNaN(date.getTime())) { - const timestampElement = document.createElement('div'); - timestampElement.className = 'message-timestamp'; - timestampElement.textContent = date.toLocaleTimeString(); - messageElement.appendChild(timestampElement); + console.log('Adding message to container:', message); + + const container = document.getElementById('messageContainer'); + if (!container) { + console.error('Message container not found!'); + return; + } + + const messageElement = document.createElement('div'); + messageElement.className = `message message-${message.type}`; + + const contentElement = document.createElement('div'); + contentElement.textContent = message.content; + + messageElement.appendChild(contentElement); + + // 只有在有有效时间戳时才显示时间 + if (message.timestamp && !isNaN(message.timestamp)) { + const date = new Date(message.timestamp * 1000); + if (!isNaN(date.getTime())) { + const timestampElement = document.createElement('div'); + timestampElement.className = 'message-timestamp'; + timestampElement.textContent = date.toLocaleTimeString(); + messageElement.appendChild(timestampElement); + } + } + container.appendChild(messageElement); + + // 确保处理过程容器是可见的 + const processContainer = document.getElementById('processResult'); + if (processContainer && !processContainer.classList.contains('visible')) { + processContainer.classList.add('visible'); } - } - container.appendChild(messageElement); - - // 确保处理过程容器是可见的 - const processContainer = document.getElementById('processResult'); - if (processContainer && !processContainer.classList.contains('visible')) { - processContainer.classList.add('visible'); - } - - // 滚动到底部 - container.scrollTop = container.scrollHeight; - - console.log( - 'Message added successfully, container now has', - container.children.length, - 'messages' - ); + + // 滚动到底部 + container.scrollTop = container.scrollHeight; + + console.log( + 'Message added successfully, container now has', + container.children.length, + 'messages' + ); } // 工具函数:隐藏状态信息 function hideStatus(elementId) { - const statusElement = document.getElementById(elementId); - statusElement.classList.remove('visible'); + const statusElement = document.getElementById(elementId); + statusElement.classList.remove('visible'); } // 工具函数:显示结果 function showResult() { - const resultElement = document.getElementById('queryResult'); - resultElement.classList.add('visible'); + const resultElement = document.getElementById('queryResult'); + resultElement.classList.add('visible'); } // 工具函数:隐藏结果 function hideResult() { - const resultElement = document.getElementById('queryResult'); - resultElement.classList.remove('visible'); + const resultElement = document.getElementById('queryResult'); + resultElement.classList.remove('visible'); } // 工具函数:显示处理过程 function showProcessResult() { - const processElement = document.getElementById('processResult'); - processElement.classList.add('visible'); + const processElement = document.getElementById('processResult'); + processElement.classList.add('visible'); } // 工具函数:隐藏处理过程 function hideProcessResult() { - const processElement = document.getElementById('processResult'); - processElement.classList.remove('visible'); + const processElement = document.getElementById('processResult'); + processElement.classList.remove('visible'); } // 工具函数:转义HTML特殊字符 function escapeHtml(text) { - const map = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - return text.replace(/[&<>"']/g, function (m) { - return map[m]; - }); + const map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g, function (m) { + return map[m]; + }); } // 工具函数:设置按钮加载状态 function setButtonLoading(button, loading) { - if (loading) { - button.classList.add('loading'); - button.disabled = true; - } else { - button.classList.remove('loading'); - button.disabled = false; - } + if (loading) { + button.classList.add('loading'); + button.disabled = true; + } else { + button.classList.remove('loading'); + button.disabled = false; + } } // 工具函数:关闭EventSource连接 function closeEventSource() { - if (eventSource) { - console.log('Closing eventSource in closeEventSource function'); - eventSource.close(); - eventSource = null; - } - if (window.currentEventSource) { - console.log('Closing currentEventSource in closeEventSource function'); - window.currentEventSource.close(); - window.currentEventSource = null; - } - isStreaming = false; + if (eventSource) { + console.log('Closing eventSource in closeEventSource function'); + eventSource.close(); + eventSource = null; + } + if (window.currentEventSource) { + console.log('Closing currentEventSource in closeEventSource function'); + window.currentEventSource.close(); + window.currentEventSource = null; + } + isStreaming = false; } // 工具函数:处理实时消息流 function handleStreamMessage(data) { - try { - const message = JSON.parse(data); - - switch (message.type) { - case 'connection': - console.log('Connected to message stream:', message.message); - break; - case 'heartbeat': - // 心跳消息,不需要处理 - break; - case 'start': - console.log('Query started:', message.content); - showStatus('queryStatus', ' 正在处理...', 'loading'); - addMessageToContainer(message); - break; - case 'complete': - console.log('Query completed - closing connection'); - showStatus('queryStatus', '查询完成', 'success'); - addMessageToContainer(message); - // 关闭EventSource连接 - if (window.currentEventSource) { - console.log('Closing currentEventSource'); - window.currentEventSource.close(); - window.currentEventSource = null; - } - isStreaming = false; - setButtonLoading(document.getElementById('queryBtn'), false); - console.log( - 'Query completed - connection closed, isStreaming set to false' - ); - break; - case 'error': - console.error('Error:', message.content); - showStatus('queryStatus', message.content, 'error'); - addMessageToContainer(message); - // 关闭EventSource连接 - if (window.currentEventSource) { - window.currentEventSource.close(); - window.currentEventSource = null; - } - isStreaming = false; - setButtonLoading(document.getElementById('queryBtn'), false); - break; - case 'info': - // 处理信息消息 - console.log( - 'Processing info message:', - message.content.substring(0, 100) + '...' - ); - addMessageToContainer(message); - break; - case 'answer': - // 处理answer类型,显示查询结果 - console.log( - 'Processing answer message:', - message.content.substring(0, 100) + '...' - ); - // 将结果内容显示在结果区域 - if (message.content && message.content !== '==== FINAL ANSWER====') { - // document.getElementById('resultText').textContent = message.content; - document.getElementById('resultText').innerHTML = marked.parse( - message.content - ); - showResult(); + try { + const message = JSON.parse(data); + + switch (message.type) { + case 'connection': + console.log('Connected to message stream:', message.message); + break; + case 'heartbeat': + // 心跳消息,不需要处理 + break; + case 'start': + console.log('Query started:', message.content); + showStatus('queryStatus', ' 正在处理...', 'loading'); + addMessageToContainer(message); + break; + case 'complete': + console.log('Query completed - closing connection'); + showStatus('queryStatus', '查询完成', 'success'); + addMessageToContainer(message); + // 关闭EventSource连接 + if (window.currentEventSource) { + console.log('Closing currentEventSource'); + window.currentEventSource.close(); + window.currentEventSource = null; + } + isStreaming = false; + setButtonLoading(document.getElementById('queryBtn'), false); + console.log( + 'Query completed - connection closed, isStreaming set to false' + ); + break; + case 'error': + console.error('Error:', message.content); + showStatus('queryStatus', message.content, 'error'); + addMessageToContainer(message); + // 关闭EventSource连接 + if (window.currentEventSource) { + window.currentEventSource.close(); + window.currentEventSource = null; + } + isStreaming = false; + setButtonLoading(document.getElementById('queryBtn'), false); + break; + case 'info': + // 处理信息消息 + console.log( + 'Processing info message:', + message.content.substring(0, 100) + '...' + ); + addMessageToContainer(message); + break; + case 'answer': + // 处理answer类型,显示查询结果 + console.log( + 'Processing answer message:', + message.content.substring(0, 100) + '...' + ); + // 将结果内容显示在结果区域 + if (message.content && message.content !== '==== FINAL ANSWER====') { + // document.getElementById('resultText').textContent = message.content; + document.getElementById('resultText').innerHTML = marked.parse( + message.content + ); + showResult(); + } + // 不将answer消息添加到处理过程容器中,只显示在查询结果框中 + break; + default: + console.log('Unknown message type:', message.type); } - // 不将answer消息添加到处理过程容器中,只显示在查询结果框中 - break; - default: - console.log('Unknown message type:', message.type); + } catch (error) { + console.error('Error parsing message:', error); } - } catch (error) { - console.error('Error parsing message:', error); - } } // 工具函数:开始实时消息流 function startMessageStream() { - closeEventSource(); // 关闭之前的连接 + closeEventSource(); // 关闭之前的连接 - eventSource = new EventSource('/stream-messages/'); + eventSource = new EventSource('/stream-messages/'); - eventSource.onopen = function (event) { - console.log('EventSource connection opened'); - }; + eventSource.onopen = function (event) { + console.log('EventSource connection opened'); + }; - eventSource.onmessage = function (event) { - handleStreamMessage(event.data); - }; + eventSource.onmessage = function (event) { + handleStreamMessage(event.data); + }; - eventSource.onerror = function (event) { - console.error('EventSource error:', event); - if (eventSource.readyState === EventSource.CLOSED) { - console.log('EventSource connection closed'); - } - }; + eventSource.onerror = function (event) { + console.error('EventSource error:', event); + if (eventSource.readyState === EventSource.CLOSED) { + console.log('EventSource connection closed'); + } + }; } // 加载文件功能 document - .getElementById('loadFilesBtn') - .addEventListener('click', async function () { - const button = this; - const filePathsInput = document.getElementById('filePaths').value; - const collectionName = document.getElementById('collectionName').value; - const collectionDesc = document.getElementById('collectionDesc').value; - - if (!filePathsInput) { - showStatus('loadStatus', '请提供至少一个文件路径', 'error'); - return; - } + .getElementById('loadFilesBtn') + .addEventListener('click', async function () { + const button = this; + const filePathsInput = document.getElementById('filePaths').value; + const collectionName = document.getElementById('collectionName').value; + const collectionDesc = document.getElementById('collectionDesc').value; + + if (!filePathsInput) { + showStatus('loadStatus', '请提供至少一个文件路径', 'error'); + return; + } - const filePaths = filePathsInput - .split(',') - .map((path) => path.trim()) - .filter((path) => path); + const filePaths = filePathsInput + .split(',') + .map((path) => path.trim()) + .filter((path) => path); - setButtonLoading(button, true); - showStatus('loadStatus', ' 正在加载文件...', 'loading'); - hideResult(); - hideProcessResult(); + setButtonLoading(button, true); + showStatus('loadStatus', ' 正在加载文件...', 'loading'); + hideResult(); + hideProcessResult(); - try { - const response = await fetch('/load-files/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - paths: filePaths, - collection_name: collectionName || undefined, - collection_description: collectionDesc || undefined - }) - }); - - const data = await response.json(); - - if (response.ok) { - showStatus('loadStatus', data.message, 'success'); - } else { - showStatus('loadStatus', `加载失败: ${data.detail}`, 'error'); - } - } catch (error) { - showStatus('loadStatus', `请求失败: ${error.message}`, 'error'); - } finally { - setButtonLoading(button, false); - } - }); + try { + const response = await fetch('/load-files/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + paths: filePaths, + collection_name: collectionName || undefined, + collection_description: collectionDesc || undefined + }) + }); + + const data = await response.json(); + + if (response.ok) { + showStatus('loadStatus', data.message, 'success'); + } else { + showStatus('loadStatus', `加载失败: ${data.detail}`, 'error'); + } + } catch (error) { + showStatus('loadStatus', `请求失败: ${error.message}`, 'error'); + } finally { + setButtonLoading(button, false); + } + }); // 清空消息功能 document - .getElementById('clearMessagesBtn') - .addEventListener('click', async function () { - try { - const response = await fetch('/clear-messages/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' + .getElementById('clearMessagesBtn') + .addEventListener('click', async function () { + try { + const response = await fetch('/clear-messages/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }); + + if (response.ok) { + // 清空消息容器 + const container = document.getElementById('messageContainer'); + container.innerHTML = ''; + + // 清空查询结果 + const resultText = document.getElementById('resultText'); + resultText.textContent = ''; + + // 隐藏处理过程容器 + hideProcessResult(); + + // 隐藏查询结果容器 + hideResult(); + + showStatus('queryStatus', '消息已清空', 'success'); + } else { + showStatus('queryStatus', '清空消息失败', 'error'); + } + } catch (error) { + showStatus('queryStatus', `请求失败: ${error.message}`, 'error'); } - }); - - if (response.ok) { - // 清空消息容器 - const container = document.getElementById('messageContainer'); - container.innerHTML = ''; - - // 清空查询结果 - const resultText = document.getElementById('resultText'); - resultText.textContent = ''; - - // 隐藏处理过程容器 - hideProcessResult(); - - // 隐藏查询结果容器 - hideResult(); - - showStatus('queryStatus', '消息已清空', 'success'); - } else { - showStatus('queryStatus', '清空消息失败', 'error'); - } - } catch (error) { - showStatus('queryStatus', `请求失败: ${error.message}`, 'error'); - } - }); + }); // 加载网站内容功能 document - .getElementById('loadWebsiteBtn') - .addEventListener('click', async function () { - const button = this; - const urlsInput = document.getElementById('websiteUrls').value; - const collectionName = document.getElementById('webCollectionName').value; - const collectionDesc = document.getElementById('webCollectionDesc').value; - - if (!urlsInput) { - showStatus('webLoadStatus', '请提供至少一个网站URL', 'error'); - return; - } + .getElementById('loadWebsiteBtn') + .addEventListener('click', async function () { + const button = this; + const urlsInput = document.getElementById('websiteUrls').value; + const collectionName = document.getElementById('webCollectionName').value; + const collectionDesc = document.getElementById('webCollectionDesc').value; + + if (!urlsInput) { + showStatus('webLoadStatus', '请提供至少一个网站URL', 'error'); + return; + } - const urls = urlsInput - .split(',') - .map((url) => url.trim()) - .filter((url) => url); + const urls = urlsInput + .split(',') + .map((url) => url.trim()) + .filter((url) => url); - setButtonLoading(button, true); - showStatus('webLoadStatus', ' 正在加载网站...', 'loading'); - hideResult(); - hideProcessResult(); + setButtonLoading(button, true); + showStatus('webLoadStatus', ' 正在加载网站...', 'loading'); + hideResult(); + hideProcessResult(); - try { - const response = await fetch('/load-website/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - urls: urls, - collection_name: collectionName || undefined, - collection_description: collectionDesc || undefined - }) - }); - - const data = await response.json(); - - if (response.ok) { - showStatus('webLoadStatus', data.message, 'success'); - } else { - showStatus('webLoadStatus', `加载失败: ${data.detail}`, 'error'); - } - } catch (error) { - showStatus('webLoadStatus', `请求失败: ${error.message}`, 'error'); - } finally { - setButtonLoading(button, false); - } - }); + try { + const response = await fetch('/load-website/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + urls: urls, + collection_name: collectionName || undefined, + collection_description: collectionDesc || undefined + }) + }); + + const data = await response.json(); + + if (response.ok) { + showStatus('webLoadStatus', data.message, 'success'); + } else { + showStatus('webLoadStatus', `加载失败: ${data.detail}`, 'error'); + } + } catch (error) { + showStatus('webLoadStatus', `请求失败: ${error.message}`, 'error'); + } finally { + setButtonLoading(button, false); + } + }); // 查询功能 - 使用实时流 document - .getElementById('queryBtn') - .addEventListener('click', async function () { - const button = this; - const queryText = document.getElementById('queryText').value; - const maxIter = parseInt(document.getElementById('maxIter').value); - - if (!queryText) { - showStatus('queryStatus', '请输入查询问题', 'error'); - return; - } - - if (isNaN(maxIter) || maxIter < 1 || maxIter > 10) { - showStatus('queryStatus', '迭代次数必须是1到10之间的整数', 'error'); - return; - } - - if (isStreaming) { - console.log('Query already in progress, isStreaming:', isStreaming); - showStatus('queryStatus', '查询正在进行中,请等待完成', 'error'); - return; - } - - setButtonLoading(button, true); - showStatus('queryStatus', '正在启动查询...', 'loading'); - hideResult(); - hideProcessResult(); - - // 清空消息容器 - const container = document.getElementById('messageContainer'); - container.innerHTML = ''; - - try { - console.log('Starting new query, setting isStreaming to true'); - isStreaming = true; - - // 确保没有其他连接存在 - if (window.currentEventSource) { - console.log('Closing existing EventSource connection'); - window.currentEventSource.close(); - window.currentEventSource = null; - } + .getElementById('queryBtn') + .addEventListener('click', async function () { + const button = this; + const queryText = document.getElementById('queryText').value; + const maxIter = parseInt(document.getElementById('maxIter').value); + + if (!queryText) { + showStatus('queryStatus', '请输入查询问题', 'error'); + return; + } - // 使用EventSource直接连接到查询流 - const eventSource = new EventSource( - `/query-stream/?original_query=${encodeURIComponent( - queryText - )}&max_iter=${maxIter}` - ); + if (isNaN(maxIter) || maxIter < 1 || maxIter > 10) { + showStatus('queryStatus', '迭代次数必须是1到10之间的整数', 'error'); + return; + } - // 保存EventSource引用以便后续关闭 - window.currentEventSource = eventSource; + if (isStreaming) { + console.log('Query already in progress, isStreaming:', isStreaming); + showStatus('queryStatus', '查询正在进行中,请等待完成', 'error'); + return; + } - eventSource.onopen = function (event) { - console.log('EventSource connection opened for query'); - showStatus('queryStatus', ' 正在处理...', 'loading'); - }; + setButtonLoading(button, true); + showStatus('queryStatus', '正在启动查询...', 'loading'); + hideResult(); + hideProcessResult(); - eventSource.onmessage = function (event) { - console.log('Received message:', event.data); - handleStreamMessage(event.data); - }; + // 清空消息容器 + const container = document.getElementById('messageContainer'); + container.innerHTML = ''; - eventSource.onerror = function (event) { - console.error('EventSource error:', event); - if (eventSource.readyState === EventSource.CLOSED) { - console.log('EventSource connection closed due to error'); - isStreaming = false; - setButtonLoading(button, false); - window.currentEventSource = null; + try { + console.log('Starting new query, setting isStreaming to true'); + isStreaming = true; + + // 确保没有其他连接存在 + if (window.currentEventSource) { + console.log('Closing existing EventSource connection'); + window.currentEventSource.close(); + window.currentEventSource = null; + } + + // 使用EventSource直接连接到查询流 + const eventSource = new EventSource( + `/query-stream/?original_query=${encodeURIComponent( + queryText + )}&max_iter=${maxIter}` + ); + + // 保存EventSource引用以便后续关闭 + window.currentEventSource = eventSource; + + eventSource.onopen = function (event) { + console.log('EventSource connection opened for query'); + showStatus('queryStatus', ' 正在处理...', 'loading'); + }; + + eventSource.onmessage = function (event) { + console.log('Received message:', event.data); + handleStreamMessage(event.data); + }; + + eventSource.onerror = function (event) { + console.error('EventSource error:', event); + if (eventSource.readyState === EventSource.CLOSED) { + console.log('EventSource connection closed due to error'); + isStreaming = false; + setButtonLoading(button, false); + window.currentEventSource = null; + } + }; + } catch (error) { + console.error('Query error:', error); + showStatus('queryStatus', `请求失败: ${error.message}`, 'error'); + isStreaming = false; + setButtonLoading(button, false); } - }; - } catch (error) { - console.error('Query error:', error); - showStatus('queryStatus', `请求失败: ${error.message}`, 'error'); - isStreaming = false; - setButtonLoading(button, false); - } - }); + }); // 页面卸载时清理连接 window.addEventListener('beforeunload', function () { - if (window.currentEventSource) { - window.currentEventSource.close(); - window.currentEventSource = null; - } + if (window.currentEventSource) { + window.currentEventSource.close(); + window.currentEventSource = null; + } });