mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
✨ 新增統一調試日誌模組,重構現有代碼以使用新模組進行調試日誌輸出。更新多個模組以整合調試功能,並在測試模組中添加調試日誌支持。
This commit is contained in:
parent
c717db9e7f
commit
7ccd9f5397
85
src/mcp_feedback_enhanced/debug.py
Normal file
85
src/mcp_feedback_enhanced/debug.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
統一調試日誌模組
|
||||||
|
================
|
||||||
|
|
||||||
|
提供統一的調試日誌功能,確保調試輸出不會干擾 MCP 通信。
|
||||||
|
所有調試輸出都會發送到 stderr,並且只在調試模式啟用時才輸出。
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
```python
|
||||||
|
from .debug import debug_log
|
||||||
|
|
||||||
|
debug_log("這是一條調試信息")
|
||||||
|
```
|
||||||
|
|
||||||
|
環境變數控制:
|
||||||
|
- MCP_DEBUG=true/1/yes/on: 啟用調試模式
|
||||||
|
- MCP_DEBUG=false/0/no/off: 關閉調試模式(默認)
|
||||||
|
|
||||||
|
作者: Minidoracat
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
def debug_log(message: Any, prefix: str = "DEBUG") -> None:
|
||||||
|
"""
|
||||||
|
輸出調試訊息到標準錯誤,避免污染標準輸出
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: 要輸出的調試信息
|
||||||
|
prefix: 調試信息的前綴標識,默認為 "DEBUG"
|
||||||
|
"""
|
||||||
|
# 只在啟用調試模式時才輸出,避免干擾 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"[{prefix}] {message}", file=sys.stderr, flush=True)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
# 如果遇到編碼問題,使用 ASCII 安全模式
|
||||||
|
safe_message = message.encode('ascii', errors='replace').decode('ascii')
|
||||||
|
print(f"[{prefix}] {safe_message}", file=sys.stderr, flush=True)
|
||||||
|
except Exception:
|
||||||
|
# 最後的備用方案:靜默失敗,不影響主程序
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def gui_debug_log(message: Any) -> None:
|
||||||
|
"""GUI 模組專用的調試日誌"""
|
||||||
|
debug_log(message, "GUI")
|
||||||
|
|
||||||
|
|
||||||
|
def i18n_debug_log(message: Any) -> None:
|
||||||
|
"""國際化模組專用的調試日誌"""
|
||||||
|
debug_log(message, "I18N")
|
||||||
|
|
||||||
|
|
||||||
|
def server_debug_log(message: Any) -> None:
|
||||||
|
"""伺服器模組專用的調試日誌"""
|
||||||
|
debug_log(message, "SERVER")
|
||||||
|
|
||||||
|
|
||||||
|
def web_debug_log(message: Any) -> None:
|
||||||
|
"""Web UI 模組專用的調試日誌"""
|
||||||
|
debug_log(message, "WEB")
|
||||||
|
|
||||||
|
|
||||||
|
def is_debug_enabled() -> bool:
|
||||||
|
"""檢查是否啟用了調試模式"""
|
||||||
|
return os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on")
|
||||||
|
|
||||||
|
|
||||||
|
def set_debug_mode(enabled: bool) -> None:
|
||||||
|
"""設置調試模式(用於測試)"""
|
||||||
|
os.environ["MCP_DEBUG"] = "true" if enabled else "false"
|
@ -35,28 +35,7 @@ from PySide6.QtGui import QFont, QPixmap, QDragEnterEvent, QDropEvent, QKeySeque
|
|||||||
# 導入多語系支援
|
# 導入多語系支援
|
||||||
from .i18n import t, get_i18n_manager
|
from .i18n import t, get_i18n_manager
|
||||||
|
|
||||||
# ===== 調試日誌函數 =====
|
from .debug import gui_debug_log as debug_log
|
||||||
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):
|
class FeedbackResult(TypedDict):
|
||||||
@ -679,6 +658,9 @@ class FeedbackWindow(QMainWindow):
|
|||||||
|
|
||||||
# 操作按鈕
|
# 操作按鈕
|
||||||
self._create_action_buttons(layout)
|
self._create_action_buttons(layout)
|
||||||
|
|
||||||
|
# 設置快捷鍵
|
||||||
|
self._setup_shortcuts()
|
||||||
|
|
||||||
def _create_menu_bar(self) -> None:
|
def _create_menu_bar(self) -> None:
|
||||||
"""創建菜單欄"""
|
"""創建菜單欄"""
|
||||||
@ -943,6 +925,22 @@ class FeedbackWindow(QMainWindow):
|
|||||||
|
|
||||||
layout.addLayout(button_layout)
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
def _setup_shortcuts(self) -> None:
|
||||||
|
"""設置快捷鍵"""
|
||||||
|
# Ctrl+Enter 提交回饋
|
||||||
|
submit_shortcut = QShortcut(QKeySequence("Ctrl+Return"), self)
|
||||||
|
submit_shortcut.activated.connect(self._submit_feedback)
|
||||||
|
|
||||||
|
# Escape 取消
|
||||||
|
cancel_shortcut = QShortcut(QKeySequence("Esc"), self)
|
||||||
|
cancel_shortcut.activated.connect(self._cancel_feedback)
|
||||||
|
|
||||||
|
# 更新提交按鈕的提示文字,顯示快捷鍵
|
||||||
|
if hasattr(self, 'submit_button'):
|
||||||
|
self.submit_button.setToolTip(f"{t('btn_submit_feedback')} (Ctrl+Enter)")
|
||||||
|
if hasattr(self, 'cancel_button'):
|
||||||
|
self.cancel_button.setToolTip(f"{t('btn_cancel')} (Esc)")
|
||||||
|
|
||||||
def _apply_dark_style(self) -> None:
|
def _apply_dark_style(self) -> None:
|
||||||
"""應用深色主題"""
|
"""應用深色主題"""
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
|
@ -23,6 +23,8 @@ import json
|
|||||||
from typing import Dict, Any, Optional, Union
|
from typing import Dict, Any, Optional, Union
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .debug import i18n_debug_log as debug_log
|
||||||
|
|
||||||
|
|
||||||
class I18nManager:
|
class I18nManager:
|
||||||
"""國際化管理器 - 新架構版本"""
|
"""國際化管理器 - 新架構版本"""
|
||||||
@ -60,13 +62,13 @@ class I18nManager:
|
|||||||
with open(translation_file, 'r', encoding='utf-8') as f:
|
with open(translation_file, 'r', encoding='utf-8') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
self._translations[lang_code] = data
|
self._translations[lang_code] = data
|
||||||
print(f"[I18N] 成功載入語言 {lang_code}: {data.get('meta', {}).get('displayName', lang_code)}")
|
debug_log(f"成功載入語言 {lang_code}: {data.get('meta', {}).get('displayName', lang_code)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[I18N] 載入語言檔案失敗 {lang_code}: {e}")
|
debug_log(f"載入語言檔案失敗 {lang_code}: {e}")
|
||||||
# 如果載入失敗,使用空的翻譯
|
# 如果載入失敗,使用空的翻譯
|
||||||
self._translations[lang_code] = {}
|
self._translations[lang_code] = {}
|
||||||
else:
|
else:
|
||||||
print(f"[I18N] 找不到語言檔案: {translation_file}")
|
debug_log(f"找不到語言檔案: {translation_file}")
|
||||||
self._translations[lang_code] = {}
|
self._translations[lang_code] = {}
|
||||||
|
|
||||||
def _detect_language(self) -> str:
|
def _detect_language(self) -> str:
|
||||||
@ -312,10 +314,10 @@ class I18nManager:
|
|||||||
if language_code not in self._supported_languages:
|
if language_code not in self._supported_languages:
|
||||||
self._supported_languages.append(language_code)
|
self._supported_languages.append(language_code)
|
||||||
|
|
||||||
print(f"[I18N] 成功添加語言 {language_code}: {data.get('meta', {}).get('displayName', language_code)}")
|
debug_log(f"成功添加語言 {language_code}: {data.get('meta', {}).get('displayName', language_code)}")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[I18N] 添加語言失敗 {language_code}: {e}")
|
debug_log(f"添加語言失敗 {language_code}: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
互動式回饋收集 MCP 服務器
|
MCP 伺服器主程式
|
||||||
============================
|
================
|
||||||
|
|
||||||
這是一個基於 Model Context Protocol (MCP) 的服務器,提供互動式用戶回饋收集功能。
|
Interactive Feedback MCP 的核心伺服器程式,提供用戶互動回饋功能。
|
||||||
支援文字回饋、圖片上傳,並自動偵測運行環境選擇適當的用戶介面。
|
支援智能環境檢測,自動選擇 Qt GUI 或 Web UI 介面。
|
||||||
|
|
||||||
作者: Fábio Ferreira
|
主要功能:
|
||||||
靈感來源: dotcursorrules.com
|
- 環境檢測(本地/遠端)
|
||||||
增強功能: 圖片支援和環境偵測
|
- 介面選擇(GUI/Web UI)
|
||||||
|
- 圖片處理和 MCP 整合
|
||||||
|
- 回饋結果標準化
|
||||||
|
|
||||||
|
作者: Fábio Ferreira (原作者)
|
||||||
|
增強: Minidoracat (Web UI, 圖片支援, 環境檢測)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@ -26,6 +31,12 @@ from mcp.server.fastmcp.utilities.types import Image as MCPImage
|
|||||||
from mcp.types import TextContent
|
from mcp.types import TextContent
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
|
# 導入多語系支援
|
||||||
|
from .i18n import get_i18n_manager
|
||||||
|
|
||||||
|
# 導入統一的調試功能
|
||||||
|
from .debug import server_debug_log as debug_log
|
||||||
|
|
||||||
# ===== 編碼初始化 =====
|
# ===== 編碼初始化 =====
|
||||||
def init_encoding():
|
def init_encoding():
|
||||||
"""初始化編碼設置,確保正確處理中文字符"""
|
"""初始化編碼設置,確保正確處理中文字符"""
|
||||||
@ -90,29 +101,6 @@ mcp = FastMCP(SERVER_NAME, version=__version__)
|
|||||||
|
|
||||||
|
|
||||||
# ===== 工具函數 =====
|
# ===== 工具函數 =====
|
||||||
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"[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:
|
def is_remote_environment() -> bool:
|
||||||
"""
|
"""
|
||||||
檢測是否在遠端環境中運行
|
檢測是否在遠端環境中運行
|
||||||
|
@ -1,17 +1,45 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Test script for Qt GUI functionality
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
"""
|
||||||
import sys
|
Qt GUI 測試模組
|
||||||
from pathlib import Path
|
===============
|
||||||
|
|
||||||
# 添加項目路徑到 Python 路徑
|
用於測試 Interactive Feedback MCP 的 Qt GUI 功能。
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
包含完整的 GUI 功能測試,支援持久化模式。
|
||||||
|
|
||||||
|
功能測試:
|
||||||
|
- Qt GUI 界面啟動
|
||||||
|
- 多語言支援
|
||||||
|
- 圖片上傳功能
|
||||||
|
- 回饋提交功能
|
||||||
|
- 快捷鍵功能
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
python -m mcp_feedback_enhanced.test_qt_gui [--persistent]
|
||||||
|
|
||||||
|
作者: Minidoracat
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
|
# 添加專案根目錄到 Python 路徑
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||||
|
|
||||||
|
from .debug import debug_log
|
||||||
|
|
||||||
|
# 嘗試導入 Qt GUI 模組
|
||||||
|
try:
|
||||||
|
from .feedback_ui import feedback_ui
|
||||||
|
QT_GUI_AVAILABLE = True
|
||||||
|
except ImportError as e:
|
||||||
|
debug_log(f"⚠️ 無法導入 Qt GUI 模組: {e}")
|
||||||
|
QT_GUI_AVAILABLE = False
|
||||||
|
|
||||||
def test_qt_gui():
|
def test_qt_gui():
|
||||||
"""測試 Qt GUI 功能"""
|
"""測試 Qt GUI 功能"""
|
||||||
try:
|
try:
|
||||||
from .feedback_ui import feedback_ui
|
|
||||||
|
|
||||||
# 測試參數
|
# 測試參數
|
||||||
project_directory = os.getcwd()
|
project_directory = os.getcwd()
|
||||||
prompt = """🎯 圖片預覽和視窗調整測試
|
prompt = """🎯 圖片預覽和視窗調整測試
|
||||||
@ -35,54 +63,54 @@ def test_qt_gui():
|
|||||||
|
|
||||||
請測試這些功能並提供回饋!"""
|
請測試這些功能並提供回饋!"""
|
||||||
|
|
||||||
print("🚀 啟動 Qt GUI 測試...")
|
debug_log("🚀 啟動 Qt GUI 測試...")
|
||||||
print("📝 測試項目:")
|
debug_log("📝 測試項目:")
|
||||||
print(" - 圖片預覽功能")
|
debug_log(" - 圖片預覽功能")
|
||||||
print(" - X刪除按鈕")
|
debug_log(" - X刪除按鈕")
|
||||||
print(" - 視窗大小調整")
|
debug_log(" - 視窗大小調整")
|
||||||
print(" - 分割器調整")
|
debug_log(" - 分割器調整")
|
||||||
print()
|
debug_log()
|
||||||
|
|
||||||
# 啟動 GUI
|
# 啟動 GUI
|
||||||
result = feedback_ui(project_directory, prompt)
|
result = feedback_ui(project_directory, prompt)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
print("\n✅ 測試完成!")
|
debug_log("\n✅ 測試完成!")
|
||||||
print(f"📄 收到回饋: {result.get('interactive_feedback', '無')}")
|
debug_log(f"📄 收到回饋: {result.get('interactive_feedback', '無')}")
|
||||||
if result.get('images'):
|
if result.get('images'):
|
||||||
print(f"🖼️ 收到圖片: {len(result['images'])} 張")
|
debug_log(f"🖼️ 收到圖片: {len(result['images'])} 張")
|
||||||
if result.get('logs'):
|
if result.get('logs'):
|
||||||
print(f"📋 命令日誌: {len(result['logs'])} 行")
|
debug_log(f"📋 命令日誌: {len(result['logs'])} 行")
|
||||||
else:
|
else:
|
||||||
print("\n❌ 測試取消或無回饋")
|
debug_log("\n❌ 測試取消或無回饋")
|
||||||
|
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
print(f"❌ 導入錯誤: {e}")
|
debug_log(f"❌ 導入錯誤: {e}")
|
||||||
print("請確保已安裝 PySide6: pip install PySide6")
|
debug_log("請確保已安裝 PySide6: pip install PySide6")
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 測試錯誤: {e}")
|
debug_log(f"❌ 測試錯誤: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("🧪 Interactive Feedback MCP - Qt GUI 測試")
|
debug_log("🧪 Interactive Feedback MCP - Qt GUI 測試")
|
||||||
print("=" * 50)
|
debug_log("=" * 50)
|
||||||
|
|
||||||
# 檢查環境
|
# 檢查環境
|
||||||
try:
|
try:
|
||||||
from PySide6.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
print("✅ PySide6 已安裝")
|
debug_log("✅ PySide6 已安裝")
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("❌ PySide6 未安裝,請執行: pip install PySide6")
|
debug_log("❌ PySide6 未安裝,請執行: pip install PySide6")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# 運行測試
|
# 運行測試
|
||||||
success = test_qt_gui()
|
success = test_qt_gui()
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
print("\n🎉 測試程序運行完成")
|
debug_log("\n🎉 測試程序運行完成")
|
||||||
else:
|
else:
|
||||||
print("\n💥 測試程序運行失敗")
|
debug_log("\n💥 測試程序運行失敗")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
@ -1,12 +1,44 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Test script for Interactive Feedback MCP Web UI
|
Web UI 測試模組
|
||||||
|
===============
|
||||||
|
|
||||||
|
用於測試 Interactive Feedback MCP 的 Web UI 功能。
|
||||||
|
包含完整的 Web UI 功能測試,支援持久化模式。
|
||||||
|
|
||||||
|
功能測試:
|
||||||
|
- Web UI 服務器啟動
|
||||||
|
- WebSocket 通訊
|
||||||
|
- 回饋提交功能
|
||||||
|
- 圖片上傳功能
|
||||||
|
- 命令執行功能
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
python -m mcp_feedback_enhanced.test_web_ui [--persistent]
|
||||||
|
|
||||||
|
作者: Minidoracat
|
||||||
"""
|
"""
|
||||||
import sys
|
|
||||||
import threading
|
import asyncio
|
||||||
|
import webbrowser
|
||||||
import time
|
import time
|
||||||
import socket
|
import sys
|
||||||
from pathlib import Path
|
import os
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
|
# 添加專案根目錄到 Python 路徑
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||||
|
|
||||||
|
from .debug import debug_log
|
||||||
|
|
||||||
|
# 嘗試導入 Web UI 模組
|
||||||
|
try:
|
||||||
|
from .web_ui import get_web_ui_manager, launch_web_feedback_ui
|
||||||
|
WEB_UI_AVAILABLE = True
|
||||||
|
except ImportError as e:
|
||||||
|
debug_log(f"⚠️ 無法導入 Web UI 模組: {e}")
|
||||||
|
WEB_UI_AVAILABLE = False
|
||||||
|
|
||||||
def find_free_port():
|
def find_free_port():
|
||||||
"""Find a free port to use for testing"""
|
"""Find a free port to use for testing"""
|
||||||
@ -19,44 +51,44 @@ def find_free_port():
|
|||||||
def test_web_ui(keep_running=False):
|
def test_web_ui(keep_running=False):
|
||||||
"""Test the Web UI functionality"""
|
"""Test the Web UI functionality"""
|
||||||
|
|
||||||
print("🧪 測試 Interactive Feedback MCP Web UI")
|
debug_log("🧪 測試 Interactive Feedback MCP Web UI")
|
||||||
print("=" * 50)
|
debug_log("=" * 50)
|
||||||
|
|
||||||
# Test import
|
# Test import
|
||||||
try:
|
try:
|
||||||
from .web_ui import WebUIManager, launch_web_feedback_ui
|
from .web_ui import WebUIManager, launch_web_feedback_ui
|
||||||
print("✅ Web UI 模組匯入成功")
|
debug_log("✅ Web UI 模組匯入成功")
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
print(f"❌ Web UI 模組匯入失敗: {e}")
|
debug_log(f"❌ Web UI 模組匯入失敗: {e}")
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
# Find free port
|
# Find free port
|
||||||
try:
|
try:
|
||||||
free_port = find_free_port()
|
free_port = find_free_port()
|
||||||
print(f"🔍 找到可用端口: {free_port}")
|
debug_log(f"🔍 找到可用端口: {free_port}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 尋找可用端口失敗: {e}")
|
debug_log(f"❌ 尋找可用端口失敗: {e}")
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
# Test manager creation
|
# Test manager creation
|
||||||
try:
|
try:
|
||||||
manager = WebUIManager(port=free_port)
|
manager = WebUIManager(port=free_port)
|
||||||
print("✅ WebUIManager 創建成功")
|
debug_log("✅ WebUIManager 創建成功")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ WebUIManager 創建失敗: {e}")
|
debug_log(f"❌ WebUIManager 創建失敗: {e}")
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
# Test server start (with timeout)
|
# Test server start (with timeout)
|
||||||
server_started = False
|
server_started = False
|
||||||
try:
|
try:
|
||||||
print("🚀 啟動 Web 服務器...")
|
debug_log("🚀 啟動 Web 服務器...")
|
||||||
|
|
||||||
def start_server():
|
def start_server():
|
||||||
try:
|
try:
|
||||||
manager.start_server()
|
manager.start_server()
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"服務器啟動錯誤: {e}")
|
debug_log(f"服務器啟動錯誤: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Start server in thread
|
# Start server in thread
|
||||||
@ -73,17 +105,17 @@ def test_web_ui(keep_running=False):
|
|||||||
result = s.connect_ex((manager.host, manager.port))
|
result = s.connect_ex((manager.host, manager.port))
|
||||||
if result == 0:
|
if result == 0:
|
||||||
server_started = True
|
server_started = True
|
||||||
print("✅ Web 服務器啟動成功")
|
debug_log("✅ Web 服務器啟動成功")
|
||||||
print(f"🌐 服務器運行在: http://{manager.host}:{manager.port}")
|
debug_log(f"🌐 服務器運行在: http://{manager.host}:{manager.port}")
|
||||||
else:
|
else:
|
||||||
print(f"❌ 無法連接到服務器端口 {manager.port}")
|
debug_log(f"❌ 無法連接到服務器端口 {manager.port}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Web 服務器啟動失敗: {e}")
|
debug_log(f"❌ Web 服務器啟動失敗: {e}")
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
if not server_started:
|
if not server_started:
|
||||||
print("❌ 服務器未能正常啟動")
|
debug_log("❌ 服務器未能正常啟動")
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
# Test session creation
|
# Test session creation
|
||||||
@ -97,26 +129,26 @@ def test_web_ui(keep_running=False):
|
|||||||
'session_id': session_id,
|
'session_id': session_id,
|
||||||
'url': f"http://{manager.host}:{manager.port}/session/{session_id}"
|
'url': f"http://{manager.host}:{manager.port}/session/{session_id}"
|
||||||
}
|
}
|
||||||
print(f"✅ 測試會話創建成功 (ID: {session_id[:8]}...)")
|
debug_log(f"✅ 測試會話創建成功 (ID: {session_id[:8]}...)")
|
||||||
print(f"🔗 測試 URL: {session_info['url']}")
|
debug_log(f"🔗 測試 URL: {session_info['url']}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 會話創建失敗: {e}")
|
debug_log(f"❌ 會話創建失敗: {e}")
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
print("\n" + "=" * 50)
|
debug_log("\n" + "=" * 50)
|
||||||
print("🎉 所有測試通過!Web UI 準備就緒")
|
debug_log("🎉 所有測試通過!Web UI 準備就緒")
|
||||||
print("📝 注意事項:")
|
debug_log("📝 注意事項:")
|
||||||
print(" - Web UI 會在 SSH remote 環境下自動啟用")
|
debug_log(" - Web UI 會在 SSH remote 環境下自動啟用")
|
||||||
print(" - 本地環境會繼續使用 Qt GUI")
|
debug_log(" - 本地環境會繼續使用 Qt GUI")
|
||||||
print(" - 支援即時命令執行和 WebSocket 通訊")
|
debug_log(" - 支援即時命令執行和 WebSocket 通訊")
|
||||||
print(" - 提供現代化的深色主題界面")
|
debug_log(" - 提供現代化的深色主題界面")
|
||||||
|
|
||||||
return True, session_info
|
return True, session_info
|
||||||
|
|
||||||
def test_environment_detection():
|
def test_environment_detection():
|
||||||
"""Test environment detection logic"""
|
"""Test environment detection logic"""
|
||||||
print("🔍 測試環境檢測功能")
|
debug_log("🔍 測試環境檢測功能")
|
||||||
print("-" * 30)
|
debug_log("-" * 30)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .server import is_remote_environment, can_use_gui
|
from .server import is_remote_environment, can_use_gui
|
||||||
@ -124,47 +156,47 @@ def test_environment_detection():
|
|||||||
remote_detected = is_remote_environment()
|
remote_detected = is_remote_environment()
|
||||||
gui_available = can_use_gui()
|
gui_available = can_use_gui()
|
||||||
|
|
||||||
print(f"遠端環境檢測: {'是' if remote_detected else '否'}")
|
debug_log(f"遠端環境檢測: {'是' if remote_detected else '否'}")
|
||||||
print(f"GUI 可用性: {'是' if gui_available else '否'}")
|
debug_log(f"GUI 可用性: {'是' if gui_available else '否'}")
|
||||||
|
|
||||||
if remote_detected:
|
if remote_detected:
|
||||||
print("✅ 將使用 Web UI (適合遠端開發環境)")
|
debug_log("✅ 將使用 Web UI (適合遠端開發環境)")
|
||||||
else:
|
else:
|
||||||
print("✅ 將使用 Qt GUI (本地環境)")
|
debug_log("✅ 將使用 Qt GUI (本地環境)")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 環境檢測失敗: {e}")
|
debug_log(f"❌ 環境檢測失敗: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def test_mcp_integration():
|
def test_mcp_integration():
|
||||||
"""Test MCP server integration"""
|
"""Test MCP server integration"""
|
||||||
print("\n🔧 測試 MCP 整合功能")
|
debug_log("\n🔧 測試 MCP 整合功能")
|
||||||
print("-" * 30)
|
debug_log("-" * 30)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .server import interactive_feedback
|
from .server import interactive_feedback
|
||||||
print("✅ MCP 工具函數可用")
|
debug_log("✅ MCP 工具函數可用")
|
||||||
|
|
||||||
# Test timeout parameter
|
# Test timeout parameter
|
||||||
print("✅ 支援 timeout 參數")
|
debug_log("✅ 支援 timeout 參數")
|
||||||
|
|
||||||
# Test force_web_ui parameter
|
# Test force_web_ui parameter
|
||||||
print("✅ 支援 force_web_ui 參數")
|
debug_log("✅ 支援 force_web_ui 參數")
|
||||||
|
|
||||||
# Test would require actual MCP call, so just verify import
|
# Test would require actual MCP call, so just verify import
|
||||||
print("✅ 準備接受來自 AI 助手的調用")
|
debug_log("✅ 準備接受來自 AI 助手的調用")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ MCP 整合測試失敗: {e}")
|
debug_log(f"❌ MCP 整合測試失敗: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def test_new_parameters():
|
def test_new_parameters():
|
||||||
"""Test new timeout and force_web_ui parameters"""
|
"""Test new timeout and force_web_ui parameters"""
|
||||||
print("\n🆕 測試新增參數功能")
|
debug_log("\n🆕 測試新增參數功能")
|
||||||
print("-" * 30)
|
debug_log("-" * 30)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .server import interactive_feedback
|
from .server import interactive_feedback
|
||||||
@ -176,30 +208,30 @@ def test_new_parameters():
|
|||||||
# 檢查 timeout 參數
|
# 檢查 timeout 參數
|
||||||
if 'timeout' in sig.parameters:
|
if 'timeout' in sig.parameters:
|
||||||
timeout_param = sig.parameters['timeout']
|
timeout_param = sig.parameters['timeout']
|
||||||
print(f"✅ timeout 參數存在,預設值: {timeout_param.default}")
|
debug_log(f"✅ timeout 參數存在,預設值: {timeout_param.default}")
|
||||||
else:
|
else:
|
||||||
print("❌ timeout 參數不存在")
|
debug_log("❌ timeout 參數不存在")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 檢查 force_web_ui 參數
|
# 檢查 force_web_ui 參數
|
||||||
if 'force_web_ui' in sig.parameters:
|
if 'force_web_ui' in sig.parameters:
|
||||||
force_web_ui_param = sig.parameters['force_web_ui']
|
force_web_ui_param = sig.parameters['force_web_ui']
|
||||||
print(f"✅ force_web_ui 參數存在,預設值: {force_web_ui_param.default}")
|
debug_log(f"✅ force_web_ui 參數存在,預設值: {force_web_ui_param.default}")
|
||||||
else:
|
else:
|
||||||
print("❌ force_web_ui 參數不存在")
|
debug_log("❌ force_web_ui 參數不存在")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print("✅ 所有新參數功能正常")
|
debug_log("✅ 所有新參數功能正常")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 新參數測試失敗: {e}")
|
debug_log(f"❌ 新參數測試失敗: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def test_force_web_ui_mode():
|
def test_force_web_ui_mode():
|
||||||
"""Test force web UI mode"""
|
"""Test force web UI mode"""
|
||||||
print("\n🌐 測試強制 Web UI 模式")
|
debug_log("\n🌐 測試強制 Web UI 模式")
|
||||||
print("-" * 30)
|
debug_log("-" * 30)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .server import interactive_feedback, is_remote_environment, can_use_gui
|
from .server import interactive_feedback, is_remote_environment, can_use_gui
|
||||||
@ -208,65 +240,65 @@ def test_force_web_ui_mode():
|
|||||||
is_remote = is_remote_environment()
|
is_remote = is_remote_environment()
|
||||||
gui_available = can_use_gui()
|
gui_available = can_use_gui()
|
||||||
|
|
||||||
print(f"當前環境 - 遠端: {is_remote}, GUI 可用: {gui_available}")
|
debug_log(f"當前環境 - 遠端: {is_remote}, GUI 可用: {gui_available}")
|
||||||
|
|
||||||
if not is_remote and gui_available:
|
if not is_remote and gui_available:
|
||||||
print("✅ 在本地 GUI 環境中可以使用 force_web_ui=True 強制使用 Web UI")
|
debug_log("✅ 在本地 GUI 環境中可以使用 force_web_ui=True 強制使用 Web UI")
|
||||||
print("💡 這對於測試 Web UI 功能非常有用")
|
debug_log("💡 這對於測試 Web UI 功能非常有用")
|
||||||
else:
|
else:
|
||||||
print("ℹ️ 當前環境會自動使用 Web UI")
|
debug_log("ℹ️ 當前環境會自動使用 Web UI")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 強制 Web UI 測試失敗: {e}")
|
debug_log(f"❌ 強制 Web UI 測試失敗: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def interactive_demo(session_info):
|
def interactive_demo(session_info):
|
||||||
"""Run interactive demo with the Web UI"""
|
"""Run interactive demo with the Web UI"""
|
||||||
print(f"\n🌐 Web UI 持久化運行模式")
|
debug_log(f"\n🌐 Web UI 持久化運行模式")
|
||||||
print("=" * 50)
|
debug_log("=" * 50)
|
||||||
print(f"服務器地址: http://{session_info['manager'].host}:{session_info['manager'].port}")
|
debug_log(f"服務器地址: http://{session_info['manager'].host}:{session_info['manager'].port}")
|
||||||
print(f"測試會話: {session_info['url']}")
|
debug_log(f"測試會話: {session_info['url']}")
|
||||||
print("\n📖 操作指南:")
|
debug_log("\n📖 操作指南:")
|
||||||
print(" 1. 在瀏覽器中開啟上面的測試 URL")
|
debug_log(" 1. 在瀏覽器中開啟上面的測試 URL")
|
||||||
print(" 2. 嘗試以下功能:")
|
debug_log(" 2. 嘗試以下功能:")
|
||||||
print(" - 點擊 '顯示命令區塊' 按鈕")
|
debug_log(" - 點擊 '顯示命令區塊' 按鈕")
|
||||||
print(" - 輸入命令如 'echo Hello World' 並執行")
|
debug_log(" - 輸入命令如 'echo Hello World' 並執行")
|
||||||
print(" - 在回饋區域輸入文字")
|
debug_log(" - 在回饋區域輸入文字")
|
||||||
print(" - 使用 Ctrl+Enter 提交回饋")
|
debug_log(" - 使用 Ctrl+Enter 提交回饋")
|
||||||
print(" 3. 測試 WebSocket 即時通訊功能")
|
debug_log(" 3. 測試 WebSocket 即時通訊功能")
|
||||||
print("\n⌨️ 控制選項:")
|
debug_log("\n⌨️ 控制選項:")
|
||||||
print(" - 按 Enter 繼續運行")
|
debug_log(" - 按 Enter 繼續運行")
|
||||||
print(" - 輸入 'q' 或 'quit' 停止服務器")
|
debug_log(" - 輸入 'q' 或 'quit' 停止服務器")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
user_input = input("\n>>> ").strip().lower()
|
user_input = input("\n>>> ").strip().lower()
|
||||||
if user_input in ['q', 'quit', 'exit']:
|
if user_input in ['q', 'quit', 'exit']:
|
||||||
print("🛑 停止服務器...")
|
debug_log("🛑 停止服務器...")
|
||||||
break
|
break
|
||||||
elif user_input == '':
|
elif user_input == '':
|
||||||
print(f"🔄 服務器持續運行在: {session_info['url']}")
|
debug_log(f"🔄 服務器持續運行在: {session_info['url']}")
|
||||||
print(" 瀏覽器應該仍可正常訪問")
|
debug_log(" 瀏覽器應該仍可正常訪問")
|
||||||
else:
|
else:
|
||||||
print("❓ 未知命令。按 Enter 繼續運行,或輸入 'q' 退出")
|
debug_log("❓ 未知命令。按 Enter 繼續運行,或輸入 'q' 退出")
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\n🛑 收到中斷信號,停止服務器...")
|
debug_log("\n🛑 收到中斷信號,停止服務器...")
|
||||||
break
|
break
|
||||||
|
|
||||||
print("✅ Web UI 測試完成")
|
debug_log("✅ Web UI 測試完成")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("Interactive Feedback MCP - Web UI 測試")
|
debug_log("Interactive Feedback MCP - Web UI 測試")
|
||||||
print("=" * 60)
|
debug_log("=" * 60)
|
||||||
|
|
||||||
# Check if user wants persistent mode
|
# Check if user wants persistent mode
|
||||||
persistent_mode = len(sys.argv) > 1 and sys.argv[1] in ['--persistent', '-p', '--demo']
|
persistent_mode = len(sys.argv) > 1 and sys.argv[1] in ['--persistent', '-p', '--demo']
|
||||||
|
|
||||||
if not persistent_mode:
|
if not persistent_mode:
|
||||||
print("💡 提示: 使用 'python test_web_ui.py --persistent' 啟動持久化測試模式")
|
debug_log("💡 提示: 使用 'python test_web_ui.py --persistent' 啟動持久化測試模式")
|
||||||
print()
|
debug_log()
|
||||||
|
|
||||||
# Test environment detection
|
# Test environment detection
|
||||||
env_test = test_environment_detection()
|
env_test = test_environment_detection()
|
||||||
@ -283,30 +315,30 @@ if __name__ == "__main__":
|
|||||||
# Test Web UI
|
# Test Web UI
|
||||||
web_test, session_info = test_web_ui()
|
web_test, session_info = test_web_ui()
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
debug_log("\n" + "=" * 60)
|
||||||
if env_test and params_test and force_test and mcp_test and web_test:
|
if env_test and params_test and force_test and mcp_test and web_test:
|
||||||
print("🎊 所有測試完成!準備使用 Interactive Feedback MCP")
|
debug_log("🎊 所有測試完成!準備使用 Interactive Feedback MCP")
|
||||||
print("\n📖 使用方法:")
|
debug_log("\n📖 使用方法:")
|
||||||
print(" 1. 在 Cursor/Cline 中配置此 MCP 服務器")
|
debug_log(" 1. 在 Cursor/Cline 中配置此 MCP 服務器")
|
||||||
print(" 2. AI 助手會自動調用 interactive_feedback 工具")
|
debug_log(" 2. AI 助手會自動調用 interactive_feedback 工具")
|
||||||
print(" 3. 根據環境自動選擇 GUI 或 Web UI")
|
debug_log(" 3. 根據環境自動選擇 GUI 或 Web UI")
|
||||||
print(" 4. 提供回饋後繼續工作流程")
|
debug_log(" 4. 提供回饋後繼續工作流程")
|
||||||
|
|
||||||
print("\n✨ Web UI 新功能:")
|
debug_log("\n✨ Web UI 新功能:")
|
||||||
print(" - 支援 SSH remote 開發環境")
|
debug_log(" - 支援 SSH remote 開發環境")
|
||||||
print(" - 現代化深色主題界面")
|
debug_log(" - 現代化深色主題界面")
|
||||||
print(" - WebSocket 即時通訊")
|
debug_log(" - WebSocket 即時通訊")
|
||||||
print(" - 自動瀏覽器啟動")
|
debug_log(" - 自動瀏覽器啟動")
|
||||||
print(" - 命令執行和即時輸出")
|
debug_log(" - 命令執行和即時輸出")
|
||||||
|
|
||||||
if persistent_mode and session_info:
|
if persistent_mode and session_info:
|
||||||
interactive_demo(session_info)
|
interactive_demo(session_info)
|
||||||
else:
|
else:
|
||||||
print("\n✅ 測試完成 - 系統已準備就緒!")
|
debug_log("\n✅ 測試完成 - 系統已準備就緒!")
|
||||||
if session_info:
|
if session_info:
|
||||||
print(f"💡 您可以現在就在瀏覽器中測試: {session_info['url']}")
|
debug_log(f"💡 您可以現在就在瀏覽器中測試: {session_info['url']}")
|
||||||
print(" (服務器會繼續運行一小段時間)")
|
debug_log(" (服務器會繼續運行一小段時間)")
|
||||||
time.sleep(10) # Keep running for a short time for immediate testing
|
time.sleep(10) # Keep running for a short time for immediate testing
|
||||||
else:
|
else:
|
||||||
print("❌ 部分測試失敗,請檢查錯誤信息")
|
debug_log("❌ 部分測試失敗,請檢查錯誤信息")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
@ -33,6 +33,8 @@ from fastapi.responses import HTMLResponse, JSONResponse
|
|||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
|
from .debug import web_debug_log as debug_log
|
||||||
|
|
||||||
# ===== 常數定義 =====
|
# ===== 常數定義 =====
|
||||||
MAX_IMAGE_SIZE = 1 * 1024 * 1024 # 1MB 圖片大小限制
|
MAX_IMAGE_SIZE = 1 * 1024 * 1024 # 1MB 圖片大小限制
|
||||||
SUPPORTED_IMAGE_TYPES = {'image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp', 'image/webp'}
|
SUPPORTED_IMAGE_TYPES = {'image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp', 'image/webp'}
|
||||||
@ -517,17 +519,4 @@ if __name__ == "__main__":
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
debug_log("\n👋 Web UI 已停止")
|
debug_log("\n👋 Web UI 已停止")
|
||||||
|
|
||||||
asyncio.run(main())
|
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