mirror of
https://github.com/Zippland/Snap-Solver.git
synced 2026-02-25 08:58:21 +08:00
重构流式响应处理逻辑,优化服务端和客户端缓冲区管理
This commit is contained in:
54
app.py
54
app.py
@@ -81,8 +81,9 @@ def stream_model_response(response_generator, sid):
|
|||||||
}, room=sid)
|
}, room=sid)
|
||||||
print("Sent initial status to client")
|
print("Sent initial status to client")
|
||||||
|
|
||||||
# 跟踪已发送的内容
|
# 维护服务端缓冲区以累积完整内容
|
||||||
previous_thinking_content = ""
|
response_buffer = ""
|
||||||
|
thinking_buffer = ""
|
||||||
|
|
||||||
# 流式处理响应
|
# 流式处理响应
|
||||||
for response in response_generator:
|
for response in response_generator:
|
||||||
@@ -99,34 +100,37 @@ def stream_model_response(response_generator, sid):
|
|||||||
|
|
||||||
# 根据不同的状态进行处理
|
# 根据不同的状态进行处理
|
||||||
if status == 'thinking':
|
if status == 'thinking':
|
||||||
# 确保只发送新增的思考内容
|
# 累积思考内容到缓冲区
|
||||||
if previous_thinking_content and content.startswith(previous_thinking_content):
|
thinking_buffer += content
|
||||||
# 只发送新增部分
|
|
||||||
new_content = content[len(previous_thinking_content):]
|
|
||||||
if new_content: # 只有当有新内容时才发送
|
|
||||||
print(f"Streaming thinking content: {len(new_content)} chars")
|
|
||||||
socketio.emit('claude_response', {
|
|
||||||
'status': 'thinking',
|
|
||||||
'content': new_content
|
|
||||||
}, room=sid)
|
|
||||||
else:
|
|
||||||
# 直接发送全部内容(首次或内容不连续的情况)
|
|
||||||
print(f"Streaming thinking content (reset): {len(content)} chars")
|
|
||||||
socketio.emit('claude_response', {
|
|
||||||
'status': 'thinking',
|
|
||||||
'content': content
|
|
||||||
}, room=sid)
|
|
||||||
|
|
||||||
# 更新已发送的思考内容记录
|
# 发送完整的思考内容
|
||||||
previous_thinking_content = content
|
print(f"Streaming thinking content: {len(thinking_buffer)} chars")
|
||||||
|
socketio.emit('claude_response', {
|
||||||
|
'status': 'thinking',
|
||||||
|
'content': thinking_buffer
|
||||||
|
}, room=sid)
|
||||||
|
|
||||||
|
elif status == 'thinking_complete':
|
||||||
|
# 直接使用完整的思考内容
|
||||||
|
thinking_buffer = content # 使用服务器提供的完整内容
|
||||||
|
|
||||||
|
print(f"Thinking complete, total length: {len(thinking_buffer)} chars")
|
||||||
|
socketio.emit('claude_response', {
|
||||||
|
'status': 'thinking_complete',
|
||||||
|
'content': thinking_buffer
|
||||||
|
}, room=sid)
|
||||||
|
|
||||||
elif status == 'streaming':
|
elif status == 'streaming':
|
||||||
# 流式输出正常内容
|
# 流式输出正常内容
|
||||||
if content:
|
if content:
|
||||||
print(f"Streaming response content: {len(content)} chars")
|
# 累积到服务端缓冲区
|
||||||
|
response_buffer += content
|
||||||
|
|
||||||
|
# 发送完整的内容
|
||||||
|
print(f"Streaming response content: {len(response_buffer)} chars")
|
||||||
socketio.emit('claude_response', {
|
socketio.emit('claude_response', {
|
||||||
'status': 'streaming',
|
'status': 'streaming',
|
||||||
'content': content
|
'content': response_buffer
|
||||||
}, room=sid)
|
}, room=sid)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -134,9 +138,7 @@ def stream_model_response(response_generator, sid):
|
|||||||
socketio.emit('claude_response', response, room=sid)
|
socketio.emit('claude_response', response, room=sid)
|
||||||
|
|
||||||
# 调试信息
|
# 调试信息
|
||||||
if status == 'thinking_complete':
|
if status == 'completed':
|
||||||
print(f"Thinking complete, total length: {len(content)} chars")
|
|
||||||
elif status == 'completed':
|
|
||||||
print("Response completed")
|
print("Response completed")
|
||||||
elif status == 'error':
|
elif status == 'error':
|
||||||
print(f"Error: {response.get('error', 'Unknown error')}")
|
print(f"Error: {response.get('error', 'Unknown error')}")
|
||||||
|
|||||||
@@ -66,10 +66,6 @@ class SnapSolver {
|
|||||||
latex: ''
|
latex: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// 新增:流式输出的内存缓冲区
|
|
||||||
this.responseBuffer = "";
|
|
||||||
this.thinkingBuffer = "";
|
|
||||||
|
|
||||||
// 确保裁剪容器和其他面板初始为隐藏状态
|
// 确保裁剪容器和其他面板初始为隐藏状态
|
||||||
if (this.cropContainer) {
|
if (this.cropContainer) {
|
||||||
this.cropContainer.classList.add('hidden');
|
this.cropContainer.classList.add('hidden');
|
||||||
@@ -236,9 +232,6 @@ class SnapSolver {
|
|||||||
switch (data.status) {
|
switch (data.status) {
|
||||||
case 'started':
|
case 'started':
|
||||||
console.log('Analysis started');
|
console.log('Analysis started');
|
||||||
// 重置内存缓冲区
|
|
||||||
this.responseBuffer = "";
|
|
||||||
this.thinkingBuffer = "";
|
|
||||||
// 清空显示内容
|
// 清空显示内容
|
||||||
this.responseContent.innerHTML = '';
|
this.responseContent.innerHTML = '';
|
||||||
this.thinkingContent.innerHTML = '';
|
this.thinkingContent.innerHTML = '';
|
||||||
@@ -250,14 +243,11 @@ class SnapSolver {
|
|||||||
case 'thinking':
|
case 'thinking':
|
||||||
// 处理思考内容
|
// 处理思考内容
|
||||||
if (data.content) {
|
if (data.content) {
|
||||||
console.log('Received thinking:', data.content);
|
console.log('Received thinking content');
|
||||||
this.thinkingSection.classList.remove('hidden');
|
this.thinkingSection.classList.remove('hidden');
|
||||||
|
|
||||||
// 添加到内存缓冲区
|
// 直接设置完整内容而不是追加
|
||||||
this.thinkingBuffer += data.content;
|
this.setElementContent(this.thinkingContent, data.content);
|
||||||
|
|
||||||
// 安全地更新DOM
|
|
||||||
this.updateElementContent(this.thinkingContent, this.thinkingBuffer);
|
|
||||||
|
|
||||||
// 添加打字动画效果
|
// 添加打字动画效果
|
||||||
this.thinkingContent.classList.add('thinking-typing');
|
this.thinkingContent.classList.add('thinking-typing');
|
||||||
@@ -270,9 +260,8 @@ class SnapSolver {
|
|||||||
console.log('Thinking complete');
|
console.log('Thinking complete');
|
||||||
this.thinkingSection.classList.remove('hidden');
|
this.thinkingSection.classList.remove('hidden');
|
||||||
|
|
||||||
// 重置内存缓冲区并更新
|
// 设置完整内容
|
||||||
this.thinkingBuffer = data.content;
|
this.setElementContent(this.thinkingContent, data.content);
|
||||||
this.updateElementContent(this.thinkingContent, this.thinkingBuffer);
|
|
||||||
|
|
||||||
// 移除打字动画
|
// 移除打字动画
|
||||||
this.thinkingContent.classList.remove('thinking-typing');
|
this.thinkingContent.classList.remove('thinking-typing');
|
||||||
@@ -281,13 +270,10 @@ class SnapSolver {
|
|||||||
|
|
||||||
case 'streaming':
|
case 'streaming':
|
||||||
if (data.content) {
|
if (data.content) {
|
||||||
console.log('Received content:', data.content);
|
console.log('Received content');
|
||||||
|
|
||||||
// 添加到内存缓冲区
|
// 直接设置完整内容
|
||||||
this.responseBuffer += data.content;
|
this.setElementContent(this.responseContent, data.content);
|
||||||
|
|
||||||
// 安全地更新DOM
|
|
||||||
this.updateElementContent(this.responseContent, this.responseBuffer);
|
|
||||||
|
|
||||||
// 移除思考部分的打字动画
|
// 移除思考部分的打字动画
|
||||||
this.thinkingContent.classList.remove('thinking-typing');
|
this.thinkingContent.classList.remove('thinking-typing');
|
||||||
@@ -298,17 +284,25 @@ class SnapSolver {
|
|||||||
console.log('Analysis completed');
|
console.log('Analysis completed');
|
||||||
this.sendToClaudeBtn.disabled = false;
|
this.sendToClaudeBtn.disabled = false;
|
||||||
this.sendExtractedTextBtn.disabled = false;
|
this.sendExtractedTextBtn.disabled = false;
|
||||||
this.addToHistory(this.croppedImage, this.responseBuffer, this.thinkingBuffer);
|
|
||||||
|
// 保存到历史记录
|
||||||
|
const responseText = this.responseContent.textContent || '';
|
||||||
|
const thinkingText = this.thinkingContent.textContent || '';
|
||||||
|
this.addToHistory(this.croppedImage, responseText, thinkingText);
|
||||||
|
|
||||||
window.showToast('Analysis completed successfully');
|
window.showToast('Analysis completed successfully');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
console.error('Analysis error:', data.error);
|
console.error('Analysis error:', data.error);
|
||||||
const errorMessage = data.error || 'Unknown error occurred';
|
const errorMessage = data.error || 'Unknown error occurred';
|
||||||
// 错误信息添加到缓冲区
|
|
||||||
this.responseBuffer += '\nError: ' + errorMessage;
|
// 显示错误信息
|
||||||
// 更新DOM
|
if (errorMessage) {
|
||||||
this.updateElementContent(this.responseContent, this.responseBuffer);
|
const currentText = this.responseContent.textContent || '';
|
||||||
|
this.setElementContent(this.responseContent, currentText + '\nError: ' + errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
this.sendToClaudeBtn.disabled = false;
|
this.sendToClaudeBtn.disabled = false;
|
||||||
this.sendExtractedTextBtn.disabled = false;
|
this.sendExtractedTextBtn.disabled = false;
|
||||||
window.showToast('Analysis failed: ' + errorMessage, 'error');
|
window.showToast('Analysis failed: ' + errorMessage, 'error');
|
||||||
@@ -317,10 +311,8 @@ class SnapSolver {
|
|||||||
default:
|
default:
|
||||||
console.warn('Unknown response status:', data.status);
|
console.warn('Unknown response status:', data.status);
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
// 错误信息添加到缓冲区
|
const currentText = this.responseContent.textContent || '';
|
||||||
this.responseBuffer += '\nError: ' + data.error;
|
this.setElementContent(this.responseContent, currentText + '\nError: ' + data.error);
|
||||||
// 更新DOM
|
|
||||||
this.updateElementContent(this.responseContent, this.responseBuffer);
|
|
||||||
this.sendToClaudeBtn.disabled = false;
|
this.sendToClaudeBtn.disabled = false;
|
||||||
this.sendExtractedTextBtn.disabled = false;
|
this.sendExtractedTextBtn.disabled = false;
|
||||||
window.showToast('Unknown error occurred', 'error');
|
window.showToast('Unknown error occurred', 'error');
|
||||||
@@ -336,25 +328,12 @@ class SnapSolver {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增:安全更新DOM内容的辅助方法
|
// 新方法:安全设置DOM内容的方法(替代updateElementContent)
|
||||||
updateElementContent(element, content) {
|
setElementContent(element, content) {
|
||||||
if (!element) return;
|
if (!element) return;
|
||||||
|
|
||||||
// 创建文档片段以提高性能
|
// 直接设置内容
|
||||||
const fragment = document.createDocumentFragment();
|
element.textContent = content;
|
||||||
const tempDiv = document.createElement('div');
|
|
||||||
|
|
||||||
// 设置内容
|
|
||||||
tempDiv.textContent = content;
|
|
||||||
|
|
||||||
// 将所有子节点移动到文档片段
|
|
||||||
while (tempDiv.firstChild) {
|
|
||||||
fragment.appendChild(tempDiv.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空目标元素并添加文档片段
|
|
||||||
element.innerHTML = '';
|
|
||||||
element.appendChild(fragment);
|
|
||||||
|
|
||||||
// 自动滚动到底部
|
// 自动滚动到底部
|
||||||
element.scrollTop = element.scrollHeight;
|
element.scrollTop = element.scrollHeight;
|
||||||
|
|||||||
Reference in New Issue
Block a user