"""配置 schema 元数据契约层定义。 定义配置字段的完整 schema 元数据(``CONFIG_SCHEMA``),包括键、类型、 是否必填、默认值、是否可热更新与约束,作为配置 schema 的唯一真相源 (§13.1 配置分层 / §6.1 契约层职责)。适配器(如 ``RedisConfigAdapter``) 与应用层(如 ``ConfigManager``)共享引用本模块,消除 schema 元数据 下沉至适配器层的 DRY 违反。 ``HOT_RELOADABLE_KEYS`` / ``NON_HOT_RELOADABLE_KEYS`` 从 ``CONFIG_SCHEMA`` 派生,供 ``ConfigManager`` 做热更新策略校验(ADP-030)。 """ from __future__ import annotations from yuxi.channels.contract.dtos.config import ConfigField # 配置 schema:全部配置字段的完整元数据(FR-37 配置热更新)。 # 40 个可热更新字段 + 13 个不可热更新字段,共 53 个。 # 字段顺序:先可热更新(hot_reloadable=True),后不可热更新(hot_reloadable=False)。 CONFIG_SCHEMA: tuple[ConfigField, ...] = ( # === 可热更新字段(hot_reloadable=True)=== ConfigField( key="dm_policy", type="json", required=False, default="allow", hot_reloadable=True, title="私信策略", description="控制私信/群聊的自动回复与路由策略,如是否允许陌生人私信、是否自动创建会话等。", category="消息策略", ), ConfigField( key="allow_from", type="json", required=False, default=None, hot_reloadable=True, title="来源白名单", description="允许向当前账户发起对话的对端白名单,未配置时不限制。", category="访问控制", ), ConfigField( key="rate_limit", type="json", required=False, default=None, hot_reloadable=True, title="消息限流", description="控制账户级别的消息发送频率限制,防止滥用或触发渠道风控。", category="限流与预算", ), ConfigField( key="bot_loop_budget", type="json", required=False, default={"max_replies_per_hour": 20, "cooldown_seconds": 60}, hot_reloadable=True, title="Bot 循环预算", description=( "Bot 循环预算配置,包含 max_replies_per_hour" "(每小时最大回复数,≤0 表示不限制)和 cooldown_seconds(冷却秒数)。" ), category="限流与预算", ), # FR-08~14 功能开关(Task 30) ConfigField( key="rich_message_enabled", type="bool", required=False, default=False, hot_reloadable=True, title="富消息开关", description="是否启用富媒体消息(卡片、按钮等)发送能力。", category="功能开关", ), ConfigField( key="status_writeback_enabled", type="bool", required=False, default=False, hot_reloadable=True, title="状态回写开关", description="是否将渠道用户状态(在线/离开等)回写到知识库。", category="功能开关", ), ConfigField( key="agent_prompt_enabled", type="bool", required=False, default=False, hot_reloadable=True, title="Agent 提示开关", description="是否在消息中附加 Agent 提示上下文。", category="功能开关", ), ConfigField( key="channel_tools_enabled", type="bool", required=False, default=False, hot_reloadable=True, title="渠道工具开关", description="是否允许 Agent 调用渠道相关工具。", category="功能开关", ), # AC-22: 消息操作开关默认"开"(FR-12) ConfigField( key="message_ops_enabled", type="bool", required=False, default=True, hot_reloadable=True, title="消息操作开关", description="总开关:是否启用消息操作(反应、置顶、卡片更新等)。", category="功能开关", ), ConfigField( key="streaming_enabled", type="bool", required=False, default=False, hot_reloadable=True, title="流式响应开关", description="是否启用流式(打字机效果)响应输出。", category="功能开关", ), ConfigField( key="enable_typing", type="bool", required=False, default=True, hot_reloadable=True, title="输入中提示开关", description="是否向用户展示'正在输入'状态提示。", category="功能开关", ), ConfigField( key="directory_enabled", type="bool", required=False, default=True, hot_reloadable=True, title="目录服务开关", description="是否启用渠道目录(用户/群组列表)查询能力。", category="功能开关", ), ConfigField( key="command_enabled", type="bool", required=False, default=True, hot_reloadable=True, title="命令处理开关", description="是否启用以 command_prefix 开头的命令解析与处理。", category="功能开关", ), # FR-12 消息操作细粒度开关(PRD §FR-12 配置项)。 # AC-22 默认行为:表情反应"开"、置顶"关"、卡片更新"关"。 ConfigField( key="enable_reaction", type="bool", required=False, default=True, hot_reloadable=True, title="表情反应开关", description="是否允许对消息添加表情反应。", category="功能开关", ), ConfigField( key="enable_pin", type="bool", required=False, default=False, hot_reloadable=True, title="消息置顶开关", description="是否允许置顶/取消置顶消息。", category="功能开关", ), ConfigField( key="enable_card_update", type="bool", required=False, default=False, hot_reloadable=True, title="卡片更新开关", description="是否允许更新已发送的卡片消息。", category="功能开关", ), # FR-15 管理员命令权限白名单(账户级,JSON 数组,存放允许执行 ADMIN 权限命令的 peer_id) ConfigField( key="admin_users", type="json", required=False, default=None, hot_reloadable=True, title="管理员白名单", description="允许执行 ADMIN 权限命令的 peer_id 列表,账户级配置。", category="身份与权限", ), # FR-29 入站提及策略(账户级,JSON,含 allowed_implicit_kinds 列表, # 控制哪些隐式提及类型被视为"已提及",未配置时默认空策略) ConfigField( key="mention_policy", type="json", required=False, default=None, hot_reloadable=True, title="提及策略", description="控制哪些隐式提及类型被视为'已提及',影响命令触发与会话路由。", category="消息策略", ), # FR-07 会话合并策略开关(默认禁用) ConfigField( key="merge_strategy_enabled", type="bool", required=False, default=False, hot_reloadable=True, title="会话合并开关", description="是否启用按策略自动合并会话。", category="会话策略", ), # FR-06 跨渠道身份策略(默认隔离,可选 association 开启跨渠道关联) ConfigField( key="cross_channel_identity_strategy", type="str", required=False, default="isolation", hot_reloadable=True, title="跨渠道身份策略", description="跨渠道身份关联策略:isolation(隔离)或 association(关联)。", category="身份与权限", ), # 可配置值(Task 30) ConfigField( key="command_prefix", type="str", required=False, default="/", hot_reloadable=True, title="命令前缀", description="命令触发前缀,默认为 '/'。", category="命令与交互", ), ConfigField( key="fence_ttl_seconds", type="int", required=False, default=120, hot_reloadable=True, title="围栏 TTL", description="对话围栏(fence)的默认过期时间(秒)。", category="命令与交互", ), ConfigField( key="streaming_min_chunk_interval_ms", type="int", required=False, default=200, hot_reloadable=True, title="流式最小间隔", description="流式响应相邻分块之间的最小间隔(毫秒)。", category="流式与传输体验", ), ConfigField( key="streaming_ttl_seconds", type="int", required=False, default=60, hot_reloadable=True, title="流式 TTL", description="流式响应状态的最大保留时间(秒)。", category="流式与传输体验", ), ConfigField( key="typing_ttl_ms", type="int", required=False, default=10000, hot_reloadable=True, title="输入提示 TTL", description="'正在输入'提示状态的最大持续时间(毫秒)。", category="流式与传输体验", ), ConfigField( key="durability_policy_default", type="str", required=False, default="required", hot_reloadable=True, title="默认持久化策略", description="消息默认持久化策略:required / best_effort / none。", category="可靠性", ), # FR-24 分阶段 ACK 策略(PRD §FR-24)。 # 四阶段策略:after_record / after_agent_dispatch / after_persist / manual, # 默认 after_agent_dispatch(平衡可靠性与响应速度)。 ConfigField( key="ack_policy", type="str", required=False, default="after_agent_dispatch", hot_reloadable=True, title="ACK 策略", description="分阶段 ACK 策略:after_record / after_agent_dispatch / after_persist / manual。", category="可靠性", ), # 手动策略下插件忘记 ACK 时核心兜底超时(默认 60s)自动 ACK。 ConfigField( key="ack_fallback_timeout_seconds", type="int", required=False, default=60, hot_reloadable=True, title="ACK 兜底超时", description="手动 ACK 策略下,插件忘记 ACK 时核心自动 ACK 的超时时间(秒)。", category="可靠性", ), # 传输引擎配置 ConfigField( key="transport.stall_timeout_ms", type="int", required=False, default=120000, hot_reloadable=True, title="传输停滞超时", description="传输流判定为停滞的超时时间(毫秒)。", category="传输引擎", ), ConfigField( key="transport.backoff_schedule", type="str", required=False, default="1,2,5,10,30", hot_reloadable=True, title="传输退避序列", description="传输重试的退避时间序列,逗号分隔的秒数列表。", category="传输引擎", ), ConfigField( key="transport.backoff_jitter", type="float", required=False, default=0.2, hot_reloadable=True, title="传输退避抖动", description="传输重试退避的随机抖动系数。", category="传输引擎", ), # FR-22 Outbox 重试策略(OBX-POLICY-UPDATE),JSON 格式: # {"max_retry": int, "ttl_seconds": int, "retry_backoff_schedule": [int, ...]} # 由 OutboxHandler.updateRetryPolicy 双写到 ConfigPort(Redis), # 支持跨请求 / 跨进程最终一致性。 ConfigField( key="outbox_retry_policy", type="json", required=False, default=None, hot_reloadable=True, title="Outbox 重试策略", description="出站消息重试策略,包含 max_retry / ttl_seconds / retry_backoff_schedule。", category="可靠性", ), # === 渠道用户与 Agent 交互链路优化(Task 6/7/8)=== # 身份解析置信度阈值(账户级,scope=ACCOUNT),低于此阈值的解析结果视为 # 未命中,进入 channel_guest 身份生成路径。缺口补齐:原 _getThreshold # 使用硬编码默认值,现统一收敛至 CONFIG_SCHEMA。 ConfigField( key="identity_confidence_threshold", type="float", required=False, default=0.5, hot_reloadable=True, title="身份置信度阈值", description="身份解析结果低于此阈值时视为未命中,进入 channel_guest 生成路径。", category="身份与权限", ), # 新建 Agent 的默认渠道访问级别(全局级,scope=GLOBAL),取值如 # "none" / "read" / "write" 等,由 Agent 创建流程读取。 ConfigField( key="channel_access_default_level", type="str", required=False, default="none", hot_reloadable=True, title="默认渠道访问级别", description="新建 Agent 默认的渠道访问级别,如 none / read / write。", category="身份与权限", ), # 渠道访客身份置信度(全局级,scope=GLOBAL),channel_guest 身份生成时 # 使用此置信度,低于 identity_confidence_threshold 以避免被误判为 # 已解析身份。 ConfigField( key="channel_guest_confidence", type="float", required=False, default=0.3, hot_reloadable=True, title="访客身份置信度", description="channel_guest 身份生成时使用的置信度,应低于身份置信度阈值。", category="身份与权限", ), # 是否启用管理员手动绑定功能(全局级,scope=GLOBAL),控制渠道用户与 # 统一身份的手动绑定入口是否可用。 ConfigField( key="enable_channel_user_binding", type="bool", required=False, default=True, hot_reloadable=True, title="手动绑定开关", description="是否启用管理员手动绑定渠道用户与统一身份的功能入口。", category="身份与权限", ), # === 渠道账号运营流程优化(Task 14:契约层 CONFIG_SCHEMA 健康检查配置键)=== # 以下 2 个键均为渠道级(scope=CHANNEL),归属 ConfigPort(Redis 热重载, # CHANNEL 业务策略体系),不得声明在 app_config。 # 凭证失效计数阈值。健康检查探活返回 401/403 时计入凭证失效计数, # 达阈值后调聚合根 markFailed。窗口外(credential_failure_window_seconds) # 重置计数。 ConfigField( key="credential_failure_threshold", type="int", required=False, default=3, hot_reloadable=True, title="凭证失效阈值", description="健康检查中凭证失效计数的阈值。", category="健康检查", ), # 凭证失效计数窗口(秒)。窗口外的失效计数被重置,避免历史故障永久影响账号状态。 ConfigField( key="credential_failure_window_seconds", type="int", required=False, default=300, hot_reloadable=True, title="凭证失效窗口", description="凭证失效计数的滑动窗口时长(秒)。", category="健康检查", ), # 审计日志保留策略(全局级,scope=GLOBAL)。JSON 格式: # {"retention_days": int, "cleanup_interval_hours": int} # 控制审计日志的保留天数与清理周期,由审计清理任务读取。 ConfigField( key="audit_retention_policy", type="json", required=False, default={"retention_days": 90, "cleanup_interval_hours": 24}, hot_reloadable=True, title="审计保留策略", description="审计日志保留天数与清理周期配置,JSON 格式。", category="审计与登录", ), # 扫码登录重定向 URI(全局级,scope=GLOBAL)。扫码登录成功后的前端跳转地址, # 为空时使用默认跳转行为。 ConfigField( key="qr_login_redirect_uri", type="str", required=False, default="", hot_reloadable=True, title="扫码登录跳转地址", description="扫码登录成功后的前端跳转地址。", category="审计与登录", ), # === 不可热更新字段(hot_reloadable=False)=== ConfigField( key="webhook_port", type="int", required=False, default=None, hot_reloadable=False, title="Webhook 端口", description="渠道 Webhook 服务监听端口。", category="基础设施", ), ConfigField( key="webhook_secret", type="str", required=False, default=None, hot_reloadable=False, title="Webhook 密钥", description="渠道 Webhook 签名验证密钥,敏感字段。", category="基础设施", ), ConfigField( key="tls_cert", type="str", required=False, default=None, hot_reloadable=False, title="TLS 证书", description="TLS 证书内容,敏感字段。", category="基础设施", ), ConfigField( key="tls_key", type="str", required=False, default=None, hot_reloadable=False, title="TLS 私钥", description="TLS 私钥内容,敏感字段。", category="基础设施", ), ConfigField( key="plugin_entry", type="str", required=False, default=None, hot_reloadable=False, title="插件入口", description="插件入口模块路径。", category="基础设施", ), ConfigField( key="database_url", type="str", required=False, default=None, hot_reloadable=False, title="数据库地址", description="业务数据库连接地址,敏感字段。", category="基础设施", ), ConfigField( key="redis_url", type="str", required=False, default=None, hot_reloadable=False, title="Redis 地址", description="Redis 连接地址,敏感字段。", category="基础设施", ), ConfigField( key="transport.max_restart_attempts", type="int", required=False, default=None, hot_reloadable=False, title="最大重启尝试", description="传输进程异常后的最大重启次数。", category="传输引擎", ), ConfigField( key="transport.graceful_shutdown_timeout_s", type="float", required=False, default=10.0, hot_reloadable=False, title="优雅关闭超时", description="传输进程优雅关闭的最大等待时间(秒)。", category="传输引擎", ), # === 渠道账号运营流程优化(Task 3:契约层 CONFIG_SCHEMA 凭证配置键)=== # 以下 3 个键均为账户级(scope=ACCOUNT),归属 ConfigPort(Redis 热重载, # CHANNEL 业务策略/toggles/TTLs 体系),不得声明在 app_config。 # 凭证包,敏感字段加密存储。由 CredentialService 唯一写入入口维护, # 包含 bot_token/app_secret/user_access_token 等渠道认证凭证。 ConfigField( key="credentials", type="dict", required=False, default=None, hot_reloadable=False, sensitive=True, title="渠道凭证包", description="渠道认证凭证集合,敏感字段。", category="凭证与登录态", ), # 登录态:unlogged/logging_in/logged_in/logout_failed。由 QrLoginService # 通过 ConfigPort 维护,区分扫码会话状态与最终凭证落库状态。 ConfigField( key="login_status", type="str", required=False, default="unlogged", hot_reloadable=False, title="登录状态", description="当前账户扫码登录状态。", category="凭证与登录态", ), # 凭证版本号,每次 rotateCredentials 自增。用于乐观锁控制与缓存失效判定。 ConfigField( key="credential_version", type="int", required=False, default=0, hot_reloadable=False, title="凭证版本", description="凭证轮换版本号,用于乐观锁与缓存失效。", category="凭证与登录态", ), # FR17-P0-3 已应用迁移列表(ACCOUNT 作用域,target=channel_type 或 # {channel_type}:{account_id})。由 PluginLifecycleManager 在插件 onStart # 成功后追加 manifest.version 标记配置已加载,DoctorService 只读不写。 # hot_reloadable=False:非业务策略,启动期写入,无需热更新。 # default=[] 让首次加载时 ConfigPort.get 返回空列表而非 NotFoundError, # 避免 PluginLifecycleManager 与 DoctorService 的 catch 块接不到异常。 ConfigField( key="applied_migrations", type="json", required=False, default=[], hot_reloadable=False, title="已应用迁移", description="账户已应用的插件迁移版本列表。", category="凭证与登录态", ), ) # 可热更新的配置项键集合(FR-37),从 CONFIG_SCHEMA 派生。 HOT_RELOADABLE_KEYS = frozenset(f.key for f in CONFIG_SCHEMA if f.hot_reloadable) # 不可热更新的配置项键集合(FR-37),从 CONFIG_SCHEMA 派生。 NON_HOT_RELOADABLE_KEYS = frozenset(f.key for f in CONFIG_SCHEMA if not f.hot_reloadable) __all__ = ["CONFIG_SCHEMA", "HOT_RELOADABLE_KEYS", "NON_HOT_RELOADABLE_KEYS"]