mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
🐛 修復 websocket 不正常斷開問題
This commit is contained in:
parent
ad44568c7c
commit
466dcffaa9
@ -332,30 +332,34 @@ class WebUIManager:
|
|||||||
old_websocket = self._old_websocket_for_update
|
old_websocket = self._old_websocket_for_update
|
||||||
new_session = self._new_session_for_update
|
new_session = self._new_session_for_update
|
||||||
|
|
||||||
# 發送會話更新通知
|
# 檢查舊連接是否仍然有效
|
||||||
await old_websocket.send_json({
|
if old_websocket and not old_websocket.client_state.DISCONNECTED:
|
||||||
"type": "session_updated",
|
try:
|
||||||
"message": "新會話已創建,正在更新頁面內容",
|
# 發送會話更新通知
|
||||||
"session_info": {
|
await old_websocket.send_json({
|
||||||
"project_directory": new_session.project_directory,
|
"type": "session_updated",
|
||||||
"summary": new_session.summary,
|
"message": "新會話已創建,正在更新頁面內容",
|
||||||
"session_id": new_session.session_id
|
"session_info": {
|
||||||
}
|
"project_directory": new_session.project_directory,
|
||||||
})
|
"summary": new_session.summary,
|
||||||
debug_log("已通過舊 WebSocket 連接發送會話更新通知")
|
"session_id": new_session.session_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
debug_log("已通過舊 WebSocket 連接發送會話更新通知")
|
||||||
|
|
||||||
|
# 延遲一小段時間讓前端處理消息
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
|
||||||
|
except Exception as send_error:
|
||||||
|
debug_log(f"發送會話更新通知失敗: {send_error}")
|
||||||
|
|
||||||
|
# 安全關閉舊連接
|
||||||
|
await self._safe_close_websocket(old_websocket)
|
||||||
|
|
||||||
# 清理臨時變數
|
# 清理臨時變數
|
||||||
delattr(self, '_old_websocket_for_update')
|
delattr(self, '_old_websocket_for_update')
|
||||||
delattr(self, '_new_session_for_update')
|
delattr(self, '_new_session_for_update')
|
||||||
|
|
||||||
# 延遲一小段時間讓前端處理消息,然後關閉舊連接
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
try:
|
|
||||||
await old_websocket.close()
|
|
||||||
debug_log("已關閉舊 WebSocket 連接")
|
|
||||||
except Exception as e:
|
|
||||||
debug_log(f"關閉舊 WebSocket 連接失敗: {e}")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 沒有舊連接,設置待更新標記
|
# 沒有舊連接,設置待更新標記
|
||||||
self._pending_session_update = True
|
self._pending_session_update = True
|
||||||
@ -366,6 +370,31 @@ class WebUIManager:
|
|||||||
# 回退到待更新標記
|
# 回退到待更新標記
|
||||||
self._pending_session_update = True
|
self._pending_session_update = True
|
||||||
|
|
||||||
|
async def _safe_close_websocket(self, websocket):
|
||||||
|
"""安全關閉 WebSocket 連接,避免事件循環衝突"""
|
||||||
|
if not websocket:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 檢查連接狀態
|
||||||
|
if websocket.client_state.DISCONNECTED:
|
||||||
|
debug_log("WebSocket 已斷開,跳過關閉操作")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 嘗試正常關閉
|
||||||
|
await asyncio.wait_for(websocket.close(code=1000, reason="會話更新"), timeout=2.0)
|
||||||
|
debug_log("已正常關閉舊 WebSocket 連接")
|
||||||
|
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
debug_log("WebSocket 關閉超時,強制斷開")
|
||||||
|
except RuntimeError as e:
|
||||||
|
if "attached to a different loop" in str(e):
|
||||||
|
debug_log(f"WebSocket 事件循環衝突,忽略關閉錯誤: {e}")
|
||||||
|
else:
|
||||||
|
debug_log(f"WebSocket 關閉時發生運行時錯誤: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"關閉 WebSocket 連接時發生未知錯誤: {e}")
|
||||||
|
|
||||||
async def _check_active_tabs(self) -> bool:
|
async def _check_active_tabs(self) -> bool:
|
||||||
"""檢查是否有活躍標籤頁 - 優先檢查全局狀態,回退到 API"""
|
"""檢查是否有活躍標籤頁 - 優先檢查全局狀態,回退到 API"""
|
||||||
try:
|
try:
|
||||||
|
@ -324,7 +324,9 @@ class WebFeedbackSession:
|
|||||||
"message": "會話已超時,介面將自動關閉"
|
"message": "會話已超時,介面將自動關閉"
|
||||||
})
|
})
|
||||||
await asyncio.sleep(0.1) # 給前端一點時間處理消息
|
await asyncio.sleep(0.1) # 給前端一點時間處理消息
|
||||||
await self.websocket.close()
|
|
||||||
|
# 安全關閉 WebSocket
|
||||||
|
await self._safe_close_websocket()
|
||||||
debug_log(f"會話 {self.session_id} WebSocket 已關閉")
|
debug_log(f"會話 {self.session_id} WebSocket 已關閉")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"關閉 WebSocket 時發生錯誤: {e}")
|
debug_log(f"關閉 WebSocket 時發生錯誤: {e}")
|
||||||
@ -401,4 +403,29 @@ class WebFeedbackSession:
|
|||||||
self.process = None
|
self.process = None
|
||||||
|
|
||||||
# 設置完成事件
|
# 設置完成事件
|
||||||
self.feedback_completed.set()
|
self.feedback_completed.set()
|
||||||
|
|
||||||
|
async def _safe_close_websocket(self):
|
||||||
|
"""安全關閉 WebSocket 連接,避免事件循環衝突"""
|
||||||
|
if not self.websocket:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 檢查連接狀態
|
||||||
|
if hasattr(self.websocket, 'client_state') and self.websocket.client_state.DISCONNECTED:
|
||||||
|
debug_log("WebSocket 已斷開,跳過關閉操作")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 嘗試正常關閉
|
||||||
|
await asyncio.wait_for(self.websocket.close(code=1000, reason="會話清理"), timeout=2.0)
|
||||||
|
debug_log(f"會話 {self.session_id} WebSocket 已正常關閉")
|
||||||
|
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
debug_log(f"會話 {self.session_id} WebSocket 關閉超時")
|
||||||
|
except RuntimeError as e:
|
||||||
|
if "attached to a different loop" in str(e):
|
||||||
|
debug_log(f"會話 {self.session_id} WebSocket 事件循環衝突,忽略關閉錯誤: {e}")
|
||||||
|
else:
|
||||||
|
debug_log(f"會話 {self.session_id} WebSocket 關閉時發生運行時錯誤: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"會話 {self.session_id} 關閉 WebSocket 時發生未知錯誤: {e}")
|
@ -192,11 +192,16 @@ def setup_routes(manager: 'WebUIManager'):
|
|||||||
await handle_websocket_message(manager, session, message)
|
await handle_websocket_message(manager, session, message)
|
||||||
|
|
||||||
except WebSocketDisconnect:
|
except WebSocketDisconnect:
|
||||||
debug_log(f"WebSocket 連接斷開")
|
debug_log(f"WebSocket 連接正常斷開")
|
||||||
|
except ConnectionResetError:
|
||||||
|
debug_log(f"WebSocket 連接被重置")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"WebSocket 錯誤: {e}")
|
debug_log(f"WebSocket 錯誤: {e}")
|
||||||
finally:
|
finally:
|
||||||
session.websocket = None
|
# 安全清理 WebSocket 連接
|
||||||
|
if session.websocket == websocket:
|
||||||
|
session.websocket = None
|
||||||
|
debug_log("已清理會話中的 WebSocket 連接")
|
||||||
|
|
||||||
@manager.app.post("/api/save-settings")
|
@manager.app.post("/api/save-settings")
|
||||||
async def save_settings(request: Request):
|
async def save_settings(request: Request):
|
||||||
|
@ -800,7 +800,9 @@ class FeedbackApp {
|
|||||||
* 檢查是否可以提交回饋
|
* 檢查是否可以提交回饋
|
||||||
*/
|
*/
|
||||||
canSubmitFeedback() {
|
canSubmitFeedback() {
|
||||||
return this.feedbackState === 'waiting_for_feedback' && this.isConnected;
|
const canSubmit = this.feedbackState === 'waiting_for_feedback' && this.isConnected;
|
||||||
|
console.log(`🔍 檢查提交權限: feedbackState=${this.feedbackState}, isConnected=${this.isConnected}, canSubmit=${canSubmit}`);
|
||||||
|
return canSubmit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -989,6 +991,12 @@ class FeedbackApp {
|
|||||||
// 停止心跳
|
// 停止心跳
|
||||||
this.stopWebSocketHeartbeat();
|
this.stopWebSocketHeartbeat();
|
||||||
|
|
||||||
|
// 重置回饋狀態,避免卡在處理狀態
|
||||||
|
if (this.feedbackState === 'processing') {
|
||||||
|
console.log('🔄 WebSocket 斷開,重置處理狀態');
|
||||||
|
this.setFeedbackState('waiting_for_feedback');
|
||||||
|
}
|
||||||
|
|
||||||
if (event.code === 4004) {
|
if (event.code === 4004) {
|
||||||
// 沒有活躍會話
|
// 沒有活躍會話
|
||||||
this.updateConnectionStatus('disconnected', '沒有活躍會話');
|
this.updateConnectionStatus('disconnected', '沒有活躍會話');
|
||||||
@ -998,7 +1006,10 @@ class FeedbackApp {
|
|||||||
// 只有在非正常關閉時才重連
|
// 只有在非正常關閉時才重連
|
||||||
if (event.code !== 1000) {
|
if (event.code !== 1000) {
|
||||||
console.log('3秒後嘗試重連...');
|
console.log('3秒後嘗試重連...');
|
||||||
setTimeout(() => this.setupWebSocket(), 3000);
|
setTimeout(() => {
|
||||||
|
console.log('🔄 開始重連 WebSocket...');
|
||||||
|
this.setupWebSocket();
|
||||||
|
}, 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1107,12 +1118,16 @@ class FeedbackApp {
|
|||||||
// 顯示更新通知
|
// 顯示更新通知
|
||||||
this.showSuccessMessage(data.message || '會話已更新,正在局部更新內容...');
|
this.showSuccessMessage(data.message || '會話已更新,正在局部更新內容...');
|
||||||
|
|
||||||
// 重置回饋狀態為等待新回饋
|
|
||||||
this.setFeedbackState('waiting_for_feedback');
|
|
||||||
|
|
||||||
// 更新會話信息
|
// 更新會話信息
|
||||||
if (data.session_info) {
|
if (data.session_info) {
|
||||||
this.currentSessionId = data.session_info.session_id;
|
const newSessionId = data.session_info.session_id;
|
||||||
|
console.log(`📋 會話 ID 更新: ${this.currentSessionId} -> ${newSessionId}`);
|
||||||
|
|
||||||
|
// 重置回饋狀態為等待新回饋(使用新的會話 ID)
|
||||||
|
this.setFeedbackState('waiting_for_feedback', newSessionId);
|
||||||
|
|
||||||
|
// 更新當前會話 ID
|
||||||
|
this.currentSessionId = newSessionId;
|
||||||
|
|
||||||
// 更新頁面標題
|
// 更新頁面標題
|
||||||
if (data.session_info.project_directory) {
|
if (data.session_info.project_directory) {
|
||||||
@ -1122,6 +1137,10 @@ class FeedbackApp {
|
|||||||
|
|
||||||
// 使用局部更新替代整頁刷新
|
// 使用局部更新替代整頁刷新
|
||||||
this.refreshPageContent();
|
this.refreshPageContent();
|
||||||
|
} else {
|
||||||
|
// 如果沒有會話信息,仍然重置狀態
|
||||||
|
console.log('⚠️ 會話更新沒有包含會話信息,僅重置狀態');
|
||||||
|
this.setFeedbackState('waiting_for_feedback');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ 會話更新處理完成');
|
console.log('✅ 會話更新處理完成');
|
||||||
@ -1576,16 +1595,22 @@ class FeedbackApp {
|
|||||||
// 所有事件監聽器已在 setupEventListeners() 中統一設置
|
// 所有事件監聽器已在 setupEventListeners() 中統一設置
|
||||||
|
|
||||||
submitFeedback() {
|
submitFeedback() {
|
||||||
|
console.log('📤 嘗試提交回饋...');
|
||||||
|
|
||||||
// 檢查是否可以提交回饋
|
// 檢查是否可以提交回饋
|
||||||
if (!this.canSubmitFeedback()) {
|
if (!this.canSubmitFeedback()) {
|
||||||
console.log('⚠️ 無法提交回饋 - 當前狀態:', this.feedbackState);
|
console.log('⚠️ 無法提交回饋 - 當前狀態:', this.feedbackState, '連接狀態:', this.isConnected);
|
||||||
|
|
||||||
if (this.feedbackState === 'feedback_submitted') {
|
if (this.feedbackState === 'feedback_submitted') {
|
||||||
this.showMessage('回饋已提交,請等待下次 MCP 調用', 'warning');
|
this.showMessage('回饋已提交,請等待下次 MCP 調用', 'warning');
|
||||||
} else if (this.feedbackState === 'processing') {
|
} else if (this.feedbackState === 'processing') {
|
||||||
this.showMessage('正在處理中,請稍候', 'warning');
|
this.showMessage('正在處理中,請稍候', 'warning');
|
||||||
} else if (!this.isConnected) {
|
} else if (!this.isConnected) {
|
||||||
this.showMessage('WebSocket 未連接', 'error');
|
this.showMessage('WebSocket 未連接,正在嘗試重連...', 'error');
|
||||||
|
// 嘗試重新建立連接
|
||||||
|
this.setupWebSocket();
|
||||||
|
} else {
|
||||||
|
this.showMessage(`當前狀態不允許提交: ${this.feedbackState}`, 'warning');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user