Skip to main content
Glama
interaction.py15.5 kB
""" 用户交互工具模块 提供交互式配置、错误处理、用户提示等体验优化功能 """ import sys import traceback from typing import Optional, Dict, Any, List, Union, Callable from dataclasses import dataclass from enum import Enum import json import re class InteractionLevel(Enum): """交互级别""" SILENT = "silent" # 静默模式,不输出任何信息 BASIC = "basic" # 基本信息,只显示关键结果 NORMAL = "normal" # 正常模式,显示进度和基本信息 VERBOSE = "verbose" # 详细模式,显示所有信息 DEBUG = "debug" # 调试模式,显示详细信息 class MessageType(Enum): """消息类型""" INFO = "info" SUCCESS = "success" WARNING = "warning" ERROR = "error" DEBUG = "debug" @dataclass class UserMessage: """用户消息""" type: MessageType title: str content: str details: Optional[Dict[str, Any]] = None timestamp: Optional[float] = None def __str__(self) -> str: """格式化消息输出""" icons = { MessageType.INFO: "ℹ️", MessageType.SUCCESS: "✅", MessageType.WARNING: "⚠️", MessageType.ERROR: "❌", MessageType.DEBUG: "🐛" } icon = icons.get(self.type, "📝") result = f"{icon} {self.title}" if self.content: result += f": {self.content}" return result class InteractionManager: """交互管理器""" def __init__(self, level: InteractionLevel = InteractionLevel.NORMAL): self.level = level self.message_history: List[UserMessage] = [] self.error_handlers: Dict[type, Callable] = {} self.confirmation_handlers: Dict[str, Callable] = {} def set_level(self, level: InteractionLevel) -> None: """设置交互级别""" self.level = level def should_show(self, message_type: MessageType) -> bool: """判断是否应该显示某类型的消息""" level_map = { InteractionLevel.SILENT: [], InteractionLevel.BASIC: [MessageType.ERROR, MessageType.SUCCESS], InteractionLevel.NORMAL: [MessageType.ERROR, MessageType.WARNING, MessageType.SUCCESS, MessageType.INFO], InteractionLevel.VERBOSE: [MessageType.ERROR, MessageType.WARNING, MessageType.SUCCESS, MessageType.INFO, MessageType.DEBUG], InteractionLevel.DEBUG: list(MessageType) } return message_type in level_map.get(self.level, []) def message(self, message_type: MessageType, title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送消息""" user_message = UserMessage( type=message_type, title=title, content=content, details=details ) self.message_history.append(user_message) if self.should_show(message_type): print(user_message) # 如果是详细信息且交互级别为DEBUG,打印详细信息 if details and self.level == InteractionLevel.DEBUG: print("详细信息:") for key, value in details.items(): print(f" {key}: {value}") return user_message def info(self, title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送信息消息""" return self.message(MessageType.INFO, title, content, details) def success(self, title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送成功消息""" return self.message(MessageType.SUCCESS, title, content, details) def warning(self, title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送警告消息""" return self.message(MessageType.WARNING, title, content, details) def error(self, title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送错误消息""" return self.message(MessageType.ERROR, title, content, details) def debug(self, title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送调试消息""" return self.message(MessageType.DEBUG, title, content, details) def confirm(self, title: str, content: str = "", default: bool = False) -> bool: """确认对话框""" message = f"❓ {title}" if content: message += f": {content}" suffix = " [Y/n]" if default else " [y/N]" message += suffix while True: try: response = input(f"{message}: ").strip().lower() if not response: return default if response in ['y', 'yes', '是', '确定']: return True elif response in ['n', 'no', '否', '取消']: return False else: print("请输入 y/yes/是/确定 或 n/no/否/取消") except (EOFError, KeyboardInterrupt): print("\n操作已取消") return False def select(self, title: str, options: List[Union[str, Dict[str, Any]]], default_index: int = 0) -> Any: """选择对话框""" message = f"🔘 {title}" # 格式化选项 formatted_options = [] for i, option in enumerate(options): if isinstance(option, str): formatted_options.append({"value": option, "label": option, "index": i}) elif isinstance(option, dict) and "label" in option: formatted_options.append({"value": option.get("value", option["label"]), "label": option["label"], "index": i}) else: formatted_options.append({"value": str(option), "label": str(option), "index": i}) # 显示选项 print(f"{message}:") for option in formatted_options: marker = "→" if option["index"] == default_index else " " print(f" {marker} {option['index'] + 1}. {option['label']}") while True: try: response = input(f"请选择 (1-{len(formatted_options)}) [默认: {default_index + 1}]: ").strip() if not response: selected_index = default_index else: try: selected_index = int(response) - 1 except ValueError: print("请输入有效的数字") continue if 0 <= selected_index < len(formatted_options): return formatted_options[selected_index]["value"] else: print(f"请输入 1-{len(formatted_options)} 之间的数字") except (EOFError, KeyboardInterrupt): print("\n操作已取消,使用默认选项") return formatted_options[default_index]["value"] def input_text(self, title: str, default: str = "", validator: Callable[[str], bool] = None, error_message: str = "输入无效") -> str: """文本输入对话框""" message = f"✏️ {title}" if default: message += f" [默认: {default}]" while True: try: response = input(f"{message}: ").strip() if not response and default: return default if validator is None or validator(response): return response else: print(error_message) except (EOFError, KeyboardInterrupt): print("\n操作已取消") return default def register_error_handler(self, exception_type: type, handler: Callable) -> None: """注册错误处理器""" self.error_handlers[exception_type] = handler def handle_exception(self, exception: Exception, context: str = "") -> Optional[Any]: """处理异常""" exception_type = type(exception) # 调用注册的错误处理器 if exception_type in self.error_handlers: try: return self.error_handlers[exception_type](exception) except (RuntimeError, ValueError) as e: self.error("错误处理器失败", f"处理 {exception_type.__name__} 时出错: {e}") # 默认错误处理 error_details = { "exception_type": exception_type.__name__, "context": context, "traceback": traceback.format_exc() if self.level == InteractionLevel.DEBUG else None } self.error("发生错误", str(exception), error_details) return None def get_history(self, message_type: Optional[MessageType] = None, limit: int = 100) -> List[UserMessage]: """获取消息历史""" history = self.message_history if message_type: history = [msg for msg in history if msg.type == message_type] return history[-limit:] def clear_history(self) -> None: """清空消息历史""" self.message_history.clear() def export_history(self, filepath: str) -> None: """导出消息历史到JSON文件""" history_data = [] for msg in self.message_history: history_data.append({ "type": msg.type.value, "title": msg.title, "content": msg.content, "details": msg.details, "timestamp": msg.timestamp }) with open(filepath, 'w', encoding='utf-8') as f: json.dump(history_data, f, ensure_ascii=False, indent=2) class ConfigurableInteraction(InteractionManager): """可配置的交互管理器""" def __init__(self, config: Dict[str, Any] = None): default_config = { "level": InteractionLevel.NORMAL, "auto_confirm": False, "auto_select_default": False, "timeout": None, "retry_count": 3, "case_sensitive": False } if config: default_config.update(config) super().__init__(default_config["level"]) self.auto_confirm = default_config["auto_confirm"] self.auto_select_default = default_config["auto_select_default"] self.timeout = default_config["timeout"] self.retry_count = default_config["retry_count"] self.case_sensitive = default_config["case_sensitive"] def confirm(self, title: str, content: str = "", default: bool = False) -> bool: """确认对话框(支持自动确认)""" if self.auto_confirm: self.info("自动确认", f"已自动确认: {title}") return True return super().confirm(title, content, default) def select(self, title: str, options: List[Union[str, Dict[str, Any]]], default_index: int = 0) -> Any: """选择对话框(支持自动选择默认项)""" if self.auto_select_default: self.info("自动选择", f"已自动选择默认选项: {options[default_index] if default_index < len(options) else options[0]}") return options[default_index] if default_index < len(options) else options[0] return super().select(title, options, default_index) def validate_input(self, value: str, pattern: str) -> bool: """验证输入模式""" flags = re.IGNORECASE if not self.case_sensitive else 0 return bool(re.match(pattern, value, flags)) def input_text(self, title: str, default: str = "", validator: Callable[[str], bool] = None, error_message: str = "输入无效", pattern: str = None) -> str: """文本输入对话框(支持模式验证)""" if pattern: def pattern_validator(value): return self.validate_input(value, pattern) validator = validator or pattern_validator error_message = error_message or f"输入必须匹配模式: {pattern}" return super().input_text(title, default, validator, error_message) # 全局交互管理器实例 global_interaction = InteractionManager() def set_interaction_level(level: InteractionLevel) -> None: """设置全局交互级别""" global_interaction.set_level(level) def info(title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送信息消息的便捷函数""" return global_interaction.info(title, content, details) def success(title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送成功消息的便捷函数""" return global_interaction.success(title, content, details) def warning(title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送警告消息的便捷函数""" return global_interaction.warning(title, content, details) def error(title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送错误消息的便捷函数""" return global_interaction.error(title, content, details) def debug(title: str, content: str = "", details: Dict[str, Any] = None) -> UserMessage: """发送调试消息的便捷函数""" return global_interaction.debug(title, content, details) def confirm(title: str, content: str = "", default: bool = False) -> bool: """确认对话框的便捷函数""" return global_interaction.confirm(title, content, default) def select(title: str, options: List[Union[str, Dict[str, Any]]], default_index: int = 0) -> Any: """选择对话框的便捷函数""" return global_interaction.select(title, options, default_index) def input_text(title: str, default: str = "", validator: Callable[[str], bool] = None, error_message: str = "输入无效") -> str: """文本输入对话框的便捷函数""" return global_interaction.input_text(title, default, validator, error_message) def handle_exception(exception: Exception, context: str = "") -> Optional[Any]: """处理异常的便捷函数""" return global_interaction.handle_exception(exception, context) class InteractiveError(Exception): """交互式错误""" def __init__(self, title: str, content: str = "", details: Dict[str, Any] = None): self.title = title self.content = content self.details = details or {} super().__init__(f"{title}: {content}") class UserCancelledError(InteractiveError): """用户取消操作错误""" def __init__(self, operation: str = "操作"): super().__init__("用户取消", f"用户取消了{operation}") class ValidationError(InteractiveError): """验证错误""" def __init__(self, field: str, value: str, reason: str = ""): title = f"{field}验证失败" content = f"值 '{value}' 无效" if reason: content += f": {reason}" super().__init__(title, content, {"field": field, "value": value, "reason": reason})

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kscz0000/Zhiwen-Assistant-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server