from typing import Literal, Optional import re from .config_base import ConfigBase, Field """ 须知: 1. 本文件中记录了所有的配置项 2. 所有新增的class都需要继承自ConfigBase 3. 所有新增的class都应在official_configs.py中的Config类中添加字段 4. 对于新增的字段,若为可选项,则应在其后添加Field()并设置default_factory或default 5. 所有的配置项都应该按照如下方法添加字段说明: class ExampleConfig(ConfigBase): example_field: str \"""This is an example field\""" - 注释前面增加_warp_标记可以实现配置文件中注释在配置项前面单独一行显示 """ class BotConfig(ConfigBase): """机器人配置类""" __ui_label__ = "基本信息" __ui_icon__ = "bot" platform: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "wifi", }, ) """平台""" qq_account: int = Field( default=0, json_schema_extra={ "x-widget": "input", "x-icon": "user", }, ) """QQ账号""" platforms: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "layers", }, ) """其他平台""" nickname: str = Field( default="麦麦", json_schema_extra={ "x-widget": "input", "x-icon": "user-circle", }, ) """机器人昵称""" alias_names: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "tags", }, ) """别名列表""" class PersonalityConfig(ConfigBase): """人格配置类""" __ui_label__ = "人格" __ui_icon__ = "user-circle" personality: str = Field( default="是一个大二在读女大学生,现在正在上网和群友聊天,有时有点攻击性,有时比较温柔", json_schema_extra={ "x-widget": "textarea", "x-icon": "user-circle", }, ) """人格,建议100字以内,描述人格特质和身份特征""" reply_style: str = Field( default="请不要刻意突出自身学科背景。可以参考贴吧,知乎和微博的回复风格。", json_schema_extra={ "x-widget": "textarea", "x-icon": "message-square", }, ) """默认表达风格,描述麦麦说话的表达风格,表达习惯,如要修改,可以酌情新增内容,建议1-2行""" multiple_reply_style: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) """可选的多种表达风格列表,当配置不为空时可按概率随机替换 reply_style""" multiple_probability: float = Field( default=0.3, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.1, }, ) """每次构建回复时,从 multiple_reply_style 中随机替换 reply_style 的概率(0.0-1.0)""" states: list[str] = Field( default_factory=lambda: [ "是一个女大学生,喜欢上网聊天,会刷小红书。", "是一个大二心理学生,会刷贴吧和中国知网。", "是一个赛博网友,最近很想吐槽人。", ], json_schema_extra={ "x-widget": "custom", "x-icon": "shuffle", }, ) """_wrap_状态列表,用于随机替换personality""" state_probability: float = Field( default=0.3, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.1, }, ) """状态概率,每次构建人格时替换personality的概率""" class VisualConfig(ConfigBase): """视觉配置类""" __ui_label__ = "视觉" __ui_icon__ = "image" planner_mode: Literal["text", "multimodal", "auto"] = Field( default="auto", json_schema_extra={ "x-widget": "select", "x-icon": "git-branch", }, ) """规划器模式,auto根据模型信息自动选择,text为纯文本模式,multimodal为多模态模式""" replyer_mode: Literal["text", "multimodal", "auto"] = Field( default="auto", json_schema_extra={ "x-widget": "select", "x-icon": "git-branch", }, ) """回复器模式,auto根据模型信息自动选择,text为纯文本模式,multimodal为多模态模式""" visual_style: str = Field( default="请用中文描述这张图片的内容。如果有文字,请把文字描述概括出来,请留意其主题,直观感受,输出为一段平文本,最多30字,请注意不要分点,就输出一段文本", json_schema_extra={ "x-widget": "textarea", "x-icon": "image", }, ) """_wrap_识图提示词,不建议修改""" class TalkRulesItem(ConfigBase): platform: str = "" """平台,与ID一起留空表示全局""" item_id: str = "" """用户ID,与平台一起留空表示全局""" rule_type: Literal["group", "private"] = "group" """聊天流类型,group(群聊)或private(私聊)""" time: str = "" """时间段,格式为 "HH:MM-HH:MM",支持跨夜区间""" value: float = 0.5 """聊天频率值,范围0-1""" class ChatConfig(ConfigBase): """聊天配置类""" __ui_label__ = "聊天" __ui_icon__ = "message-square" talk_value: float = Field( default=1, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "message-circle", "step": 0.1, }, ) """聊天频率,越小越沉默,范围0-1""" mentioned_bot_reply: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "at-sign", }, ) """是否启用提及必回复""" inevitable_at_reply: bool = Field(default=True) """是否启用at必回复""" max_context_size: int = Field( default=30, json_schema_extra={ "x-widget": "input", "x-icon": "layers", }, ) """上下文长度""" planner_interrupt_max_consecutive_count: int = Field( default=2, ge=0, json_schema_extra={ "x-widget": "input", "x-icon": "pause-circle", }, ) """Planner 连续被新消息打断的最大次数,0 表示不启用打断""" group_chat_prompt: str = Field( default=""" 你正在qq群里聊天,下面是群里正在聊的内容,其中包含聊天记录和聊天中的图片。 回复尽量简短一些。最好一次对一个话题进行回复,免得啰嗦或者回复内容太乱。请注意把握聊天内容。 不要回复的太频繁!控制回复的频率,不要每个人的消息都回复,只回复你感兴趣的或者主动提及你的。 """, json_schema_extra={ "x-widget": "textarea", "x-icon": "users", }, ) """_wrap_群聊通用注意事项""" private_chat_prompts: str = Field( default=""" 你正在聊天,下面是正在聊的内容,其中包含聊天记录和聊天中的图片。 回复尽量简短一些。请注意把握聊天内容。 请考虑对方的发言频率,想法,思考自己何时回复以及回复内容。 """, json_schema_extra={ "x-widget": "textarea", "x-icon": "user", }, ) """_wrap_私聊通用注意事项""" chat_prompts: list["ExtraPromptItem"] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) enable_talk_value_rules: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "settings", }, ) """是否启用动态发言频率规则""" talk_value_rules: list[TalkRulesItem] = Field( default_factory=lambda: [ TalkRulesItem(platform="", item_id="", rule_type="group", time="00:00-08:59", value=0.8), TalkRulesItem(platform="", item_id="", rule_type="group", time="09:00-18:59", value=1.0), ], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) """ _wrap_思考频率规则列表,支持按聊天流/按日内时段配置。 """ class MessageReceiveConfig(ConfigBase): """消息接收配置类""" __ui_parent__ = "response_post_process" image_parse_threshold: int = Field( default=5, json_schema_extra={ "x-widget": "input", "x-icon": "image", }, ) """ 当消息中图片数量不超过此阈值时,启用图片解析功能,将图片内容解析为文本后再进行处理。 当消息中图片数量超过此阈值时,为了避免过度解析导致的性能问题,将跳过图片解析,直接进行处理。 """ ban_words: set[str] = Field( default_factory=lambda: set(), json_schema_extra={ "x-widget": "custom", "x-icon": "ban", }, ) """过滤词列表""" ban_msgs_regex: set[str] = Field( default_factory=lambda: set(), json_schema_extra={ "x-widget": "custom", "x-icon": "regex", }, ) """过滤正则表达式列表""" def model_post_init(self, context: Optional[dict] = None) -> None: for pattern in self.ban_msgs_regex: try: re.compile(pattern) except re.error as e: raise ValueError(f"Invalid regex pattern in ban_msgs_regex: '{pattern}'") from e return super().model_post_init(context) class TargetItem(ConfigBase): platform: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "wifi", }, ) """平台,与ID一起留空表示全局""" item_id: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """用户/群ID,与平台一起留空表示全局""" rule_type: Literal["group", "private"] = Field( default="group", json_schema_extra={ "x-widget": "select", "x-icon": "users", }, ) """聊天流类型,group(群聊)或private(私聊)""" class MemoryConfig(ConfigBase): """记忆配置类""" __ui_parent__ = "emoji" global_memory: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "globe", }, ) """是否允许记忆检索在聊天记录中进行全局查询(忽略当前chat_id,仅对 search_chat_history 等工具生效)""" global_memory_blacklist: list[TargetItem] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "shield-off", }, ) """_wrap_全局记忆黑名单,当启用全局记忆时,不将特定聊天流纳入检索""" enable_memory_query_tool: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "database", }, ) """是否启用 Maisaka 内置长期记忆检索工具 query_memory""" memory_query_default_limit: int = Field( default=5, ge=1, le=20, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """Maisaka 内置长期记忆检索工具 query_memory 的默认返回条数""" long_term_auto_summary_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "book-open", }, ) """是否自动启动聊天总结并导入长期记忆""" person_fact_writeback_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "user-round-pen", }, ) """是否在发送回复后自动提取并写回人物事实到长期记忆""" chat_history_topic_check_message_threshold: int = Field( default=80, ge=1, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """聊天历史话题检查的消息数量阈值,当累积消息数达到此值时触发话题检查""" chat_history_topic_check_time_hours: float = Field( default=8.0, json_schema_extra={ "x-widget": "input", "x-icon": "clock", }, ) """聊天历史话题检查的时间阈值(小时),当距离上次检查超过此时间且消息数达到最小阈值时触发话题检查""" chat_history_topic_check_min_messages: int = Field( default=20, ge=1, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """聊天历史话题检查的时间触发模式下的最小消息数阈值""" chat_history_finalize_no_update_checks: int = Field( default=3, ge=1, json_schema_extra={ "x-widget": "input", "x-icon": "check-circle", }, ) """聊天历史话题打包存储的连续无更新检查次数阈值,当话题连续N次检查无新增内容时触发打包存储""" chat_history_finalize_message_count: int = Field( default=5, ge=1, json_schema_extra={ "x-widget": "input", "x-icon": "package", }, ) """聊天历史话题打包存储的消息条数阈值,当话题的消息条数超过此值时触发打包存储""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证配置值""" if self.chat_history_topic_check_message_threshold < 1: raise ValueError( f"chat_history_topic_check_message_threshold 必须至少为1,当前值: {self.chat_history_topic_check_message_threshold}" ) if self.chat_history_topic_check_time_hours <= 0: raise ValueError( f"chat_history_topic_check_time_hours 必须大于0,当前值: {self.chat_history_topic_check_time_hours}" ) if self.chat_history_topic_check_min_messages < 1: raise ValueError( f"chat_history_topic_check_min_messages 必须至少为1,当前值: {self.chat_history_topic_check_min_messages}" ) if self.chat_history_finalize_no_update_checks < 1: raise ValueError( f"chat_history_finalize_no_update_checks 必须至少为1,当前值: {self.chat_history_finalize_no_update_checks}" ) if self.chat_history_finalize_message_count < 1: raise ValueError( f"chat_history_finalize_message_count 必须至少为1,当前值: {self.chat_history_finalize_message_count}" ) return super().model_post_init(context) class LearningItem(ConfigBase): platform: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "wifi", }, ) """平台,与ID一起留空表示全局""" item_id: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """用户ID,与平台一起留空表示全局""" rule_type: Literal["group", "private"] = Field( default="group", json_schema_extra={ "x-widget": "select", "x-icon": "users", }, ) """聊天流类型,group(群聊)或private(私聊)""" use_expression: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "message-square", }, ) """是否启用表达学习""" enable_learning: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "graduation-cap", }, ) """是否启用表达优化学习""" enable_jargon_learning: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "book", }, ) """是否启用jargon学习""" advanced_chosen: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "sparkles", }, ) """是否启用基于子代理的二次表达方式选择""" class ExpressionGroup(ConfigBase): """表达互通组配置类,若列表为空代表全局共享""" expression_groups: list[TargetItem] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "users", }, ) """_wrap_表达学习互通组""" class ExpressionConfig(ConfigBase): """表达配置类""" __ui_label__ = "表达" __ui_icon__ = "pen-tool" learning_list: list[LearningItem] = Field( default_factory=lambda: [ LearningItem( platform="", item_id="", rule_type="group", use_expression=True, enable_learning=True, enable_jargon_learning=True, advanced_chosen=False, ) ], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) """_wrap_表达学习配置列表,支持按聊天流配置""" expression_groups: list[ExpressionGroup] = Field( default_factory=list, json_schema_extra={ "x-widget": "custom", "x-icon": "users", }, ) """_wrap_表达学习互通组""" expression_checked_only: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "check", }, ) """是否仅选择已检查且未拒绝的表达方式""" expression_self_reflect: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "refresh-cw", }, ) """是否启用自动表达优化""" expression_auto_check_interval: int = Field( default=600, json_schema_extra={ "x-widget": "input", "x-icon": "clock", }, ) """表达方式自动检查的间隔时间(秒)""" expression_auto_check_count: int = Field( default=20, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """每次自动检查时随机选取的表达方式数量""" expression_auto_check_custom_criteria: list[str] = Field( default_factory=list, json_schema_extra={ "x-widget": "custom", "x-icon": "file-text", }, ) """表达方式自动检查的额外自定义评估标准""" all_global_jargon: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "globe", }, ) """是否开启全局黑话模式,注意,此功能关闭后,已经记录的全局黑话不会改变,需要手动删除""" class VoiceConfig(ConfigBase): """语音识别配置类""" __ui_parent__ = "emoji" enable_asr: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "mic", }, ) """是否启用语音识别,启用后麦麦可以识别语音消息""" class EmojiConfig(ConfigBase): """表情包配置类""" __ui_label__ = "功能" __ui_icon__ = "puzzle" emoji_send_num: int = Field( default=25, ge=1, le=64, json_schema_extra={ "x-widget": "input", "x-icon": "grid", }, ) """一次从多少个表情包中选择发送,最大为 64""" max_reg_num: int = Field( default=64, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """表情包最大注册数量""" do_replace: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "refresh-cw", }, ) """达到最大注册数量时替换旧表情包,关闭则达到最大数量时不会继续收集表情包""" check_interval: int = Field( default=10, json_schema_extra={ "x-widget": "input", "x-icon": "clock", }, ) """表情包检查间隔(分钟)""" steal_emoji: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "copy", }, ) """是否偷取表情包,让麦麦可以将一些表情包据为己有""" content_filtration: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "filter", }, ) """是否启用表情包过滤,只有符合该要求的表情包才会被保存""" filtration_prompt: str = Field( default="符合公序良俗", json_schema_extra={ "x-widget": "input", "x-icon": "shield", }, ) """表情包过滤要求,只有符合该要求的表情包才会被保存""" class KeywordRuleConfig(ConfigBase): """关键词规则配置类""" keywords: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "tag", }, ) """关键词列表""" regex: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "regex", }, ) """正则表达式列表""" reaction: str = Field( default="", json_schema_extra={ "x-widget": "textarea", "x-icon": "message-circle", }, ) """关键词触发的反应""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证配置""" if not self.keywords and not self.regex: raise ValueError("关键词规则必须至少包含keywords或regex中的一个") if not self.reaction: raise ValueError("关键词规则必须包含reaction") for pattern in self.regex: try: re.compile(pattern) except re.error as e: raise ValueError(f"无效的正则表达式 '{pattern}': {str(e)}") from e return super().model_post_init(context) class KeywordReactionConfig(ConfigBase): """关键词配置类""" __ui_parent__ = "response_post_process" keyword_rules: list[KeywordRuleConfig] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) """关键词规则列表""" regex_rules: list[KeywordRuleConfig] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) """正则表达式规则列表""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证配置""" for rule in self.keyword_rules + self.regex_rules: if not isinstance(rule, KeywordRuleConfig): raise ValueError(f"规则必须是KeywordRuleConfig类型,而不是{type(rule).__name__}") return super().model_post_init(context) class ResponsePostProcessConfig(ConfigBase): """回复后处理配置类""" __ui_label__ = "处理" __ui_icon__ = "settings" enable_response_post_process: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "settings", }, ) """是否启用回复后处理,包括错别字生成器,回复分割器""" class ChineseTypoConfig(ConfigBase): """中文错别字配置类""" __ui_parent__ = "response_post_process" enable: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "type", }, ) """是否启用中文错别字生成器""" error_rate: float = Field( default=0.01, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.01, }, ) """单字替换概率""" min_freq: int = Field( default=9, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """最小字频阈值""" tone_error_rate: float = Field( default=0.1, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.1, }, ) """声调错误概率""" word_replace_rate: float = Field( default=0.006, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.001, }, ) """整词替换概率""" class ResponseSplitterConfig(ConfigBase): """回复分割器配置类""" __ui_parent__ = "response_post_process" enable: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "scissors", }, ) """是否启用回复分割器""" max_length: int = Field( default=512, json_schema_extra={ "x-widget": "input", "x-icon": "ruler", }, ) """回复允许的最大长度""" max_sentence_num: int = Field( default=8, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """回复允许的最大句子数""" enable_kaomoji_protection: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "smile", }, ) """是否启用颜文字保护""" enable_overflow_return_all: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "maximize", }, ) """是否在句子数量超出回复允许的最大句子数时一次性返回全部内容""" class TelemetryConfig(ConfigBase): """遥测配置类""" __ui_parent__ = "debug" enable: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "activity", }, ) """是否启用遥测""" class DebugConfig(ConfigBase): """调试配置类""" __ui_label__ = "其他" __ui_icon__ = "more-horizontal" show_prompt: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "eye", }, ) """是否显示prompt""" show_maisaka_thinking: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "brain", }, ) """是否显示回复器推理""" fold_maisaka_thinking: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "minimize-2", }, ) """是否折叠 Maisaka 的 prompt 展示入口""" show_jargon_prompt: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "book", }, ) """是否显示jargon相关提示词""" show_memory_prompt: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "database", }, ) """是否显示记忆检索相关prompt""" class ExtraPromptItem(ConfigBase): platform: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "wifi", }, ) """平台,留空无效""" item_id: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """用户ID,留空无效""" rule_type: Literal["group", "private"] = Field( default="group", json_schema_extra={ "x-widget": "select", "x-icon": "users", }, ) """聊天流类型,group(群聊)或private(私聊)""" prompt: str = Field( default="", json_schema_extra={ "x-widget": "textarea", "x-icon": "file-text", }, ) """额外的prompt内容""" def model_post_init(self, context: Optional[dict] = None) -> None: if not self.platform and not self.item_id and not self.prompt: return super().model_post_init(context) if not self.platform or not self.item_id or not self.prompt: raise ValueError("ExtraPromptItem 中 platform, id 和 prompt 不能为空") return super().model_post_init(context) class MaimMessageConfig(ConfigBase): """maim_message配置类""" __ui_parent__ = "debug" ws_server_host: str = Field( default="127.0.0.1", json_schema_extra={ "x-widget": "input", "x-icon": "server", }, ) """旧版基于WS的服务器主机地址""" ws_server_port: int = Field( default=8080, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """旧版基于WS的服务器端口号""" auth_token: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "key", }, ) """认证令牌,用于旧版API验证,为空则不启用验证""" enable_api_server: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "server", }, ) """是否启用额外的新版API Server""" api_server_host: str = Field( default="0.0.0.0", json_schema_extra={ "x-widget": "input", "x-icon": "globe", }, ) """新版API Server主机地址""" api_server_port: int = Field( default=8090, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """新版API Server端口号""" api_server_use_wss: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "lock", }, ) """新版API Server是否启用WSS""" api_server_cert_file: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "file", }, ) """新版API Server SSL证书文件路径""" api_server_key_file: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "key", }, ) """新版API Server SSL密钥文件路径""" api_server_allowed_api_keys: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "shield", }, ) """新版API Server允许的API Key列表,为空则允许所有连接""" class LPMMKnowledgeConfig(ConfigBase): """LPMM知识库配置类""" __ui_label__ = "知识库" __ui_icon__ = "book-open" enable: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "database", }, ) """是否启用LPMM知识库""" lpmm_mode: Literal["classic", "agent"] = Field( default="classic", json_schema_extra={ "x-widget": "select", "x-icon": "brain", }, ) """LPMM知识库模式,可选:classic经典模式,agent 模式""" rag_synonym_search_top_k: int = Field( default=10, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """同义检索TopK""" rag_synonym_threshold: float = Field( default=0.8, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.1, }, ) """同义阈值,相似度高于该值的关系会被当作同义词""" info_extraction_workers: int = Field( default=3, json_schema_extra={ "x-widget": "input", "x-icon": "cpu", }, ) """实体抽取同时执行线程数,非Pro模型不要设置超过5""" qa_relation_search_top_k: int = Field( default=10, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """关系检索TopK""" qa_relation_threshold: float = Field( default=0.75, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.05, }, ) """关系阈值,相似度高于该值的关系会被认为是相关关系""" qa_paragraph_search_top_k: int = Field( default=1000, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """段落检索TopK(不能过小,可能影响搜索结果)""" qa_paragraph_node_weight: float = Field( default=0.05, json_schema_extra={ "x-widget": "slider", "x-icon": "weight", "step": 0.01, }, ) """段落节点权重(在图搜索&PPR计算中的权重,当搜索仅使用DPR时,此参数不起作用)""" qa_ent_filter_top_k: int = Field( default=10, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """实体过滤TopK""" qa_ppr_damping: float = Field( default=0.8, ge=0, le=1, json_schema_extra={ "x-widget": "slider", "x-icon": "percent", "step": 0.1, }, ) """PPR阻尼系数""" qa_res_top_k: int = Field( default=10, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """最终提供段落TopK""" embedding_dimension: int = Field( default=1024, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """嵌入向量维度,输出维度""" max_embedding_workers: int = Field( default=3, json_schema_extra={ "x-widget": "input", "x-icon": "cpu", }, ) """嵌入/抽取并发线程数""" embedding_chunk_size: int = Field( default=4, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """每批嵌入的条数""" max_synonym_entities: int = Field( default=2000, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """同义边参与的实体数上限,超限则跳过""" enable_ppr: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "zap", }, ) """是否启用PPR,低配机器可关闭""" class WebUIConfig(ConfigBase): """WebUI配置类""" __ui_label__ = "WebUI" __ui_icon__ = "layout" enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "monitor", }, ) """是否启用WebUI""" host: str = Field( default="127.0.0.1", json_schema_extra={ "x-widget": "input", "x-icon": "globe", }, ) """WebUI 绑定主机地址""" port: int = Field( default=8001, json_schema_extra={ "x-widget": "input", "x-icon": "hash", }, ) """WebUI 绑定端口""" mode: Literal["development", "production"] = Field( default="production", json_schema_extra={ "x-widget": "select", "x-icon": "settings", }, ) """运行模式:development(开发) 或 production(生产)""" anti_crawler_mode: Literal["false", "strict", "loose", "basic"] = Field( default="basic", json_schema_extra={ "x-widget": "select", "x-icon": "shield", }, ) """防爬虫模式:false(禁用) / strict(严格) / loose(宽松) / basic(基础-只记录不阻止)""" allowed_ips: str = Field( default="127.0.0.1", json_schema_extra={ "x-widget": "input", "x-icon": "network", }, ) """IP白名单(逗号分隔,支持精确IP、CIDR格式和通配符)""" trusted_proxies: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "server", }, ) """信任的代理IP列表(逗号分隔),只有来自这些IP的X-Forwarded-For才被信任""" trust_xff: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "shield-check", }, ) """是否启用X-Forwarded-For代理解析(默认false)""" secure_cookie: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "cookie", }, ) """是否启用安全Cookie(仅通过HTTPS传输,默认false)""" enable_paragraph_content: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "file-text", }, ) """是否在知识图谱中加载段落完整内容(需要加载embedding store,会占用额外内存)""" class DatabaseConfig(ConfigBase): """数据库配置类""" __ui_parent__ = "debug" save_binary_data: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "save", }, ) """ 是否将消息中的二进制数据保存为独立文件 若启用,消息中的语音等二进制数据将会保存为独立文件,并在消息中以特殊标记替代。启用会导致数据文件夹体积增大,但可以实现二次识别等功能。 若禁用,则消息中的二进制将会在识别后删除,并在消息中使用识别结果替代,无法二次识别 该配置项仅影响新存储的消息,已有消息不会受到影响 """ class MaiSakaConfig(ConfigBase): """MaiSaka 对话系统配置类""" __ui_label__ = "MaiSaka" __ui_icon__ = "message-circle" cli_user_name: str = Field( default="用户", json_schema_extra={ "x-widget": "input", "x-icon": "user", }, ) """MaiSaka 使用的用户名称""" show_image_path: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "image", }, ) """是否显示图片本地路径""" class MCPAuthorizationConfig(ConfigBase): """MCP HTTP 认证配置。""" mode: Literal["none", "bearer"] = Field( default="none", json_schema_extra={ "x-widget": "select", "x-icon": "shield", }, ) """认证模式,当前支持无认证和静态 Bearer Token""" bearer_token: str = Field( default="", json_schema_extra={ "x-widget": "password", "x-icon": "key", }, ) """静态 Bearer Token,仅在 `mode=\"bearer\"` 时使用""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证 MCP 认证配置。 Args: context: Pydantic 传入的上下文对象。 Returns: None """ if self.mode == "bearer" and not self.bearer_token.strip(): raise ValueError("MCP 使用 bearer 认证时必须填写 bearer_token") return super().model_post_init(context) class MCPRootItemConfig(ConfigBase): """单个 MCP Root 配置。""" enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "power", }, ) """是否启用当前 Root""" uri: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "folder", }, ) """Root URI,通常为 `file://` 路径 URI""" name: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "tag", }, ) """Root 的显示名称""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证单个 Root 配置。 Args: context: Pydantic 传入的上下文对象。 Returns: None """ if self.enabled and not self.uri.strip(): raise ValueError("启用的 MCP Root 必须填写 uri") return super().model_post_init(context) class MCPRootsConfig(ConfigBase): """MCP Roots 能力配置。""" enable: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "folder-tree", }, ) """是否向 MCP 服务器暴露 Roots 能力""" items: list[MCPRootItemConfig] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "folder", }, ) """Roots 列表""" class MCPSamplingConfig(ConfigBase): """MCP Sampling 能力配置。""" enable: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "brain", }, ) """是否启用 Sampling 能力声明""" task_name: str = Field( default="planner", json_schema_extra={ "x-widget": "input", "x-icon": "sparkles", }, ) """执行 Sampling 请求时使用的主程序模型任务名""" include_context_support: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "layers", }, ) """是否声明支持 `includeContext` 非 `none` 语义""" tool_support: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "wrench", }, ) """是否声明支持在 Sampling 中继续使用工具""" class MCPElicitationConfig(ConfigBase): """MCP Elicitation 能力配置。""" enable: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "message-circle-question", }, ) """是否启用 Elicitation 能力声明""" allow_form: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "form-input", }, ) """是否允许表单模式 Elicitation""" allow_url: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "link", }, ) """是否允许 URL 模式 Elicitation""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证 Elicitation 配置。 Args: context: Pydantic 传入的上下文对象。 Returns: None """ if self.enable and not (self.allow_form or self.allow_url): raise ValueError("启用 MCP Elicitation 时至少需要允许一种模式") return super().model_post_init(context) class MCPClientConfig(ConfigBase): """MCP 客户端宿主能力配置。""" client_name: str = Field( default="MaiBot", json_schema_extra={ "x-widget": "input", "x-icon": "bot", }, ) """MCP 客户端实现名称""" client_version: str = Field( default="1.0.0", json_schema_extra={ "x-widget": "input", "x-icon": "info", }, ) """MCP 客户端实现版本""" roots: MCPRootsConfig = Field(default_factory=MCPRootsConfig) """Roots 能力配置""" sampling: MCPSamplingConfig = Field(default_factory=MCPSamplingConfig) """Sampling 能力配置""" elicitation: MCPElicitationConfig = Field(default_factory=MCPElicitationConfig) """Elicitation 能力配置""" class MCPServerItemConfig(ConfigBase): """单个 MCP 服务器配置。""" name: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "tag", }, ) """服务器名称,必须唯一""" enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "power", }, ) """是否启用当前 MCP 服务器""" transport: Literal["stdio", "streamable_http"] = Field( default="stdio", json_schema_extra={ "x-widget": "select", "x-icon": "shuffle", }, ) """传输方式,可选 `stdio` 或 `streamable_http`""" command: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "terminal", }, ) """stdio 模式下启动服务器的命令""" args: list[str] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) """stdio 模式下的命令参数列表""" env: dict[str, str] = Field( default_factory=lambda: {}, json_schema_extra={ "x-widget": "custom", "x-icon": "variable", }, ) """stdio 模式下附加的环境变量""" url: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "link", }, ) """`streamable_http` 模式下的 MCP 端点地址""" headers: dict[str, str] = Field( default_factory=lambda: {}, json_schema_extra={ "x-widget": "custom", "x-icon": "file-json", }, ) """HTTP 模式下附加的请求头""" http_timeout_seconds: float = Field( default=30.0, gt=0, json_schema_extra={ "x-widget": "number", "x-icon": "clock-3", }, ) """HTTP 请求超时时间,单位秒""" read_timeout_seconds: float = Field( default=300.0, gt=0, json_schema_extra={ "x-widget": "number", "x-icon": "timer", }, ) """会话读取超时时间,单位秒""" authorization: MCPAuthorizationConfig = Field(default_factory=MCPAuthorizationConfig) """HTTP 认证配置""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证 MCP 服务器配置。 Args: context: Pydantic 传入的上下文对象。 Returns: None """ if not self.name.strip(): raise ValueError("MCPServerItemConfig.name 不能为空") if self.transport == "stdio" and not self.command.strip(): raise ValueError(f"MCP 服务器 {self.name} 使用 stdio 时必须填写 command") if self.transport == "streamable_http" and not self.url.strip(): raise ValueError(f"MCP 服务器 {self.name} 使用 streamable_http 时必须填写 url") return super().model_post_init(context) class MCPConfig(ConfigBase): """MCP 总配置。""" __ui_parent__ = "maisaka" enable: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "zap", }, ) """是否启用 MCP(Model Context Protocol)""" client: MCPClientConfig = Field(default_factory=MCPClientConfig) """MCP 客户端宿主能力配置""" servers: list[MCPServerItemConfig] = Field( default_factory=lambda: [], json_schema_extra={ "x-widget": "custom", "x-icon": "server", }, ) """_wrap_MCP 服务器配置列表""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证 MCP 总配置。 Args: context: Pydantic 传入的上下文对象。 Returns: None """ server_names = [server.name.strip() for server in self.servers if server.name.strip()] if len(server_names) != len(set(server_names)): raise ValueError("MCP 配置中的服务器名称不能重复") return super().model_post_init(context) class PluginRuntimeRenderConfig(ConfigBase): """插件运行时浏览器渲染配置。""" enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "image", }, ) """是否启用插件运行时浏览器渲染能力""" browser_ws_endpoint: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "link", }, ) """优先复用的现有 Chromium CDP 地址,可填写 ws/http 端点""" executable_path: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "folder", }, ) """浏览器可执行文件路径,留空时自动探测本机 Chrome/Chromium""" browser_install_root: str = Field( default="data/playwright-browsers", json_schema_extra={ "x-widget": "input", "x-icon": "hard-drive", }, ) """Playwright 托管浏览器目录,自动下载 Chromium 时会复用该目录""" headless: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "monitor", }, ) """是否以无头模式启动浏览器""" launch_args: list[str] = Field( default_factory=lambda: [ "--disable-gpu", "--disable-dev-shm-usage", "--disable-setuid-sandbox", "--no-sandbox", "--no-zygote", ], json_schema_extra={ "x-widget": "custom", "x-icon": "terminal", }, ) """浏览器启动参数列表""" concurrency_limit: int = Field( default=2, ge=1, json_schema_extra={ "x-widget": "number", "x-icon": "layers", }, ) """同时允许进行的最大渲染任务数""" startup_timeout_sec: float = Field( default=20.0, gt=0, json_schema_extra={ "x-widget": "number", "x-icon": "clock", }, ) """浏览器连接或启动超时时间(秒)""" render_timeout_sec: float = Field( default=15.0, gt=0, json_schema_extra={ "x-widget": "number", "x-icon": "timer", }, ) """单次渲染默认超时时间(秒)""" auto_download_chromium: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "download", }, ) """未检测到可用浏览器时,是否自动下载 Playwright Chromium""" download_connection_timeout_sec: float = Field( default=120.0, gt=0, json_schema_extra={ "x-widget": "number", "x-icon": "cloud-lightning", }, ) """自动下载 Chromium 时的连接超时时间(秒)""" restart_after_render_count: int = Field( default=200, ge=0, json_schema_extra={ "x-widget": "number", "x-icon": "refresh-cw", }, ) """累计渲染指定次数后自动重建本地浏览器,0 表示关闭该策略""" class PluginRuntimeConfig(ConfigBase): """插件运行时配置类""" __ui_label__ = "插件运行时" __ui_icon__ = "puzzle" enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "power", }, ) """启用插件系统""" health_check_interval_sec: float = Field( default=30.0, json_schema_extra={ "x-widget": "number", "x-icon": "activity", }, ) """健康检查间隔(秒)""" max_restart_attempts: int = Field( default=3, json_schema_extra={ "x-widget": "number", "x-icon": "refresh-cw", }, ) """Runner 崩溃后最大自动重启次数""" runner_spawn_timeout_sec: float = Field( default=30.0, json_schema_extra={ "x-widget": "number", "x-icon": "clock", }, ) """等待 Runner 子进程启动并注册的超时时间(秒)""" hook_blocking_timeout_sec: float = Field( default=30, json_schema_extra={ "x-widget": "number", "x-icon": "timer", }, ) """Hook 阻塞步骤的全局超时上限(秒)""" ipc_socket_path: str = Field( default="", json_schema_extra={ "x-widget": "input", "x-icon": "link", }, ) """ 自定义 IPC Socket 路径(仅 Linux/macOS 生效) 留空则自动生成临时路径 """ render: PluginRuntimeRenderConfig = Field(default_factory=PluginRuntimeRenderConfig) """浏览器渲染能力配置"""