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: [
            "你的风格平淡但不失讽刺，很简短,很白话。可以参考贴吧，微博的回复风格。",
            "用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"""

    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",
        },
    )
    """上下文长度"""
    
    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 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 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=True,
        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",
        },
    )
    """是否开启回复效果评分追踪，默认关闭，需要手动打开"""


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)
    """浏览器渲染能力配置"""
