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", }, ) """人格,建议200字以内,描述人格特质和身份特征;可以写完整设定。要求第二人称""" 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: [ "你的风格平淡但不失讽刺,很简短,很白话。可以参考贴吧,微博的回复风格。", "用1-2个字进行回复", "用1-2个符号进行回复", "言辭凝練古雅,穿插《論語》經句卻不晦澀,以文言短句為基,輔以淺白語意,持長者溫和風範,全用繁體字表達,具先秦儒者談吐韻致。", "带点翻译腔,但不要太长", ], json_schema_extra={ "x-widget": "custom", "x-icon": "list", }, ) """可选的多种表达风格列表,当配置不为空时可按概率随机替换 reply_style""" multiple_probability: float = Field( default=0.2, 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)""" 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为多模态模式""" 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""" private_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必回复""" enable_at: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "at-sign", }, ) """是否允许 replyer 使用 at[msg_id] 标记来发送真正的 at 消息""" enable_reply_quote: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "quote", }, ) """是否启用回复时附带引用回复""" max_context_size: int = Field( default=40, json_schema_extra={ "x-widget": "input", "x-icon": "layers", }, ) """上下文长度""" max_private_context_size: int = Field( default=40, 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群里聊天,下面是群里正在聊的内容,其中包含聊天记录和聊天中的图片和表情包。\n" "回复尽量简短一些。最好一次对一个话题进行回复,但必须考虑不同群友发言之间的交互,免得啰嗦或者回复内容太乱。请注意把握聊天内容。\n" "不要总是提及自己的身份背景,根据聊天内容自由发挥,但是要日常不浮夸,不要太关注具体的聊天内容,不要刻意找话题,。\n" "不要回复的太频繁!不用刻意回复表情包,只要关注表情包表达的含义。控制回复的频率,不要每个人的消息都回复,只回复你感兴趣的或者主动提及你的。\n" ), json_schema_extra={ "x-widget": "textarea", "x-icon": "users", }, ) """_wrap_群聊通用注意事项""" private_chat_prompts: str = Field( default=( "你正在聊天,下面是正在聊的内容,其中包含聊天记录和聊天中的图片。\n" "回复尽量简短一些。请注意把握聊天内容。\n" "请考虑对方的发言频率,想法,思考自己何时回复以及回复内容。\n" ), 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 的默认返回条数""" person_fact_writeback_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "user-round-pen", }, ) """是否在发送回复后自动提取并写回人物事实到长期记忆""" chat_summary_writeback_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "scroll-text", }, ) """是否在 Maisaka 聊天过程中按消息窗口自动写回聊天摘要到长期记忆""" chat_summary_writeback_message_threshold: int = Field( default=12, ge=1, json_schema_extra={ "x-widget": "input", "x-icon": "messages-square", }, ) """自动写回聊天摘要的消息窗口阈值""" chat_summary_writeback_context_length: int = Field( default=50, ge=1, le=500, json_schema_extra={ "x-widget": "input", "x-icon": "rows-3", }, ) """自动写回聊天摘要时,从聊天流中回看的消息条数""" feedback_correction_enabled: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "message-circle-warning", }, ) """是否启用反馈驱动的延迟记忆纠错任务""" feedback_correction_window_hours: float = Field( default=12.0, ge=0.1, json_schema_extra={ "x-widget": "input", "x-icon": "clock-4", }, ) """反馈窗口时长(小时),以 query_memory 执行时间为起点""" feedback_correction_check_interval_minutes: int = Field( default=30, ge=1, json_schema_extra={ "x-widget": "input", "x-icon": "timer", }, ) """反馈纠错定时任务轮询间隔(分钟)""" feedback_correction_batch_size: int = Field( default=20, ge=1, le=200, json_schema_extra={ "x-widget": "input", "x-icon": "list-ordered", }, ) """反馈纠错每轮最大处理任务数""" feedback_correction_auto_apply_threshold: float = Field( default=0.85, ge=0.0, le=1.0, json_schema_extra={ "x-widget": "slider", "x-icon": "gauge", "step": 0.01, }, ) """自动应用纠错动作的最低置信度阈值""" feedback_correction_max_feedback_messages: int = Field( default=30, ge=1, le=200, json_schema_extra={ "x-widget": "input", "x-icon": "messages-square", }, ) """每个纠错任务最多使用的窗口内用户反馈消息数""" feedback_correction_prefilter_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "filter", }, ) """是否启用纠错前置预筛(用于减少不必要的模型调用)""" feedback_correction_paragraph_mark_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "sticky-note", }, ) """是否为受影响 paragraph 写入已纠正旧事实标记""" feedback_correction_paragraph_hard_filter_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "eye-off", }, ) """是否在用户侧查询中硬过滤带有 stale 标记的 paragraph""" feedback_correction_profile_refresh_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "user-round-search", }, ) """是否在反馈纠错后将受影响人物画像加入刷新队列""" feedback_correction_profile_force_refresh_on_read: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "refresh-ccw", }, ) """人物画像处于脏队列时,读取是否强制刷新而不直接复用旧快照""" feedback_correction_episode_rebuild_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "clapperboard", }, ) """是否在反馈纠错后将受影响 source 加入 episode 重建队列""" feedback_correction_episode_query_block_enabled: bool = Field( default=True, json_schema_extra={ "x-widget": "switch", "x-icon": "ban", }, ) """episode source 处于重建队列时,是否对用户侧查询做屏蔽""" feedback_correction_reconcile_interval_minutes: int = Field( default=5, ge=1, json_schema_extra={ "x-widget": "input", "x-icon": "repeat", }, ) """反馈纠错二阶段一致性后台协调任务轮询间隔(分钟)""" feedback_correction_reconcile_batch_size: int = Field( default=20, ge=1, le=200, json_schema_extra={ "x-widget": "input", "x-icon": "list-restart", }, ) """反馈纠错二阶段一致性每轮处理 profile/episode 队列的批大小""" def model_post_init(self, context: Optional[dict] = None) -> None: """验证配置值""" if self.feedback_correction_window_hours <= 0: raise ValueError( f"feedback_correction_window_hours 必须大于0,当前值: {self.feedback_correction_window_hours}" ) if self.feedback_correction_check_interval_minutes < 1: raise ValueError( "feedback_correction_check_interval_minutes 必须至少为1," f"当前值: {self.feedback_correction_check_interval_minutes}" ) if self.feedback_correction_batch_size < 1: raise ValueError( f"feedback_correction_batch_size 必须至少为1,当前值: {self.feedback_correction_batch_size}" ) if not 0 <= self.feedback_correction_auto_apply_threshold <= 1: raise ValueError( "feedback_correction_auto_apply_threshold 必须在 [0, 1] 之间," f"当前值: {self.feedback_correction_auto_apply_threshold}" ) if self.feedback_correction_max_feedback_messages < 1: raise ValueError( "feedback_correction_max_feedback_messages 必须至少为1," f"当前值: {self.feedback_correction_max_feedback_messages}" ) if self.feedback_correction_reconcile_interval_minutes < 1: raise ValueError( "feedback_correction_reconcile_interval_minutes 必须至少为1," f"当前值: {self.feedback_correction_reconcile_interval_minutes}" ) if self.feedback_correction_reconcile_batch_size < 1: raise ValueError( "feedback_correction_reconcile_batch_size 必须至少为1," f"当前值: {self.feedback_correction_reconcile_batch_size}" ) return super().model_post_init(context) class AMemorixPluginConfig(ConfigBase): """A_Memorix 子系统状态""" enabled: bool = Field(default=False) """是否启用 A_Memorix""" class AMemorixStorageConfig(ConfigBase): """A_Memorix 存储位置""" data_dir: str = Field(default="data/a-memorix") """数据目录""" class AMemorixEmbeddingFallbackConfig(ConfigBase): """A_Memorix Embedding 回退""" enabled: bool = Field(default=True) """是否启用回退机制""" probe_interval_seconds: int = Field(default=180, ge=10) """探测间隔秒数""" allow_metadata_only_write: bool = Field(default=True) """是否允许仅写入元数据""" class AMemorixParagraphVectorBackfillConfig(ConfigBase): """A_Memorix 段落向量回填""" enabled: bool = Field(default=True) """是否启用回填任务""" interval_seconds: int = Field(default=60, ge=5) """回填轮询间隔""" batch_size: int = Field(default=64, ge=1) """单批回填数量""" max_retry: int = Field(default=5, ge=0) """最大重试次数""" class AMemorixEmbeddingConfig(ConfigBase): """A_Memorix Embedding 配置""" model_name: str = Field(default="auto") """Embedding 模型选择""" dimension: int = Field(default=1024, ge=1) """向量维度""" batch_size: int = Field(default=32, ge=1) """单批请求大小""" max_concurrent: int = Field(default=5, ge=1) """最大并发数""" enable_cache: bool = Field(default=False) """是否启用缓存""" quantization_type: Literal["int8"] = Field(default="int8") """量化方式,当前 vNext 仅支持 int8(SQ8)""" fallback: AMemorixEmbeddingFallbackConfig = Field(default_factory=AMemorixEmbeddingFallbackConfig) """Embedding 回退配置""" paragraph_vector_backfill: AMemorixParagraphVectorBackfillConfig = Field( default_factory=AMemorixParagraphVectorBackfillConfig ) """段落向量回填配置""" class AMemorixSparseRetrievalConfig(ConfigBase): """A_Memorix 稀疏检索配置""" enabled: bool = Field(default=True) """是否启用稀疏检索""" backend: Literal["fts5"] = Field(default="fts5") """稀疏检索后端""" mode: Literal["auto", "fallback_only", "hybrid"] = Field(default="auto") """稀疏检索模式""" tokenizer_mode: Literal["jieba", "mixed", "char_2gram"] = Field(default="jieba") """分词模式""" candidate_k: int = Field(default=80, ge=1) """段落候选数""" relation_candidate_k: int = Field(default=60, ge=1) """关系候选数""" class AMemorixRetrievalConfig(ConfigBase): """A_Memorix 检索配置""" top_k_paragraphs: int = Field(default=20, ge=1) """段落候选数""" top_k_relations: int = Field(default=10, ge=1) """关系候选数""" top_k_final: int = Field(default=10, ge=1) """最终返回条数""" alpha: float = Field(default=0.5, ge=0.0, le=1.0) """关系融合权重""" enable_ppr: bool = Field(default=True) """是否启用 PPR""" ppr_alpha: float = Field(default=0.85, ge=0.0, le=1.0) """PPR alpha""" ppr_timeout_seconds: float = Field(default=1.5, ge=0.1) """PPR 超时秒数""" ppr_concurrency_limit: int = Field(default=4, ge=1) """PPR 并发限制""" enable_parallel: bool = Field(default=True) """是否启用并行检索""" sparse: AMemorixSparseRetrievalConfig = Field(default_factory=AMemorixSparseRetrievalConfig) """稀疏检索配置""" class AMemorixThresholdConfig(ConfigBase): """A_Memorix 阈值过滤配置""" min_threshold: float = Field(default=0.3, ge=0.0, le=1.0) """最小阈值""" max_threshold: float = Field(default=0.95, ge=0.0, le=1.0) """最大阈值""" percentile: int = Field(default=75, ge=0, le=100) """动态阈值百分位""" min_results: int = Field(default=3, ge=1) """最小保留条数""" enable_auto_adjust: bool = Field(default=True) """是否启用自动阈值调整""" class AMemorixFilterConfig(ConfigBase): """A_Memorix 聊天过滤配置""" enabled: bool = Field(default=True) """是否启用聊天过滤""" mode: Literal["blacklist", "whitelist"] = Field(default="blacklist") """过滤模式""" chats: list[str] = Field(default_factory=lambda: []) """聊天流列表""" class AMemorixEpisodeConfig(ConfigBase): """A_Memorix Episode 配置""" enabled: bool = Field(default=True) """是否启用 Episode""" generation_enabled: bool = Field(default=True) """是否启用自动生成""" pending_batch_size: int = Field(default=20, ge=1) """待处理批大小""" pending_max_retry: int = Field(default=3, ge=0) """待处理最大重试次数""" max_paragraphs_per_call: int = Field(default=20, ge=1) """单次最大段落数""" max_chars_per_call: int = Field(default=6000, ge=100) """单次最大字符数""" source_time_window_hours: float = Field(default=24.0, ge=0.0) """时间窗口小时数""" segmentation_model: str = Field(default="auto") """分段模型选择""" class AMemorixPersonProfileConfig(ConfigBase): """A_Memorix 人物画像配置""" enabled: bool = Field(default=True) """是否启用画像""" refresh_interval_minutes: int = Field(default=30, ge=1) """刷新间隔分钟数""" active_window_hours: float = Field(default=72.0, ge=1.0) """活跃窗口小时数""" max_refresh_per_cycle: int = Field(default=50, ge=1) """单轮最大刷新数""" top_k_evidence: int = Field(default=12, ge=1) """证据条数""" class AMemorixMemoryEvolutionConfig(ConfigBase): """A_Memorix 记忆演化配置""" enabled: bool = Field(default=True) """是否启用记忆演化""" half_life_hours: float = Field(default=24.0, ge=0.1) """半衰期小时数""" prune_threshold: float = Field(default=0.1, ge=0.0, le=1.0) """裁剪阈值""" freeze_duration_hours: float = Field(default=24.0, ge=0.0) """冻结时长小时数""" class AMemorixAdvancedConfig(ConfigBase): """A_Memorix 高级运行时配置""" enable_auto_save: bool = Field(default=True) """是否启用自动保存""" auto_save_interval_minutes: int = Field(default=5, ge=1) """自动保存间隔""" debug: bool = Field(default=False) """是否启用调试""" class AMemorixWebImportConfig(ConfigBase): """A_Memorix 导入中心配置""" enabled: bool = Field(default=True) """是否启用导入中心""" max_queue_size: int = Field(default=20, ge=1) """最大队列长度""" max_files_per_task: int = Field(default=200, ge=1) """单任务最大文件数""" max_file_size_mb: int = Field(default=20, ge=1) """单文件大小上限 MB""" max_paste_chars: int = Field(default=200000, ge=100) """粘贴字符数上限""" default_file_concurrency: int = Field(default=2, ge=1) """默认文件并发""" default_chunk_concurrency: int = Field(default=4, ge=1) """默认分块并发""" class AMemorixWebTuningConfig(ConfigBase): """A_Memorix 调优中心配置""" enabled: bool = Field(default=True) """是否启用调优中心""" max_queue_size: int = Field(default=8, ge=1) """最大队列长度""" poll_interval_ms: int = Field(default=1200, ge=200) """轮询间隔毫秒数""" default_intensity: Literal["quick", "standard", "deep"] = Field(default="standard") """默认调优强度""" default_objective: Literal["precision_priority", "balanced", "recall_priority"] = Field( default="precision_priority" ) """默认调优目标""" default_top_k_eval: int = Field(default=20, ge=1) """默认评估 Top-K""" default_sample_size: int = Field(default=24, ge=1) """默认样本数""" class AMemorixWebConfig(ConfigBase): """A_Memorix Web 运维配置""" import_config: AMemorixWebImportConfig = Field(default_factory=AMemorixWebImportConfig) """导入中心配置""" tuning: AMemorixWebTuningConfig = Field(default_factory=AMemorixWebTuningConfig) """调优中心配置""" class AMemorixConfig(ConfigBase): """A_Memorix 长期记忆子系统配置""" __ui_label__ = "长期记忆" __ui_icon__ = "brain" plugin: AMemorixPluginConfig = Field(default_factory=AMemorixPluginConfig) """子系统状态""" storage: AMemorixStorageConfig = Field(default_factory=AMemorixStorageConfig) """存储位置""" embedding: AMemorixEmbeddingConfig = Field(default_factory=AMemorixEmbeddingConfig) """Embedding 配置""" retrieval: AMemorixRetrievalConfig = Field(default_factory=AMemorixRetrievalConfig) """检索配置""" threshold: AMemorixThresholdConfig = Field(default_factory=AMemorixThresholdConfig) """阈值过滤配置""" filter: AMemorixFilterConfig = Field(default_factory=AMemorixFilterConfig) """聊天过滤配置""" episode: AMemorixEpisodeConfig = Field(default_factory=AMemorixEpisodeConfig) """Episode 配置""" person_profile: AMemorixPersonProfileConfig = Field(default_factory=AMemorixPersonProfileConfig) """人物画像配置""" memory: AMemorixMemoryEvolutionConfig = Field(default_factory=AMemorixMemoryEvolutionConfig) """记忆演化配置""" advanced: AMemorixAdvancedConfig = Field(default_factory=AMemorixAdvancedConfig) """高级运行时配置""" web: AMemorixWebConfig = Field(default_factory=AMemorixWebConfig) """Web 运维配置""" 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学习""" 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, ) ], 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 LogConfig(ConfigBase): """日志配置类""" __ui_label__ = "日志" __ui_icon__ = "file-text" date_style: str = Field( default="m-d H:i:s", json_schema_extra={ "x-widget": "input", "x-icon": "clock", }, ) """日期格式""" log_level_style: Literal["lite", "compact", "full"] = Field( default="lite", json_schema_extra={ "x-widget": "select", "x-icon": "list", }, ) """日志等级显示样式""" color_text: Literal["none", "title", "full"] = Field( default="full", json_schema_extra={ "x-widget": "select", "x-icon": "palette", }, ) """控制台日志颜色模式""" log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field( default="INFO", json_schema_extra={ "x-widget": "select", "x-icon": "list-filter", }, ) """全局日志级别""" console_log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field( default="INFO", json_schema_extra={ "x-widget": "select", "x-icon": "terminal", }, ) """控制台日志级别""" file_log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field( default="DEBUG", json_schema_extra={ "x-widget": "select", "x-icon": "file-json", }, ) """文件日志级别""" log_file_max_bytes: int = Field( default=5 * 1024 * 1024, json_schema_extra={ "x-widget": "input", "x-icon": "hard-drive", }, ) """单个日志文件最大字节数""" max_log_files: int = Field( default=30, json_schema_extra={ "x-widget": "input", "x-icon": "files", }, ) """最多保留的主日志文件数量""" log_cleanup_days: int = Field( default=30, json_schema_extra={ "x-widget": "input", "x-icon": "calendar-days", }, ) """主日志文件保留天数""" llm_request_snapshot_limit: int = Field( default=128, json_schema_extra={ "x-widget": "input", "x-icon": "archive", }, ) """失败请求快照最多保留数量""" maisaka_prompt_preview_limit: int = Field( default=256, json_schema_extra={ "x-widget": "input", "x-icon": "panel-top", }, ) """每个会话最多保留的 Maisaka Prompt 预览组数""" maisaka_reply_effect_limit: int = Field( default=256, json_schema_extra={ "x-widget": "input", "x-icon": "clipboard-check", }, ) """每个会话最多保留的 Maisaka 回复效果记录数""" suppress_libraries: list[str] = Field( default_factory=lambda: [ "faiss", "httpx", "urllib3", "asyncio", "websockets", "httpcore", "requests", "sqlalchemy", "openai", "uvicorn", "jieba", ], json_schema_extra={ "x-widget": "custom", "x-icon": "volume-x", }, ) """完全屏蔽日志的第三方库列表""" library_log_levels: dict[str, str] = Field( default_factory=lambda: {"aiohttp": "WARNING"}, json_schema_extra={ "x-widget": "custom", "x-icon": "sliders-horizontal", }, ) """特定第三方库的日志级别""" 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" enable_maisaka_stage_board: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "layout-dashboard", }, ) """是否启用 Maisaka 阶段看板""" 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""" enable_reply_effect_tracking: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "activity", }, ) """是否开启回复效果评分追踪,默认关闭,需要手动打开""" record_reply_request: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "file-json", }, ) """是否记录 Replyer 请求体,默认关闭""" enable_llm_cache_stats: bool = Field( default=False, json_schema_extra={ "x-widget": "switch", "x-icon": "chart-no-axes-column", }, ) """是否记录 LLM prompt cache 调试统计,默认关闭""" 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=8000, 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 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) """浏览器渲染能力配置"""