mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
🔧 增強調試功能,新增 debug_log 函數以輸出調試訊息,並在多個模組中替換原有的 print 語句。更新伺服器初始化以確保編碼正確性,並在 Windows 環境下設置 stdio 為二進制模式。
This commit is contained in:
parent
86e55b89f6
commit
aa4cb3a136
@ -41,12 +41,8 @@ def main():
|
||||
elif args.command == 'version':
|
||||
show_version()
|
||||
elif args.command == 'server':
|
||||
# 明確指定伺服器命令
|
||||
print("🚀 啟動 Interactive Feedback MCP Enhanced 伺服器...")
|
||||
run_server()
|
||||
elif args.command is None:
|
||||
# 沒有指定命令,啟動伺服器(保持向後兼容)
|
||||
print("🚀 啟動 Interactive Feedback MCP Enhanced 伺服器...")
|
||||
run_server()
|
||||
else:
|
||||
# 不應該到達這裡
|
||||
|
@ -30,6 +30,29 @@ from PySide6.QtWidgets import (
|
||||
from PySide6.QtCore import Qt, Signal, QTimer
|
||||
from PySide6.QtGui import QFont, QPixmap, QDragEnterEvent, QDropEvent, QKeySequence, QShortcut
|
||||
|
||||
# ===== 調試日誌函數 =====
|
||||
def debug_log(message: str) -> None:
|
||||
"""輸出調試訊息到標準錯誤,避免污染標準輸出"""
|
||||
# 只在啟用調試模式時才輸出,避免干擾 MCP 通信
|
||||
if not os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on"):
|
||||
return
|
||||
|
||||
try:
|
||||
# 確保消息是字符串類型
|
||||
if not isinstance(message, str):
|
||||
message = str(message)
|
||||
|
||||
# 安全地輸出到 stderr,處理編碼問題
|
||||
try:
|
||||
print(f"[GUI_DEBUG] {message}", file=sys.stderr, flush=True)
|
||||
except UnicodeEncodeError:
|
||||
# 如果遇到編碼問題,使用 ASCII 安全模式
|
||||
safe_message = message.encode('ascii', errors='replace').decode('ascii')
|
||||
print(f"[GUI_DEBUG] {safe_message}", file=sys.stderr, flush=True)
|
||||
except Exception:
|
||||
# 最後的備用方案:靜默失敗,不影響主程序
|
||||
pass
|
||||
|
||||
# ===== 型別定義 =====
|
||||
class FeedbackResult(TypedDict):
|
||||
"""回饋結果的型別定義"""
|
||||
@ -265,9 +288,8 @@ class ImageUploadWidget(QWidget):
|
||||
new_height = int(image.height() * scale)
|
||||
|
||||
# 縮放圖片
|
||||
from PySide6.QtCore import Qt
|
||||
image = image.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
print(f"[DEBUG] 圖片已縮放至: {new_width}x{new_height}")
|
||||
debug_log(f"圖片已縮放至: {new_width}x{new_height}")
|
||||
|
||||
# 使用較低的質量保存以減小文件大小
|
||||
quality = 70 # 降低質量以減小文件大小
|
||||
@ -275,7 +297,7 @@ class ImageUploadWidget(QWidget):
|
||||
# 檢查保存後的文件大小
|
||||
if temp_file.exists():
|
||||
file_size = temp_file.stat().st_size
|
||||
print(f"[DEBUG] 剪貼板圖片保存成功: {temp_file}, 大小: {file_size} bytes")
|
||||
debug_log(f"剪貼板圖片保存成功: {temp_file}, 大小: {file_size} bytes")
|
||||
|
||||
# 檢查文件大小是否超過限制
|
||||
if file_size > 1 * 1024 * 1024: # 1MB 限制
|
||||
@ -319,34 +341,34 @@ class ImageUploadWidget(QWidget):
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
temp_files_cleaned += 1
|
||||
print(f"[DEBUG] 已刪除臨時文件: {file_path}")
|
||||
debug_log(f"已刪除臨時文件: {file_path}")
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 刪除臨時文件失敗: {e}")
|
||||
debug_log(f"刪除臨時文件失敗: {e}")
|
||||
|
||||
# 清除內存中的圖片數據
|
||||
self.images.clear()
|
||||
self._refresh_preview()
|
||||
self._update_status()
|
||||
self.images_changed.emit()
|
||||
print(f"[DEBUG] 已清除所有圖片,包括 {temp_files_cleaned} 個臨時文件")
|
||||
debug_log(f"已清除所有圖片,包括 {temp_files_cleaned} 個臨時文件")
|
||||
|
||||
def _add_images(self, file_paths: List[str]) -> None:
|
||||
"""添加圖片"""
|
||||
added_count = 0
|
||||
for file_path in file_paths:
|
||||
try:
|
||||
print(f"[DEBUG] 嘗試添加圖片: {file_path}")
|
||||
debug_log(f"嘗試添加圖片: {file_path}")
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
print(f"[DEBUG] 文件不存在: {file_path}")
|
||||
debug_log(f"文件不存在: {file_path}")
|
||||
continue
|
||||
|
||||
if not self._is_image_file(file_path):
|
||||
print(f"[DEBUG] 不是圖片文件: {file_path}")
|
||||
debug_log(f"不是圖片文件: {file_path}")
|
||||
continue
|
||||
|
||||
file_size = os.path.getsize(file_path)
|
||||
print(f"[DEBUG] 文件大小: {file_size} bytes")
|
||||
debug_log(f"文件大小: {file_size} bytes")
|
||||
|
||||
# 更嚴格的大小限制(1MB)
|
||||
if file_size > 1 * 1024 * 1024:
|
||||
@ -364,10 +386,10 @@ class ImageUploadWidget(QWidget):
|
||||
# 讀取圖片原始二進制數據
|
||||
with open(file_path, 'rb') as f:
|
||||
raw_data = f.read()
|
||||
print(f"[DEBUG] 讀取原始數據大小: {len(raw_data)} bytes")
|
||||
debug_log(f"讀取原始數據大小: {len(raw_data)} bytes")
|
||||
|
||||
if len(raw_data) == 0:
|
||||
print(f"[DEBUG] 讀取的數據為空!")
|
||||
debug_log(f"讀取的數據為空!")
|
||||
continue
|
||||
|
||||
# 再次檢查內存中的數據大小
|
||||
@ -386,14 +408,14 @@ class ImageUploadWidget(QWidget):
|
||||
"size": file_size
|
||||
}
|
||||
added_count += 1
|
||||
print(f"[DEBUG] 圖片添加成功: {os.path.basename(file_path)}, 數據大小: {len(raw_data)} bytes")
|
||||
debug_log(f"圖片添加成功: {os.path.basename(file_path)}, 數據大小: {len(raw_data)} bytes")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 添加圖片失敗: {e}")
|
||||
debug_log(f"添加圖片失敗: {e}")
|
||||
QMessageBox.warning(self, "錯誤", f"無法載入圖片 {os.path.basename(file_path)}:\n{str(e)}")
|
||||
|
||||
print(f"[DEBUG] 共添加 {added_count} 張圖片")
|
||||
print(f"[DEBUG] 當前總共有 {len(self.images)} 張圖片")
|
||||
debug_log(f"共添加 {added_count} 張圖片")
|
||||
debug_log(f"當前總共有 {len(self.images)} 張圖片")
|
||||
if added_count > 0:
|
||||
self._refresh_preview()
|
||||
self._update_status()
|
||||
@ -432,16 +454,16 @@ class ImageUploadWidget(QWidget):
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"[DEBUG] 已刪除臨時文件: {file_path}")
|
||||
debug_log(f"已刪除臨時文件: {file_path}")
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 刪除臨時文件失敗: {e}")
|
||||
debug_log(f"刪除臨時文件失敗: {e}")
|
||||
|
||||
# 從內存中移除圖片數據
|
||||
del self.images[image_id]
|
||||
self._refresh_preview()
|
||||
self._update_status()
|
||||
self.images_changed.emit()
|
||||
print(f"[DEBUG] 已移除圖片: {image_info['name']}")
|
||||
debug_log(f"已移除圖片: {image_info['name']}")
|
||||
|
||||
def _update_status(self) -> None:
|
||||
"""更新狀態標籤"""
|
||||
@ -464,9 +486,9 @@ class ImageUploadWidget(QWidget):
|
||||
self.status_label.setText(f"已選擇 {count} 張圖片 (總計 {size_str})")
|
||||
|
||||
# 詳細調試信息
|
||||
print(f"[DEBUG] === 圖片狀態更新 ===")
|
||||
print(f"[DEBUG] 圖片數量: {count}")
|
||||
print(f"[DEBUG] 總大小: {total_size} bytes ({size_str})")
|
||||
debug_log(f"=== 圖片狀態更新 ===")
|
||||
debug_log(f"圖片數量: {count}")
|
||||
debug_log(f"總大小: {total_size} bytes ({size_str})")
|
||||
for i, (image_id, img) in enumerate(self.images.items(), 1):
|
||||
data_size = len(img["data"]) if isinstance(img["data"], bytes) else 0
|
||||
# 智能顯示每張圖片的大小
|
||||
@ -476,8 +498,8 @@ class ImageUploadWidget(QWidget):
|
||||
data_str = f"{data_size/1024:.1f} KB"
|
||||
else:
|
||||
data_str = f"{data_size/(1024*1024):.1f} MB"
|
||||
print(f"[DEBUG] 圖片 {i}: {img['name']} - 數據大小: {data_str}")
|
||||
print(f"[DEBUG] ==================")
|
||||
debug_log(f"圖片 {i}: {img['name']} - 數據大小: {data_str}")
|
||||
debug_log(f"==================")
|
||||
|
||||
def get_images_data(self) -> List[dict]:
|
||||
"""獲取圖片數據"""
|
||||
@ -552,11 +574,11 @@ class ImageUploadWidget(QWidget):
|
||||
temp_file.unlink()
|
||||
cleaned_count += 1
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 清理舊臨時文件失敗: {e}")
|
||||
debug_log(f"清理舊臨時文件失敗: {e}")
|
||||
if cleaned_count > 0:
|
||||
print(f"[DEBUG] 清理了 {cleaned_count} 個舊的臨時文件")
|
||||
debug_log(f"清理了 {cleaned_count} 個舊的臨時文件")
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 臨時文件清理過程出錯: {e}")
|
||||
debug_log(f"臨時文件清理過程出錯: {e}")
|
||||
|
||||
|
||||
# ===== 主要回饋介面 =====
|
||||
@ -886,11 +908,11 @@ class FeedbackWindow(QMainWindow):
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
temp_files_cleaned += 1
|
||||
print(f"[DEBUG] 關閉時清理臨時文件: {file_path}")
|
||||
debug_log(f"關閉時清理臨時文件: {file_path}")
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 關閉時清理臨時文件失敗: {e}")
|
||||
debug_log(f"關閉時清理臨時文件失敗: {e}")
|
||||
if temp_files_cleaned > 0:
|
||||
print(f"[DEBUG] 視窗關閉時清理了 {temp_files_cleaned} 個臨時文件")
|
||||
debug_log(f"視窗關閉時清理了 {temp_files_cleaned} 個臨時文件")
|
||||
|
||||
event.accept()
|
||||
|
||||
@ -933,6 +955,6 @@ if __name__ == "__main__":
|
||||
# 測試用的主程式
|
||||
result = feedback_ui(".", "測試摘要")
|
||||
if result:
|
||||
print("收到回饋:", result)
|
||||
debug_log(f"收到回饋: {result}")
|
||||
else:
|
||||
print("用戶取消了回饋")
|
||||
debug_log("用戶取消了回饋")
|
@ -26,6 +26,59 @@ from mcp.server.fastmcp.utilities.types import Image as MCPImage
|
||||
from mcp.types import TextContent
|
||||
from pydantic import Field
|
||||
|
||||
# ===== 編碼初始化 =====
|
||||
def init_encoding():
|
||||
"""初始化編碼設置,確保正確處理中文字符"""
|
||||
try:
|
||||
# Windows 特殊處理
|
||||
if sys.platform == 'win32':
|
||||
import msvcrt
|
||||
# 設置為二進制模式
|
||||
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||
|
||||
# 重新包裝為 UTF-8 文本流,並禁用緩衝
|
||||
sys.stdin = io.TextIOWrapper(
|
||||
sys.stdin.detach(),
|
||||
encoding='utf-8',
|
||||
errors='replace',
|
||||
newline=None
|
||||
)
|
||||
sys.stdout = io.TextIOWrapper(
|
||||
sys.stdout.detach(),
|
||||
encoding='utf-8',
|
||||
errors='replace',
|
||||
newline='',
|
||||
write_through=True # 關鍵:禁用寫入緩衝
|
||||
)
|
||||
else:
|
||||
# 非 Windows 系統的標準設置
|
||||
if hasattr(sys.stdout, 'reconfigure'):
|
||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
if hasattr(sys.stdin, 'reconfigure'):
|
||||
sys.stdin.reconfigure(encoding='utf-8', errors='replace')
|
||||
|
||||
# 設置 stderr 編碼(用於調試訊息)
|
||||
if hasattr(sys.stderr, 'reconfigure'):
|
||||
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
# 如果編碼設置失敗,嘗試基本設置
|
||||
try:
|
||||
if hasattr(sys.stdout, 'reconfigure'):
|
||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
if hasattr(sys.stdin, 'reconfigure'):
|
||||
sys.stdin.reconfigure(encoding='utf-8', errors='replace')
|
||||
if hasattr(sys.stderr, 'reconfigure'):
|
||||
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
# 初始化編碼(在導入時就執行)
|
||||
_encoding_initialized = init_encoding()
|
||||
|
||||
# ===== 常數定義 =====
|
||||
SERVER_NAME = "互動式回饋收集 MCP"
|
||||
SSH_ENV_VARS = ['SSH_CONNECTION', 'SSH_CLIENT', 'SSH_TTY']
|
||||
@ -39,7 +92,25 @@ mcp = FastMCP(SERVER_NAME, version=__version__)
|
||||
# ===== 工具函數 =====
|
||||
def debug_log(message: str) -> None:
|
||||
"""輸出調試訊息到標準錯誤,避免污染標準輸出"""
|
||||
print(f"[DEBUG] {message}", file=sys.stderr)
|
||||
# 只在啟用調試模式時才輸出,避免干擾 MCP 通信
|
||||
if not os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on"):
|
||||
return
|
||||
|
||||
try:
|
||||
# 確保消息是字符串類型
|
||||
if not isinstance(message, str):
|
||||
message = str(message)
|
||||
|
||||
# 安全地輸出到 stderr,處理編碼問題
|
||||
try:
|
||||
print(f"[DEBUG] {message}", file=sys.stderr, flush=True)
|
||||
except UnicodeEncodeError:
|
||||
# 如果遇到編碼問題,使用 ASCII 安全模式
|
||||
safe_message = message.encode('ascii', errors='replace').decode('ascii')
|
||||
print(f"[DEBUG] {safe_message}", file=sys.stderr, flush=True)
|
||||
except Exception:
|
||||
# 最後的備用方案:靜默失敗,不影響主程序
|
||||
pass
|
||||
|
||||
|
||||
def is_remote_environment() -> bool:
|
||||
@ -331,6 +402,10 @@ async def interactive_feedback(
|
||||
3. 上傳圖片作為回饋
|
||||
4. 查看 AI 的工作摘要
|
||||
|
||||
調試模式:
|
||||
- 設置環境變數 MCP_DEBUG=true 可啟用詳細調試輸出
|
||||
- 生產環境建議關閉調試模式以避免輸出干擾
|
||||
|
||||
Args:
|
||||
project_directory: 專案目錄路徑
|
||||
summary: AI 工作完成的摘要說明
|
||||
@ -453,10 +528,11 @@ async def _run_web_ui_session(project_dir: str, summary: str, timeout: int) -> d
|
||||
session_url = f"http://{manager.host}:{manager.port}/session/{session_id}"
|
||||
|
||||
debug_log(f"Web UI 已啟動: {session_url}")
|
||||
try:
|
||||
print(f"Web UI 已啟動: {session_url}")
|
||||
except UnicodeEncodeError:
|
||||
print(f"Web UI launched: {session_url}")
|
||||
# 注意:不能使用 print() 污染 stdout,會破壞 MCP 通信
|
||||
# try:
|
||||
# print(f"Web UI 已啟動: {session_url}")
|
||||
# except UnicodeEncodeError:
|
||||
# print(f"Web UI launched: {session_url}")
|
||||
|
||||
# 開啟瀏覽器
|
||||
manager.open_browser(session_url)
|
||||
@ -474,10 +550,11 @@ async def _run_web_ui_session(project_dir: str, summary: str, timeout: int) -> d
|
||||
except TimeoutError:
|
||||
timeout_msg = f"等待用戶回饋超時({timeout} 秒)"
|
||||
debug_log(f"⏰ {timeout_msg}")
|
||||
try:
|
||||
print(f"等待用戶回饋超時({timeout} 秒)")
|
||||
except UnicodeEncodeError:
|
||||
print(f"Feedback timeout ({timeout} seconds)")
|
||||
# 注意:不能使用 print() 污染 stdout,會破壞 MCP 通信
|
||||
# try:
|
||||
# print(f"等待用戶回饋超時({timeout} 秒)")
|
||||
# except UnicodeEncodeError:
|
||||
# print(f"Feedback timeout ({timeout} seconds)")
|
||||
return {
|
||||
"logs": "",
|
||||
"interactive_feedback": f"回饋超時({timeout} 秒)",
|
||||
@ -486,10 +563,11 @@ async def _run_web_ui_session(project_dir: str, summary: str, timeout: int) -> d
|
||||
except Exception as e:
|
||||
error_msg = f"Web UI 錯誤: {e}"
|
||||
debug_log(f"❌ {error_msg}")
|
||||
try:
|
||||
print(f"Web UI 錯誤: {e}")
|
||||
except UnicodeEncodeError:
|
||||
print(f"Web UI error: {e}")
|
||||
# 注意:不能使用 print() 污染 stdout,會破壞 MCP 通信
|
||||
# try:
|
||||
# print(f"Web UI 錯誤: {e}")
|
||||
# except UnicodeEncodeError:
|
||||
# print(f"Web UI error: {e}")
|
||||
return {
|
||||
"logs": "",
|
||||
"interactive_feedback": f"錯誤: {str(e)}",
|
||||
@ -532,60 +610,34 @@ def get_system_info() -> str:
|
||||
# ===== 主程式入口 =====
|
||||
def main():
|
||||
"""主要入口點,用於套件執行"""
|
||||
debug_log("🚀 啟動互動式回饋收集 MCP 服務器")
|
||||
debug_log(f" 遠端環境: {is_remote_environment()}")
|
||||
debug_log(f" GUI 可用: {can_use_gui()}")
|
||||
debug_log(f" 建議介面: {'Web UI' if is_remote_environment() or not can_use_gui() else 'Qt GUI'}")
|
||||
debug_log(" 等待來自 AI 助手的調用...")
|
||||
# 檢查是否啟用調試模式
|
||||
debug_enabled = os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on")
|
||||
|
||||
# Windows 特殊處理:設置 stdio 為二進制模式,避免編碼問題
|
||||
if sys.platform == 'win32':
|
||||
debug_log("偵測到 Windows 環境,設置 stdio 二進制模式")
|
||||
try:
|
||||
# 設置 stdin/stdout 為二進制模式,避免 Windows 下的編碼問題
|
||||
import msvcrt
|
||||
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||
debug_log("Windows stdio 二進制模式設置成功")
|
||||
|
||||
# 重新包裝 stdin/stdout 為 UTF-8 編碼的文本流
|
||||
sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding='utf-8', errors='replace')
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='utf-8', errors='replace', newline='')
|
||||
debug_log("Windows stdio UTF-8 包裝設置成功")
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"Windows stdio 設置失敗,使用預設模式: {e}")
|
||||
else:
|
||||
# 非 Windows 系統:確保使用 UTF-8 編碼
|
||||
try:
|
||||
if hasattr(sys.stdin, 'reconfigure'):
|
||||
sys.stdin.reconfigure(encoding='utf-8', errors='replace')
|
||||
if hasattr(sys.stdout, 'reconfigure'):
|
||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
debug_log("非 Windows 系統 UTF-8 編碼設置成功")
|
||||
except Exception as e:
|
||||
debug_log(f"UTF-8 編碼設置失敗: {e}")
|
||||
|
||||
# 確保 stderr 使用 UTF-8 編碼(用於 debug 訊息)
|
||||
if hasattr(sys.stderr, 'reconfigure'):
|
||||
try:
|
||||
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
||||
debug_log("stderr UTF-8 編碼設置成功")
|
||||
except Exception as e:
|
||||
debug_log(f"stderr 編碼設置失敗: {e}")
|
||||
|
||||
# 強制 stdout 立即刷新,確保 JSON-RPC 消息及時發送
|
||||
sys.stdout.reconfigure(line_buffering=True) if hasattr(sys.stdout, 'reconfigure') else None
|
||||
if debug_enabled:
|
||||
debug_log("🚀 啟動互動式回饋收集 MCP 服務器")
|
||||
debug_log(f" 服務器名稱: {SERVER_NAME}")
|
||||
debug_log(f" 版本: {__version__}")
|
||||
debug_log(f" 平台: {sys.platform}")
|
||||
debug_log(f" 編碼初始化: {'成功' if _encoding_initialized else '失敗'}")
|
||||
debug_log(f" 遠端環境: {is_remote_environment()}")
|
||||
debug_log(f" GUI 可用: {can_use_gui()}")
|
||||
debug_log(f" 建議介面: {'Web UI' if is_remote_environment() or not can_use_gui() else 'Qt GUI'}")
|
||||
debug_log(" 等待來自 AI 助手的調用...")
|
||||
debug_log("準備啟動 MCP 伺服器...")
|
||||
debug_log("調用 mcp.run()...")
|
||||
|
||||
try:
|
||||
# 使用正確的 FastMCP API
|
||||
mcp.run()
|
||||
except KeyboardInterrupt:
|
||||
debug_log("收到中斷信號,正常退出")
|
||||
if debug_enabled:
|
||||
debug_log("收到中斷信號,正常退出")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
debug_log(f"MCP 服務器啟動失敗: {e}")
|
||||
import traceback
|
||||
debug_log(f"詳細錯誤: {traceback.format_exc()}")
|
||||
if debug_enabled:
|
||||
debug_log(f"MCP 服務器啟動失敗: {e}")
|
||||
import traceback
|
||||
debug_log(f"詳細錯誤: {traceback.format_exc()}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
@ -120,7 +120,7 @@ class WebFeedbackSession:
|
||||
|
||||
# 檢查文件大小
|
||||
if img["size"] > MAX_IMAGE_SIZE:
|
||||
print(f"[DEBUG] 圖片 {img['name']} 超過大小限制,跳過")
|
||||
debug_log(f"圖片 {img['name']} 超過大小限制,跳過")
|
||||
continue
|
||||
|
||||
# 解碼 base64 數據
|
||||
@ -128,13 +128,13 @@ class WebFeedbackSession:
|
||||
try:
|
||||
image_bytes = base64.b64decode(img["data"])
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 圖片 {img['name']} base64 解碼失敗: {e}")
|
||||
debug_log(f"圖片 {img['name']} base64 解碼失敗: {e}")
|
||||
continue
|
||||
else:
|
||||
image_bytes = img["data"]
|
||||
|
||||
if len(image_bytes) == 0:
|
||||
print(f"[DEBUG] 圖片 {img['name']} 數據為空,跳過")
|
||||
debug_log(f"圖片 {img['name']} 數據為空,跳過")
|
||||
continue
|
||||
|
||||
processed_images.append({
|
||||
@ -143,10 +143,10 @@ class WebFeedbackSession:
|
||||
"size": len(image_bytes)
|
||||
})
|
||||
|
||||
print(f"[DEBUG] 圖片 {img['name']} 處理成功,大小: {len(image_bytes)} bytes")
|
||||
debug_log(f"圖片 {img['name']} 處理成功,大小: {len(image_bytes)} bytes")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] 圖片處理錯誤: {e}")
|
||||
debug_log(f"圖片處理錯誤: {e}")
|
||||
continue
|
||||
|
||||
return processed_images
|
||||
@ -207,7 +207,7 @@ class WebFeedbackSession:
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"命令執行錯誤: {e}")
|
||||
debug_log(f"命令執行錯誤: {e}")
|
||||
finally:
|
||||
self.process = None
|
||||
|
||||
@ -293,9 +293,9 @@ class WebUIManager:
|
||||
await self.handle_websocket_message(session, data)
|
||||
|
||||
except WebSocketDisconnect:
|
||||
print(f"WebSocket 斷開連接: {session_id}")
|
||||
debug_log(f"WebSocket 斷開連接: {session_id}")
|
||||
except Exception as e:
|
||||
print(f"WebSocket 錯誤: {e}")
|
||||
debug_log(f"WebSocket 錯誤: {e}")
|
||||
finally:
|
||||
session.websocket = None
|
||||
|
||||
@ -364,7 +364,7 @@ class WebUIManager:
|
||||
try:
|
||||
webbrowser.open(url)
|
||||
except Exception as e:
|
||||
print(f"無法開啟瀏覽器: {e}")
|
||||
debug_log(f"無法開啟瀏覽器: {e}")
|
||||
|
||||
def _get_simple_index_html(self) -> str:
|
||||
"""簡單的首頁 HTML"""
|
||||
@ -446,7 +446,7 @@ async def launch_web_feedback_ui(project_directory: str, summary: str) -> dict:
|
||||
session_id = manager.create_session(project_directory, summary)
|
||||
session_url = f"http://{manager.host}:{manager.port}/session/{session_id}"
|
||||
|
||||
print(f"🌐 Web UI 已啟動: {session_url}")
|
||||
debug_log(f"🌐 Web UI 已啟動: {session_url}")
|
||||
|
||||
# 開啟瀏覽器
|
||||
manager.open_browser(session_url)
|
||||
@ -461,14 +461,14 @@ async def launch_web_feedback_ui(project_directory: str, summary: str) -> dict:
|
||||
return result
|
||||
|
||||
except TimeoutError:
|
||||
print("⏰ 等待用戶回饋超時")
|
||||
debug_log("⏰ 等待用戶回饋超時")
|
||||
return {
|
||||
"logs": "",
|
||||
"interactive_feedback": "回饋超時",
|
||||
"images": []
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"❌ Web UI 錯誤: {e}")
|
||||
debug_log(f"❌ Web UI 錯誤: {e}")
|
||||
return {
|
||||
"logs": "",
|
||||
"interactive_feedback": f"錯誤: {str(e)}",
|
||||
@ -507,7 +507,7 @@ if __name__ == "__main__":
|
||||
session_id = manager.create_session(args.project_directory, args.summary)
|
||||
session_url = f"http://{args.host}:{args.port}/session/{session_id}"
|
||||
|
||||
print(f"🌐 Web UI 已啟動: {session_url}")
|
||||
debug_log(f"🌐 Web UI 已啟動: {session_url}")
|
||||
manager.open_browser(session_url)
|
||||
|
||||
try:
|
||||
@ -515,6 +515,19 @@ if __name__ == "__main__":
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n👋 Web UI 已停止")
|
||||
debug_log("\n👋 Web UI 已停止")
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
# === 工具函數 ===
|
||||
def debug_log(message: str) -> None:
|
||||
"""輸出調試訊息到標準錯誤,避免污染標準輸出"""
|
||||
# 只在啟用調試模式時才輸出,避免干擾 MCP 通信
|
||||
if not os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on"):
|
||||
return
|
||||
|
||||
try:
|
||||
print(f"[WEB_UI] {message}", file=sys.stderr, flush=True)
|
||||
except Exception:
|
||||
# 靜默失敗,不影響主程序
|
||||
pass
|
Loading…
x
Reference in New Issue
Block a user