# -*- coding: utf-8 -*-
"""Configuration field metadata registry.

This module is the single source of truth for configuration UI metadata,
validation hints, and category grouping.
"""

from __future__ import annotations

from copy import deepcopy
from typing import Any, Dict, List, Optional

from src.config import (
    AGENT_CONTEXT_COMPRESSION_PROFILES,
    AGENT_MAX_STEPS_DEFAULT,
    DEFAULT_ALPHASIFT_INSTALL_SPEC,
)
from src.notification_noise import NOTIFICATION_SEVERITIES
from src.notification_routing import ROUTABLE_NOTIFICATION_CHANNELS

SCHEMA_VERSION = "2026-06-23-local-cli-backend"

_CATEGORY_DEFINITIONS: List[Dict[str, Any]] = [
    {
        "category": "base",
        "title": "Base Settings",
        "description": "Watchlist and foundational application settings.",
        "display_order": 10,
    },
    {
        "category": "ai_model",
        "title": "AI Model",
        "description": "Model providers, model names, and inference parameters.",
        "display_order": 20,
    },
    {
        "category": "data_source",
        "title": "Data Source",
        "description": "Market data provider credentials and priority settings.",
        "display_order": 30,
    },
    {
        "category": "notification",
        "title": "Notification",
        "description": "Bot, webhook, and push channel related settings.",
        "display_order": 40,
    },
    {
        "category": "system",
        "title": "System",
        "description": "Runtime and scheduling controls.",
        "display_order": 50,
    },
    {
        "category": "agent",
        "title": "Agent",
        "description": "Agent mode and strategy-skill settings.",
        "display_order": 55,
    },
    {
        "category": "backtest",
        "title": "Backtest",
        "description": "Backtest engine behavior and evaluation parameters.",
        "display_order": 60,
    },
    {
        "category": "uncategorized",
        "title": "Uncategorized",
        "description": "Keys not mapped in the field registry.",
        "display_order": 99,
    },
]

WEB_SETTINGS_HIDDEN_FROM_UI = {
    "DATABASE_PATH",
    "SQLITE_WAL_ENABLED",
    "SQLITE_BUSY_TIMEOUT_MS",
    "SQLITE_WRITE_RETRY_MAX",
    "SQLITE_WRITE_RETRY_BASE_DELAY",
    "USE_PROXY",
    "PROXY_HOST",
    "PROXY_PORT",
}

_FIELD_DEFINITIONS: Dict[str, Dict[str, Any]] = {
    "STOCK_LIST": {
        "title": "Stock List",
        "description": "Comma-separated watchlist stock codes.",
        "category": "base",
        "data_type": "array",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "600519,300750,002594",
        "options": [],
        "validation": {"min_items": 1},
        "display_order": 10,
        "help_key": "settings.base.STOCK_LIST",
        "examples": [
            "STOCK_LIST=600519,300750,002594",
            "STOCK_LIST=600519,hk00700,AAPL",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
            {
                "label": "Tushare 股票列表指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/TUSHARE_STOCK_LIST_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    # ------------------------------------------------------------------
    # AI Model – LiteLLM unified config
    # ------------------------------------------------------------------
    "GENERATION_BACKEND": {
        "title": "Analysis Generation Method",
        "description": "Generation method used by stock analysis, market reviews, and regular text generation.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "litellm",
        "options": [
            {"label": "Default model settings", "value": "litellm"},
            {"label": "Codex CLI (experimental)", "value": "codex_cli"},
        ],
        "validation": {"enum": ["litellm", "codex_cli"]},
        "display_order": 0,
        "help_key": "settings.ai_model.GENERATION_BACKEND",
        "examples": ["GENERATION_BACKEND=litellm", "GENERATION_BACKEND=codex_cli"],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "GENERATION_FALLBACK_BACKEND": {
        "title": "Fallback Generation Method",
        "description": "Backend-level fallback method. Empty disables backend fallback; litellm can be used as fallback for Codex CLI.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "litellm",
        "options": [
            {"label": "Disabled", "value": ""},
            {"label": "Default model settings", "value": "litellm"},
        ],
        "validation": {"enum": ["", "litellm"]},
        "display_order": 0,
        "help_key": "settings.ai_model.GENERATION_FALLBACK_BACKEND",
        "examples": ["GENERATION_FALLBACK_BACKEND=litellm", "GENERATION_FALLBACK_BACKEND="],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "GENERATION_BACKEND_TIMEOUT_SECONDS": {
        "title": "Generation Backend Timeout",
        "description": "Maximum seconds allowed for one generation backend call. Applies to local CLI backends; LiteLLM behavior is unchanged.",
        "category": "ai_model",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "300",
        "options": [],
        "validation": {"min": 1, "max": 3600},
        "display_order": 1,
        "help_key": "settings.ai_model.GENERATION_BACKEND_TIMEOUT_SECONDS",
        "examples": ["GENERATION_BACKEND_TIMEOUT_SECONDS=300"],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "GENERATION_BACKEND_MAX_OUTPUT_BYTES": {
        "title": "Generation Backend Max Output Bytes",
        "description": (
            "Maximum captured diagnostic stdout/stderr and final-response bytes "
            "for one local CLI backend call."
        ),
        "category": "ai_model",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "1048576",
        "options": [],
        "validation": {"min": 1, "max": 33554432},
        "display_order": 1,
        "help_key": "settings.ai_model.GENERATION_BACKEND_MAX_OUTPUT_BYTES",
        "examples": ["GENERATION_BACKEND_MAX_OUTPUT_BYTES=1048576"],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "GENERATION_BACKEND_MAX_CONCURRENCY": {
        "title": "Generation Backend Max Concurrency",
        "description": "Global generation backend concurrency cap. Local CLI effective concurrency also respects LOCAL_CLI_BACKEND_MAX_CONCURRENCY.",
        "category": "ai_model",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "1",
        "options": [],
        "validation": {"min": 1, "max": 16},
        "display_order": 1,
        "help_key": "settings.ai_model.GENERATION_BACKEND_MAX_CONCURRENCY",
        "examples": ["GENERATION_BACKEND_MAX_CONCURRENCY=1"],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "LOCAL_CLI_BACKEND_MAX_CONCURRENCY": {
        "title": "Local CLI Backend Max Concurrency",
        "description": "Local CLI backend concurrency cap. Effective local CLI concurrency is the minimum of this value and GENERATION_BACKEND_MAX_CONCURRENCY.",
        "category": "ai_model",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "1",
        "options": [],
        "validation": {"min": 1, "max": 4},
        "display_order": 1,
        "help_key": "settings.ai_model.LOCAL_CLI_BACKEND_MAX_CONCURRENCY",
        "examples": ["LOCAL_CLI_BACKEND_MAX_CONCURRENCY=1"],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "LITELLM_MODEL": {
        "title": "Primary Model",
        "description": "Primary model in provider/model format (e.g. gemini/gemini-3.1-pro-preview, deepseek/deepseek-v4-flash, anthropic/claude-sonnet-4-6). If empty, it is auto-inferred from available API keys or channel declarations.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 1,
        "help_key": "settings.ai_model.LITELLM_MODEL",
        "examples": [
            "LITELLM_MODEL=deepseek/deepseek-v4-flash",
            "LITELLM_MODEL=gemini/gemini-3.1-pro-preview",
            "LITELLM_MODEL=ollama/qwen3:8b",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "完整指南：AI 模型配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#ai-模型配置",
            },
            {
                "label": "LiteLLM Providers（官方）",
                "href": "https://docs.litellm.ai/docs/providers",
            },
            {
                "label": "LiteLLM OpenAI-compatible（官方）",
                "href": "https://docs.litellm.ai/docs/providers/openai_compatible",
            },
            {
                "label": "LiteLLM model_list（官方）",
                "href": "https://docs.litellm.ai/docs/proxy/configs#the-model_list-key",
            },
        ],
        "warning_codes": ["provider_prefix_required"],
    },
    "AGENT_LITELLM_MODEL": {
        "title": "Agent Primary Model",
        "description": "Optional Agent-only primary model in provider/model format. When empty, Agent inherits the primary model. Bare model names are normalized to openai/<model>.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 2,
        "help_key": "settings.ai_model.AGENT_LITELLM_MODEL",
        "examples": [
            "AGENT_LITELLM_MODEL=deepseek/deepseek-v4-pro",
            "AGENT_LITELLM_MODEL=gemini/gemini-3.1-pro-preview",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "完整指南：AI 模型配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#ai-模型配置",
            },
        ],
        "warning_codes": ["inherits_primary_when_empty"],
    },
    "LITELLM_FALLBACK_MODELS": {
        "title": "Fallback Models",
        "description": "Comma-separated fallback models tried when the primary model fails (e.g. anthropic/claude-sonnet-4-6,openai/gpt-5.4-mini). Useful for cross-provider redundancy.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 2,
        "help_key": "settings.ai_model.LITELLM_FALLBACK_MODELS",
        "examples": [
            "LITELLM_FALLBACK_MODELS=deepseek/deepseek-v4-pro,gemini/gemini-3-flash-preview",
            "LITELLM_FALLBACK_MODELS=openai/gpt-5.4-mini",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "完整指南：AI 模型配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#ai-模型配置",
            },
            {
                "label": "LiteLLM Providers（官方）",
                "href": "https://docs.litellm.ai/docs/providers",
            },
            {
                "label": "LiteLLM config（官方）",
                "href": "https://docs.litellm.ai/docs/proxy/configs",
            },
        ],
        "warning_codes": ["fallback_models_must_be_available"],
    },
    # ------------------------------------------------------------------
    # AI Model – Multi-channel LLM configuration
    # ------------------------------------------------------------------
    "LITELLM_CONFIG": {
        "title": "Advanced Model Routing Config",
        "description": "Path to an advanced model routing YAML file (expert use). When valid/parseable and yields a model_list, it takes priority over channels and legacy keys; otherwise channels/legacy are used as fallback.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 3,
        "help_key": "settings.ai_model.LITELLM_CONFIG",
        "examples": [
            "LITELLM_CONFIG=./litellm_config.yaml",
        ],
        "docs": [
            {
                "label": "LLM 配置指南：YAML 模式",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md#方式三litellm-原生-yaml-配置适合专家或复杂路由",
            },
            {
                "label": "LiteLLM proxy config（官方）",
                "href": "https://docs.litellm.ai/docs/proxy/configs",
            },
        ],
        "warning_codes": ["yaml_config_overrides_channel_editor"],
    },
    "LLM_CHANNELS": {
        "title": "LLM Channels",
        "description": "Channel names (comma-separated). Managed by the channel editor above.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 4,
        "help_key": "settings.ai_model.LLM_CHANNELS",
        "examples": [
            "LLM_CHANNELS=deepseek,aihubmix",
            "LLM_DEEPSEEK_BASE_URL=https://api.deepseek.com",
            "LLM_DEEPSEEK_API_KEY=sk-xxxx",
            "LLM_DEEPSEEK_MODELS=deepseek-v4-flash,deepseek-v4-pro",
        ],
        "docs": [
            {
                "label": "LLM 配置指南：渠道模式",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md#方式二渠道channels模式配置适合进阶多模型",
            },
            {
                "label": "LLM 服务商配置速查",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
            },
            {
                "label": "LiteLLM OpenAI-compatible（官方）",
                "href": "https://docs.litellm.ai/docs/providers/openai_compatible",
            },
        ],
        "warning_codes": ["channels_override_legacy_keys"],
    },
    "LLM_TEMPERATURE": {
        "title": "Temperature",
        "description": "Unified sampling temperature for all LLM calls. Range [0.0, 2.0], default 0.7.",
        "category": "ai_model",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0.7",
        "options": [],
        "validation": {"min": 0.0, "max": 2.0},
        "display_order": 5,
        "help_key": "settings.ai_model.LLM_TEMPERATURE",
        "examples": [
            "LLM_TEMPERATURE=0.2",
            "LLM_TEMPERATURE=0.7",
        ],
        "docs": [
            {
                "label": "完整指南：AI 模型配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#ai-模型配置",
            },
        ],
        "warning_codes": [],
    },
    "LLM_PROMPT_CACHE_TELEMETRY_ENABLED": {
        "title": "Prompt Cache Telemetry",
        "description": "Records provider prompt-cache usage telemetry and normalized cache diagnostics when providers return cache usage fields. This does not enable or disable provider implicit cache.",
        "category": "ai_model",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 6,
        "help_key": "settings.ai_model.LLM_PROMPT_CACHE_TELEMETRY_ENABLED",
        "examples": [
            "LLM_PROMPT_CACHE_TELEMETRY_ENABLED=true",
            "LLM_PROMPT_CACHE_TELEMETRY_ENABLED=false",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "LLM_PROMPT_CACHE_HINTS_ENABLED": {
        "title": "Prompt Cache Hints",
        "description": "Allows the project to send verified provider-specific prompt-cache hints such as prompt_cache_key or cache_control. Unknown providers and unverified routes remain telemetry-only.",
        "category": "ai_model",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 7,
        "help_key": "settings.ai_model.LLM_PROMPT_CACHE_HINTS_ENABLED",
        "examples": [
            "LLM_PROMPT_CACHE_HINTS_ENABLED=false",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL": {
        "title": "Prompt Cache Diagnostics",
        "description": "Controls non-sensitive prompt-cache capability and hint decision diagnostics. Values: off, basic, debug.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "off",
        "options": [
            {"value": "off", "label": "Off"},
            {"value": "basic", "label": "Basic"},
            {"value": "debug", "label": "Debug"},
        ],
        "validation": {"enum": ["off", "basic", "debug"]},
        "display_order": 8,
        "help_key": "settings.ai_model.LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL",
        "examples": [
            "LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL=off",
            "LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL=basic",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "LLM_USAGE_HMAC_SECRET": {
        "title": "LLM Usage HMAC Secret",
        "description": "Optional deployment-scoped secret for LLM usage telemetry prompt/message HMAC. Leave empty to use a local generated secret file. If set, use a high-entropy random value and do not commit it to version control.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 9,
        "help_key": "settings.ai_model.LLM_USAGE_HMAC_SECRET",
        "examples": [
            "LLM_USAGE_HMAC_SECRET=<64-char random hex from openssl rand -hex 32>",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "LLM_USAGE_HMAC_KEY_VERSION": {
        "title": "LLM Usage HMAC Key Version",
        "description": "Version label for the LLM usage telemetry HMAC key. Change it when rotating LLM_USAGE_HMAC_SECRET.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "local-v1",
        "options": [],
        "validation": {},
        "display_order": 10,
        "help_key": "settings.ai_model.LLM_USAGE_HMAC_KEY_VERSION",
        "examples": [
            "LLM_USAGE_HMAC_KEY_VERSION=prod-2026-06",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "AIHUBMIX_KEY": {
        "title": "AIHubmix Key",
        "description": "AIHubmix one-stop API key – access all mainstream models with a single key, no VPN required. Auto-sets base URL to aihubmix.com/v1. Get key: https://aihubmix.com/?aff=CfMq",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 5,
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "AIHUBMIX_KEY=sk-xxxx",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "LLM 服务商配置速查",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "ANSPIRE_LLM_ENABLED": {
        "title": "Anspire LLM Enabled",
        "description": "Use ANSPIRE_API_KEYS as an OpenAI-compatible Anspire LLM key when no higher-priority LLM channel or OpenAI-compatible key is configured.",
        "category": "ai_model",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 6,
    },
    "ANSPIRE_LLM_BASE_URL": {
        "title": "Anspire LLM Base URL",
        "description": "Anspire OpenAI-compatible gateway. Default: https://open-gateway.anspire.cn/v6; global endpoint: https://open-gateway.anspire.ai/v6.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "https://open-gateway.anspire.cn/v6",
        "options": [],
        "validation": {"format": "url"},
        "display_order": 7,
    },
    "ANSPIRE_LLM_MODEL": {
        "title": "Anspire LLM Model",
        "description": "Default model used when ANSPIRE_API_KEYS enables the Anspire LLM gateway without an explicit LITELLM_MODEL.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "Doubao-Seed-2.0-lite",
        "options": [],
        "validation": {},
        "display_order": 8,
    },
    # ------------------------------------------------------------------
    # AI Model – DeepSeek official (independent from OpenAI-compatible)
    # ------------------------------------------------------------------
    "DEEPSEEK_API_KEY": {
        "title": "DeepSeek API Key",
        "description": "Official DeepSeek API key (from https://platform.deepseek.com). For compatibility, a key set alone still auto-infers deepseek/deepseek-chat and logs a deprecation warning; new configs should migrate to deepseek/deepseek-v4-flash. Also works in multi-channel mode.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 6,
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "DEEPSEEK_API_KEY=sk-xxxx",
            "LITELLM_MODEL=deepseek/deepseek-v4-flash",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "LLM 服务商配置速查",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "DEEPSEEK_API_KEYS": {
        "title": "DeepSeek API Keys (Multi)",
        "description": "Comma-separated DeepSeek API keys for load balancing. Takes priority over DEEPSEEK_API_KEY.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 7,
    },
    "TUSHARE_TOKEN": {
        "title": "Tushare Token",
        "description": "Token for Tushare Pro API.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 10,
        "help_key": "settings.data_source.TUSHARE_TOKEN",
        "examples": [
            "TUSHARE_TOKEN=your_tushare_token",
        ],
        "docs": [
            {
                "label": "Tushare 股票列表指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/TUSHARE_STOCK_LIST_GUIDE.md",
            },
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "TICKFLOW_API_KEY": {
        "title": "TickFlow API Key",
        "description": "API key for TickFlow market review enhancement (A-share indices, plus market stats when universe queries are enabled).",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 15,
    },
    "STOCK_INDEX_REMOTE_UPDATE_ENABLED": {
        "title": "Remote Stock Index Updates",
        "description": "Automatically refresh the local stock autocomplete index from the built-in GitHub main source.",
        "category": "data_source",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 16,
        "help_key": "settings.data_source.stock_index_remote",
        "examples": [
            "STOCK_INDEX_REMOTE_UPDATE_ENABLED=true",
            "STOCK_INDEX_REMOTE_UPDATE_ENABLED=false",
        ],
        "docs": [
            {
                "label": "Tushare 股票列表指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/TUSHARE_STOCK_LIST_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "ALPHASIFT_ENABLED": {
        "title": "AlphaSift Screening",
        "description": "Enable the built-in AlphaSift stock screening tab. Disabled by default. This switch only affects the AlphaSift screening path; it does not migrate, sanitize, or clear existing LLM/runtime fields in `.env`.",
        "category": "data_source",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 17,
        "help_key": "settings.data_source.ALPHASIFT_ENABLED",
        "examples": [
            "ALPHASIFT_ENABLED=false",
            "ALPHASIFT_ENABLED=true",
        ],
        "docs": [
            {
                "label": "LiteLLM Providers（官方）",
                "href": "https://docs.litellm.ai/docs/providers",
            },
            {
                "label": "LiteLLM OpenAI-compatible（官方）",
                "href": "https://docs.litellm.ai/docs/providers/openai_compatible",
            },
            {
                "label": "OpenAI 请求与鉴权（官方）",
                "href": "https://platform.openai.com/docs/api-reference/authentication",
            },
            {
                "label": "AlphaSift 集成说明",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/alphasift-integration.md",
            },
        ],
    },
    "ALPHASIFT_INSTALL_SPEC": {
        "title": "AlphaSift Install Spec",
        "description": "Pinned AlphaSift pip source used for explicit repair installs and source verification. It is not used for normal runtime calls after startup dependency installation; runtime compatibility is built from DSA's resolved LLM/runtime context.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": DEFAULT_ALPHASIFT_INSTALL_SPEC,
        "options": [],
        "validation": {},
        "display_order": 18,
        "help_key": "settings.data_source.ALPHASIFT_INSTALL_SPEC",
        "examples": [
            f"ALPHASIFT_INSTALL_SPEC={DEFAULT_ALPHASIFT_INSTALL_SPEC}",
        ],
        "docs": [
            {
                "label": "requirements.txt（版本与依赖边界）",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/requirements.txt",
            },
            {
                "label": "AlphaSift 集成说明",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/alphasift-integration.md",
            },
        ],
    },
    "REALTIME_SOURCE_PRIORITY": {
        "title": "Realtime Source Priority",
        "description": "Comma-separated priority for realtime quote providers.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "tencent,akshare_sina,efinance,akshare_em",
        "options": [],
        "validation": {},
        "display_order": 20,
        "help_key": "settings.data_source.REALTIME_SOURCE_PRIORITY",
        "examples": [
            "REALTIME_SOURCE_PRIORITY=tencent,akshare_sina,efinance,akshare_em",
        ],
        "docs": [
            {
                "label": "完整指南：数据源配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#数据源配置",
            },
        ],
        "warning_codes": ["provider_priority_order"],
    },
    "ENABLE_REALTIME_TECHNICAL_INDICATORS": {
        "title": "Realtime Technical Indicators",
        "description": "Use intraday realtime price for MA5/MA10/MA20 and trend analysis (Issue #234). Disable to use yesterday close.",
        "category": "data_source",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 21,
        "help_key": "settings.data_source.realtime_quotes",
        "examples": [
            "ENABLE_REALTIME_TECHNICAL_INDICATORS=true",
            "ENABLE_REALTIME_TECHNICAL_INDICATORS=false",
        ],
        "docs": [
            {
                "label": "完整指南：数据源配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#数据源配置",
            },
        ],
        "warning_codes": [],
    },
    "ANSPIRE_API_KEYS": {
        "title": "Anspire API Keys",
        "description": "Comma-separated Anspire Open API keys. Used by Anspire Search and, by default, the Anspire OpenAI-compatible LLM gateway.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 22,
        "help_key": "settings.data_source.search_api_keys",
        "examples": [
            "ANSPIRE_API_KEYS=key1,key2",
        ],
        "docs": [
            {
                "label": "完整指南：搜索服务配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#搜索服务配置",
            },
        ],
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "TAVILY_API_KEYS": {
        "title": "Tavily API Keys",
        "description": "Comma-separated Tavily API keys.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 30,
        "help_key": "settings.data_source.search_api_keys",
        "examples": [
            "TAVILY_API_KEYS=tvly-xxxx",
            "TAVILY_API_KEYS=tvly-key-1,tvly-key-2",
        ],
        "docs": [
            {
                "label": "完整指南：搜索服务配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#搜索服务配置",
            },
        ],
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "SERPAPI_API_KEYS": {
        "title": "SerpAPI Keys",
        "description": "Comma-separated SerpAPI keys.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 40,
    },
    "BRAVE_API_KEYS": {
        "title": "Brave API Keys",
        "description": "Comma-separated Brave Search API keys.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 50,
    },
    "BOCHA_API_KEYS": {
        "title": "Bocha API Keys",
        "description": "Comma-separated Bocha Search API keys.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 51,
    },
    "MINIMAX_API_KEYS": {
        "title": "MiniMax API Key",
        "description": "MiniMax API key (search priority: Bocha > Tavily > Brave > SerpAPI > MiniMax > SearXNG).",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 53,
    },
    "SEARXNG_BASE_URLS": {
        "title": "SearXNG Base URLs",
        "description": "Comma-separated SearXNG instance URLs (self-hosted, no quota). Enable format: json in settings.yml.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {
            "multi_value": True,
            "delimiter": ",",
            "item_type": "url",
            "allowed_schemes": ["http", "https"],
        },
        "display_order": 52,
        "help_key": "settings.data_source.SEARXNG_BASE_URLS",
        "examples": [
            "SEARXNG_BASE_URLS=https://search.example.com",
            "SEARXNG_PUBLIC_INSTANCES_ENABLED=false",
        ],
        "docs": [
            {
                "label": "完整指南：搜索服务配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#搜索服务配置",
            },
        ],
        "warning_codes": ["requires_json_format"],
    },
    "SEARXNG_PUBLIC_INSTANCES_ENABLED": {
        "title": "SearXNG Public Instances",
        "description": "Auto-discover public SearXNG instances from searx.space when SEARXNG_BASE_URLS is empty. Default: true; set false to disable.",
        "category": "data_source",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 53,
    },
    "ENABLE_REALTIME_QUOTE": {
        "title": "Enable Realtime Quote",
        "description": "Enable realtime market quotes. Disable to only use historical close prices.",
        "category": "data_source",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 22,
        "help_key": "settings.data_source.realtime_quotes",
        "examples": [
            "ENABLE_REALTIME_QUOTE=true",
            "ENABLE_REALTIME_QUOTE=false",
        ],
        "docs": [
            {
                "label": "完整指南：数据源配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#数据源配置",
            },
        ],
        "warning_codes": [],
    },
    "ENABLE_CHIP_DISTRIBUTION": {
        "title": "Enable Chip Distribution",
        "description": "Enable chip distribution analysis. May be unstable; recommended to disable on cloud deployments.",
        "category": "data_source",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 23,
        "help_key": "settings.data_source.ENABLE_CHIP_DISTRIBUTION",
        "examples": [
            "ENABLE_CHIP_DISTRIBUTION=true",
            "ENABLE_CHIP_DISTRIBUTION=false",
        ],
        "docs": [
            {
                "label": "完整指南：数据源配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#数据源配置",
            },
        ],
        "warning_codes": ["cloud_deployments_may_disable"],
    },
    "NEWS_MAX_AGE_DAYS": {
        "title": "News Max Age (Days)",
        "description": "Maximum age of news in days. Older articles are excluded from analysis context.",
        "category": "data_source",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "3",
        "options": [],
        "validation": {"min": 1, "max": 30},
        "display_order": 60,
        "help_key": "settings.data_source.news_window",
        "examples": [
            "NEWS_MAX_AGE_DAYS=3",
            "NEWS_STRATEGY_PROFILE=short",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "NEWS_STRATEGY_PROFILE": {
        "title": "News Strategy Profile",
        "description": "News window profile: ultra_short(1d), short(3d), medium(7d), long(30d). Effective window = min(profile, NEWS_MAX_AGE_DAYS).",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "short",
        "options": ["ultra_short", "short", "medium", "long"],
        "validation": {"enum": ["ultra_short", "short", "medium", "long"]},
        "display_order": 61,
        "help_key": "settings.data_source.news_window",
        "examples": [
            "NEWS_STRATEGY_PROFILE=short",
            "NEWS_MAX_AGE_DAYS=3",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "BIAS_THRESHOLD": {
        "title": "Bias Threshold (%)",
        "description": "Deviation threshold from MA5 (%). Exceeding this triggers 'do not chase' warning. Strong trend stocks auto-widen to 1.5x.",
        "category": "data_source",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "5.0",
        "options": [],
        "validation": {"min": 0.0, "max": 50.0},
        "display_order": 62,
    },
    "PYTDX_HOST": {
        "title": "Pytdx Host",
        "description": "Tongdaxin data server IP. Used with PYTDX_PORT. Overrides built-in defaults.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 55,
    },
    "PYTDX_PORT": {
        "title": "Pytdx Port",
        "description": "Tongdaxin data server port (e.g. 7709). Used with PYTDX_HOST.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 56,
    },
    "PYTDX_SERVERS": {
        "title": "Pytdx Servers",
        "description": "Comma-separated ip:port (e.g. 192.168.1.1:7709,10.0.0.1:7709). Overrides PYTDX_HOST+PYTDX_PORT.",
        "category": "data_source",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 57,
    },
    "GEMINI_API_KEY": {
        "title": "Gemini API Key",
        "description": "Single API key for Gemini service (from https://aistudio.google.com).",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 10,
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "GEMINI_API_KEY=your_gemini_api_key",
            "LITELLM_MODEL=gemini/gemini-3.1-pro-preview",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "LLM 服务商配置速查",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "GEMINI_API_KEYS": {
        "title": "Gemini API Keys (Multi)",
        "description": "Comma-separated Gemini API keys for load balancing. Takes priority over GEMINI_API_KEY.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 11,
    },
    "GEMINI_MODEL": {
        "title": "Gemini Model",
        "description": "Gemini model name.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "gemini-3.1-pro-preview",
        "options": [],
        "validation": {},
        "display_order": 20,
    },
    "GEMINI_MODEL_FALLBACK": {
        "title": "Gemini Fallback Model",
        "description": "Fallback Gemini model name (used when LITELLM_FALLBACK_MODELS is not set and primary is Gemini).",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "gemini-3-flash-preview",
        "options": [],
        "validation": {},
        "display_order": 21,
    },
    "GEMINI_TEMPERATURE": {
        "title": "Gemini Temperature",
        "description": "Temperature in range [0.0, 2.0].",
        "category": "ai_model",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0.7",
        "options": [],
        "validation": {"min": 0.0, "max": 2.0},
        "display_order": 30,
    },
    "OPENAI_API_KEY": {
        "title": "OpenAI API Key",
        "description": "API key for OpenAI-compatible service.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 40,
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "OPENAI_API_KEY=sk-xxxx",
            "OPENAI_BASE_URL=https://api.example.com/v1",
            "LITELLM_MODEL=openai/gpt-5.5",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "LLM 服务商配置速查",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "OPENAI_API_KEYS": {
        "title": "OpenAI API Keys (Multi)",
        "description": "Comma-separated OpenAI-compatible API keys for load balancing. Takes priority over AIHUBMIX_KEY and OPENAI_API_KEY.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 41,
    },
    "OPENAI_BASE_URL": {
        "title": "OpenAI Base URL",
        "description": "Base URL for OpenAI-compatible endpoint.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 50,
        "help_key": "settings.ai_model.OPENAI_BASE_URL",
        "examples": [
            "OPENAI_BASE_URL=https://api.openai.com/v1",
            "OPENAI_BASE_URL=https://api.example.com/v1",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "LLM 服务商配置速查",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
            },
        ],
        "warning_codes": ["base_url_must_match_provider"],
    },
    "OPENAI_MODEL": {
        "title": "OpenAI Model",
        "description": "Model name for OpenAI-compatible endpoint.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "gpt-5.5",
        "options": [],
        "validation": {},
        "display_order": 60,
    },
    "OPENAI_VISION_MODEL": {
        "title": "OpenAI Vision Model",
        "description": "Model for image extraction (some APIs e.g. DeepSeek lack vision). Leave empty to use OPENAI_MODEL.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 61,
    },
    "OPENAI_TEMPERATURE": {
        "title": "OpenAI Temperature",
        "description": "Temperature for OpenAI-compatible models in range [0.0, 2.0].",
        "category": "ai_model",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0.7",
        "options": [],
        "validation": {"min": 0.0, "max": 2.0},
        "display_order": 62,
    },
    "ANTHROPIC_API_KEY": {
        "title": "Anthropic API Key",
        "description": "Anthropic Claude API key (from https://console.anthropic.com).",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 35,
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "ANTHROPIC_API_KEY=sk-ant-xxxx",
            "LITELLM_MODEL=anthropic/claude-sonnet-4-6",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
            {
                "label": "LLM 服务商配置速查",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "ANTHROPIC_API_KEYS": {
        "title": "Anthropic API Keys (Multi)",
        "description": "Comma-separated Anthropic API keys for load balancing. Takes priority over ANTHROPIC_API_KEY.",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 35,
    },
    "ANTHROPIC_MODEL": {
        "title": "Anthropic Model",
        "description": "Claude 模型名称（如 claude-sonnet-4-6）。",
        "category": "ai_model",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "claude-sonnet-4-6",
        "options": [],
        "validation": {},
        "display_order": 36,
    },
    "ANTHROPIC_TEMPERATURE": {
        "title": "Anthropic Temperature",
        "description": "温度参数，范围 [0.0, 1.0]。",
        "category": "ai_model",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0.7",
        "options": [],
        "validation": {"min": 0.0, "max": 1.0},
        "display_order": 37,
    },
    "ANTHROPIC_MAX_TOKENS": {
        "title": "Anthropic Max Tokens",
        "description": "Anthropic API 响应最大 token 数（默认 8192）。",
        "category": "ai_model",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "8192",
        "options": [],
        "validation": {"min": 256, "max": 8192},
        "display_order": 38,
    },
    "WECHAT_WEBHOOK_URL": {
        "title": "WeChat Webhook URL",
        "description": "Webhook URL for enterprise WeChat bot.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 10,
        "help_key": "settings.notification.webhooks",
        "examples": [
            "WECHAT_WEBHOOK_URL=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxx",
        ],
        "docs": [
            {
                "label": "完整指南：通知渠道配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#通知渠道详细配置",
            },
        ],
        "warning_codes": ["webhook_secret_value"],
    },
    "DINGTALK_APP_KEY": {
        "title": "DingTalk App Key",
        "description": "DingTalk app key.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 20,
    },
    "DINGTALK_APP_SECRET": {
        "title": "DingTalk App Secret",
        "description": "DingTalk app secret.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 30,
    },
    "DINGTALK_STREAM_ENABLED": {
        "title": "DingTalk Stream Mode",
        "description": "Enable DingTalk application bot stream/long-connection mode. This is separate from DingTalk group webhook delivery.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 35,
        "help_key": "settings.notification.DINGTALK_STREAM_ENABLED",
        "examples": [
            "DINGTALK_STREAM_ENABLED=false",
            "DINGTALK_STREAM_ENABLED=true",
            "DINGTALK_APP_KEY=your_dingtalk_app_key",
            "DINGTALK_APP_SECRET=your_dingtalk_app_secret",
        ],
        "docs": [
            {
                "label": "完整指南：通知渠道配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#通知渠道详细配置",
            },
        ],
        "warning_codes": ["not_webhook_delivery", "restart_required"],
    },
    "PUSHPLUS_TOKEN": {
        "title": "PushPlus Token",
        "description": "Token for PushPlus notifications.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 40,
    },
    "CUSTOM_WEBHOOK_URLS": {
        "title": "Custom Webhook URLs",
        "description": "Comma-separated webhook URLs for custom notifications (DingTalk, Bark, Discord, Slack, etc.).",
        "category": "notification",
        "data_type": "array",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "help_key": "settings.notification.CUSTOM_WEBHOOK_URLS",
        "examples": [
            "CUSTOM_WEBHOOK_URLS=https://api.day.app/YOUR_BARK_KEY",
            "CUSTOM_WEBHOOK_URLS=https://oapi.dingtalk.com/robot/send?access_token=xxx",
            "CUSTOM_WEBHOOK_BODY_TEMPLATE={\"msg_type\":\"text\",\"content\":$content_json}",
        ],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 50,
        "docs": [
            {
                "label": "完整指南：自定义 Webhook",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#自定义-webhook",
            },
        ],
        "warning_codes": ["json_template_must_render_object"],
    },
    "CUSTOM_WEBHOOK_BEARER_TOKEN": {
        "title": "Custom Webhook Bearer Token",
        "description": "Bearer token for authenticated custom webhooks.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 51,
    },
    "CUSTOM_WEBHOOK_BODY_TEMPLATE": {
        "title": "Custom Webhook Body Template",
        "description": (
            "Optional global JSON body template for custom webhooks. It is rendered before "
            "URL auto-detected payloads such as Bark, Slack, or Discord, and must render to a "
            "JSON object. Prefer $content_json and $title_json; raw $content and $title are "
            "not JSON-escaped and can make the template invalid."
        ),
        "category": "notification",
        "data_type": "string",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 52,
    },
    "WEBHOOK_VERIFY_SSL": {
        "title": "Webhook SSL Verify",
        "description": "Verify HTTPS certificates for webhook requests. Set to false ONLY for self-signed certs in trusted internal networks. WARNING: Disabling allows MITM attacks—do NOT use on public networks.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 53,
        "help_key": "settings.notification.WEBHOOK_VERIFY_SSL",
        "examples": [
            "WEBHOOK_VERIFY_SSL=true",
            "WEBHOOK_VERIFY_SSL=false",
        ],
        "docs": [
            {
                "label": "完整指南：自定义 Webhook",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#自定义-webhook",
            },
        ],
        "warning_codes": ["disabling_ssl_verify_is_risky"],
    },
    "REPORT_SUMMARY_ONLY": {
        "title": "Report Summary Only",
        "description": "Push only analysis summary without per-stock details. Suitable for quick overview when tracking many stocks (Issue #262).",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 53,
        "help_key": "settings.report.REPORT_SUMMARY_ONLY",
        "examples": [
            "REPORT_SUMMARY_ONLY=true",
            "REPORT_SUMMARY_ONLY=false",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_SHOW_LLM_MODEL": {
        "title": "Show LLM Model in Reports",
        "description": "Show the LLM model name in notification report footers. Disable to hide runtime model metadata. This option only affects report rendering and does not change runtime provider/model/Base URL/LiteLLM routing, migration, persistence, or cleanup behavior.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 54,
        "help_key": "settings.report.REPORT_SHOW_LLM_MODEL",
        "examples": [
            "REPORT_SHOW_LLM_MODEL=true",
            "REPORT_SHOW_LLM_MODEL=false",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    # ------------------------------------------------------------------
    # Notification – Feishu
    # ------------------------------------------------------------------
    "FEISHU_WEBHOOK_URL": {
        "title": "Feishu Webhook URL",
        "description": "Feishu custom bot webhook URL for group notifications. This is the webhook push channel; FEISHU_APP_ID / FEISHU_APP_SECRET do not enable webhook delivery by themselves.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {
            "item_type": "url",
            "allowed_schemes": ["http", "https"],
        },
        "display_order": 12,
        "help_key": "settings.notification.FEISHU_WEBHOOK_URL",
        "examples": [
            "FEISHU_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/your_hook_token",
            "FEISHU_WEBHOOK_SECRET=your_feishu_webhook_secret",
            "FEISHU_WEBHOOK_KEYWORD=股票日报",
        ],
        "docs": [
            {
                "label": "完整指南：飞书通知配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#飞书",
            },
            {
                "label": "飞书机器人配置专题",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/bot/feishu-bot-config.md",
            },
        ],
        "warning_codes": ["feishu_webhook_not_app_secret"],
    },
    "FEISHU_WEBHOOK_SECRET": {
        "title": "Feishu Webhook Secret",
        "description": "Optional signing secret from Feishu custom bot security settings. Only used for webhook push mode.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 13,
    },
    "FEISHU_WEBHOOK_KEYWORD": {
        "title": "Feishu Webhook Keyword",
        "description": "Optional keyword required by Feishu custom bot security settings. The sender prepends it to every webhook message.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 14,
    },
    "FEISHU_APP_ID": {
        "title": "Feishu App ID",
        "description": "Feishu app bot App ID for app/stream bot mode or cloud documents. It does not enable group webhook push by itself.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 15,
    },
    "FEISHU_APP_SECRET": {
        "title": "Feishu App Secret",
        "description": "Feishu app bot App Secret for app/stream bot mode or cloud documents. It does not enable group webhook push by itself.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 16,
    },
    "FEISHU_STREAM_ENABLED": {
        "title": "Feishu Stream Mode",
        "description": "Enable Feishu application bot stream mode. This is separate from Feishu group webhook delivery.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 17,
        "help_key": "settings.notification.FEISHU_STREAM_ENABLED",
        "examples": [
            "FEISHU_STREAM_ENABLED=false",
            "FEISHU_STREAM_ENABLED=true",
            "FEISHU_APP_ID=cli_xxxxx",
            "FEISHU_APP_SECRET=your_feishu_app_secret",
        ],
        "docs": [
            {
                "label": "完整指南：飞书通知配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#飞书",
            },
            {
                "label": "飞书机器人配置专题",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/bot/feishu-bot-config.md",
            },
        ],
        "warning_codes": ["not_webhook_delivery", "restart_required"],
    },
    "FEISHU_CHAT_ID": {
        "title": "Feishu Chat ID",
        "description": "Target chat_id (group mode, oc_xxx) or open_id (P2P mode, ou_xxx) for Feishu App Bot notification delivery. Requires FEISHU_APP_ID + FEISHU_APP_SECRET.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 18,
        "help_key": "settings.notification.FEISHU_CHAT_ID",
        "examples": [
            "FEISHU_CHAT_ID=oc_xxxxxxxxxxxxx",
            "FEISHU_CHAT_ID=ou_xxxxxxxxxxxxx",
        ],
        "docs": [
            {
                "label": "完整指南：飞书通知配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#飞书",
            },
        ],
    },
    "FEISHU_RECEIVE_ID_TYPE": {
        "title": "Feishu Receive ID Type",
        "description": "Type of FEISHU_CHAT_ID: 'chat_id' for group chat, 'open_id' for P2P private message.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "chat_id",
        "options": [{"label": "chat_id (群聊)", "value": "chat_id"}, {"label": "open_id (私聊)", "value": "open_id"}],
        "validation": {"enum": ["chat_id", "open_id"]},
        "display_order": 19,
        "help_key": "settings.notification.FEISHU_RECEIVE_ID_TYPE",
        "examples": [
            "FEISHU_RECEIVE_ID_TYPE=chat_id",
            "FEISHU_RECEIVE_ID_TYPE=open_id",
        ],
        "docs": [
            {
                "label": "完整指南：飞书通知配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#飞书",
            },
        ],
    },
    "FEISHU_DOMAIN": {
        "title": "Feishu Domain",
        "description": "Feishu API domain: 'feishu' (feishu.cn for mainland China) or 'lark' (larksuite.com for international).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "feishu",
        "options": [{"label": "feishu (飞书国内)", "value": "feishu"}, {"label": "lark (国际版)", "value": "lark"}],
        "validation": {"enum": ["feishu", "lark"]},
        "display_order": 20,
        "help_key": "settings.notification.FEISHU_DOMAIN",
        "examples": [
            "FEISHU_DOMAIN=feishu",
            "FEISHU_DOMAIN=lark",
        ],
        "docs": [
            {
                "label": "完整指南：飞书通知配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#飞书",
            },
        ],
    },
    # ------------------------------------------------------------------
    # Notification – Telegram
    # ------------------------------------------------------------------
    "TELEGRAM_BOT_TOKEN": {
        "title": "Telegram Bot Token",
        "description": "Telegram bot token (from @BotFather).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 17,
        "help_key": "settings.notification.telegram",
        "examples": [
            "TELEGRAM_BOT_TOKEN=123456:ABC-DEF",
            "TELEGRAM_CHAT_ID=-1001234567890",
        ],
        "docs": [
            {
                "label": "完整指南：Telegram",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#telegram",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "TELEGRAM_CHAT_ID": {
        "title": "Telegram Chat ID",
        "description": "Telegram chat/group ID to send messages to.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 18,
        "help_key": "settings.notification.telegram",
        "examples": [
            "TELEGRAM_CHAT_ID=-1001234567890",
            "TELEGRAM_MESSAGE_THREAD_ID=123",
        ],
        "docs": [
            {
                "label": "完整指南：Telegram",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#telegram",
            },
        ],
        "warning_codes": [],
    },
    "TELEGRAM_MESSAGE_THREAD_ID": {
        "title": "Telegram Thread ID",
        "description": "Telegram topic/thread ID for group messages (optional).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 19,
    },
    # ------------------------------------------------------------------
    # Notification – Email
    # ------------------------------------------------------------------
    "EMAIL_SENDER": {
        "title": "Email Sender",
        "description": "Sender email address (SMTP host auto-detected).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 25,
        "help_key": "settings.notification.email",
        "examples": [
            "EMAIL_SENDER=sender@example.com",
            "EMAIL_PASSWORD=your_smtp_authorization_code",
            "EMAIL_RECEIVERS=user1@example.com,user2@example.com",
        ],
        "docs": [
            {
                "label": "完整指南：邮件",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#邮件",
            },
        ],
        "warning_codes": [],
    },
    "EMAIL_PASSWORD": {
        "title": "Email Password",
        "description": "Email password or app-specific authorization code.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 26,
        "help_key": "settings.notification.email",
        "examples": [
            "EMAIL_PASSWORD=your_smtp_authorization_code",
        ],
        "docs": [
            {
                "label": "完整指南：邮件",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#邮件",
            },
        ],
        "warning_codes": ["secret_value", "smtp_authorization_code"],
    },
    "EMAIL_RECEIVERS": {
        "title": "Email Receivers",
        "description": "Comma-separated recipient email addresses. Leave empty to send to yourself.",
        "category": "notification",
        "data_type": "array",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {"multi_value": True, "delimiter": ","},
        "display_order": 27,
        "help_key": "settings.notification.email",
        "examples": [
            "EMAIL_RECEIVERS=user1@example.com,user2@example.com",
            "STOCK_GROUP_1=600519,300750",
            "EMAIL_GROUP_1=user1@example.com",
        ],
        "docs": [
            {
                "label": "完整指南：邮件",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#邮件",
            },
        ],
        "warning_codes": ["comma_separated_values"],
    },
    # ------------------------------------------------------------------
    # Notification – Discord
    # ------------------------------------------------------------------
    "DISCORD_WEBHOOK_URL": {
        "title": "Discord Webhook URL",
        "description": "Discord webhook URL for channel notifications.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 33,
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/xxx/yyy",
        ],
        "docs": [
            {
                "label": "完整指南：Discord",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#discord",
            },
        ],
        "warning_codes": ["webhook_secret_value"],
    },
    "DISCORD_BOT_TOKEN": {
        "title": "Discord Bot Token",
        "description": "Discord bot token for interactive bot mode.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 34,
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "DISCORD_BOT_TOKEN=your_discord_bot_token",
            "DISCORD_MAIN_CHANNEL_ID=123456789012345678",
        ],
        "docs": [
            {
                "label": "完整指南：Discord",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#discord",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "DISCORD_MAIN_CHANNEL_ID": {
        "title": "Discord Channel ID",
        "description": "Discord main channel ID for sending messages.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 35,
    },
    "DISCORD_INTERACTIONS_PUBLIC_KEY": {
        "title": "Discord Interactions Public Key",
        "description": "Discord public key used to verify inbound interaction/webhook signatures.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 36,
    },
    # ------------------------------------------------------------------
    # Notification – Slack  (Bot > Webhook when both configured)
    # ------------------------------------------------------------------
    "SLACK_BOT_TOKEN": {
        "title": "Slack Bot Token",
        "description": "Slack Bot Token (xoxb-...). Recommended; supports image upload. Takes priority over Webhook when both are configured.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 37,
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "SLACK_BOT_TOKEN=xoxb-xxxx",
            "SLACK_CHANNEL_ID=C0123456789",
        ],
        "docs": [
            {
                "label": "完整指南：通知渠道配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#通知渠道详细配置",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "SLACK_CHANNEL_ID": {
        "title": "Slack Channel ID",
        "description": "Slack channel ID (required when using Bot Token).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 38,
    },
    "SLACK_WEBHOOK_URL": {
        "title": "Slack Incoming Webhook URL",
        "description": "Slack Incoming Webhook URL (text only, no image support).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 39,
    },
    # ------------------------------------------------------------------
    # Notification – Pushover
    # ------------------------------------------------------------------
    "PUSHOVER_USER_KEY": {
        "title": "Pushover User Key",
        "description": "Pushover user key (from https://pushover.net).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 42,
    },
    "PUSHOVER_API_TOKEN": {
        "title": "Pushover API Token",
        "description": "Pushover application API token.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 43,
    },
    "NTFY_URL": {
        "title": "ntfy URL",
        "description": "Full ntfy publish endpoint including topic path, e.g. https://ntfy.sh/my-topic.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {
            "item_type": "url",
            "allowed_schemes": ["http", "https"],
        },
        "display_order": 44,
    },
    "NTFY_TOKEN": {
        "title": "ntfy Token",
        "description": "Optional ntfy bearer token for protected topics or self-hosted servers.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 45,
    },
    "GOTIFY_URL": {
        "title": "Gotify URL",
        "description": "Gotify server base URL. Do not include /message; the sender appends it.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {
            "item_type": "url",
            "allowed_schemes": ["http", "https"],
        },
        "display_order": 46,
    },
    "GOTIFY_TOKEN": {
        "title": "Gotify Token",
        "description": "Gotify application token sent via the X-Gotify-Key header.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 47,
    },
    "PUSHPLUS_TOPIC": {
        "title": "PushPlus Topic",
        "description": "PushPlus group topic code for one-to-many push.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 41,
    },
    # ------------------------------------------------------------------
    # Notification – Server酱 / misc
    # ------------------------------------------------------------------
    "SERVERCHAN3_SENDKEY": {
        "title": "ServerChan3 SendKey",
        "description": "Server酱3 SendKey for push notifications.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 46,
    },
    "ASTRBOT_URL": {
        "title": "AstrBot URL",
        "description": "AstrBot webhook endpoint URL.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {
            "item_type": "url",
            "allowed_schemes": ["http", "https"],
        },
        "display_order": 47,
    },
    "ASTRBOT_TOKEN": {
        "title": "AstrBot Token",
        "description": "Optional AstrBot bearer token.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "password",
        "is_sensitive": True,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 48,
    },
    "SINGLE_STOCK_NOTIFY": {
        "title": "Single Stock Notify",
        "description": "Push immediately after each single stock analysis instead of batching all results together.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 54,
        "help_key": "settings.report.SINGLE_STOCK_NOTIFY",
        "examples": [
            "SINGLE_STOCK_NOTIFY=true",
            "SINGLE_STOCK_NOTIFY=false",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_TYPE": {
        "title": "Report Type",
        "description": "Report format: 'simple' (concise), 'full' (detailed), or 'brief' (3-5 sentences).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "simple",
        "options": ["simple", "full", "brief"],
        "validation": {"enum": ["simple", "full", "brief"]},
        "display_order": 55,
        "help_key": "settings.notification.report_output",
        "examples": [
            "REPORT_TYPE=simple",
            "REPORT_TYPE=full",
            "REPORT_LANGUAGE=zh",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_LANGUAGE": {
        "title": "Report Language",
        "description": "Default output language for reports and notification templates. Supported values: zh, en.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "zh",
        "options": [
            {"label": "Chinese", "value": "zh"},
            {"label": "English", "value": "en"},
        ],
        "validation": {"enum": ["zh", "en"]},
        "display_order": 56,
        "help_key": "settings.notification.report_output",
        "examples": [
            "REPORT_LANGUAGE=zh",
            "REPORT_LANGUAGE=en",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_TEMPLATES_DIR": {
        "title": "Report Templates Dir",
        "description": "Directory for Jinja2 report templates (relative to project root).",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "templates",
        "options": [],
        "validation": {},
        "display_order": 57,
        "help_key": "settings.report.REPORT_TEMPLATES_DIR",
        "examples": [
            "REPORT_TEMPLATES_DIR=templates",
            "REPORT_TEMPLATES_DIR=custom_templates",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_RENDERER_ENABLED": {
        "title": "Report Renderer Enabled",
        "description": "Enable Jinja2 template rendering for reports. Default false for zero regression.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 58,
        "help_key": "settings.report.REPORT_RENDERER_ENABLED",
        "examples": [
            "REPORT_RENDERER_ENABLED=true",
            "REPORT_RENDERER_ENABLED=false",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_INTEGRITY_ENABLED": {
        "title": "Report Integrity Enabled",
        "description": "Validate mandatory report fields after LLM output; retry or placeholder on missing.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 58,
        "help_key": "settings.report.REPORT_INTEGRITY_ENABLED",
        "examples": [
            "REPORT_INTEGRITY_ENABLED=true",
            "REPORT_INTEGRITY_RETRY=1",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_INTEGRITY_RETRY": {
        "title": "Report Integrity Retry",
        "description": "Retry count when mandatory fields missing (0 = placeholder only, no retry).",
        "category": "notification",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "1",
        "options": [],
        "validation": {"min": 0, "max": 3},
        "display_order": 59,
        "help_key": "settings.report.REPORT_INTEGRITY_ENABLED",
        "examples": [
            "REPORT_INTEGRITY_RETRY=1",
            "REPORT_INTEGRITY_RETRY=0",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "REPORT_HISTORY_COMPARE_N": {
        "title": "Report History Compare N",
        "description": "Show last N analyses signal comparison per stock (0 = disabled).",
        "category": "notification",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0",
        "options": [],
        "validation": {"min": 0, "max": 10},
        "display_order": 60,
        "help_key": "settings.report.REPORT_HISTORY_COMPARE_N",
        "examples": [
            "REPORT_HISTORY_COMPARE_N=0",
            "REPORT_HISTORY_COMPARE_N=5",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "MERGE_EMAIL_NOTIFICATION": {
        "title": "Merge Email Notification",
        "description": "Merge stock analysis and market review into a single email notification.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 61,
        "help_key": "settings.report.MERGE_EMAIL_NOTIFICATION",
        "examples": [
            "MERGE_EMAIL_NOTIFICATION=true",
            "MERGE_EMAIL_NOTIFICATION=false",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_REPORT_CHANNELS": {
        "title": "Report Notification Channels",
        "description": "Comma-separated route for report notifications. Empty keeps all configured channels.",
        "category": "notification",
        "data_type": "array",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [{"label": channel, "value": channel} for channel in ROUTABLE_NOTIFICATION_CHANNELS],
        "validation": {"allowed_values": list(ROUTABLE_NOTIFICATION_CHANNELS), "delimiter": ","},
        "display_order": 62,
        "help_key": "settings.notification.channel_routing",
        "examples": [
            "NOTIFICATION_REPORT_CHANNELS=email,feishu",
            "NOTIFICATION_REPORT_CHANNELS=",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_ALERT_CHANNELS": {
        "title": "Alert Notification Channels",
        "description": "Comma-separated route for event alert notifications. Empty keeps all configured channels.",
        "category": "notification",
        "data_type": "array",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [{"label": channel, "value": channel} for channel in ROUTABLE_NOTIFICATION_CHANNELS],
        "validation": {"allowed_values": list(ROUTABLE_NOTIFICATION_CHANNELS), "delimiter": ","},
        "display_order": 63,
        "help_key": "settings.notification.channel_routing",
        "examples": [
            "NOTIFICATION_ALERT_CHANNELS=feishu,telegram",
            "NOTIFICATION_ALERT_CHANNELS=",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_SYSTEM_ERROR_CHANNELS": {
        "title": "System Error Notification Channels",
        "description": "Comma-separated route reserved for system error notifications. Empty keeps all configured channels.",
        "category": "notification",
        "data_type": "array",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [{"label": channel, "value": channel} for channel in ROUTABLE_NOTIFICATION_CHANNELS],
        "validation": {"allowed_values": list(ROUTABLE_NOTIFICATION_CHANNELS), "delimiter": ","},
        "display_order": 64,
        "help_key": "settings.notification.channel_routing",
        "examples": [
            "NOTIFICATION_SYSTEM_ERROR_CHANNELS=email",
            "NOTIFICATION_SYSTEM_ERROR_CHANNELS=",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_DEDUP_TTL_SECONDS": {
        "title": "Notification Dedup TTL Seconds",
        "description": "Suppress duplicate static notifications with the same dedup key within this TTL. 0 disables deduplication.",
        "category": "notification",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0",
        "options": [],
        "validation": {"min": 0},
        "display_order": 65,
        "help_key": "settings.notification.dedup",
        "examples": [
            "NOTIFICATION_DEDUP_TTL_SECONDS=3600",
            "NOTIFICATION_COOLDOWN_SECONDS=600",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_COOLDOWN_SECONDS": {
        "title": "Notification Cooldown Seconds",
        "description": "Suppress repeated static notifications with the same cooldown key during this window. 0 disables cooldown.",
        "category": "notification",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0",
        "options": [],
        "validation": {"min": 0},
        "display_order": 66,
        "help_key": "settings.notification.dedup",
        "examples": [
            "NOTIFICATION_COOLDOWN_SECONDS=600",
            "NOTIFICATION_DEDUP_TTL_SECONDS=3600",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_QUIET_HOURS": {
        "title": "Notification Quiet Hours",
        "description": "Quiet window in HH:MM-HH:MM format. Supports overnight ranges. Empty disables quiet hours.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [],
        "validation": {"pattern": r"^([01]\d|2[0-3]):[0-5]\d-([01]\d|2[0-3]):[0-5]\d$"},
        "display_order": 67,
        "help_key": "settings.notification.quiet_hours",
        "examples": [
            "NOTIFICATION_QUIET_HOURS=23:00-07:00",
            "NOTIFICATION_TIMEZONE=Asia/Shanghai",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_TIMEZONE": {
        "title": "Notification Timezone",
        "description": "IANA timezone for quiet hours, e.g. Asia/Shanghai. Empty follows TZ or the local system timezone.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [],
        "validation": {"timezone": True},
        "display_order": 68,
        "help_key": "settings.notification.quiet_hours",
        "examples": [
            "NOTIFICATION_TIMEZONE=Asia/Shanghai",
            "NOTIFICATION_TIMEZONE=America/New_York",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_MIN_SEVERITY": {
        "title": "Notification Minimum Severity",
        "description": "Suppress static notifications below this severity. Empty keeps current behavior.",
        "category": "notification",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [
            {"label": "Not set", "value": ""},
            *({"label": severity, "value": severity} for severity in NOTIFICATION_SEVERITIES),
        ],
        "validation": {"enum": ["", *NOTIFICATION_SEVERITIES]},
        "display_order": 69,
        "help_key": "settings.notification.MIN_SEVERITY",
        "examples": [
            "NOTIFICATION_MIN_SEVERITY=warning",
            "NOTIFICATION_MIN_SEVERITY=",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": [],
    },
    "NOTIFICATION_DAILY_DIGEST_ENABLED": {
        "title": "Notification Daily Digest Enabled (Reserved)",
        "description": "Reserved P4 flag. It is visible for compatibility but does not send daily digests yet.",
        "category": "notification",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 70,
        "help_key": "settings.notification.DAILY_DIGEST_ENABLED",
        "examples": [
            "NOTIFICATION_DAILY_DIGEST_ENABLED=false",
        ],
        "docs": [
            {
                "label": "通知渠道与路由",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/notifications.md",
            },
        ],
        "warning_codes": ["reserved_flag"],
    },
    "SCHEDULE_TIME": {
        "title": "Schedule Time",
        "description": "Daily schedule time in HH:MM format.",
        "category": "system",
        "data_type": "time",
        "ui_control": "time",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "18:00",
        "options": [],
        "validation": {"pattern": r"^([01]\d|2[0-3]):[0-5]\d$"},
        "display_order": 10,
        "help_key": "settings.system.schedule",
        "examples": [
            "SCHEDULE_TIME=18:00",
            "SCHEDULE_ENABLED=true",
            "SCHEDULE_RUN_IMMEDIATELY=false",
        ],
        "docs": [
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
        ],
        "warning_codes": ["local_timezone"],
    },
    "SCHEDULE_TIMES": {
        "title": "Schedule Times",
        "description": "Comma-separated daily schedule times in HH:MM format. Falls back to SCHEDULE_TIME when empty.",
        "category": "system",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [],
        "validation": {"pattern": r"^\s*(?:(?:[01]\d|2[0-3]):[0-5]\d\s*(?:,\s*(?:[01]\d|2[0-3]):[0-5]\d\s*)*)?$"},
        "display_order": 11,
        "help_key": "settings.system.schedule",
        "examples": [
            "SCHEDULE_TIMES=09:20,12:30,15:10,18:00",
            "SCHEDULE_TIME=18:00",
            "SCHEDULE_ENABLED=true",
        ],
        "docs": [
            {
                "label": "Full guide: configuration",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
        ],
        "warning_codes": ["local_timezone"],
    },
    "HTTP_PROXY": {
        "title": "HTTP Proxy",
        "description": "Optional HTTP proxy endpoint.",
        "category": "system",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 20,
        "help_key": "settings.system.HTTP_PROXY",
        "examples": [
            "HTTP_PROXY=http://127.0.0.1:7890",
            "HTTPS_PROXY=http://127.0.0.1:7890",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": ["network_scope"],
    },
    "LOG_LEVEL": {
        "title": "Log Level",
        "description": "Application log level.",
        "category": "system",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "INFO",
        "options": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
        "validation": {"enum": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]},
        "display_order": 30,
        "help_key": "settings.system.LOG_LEVEL",
        "examples": [
            "LOG_LEVEL=INFO",
            "LOG_LEVEL=DEBUG",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": ["restart_required"],
    },
    "LOG_DIR": {
        "title": "Log Directory",
        "description": "Directory for application logs. The runtime user or container must be able to write to this path.",
        "category": "system",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "./logs",
        "options": [],
        "validation": {},
        "display_order": 31,
        "help_key": "settings.system.LOG_DIR",
        "examples": [
            "LOG_DIR=./logs",
            "LOG_DIR=/app/logs",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": ["restart_required", "path_must_be_writable"],
    },
    "WEBUI_ENABLED": {
        "title": "Web UI Enabled",
        "description": "Startup-time compatibility flag for default WebUI/API service mode. Saving this setting does not start or stop the current process.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 37,
        "help_key": "settings.system.WEBUI_ENABLED",
        "examples": [
            "WEBUI_ENABLED=false",
            "WEBUI_ENABLED=true",
        ],
        "docs": [
            {
                "label": "完整指南：WebUI 与 API",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#webui-与-api-服务",
            },
            {
                "label": "云服务器访问 WebUI",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/deploy-webui-cloud.md",
            },
        ],
        "warning_codes": ["restart_required"],
    },
    "WEBUI_AUTO_BUILD": {
        "title": "Web UI Auto Build",
        "description": "Build or verify the Web frontend assets before backend WebUI startup. Disable only when assets are prebuilt.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 38,
        "help_key": "settings.system.WEBUI_AUTO_BUILD",
        "examples": [
            "WEBUI_AUTO_BUILD=true",
            "WEBUI_AUTO_BUILD=false",
        ],
        "docs": [
            {
                "label": "完整指南：WebUI 与 API",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#webui-与-api-服务",
            },
            {
                "label": "云服务器访问 WebUI",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/deploy-webui-cloud.md",
            },
        ],
        "warning_codes": ["restart_required", "requires_built_web_assets"],
    },
    "WEBUI_HOST": {
        "title": "Web UI Host",
        "description": "Host address for Web UI service binding.",
        "category": "system",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "127.0.0.1",
        "options": [],
        "validation": {},
        "display_order": 39,
        "help_key": "settings.system.WEBUI_HOST",
        "examples": [
            "WEBUI_HOST=127.0.0.1",
            "WEBUI_HOST=0.0.0.0",
        ],
        "docs": [
            {
                "label": "云服务器访问 WebUI",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/deploy-webui-cloud.md",
            },
            {
                "label": "完整指南：WebUI 与 API",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#webui-与-api-服务",
            },
        ],
        "warning_codes": ["public_bind_requires_auth", "restart_required"],
    },
    "WEBUI_PORT": {
        "title": "Web UI Port",
        "description": "Port for Web UI service.",
        "category": "system",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "8000",
        "options": [],
        "validation": {"min": 1, "max": 65535},
        "display_order": 40,
        "help_key": "settings.system.WEBUI_PORT",
        "examples": [
            "WEBUI_PORT=8000",
            "WEBUI_PORT=18000",
        ],
        "docs": [
            {
                "label": "云服务器访问 WebUI",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/deploy-webui-cloud.md",
            },
            {
                "label": "完整指南：WebUI 与 API",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#webui-与-api-服务",
            },
        ],
        "warning_codes": ["port_mapping_required", "restart_required"],
    },
    "RUN_IMMEDIATELY": {
        "title": "Run Immediately",
        "description": "Whether to run analysis immediately on startup (non-schedule mode).",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 45,
        "help_key": "settings.system.RUN_IMMEDIATELY",
        "examples": [
            "RUN_IMMEDIATELY=true",
            "RUN_IMMEDIATELY=false",
        ],
        "docs": [
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
        ],
        "warning_codes": ["restart_required"],
    },
    "SCHEDULE_ENABLED": {
        "title": "Schedule Enabled",
        "description": "Enable daily scheduled analysis run.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 8,
        "help_key": "settings.system.schedule",
        "examples": [
            "SCHEDULE_ENABLED=true",
            "SCHEDULE_TIME=18:00",
            "SCHEDULE_RUN_IMMEDIATELY=false",
        ],
        "docs": [
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
        ],
        "warning_codes": ["restart_required"],
    },
    "ADMIN_AUTH_ENABLED": {
        "title": "Admin Auth Enabled",
        "description": "Enable password protection for Web UI. The first visit initializes the admin password.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": False,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 41,
        "help_key": "settings.system.ADMIN_AUTH_ENABLED",
        "examples": [
            "ADMIN_AUTH_ENABLED=true",
            "python -m src.auth reset_password",
        ],
        "docs": [
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
            {
                "label": "云服务器访问 WebUI",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/deploy-webui-cloud.md",
            },
        ],
        "warning_codes": ["public_webui_requires_auth", "auth_settings_endpoint_required"],
    },
    "TRUST_X_FORWARDED_FOR": {
        "title": "Trust X-Forwarded-For",
        "description": "Use X-Forwarded-For as the client IP behind one trusted reverse proxy.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 42,
        "help_key": "settings.system.TRUST_X_FORWARDED_FOR",
        "examples": [
            "TRUST_X_FORWARDED_FOR=false",
            "TRUST_X_FORWARDED_FOR=true",
        ],
        "docs": [
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
            {
                "label": "云服务器访问 WebUI",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/deploy-webui-cloud.md",
            },
        ],
        "warning_codes": ["trusted_proxy_only"],
    },
    "SCHEDULE_RUN_IMMEDIATELY": {
        "title": "Schedule Run Immediately",
        "description": "Whether to run one analysis immediately on startup in schedule mode.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 11,
        "help_key": "settings.system.schedule",
        "examples": [
            "SCHEDULE_RUN_IMMEDIATELY=true",
            "SCHEDULE_RUN_IMMEDIATELY=false",
        ],
        "docs": [
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
        ],
        "warning_codes": ["restart_required"],
    },
    "TRADING_DAY_CHECK_ENABLED": {
        "title": "Trading Day Check",
        "description": "Skip analysis on non-trading days. Set to false or use --force-run to override.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 12,
        "help_key": "settings.system.TRADING_DAY_CHECK_ENABLED",
        "examples": [
            "TRADING_DAY_CHECK_ENABLED=true",
            "TRADING_DAY_CHECK_ENABLED=false",
        ],
        "docs": [
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
        ],
        "warning_codes": ["force_run_override"],
    },
    "MARKET_REVIEW_ENABLED": {
        "title": "Market Review Enabled",
        "description": "Enable market overview/review in analysis reports.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 46,
        "help_key": "settings.system.market_review",
        "examples": [
            "MARKET_REVIEW_ENABLED=true",
            "MARKET_REVIEW_REGION=cn",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "DAILY_MARKET_CONTEXT_ENABLED": {
        "title": "Daily Market Context Enabled",
        "description": "Inject daily market context into stock-analysis prompts and apply conservative decision guardrails.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 47,
        "help_key": "settings.system.market_review",
        "examples": [
            "DAILY_MARKET_CONTEXT_ENABLED=true",
            "DAILY_MARKET_CONTEXT_ENABLED=false",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "MARKET_REVIEW_REGION": {
        "title": "Market Review Region",
        "description": "Market region for review: cn (A-shares), hk (Hong Kong), us (US stocks), or both (all markets).",
        "category": "system",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "cn",
        "options": ["cn", "hk", "us", "both"],
        "validation": {"enum": ["cn", "hk", "us", "both"]},
        "display_order": 48,
        "help_key": "settings.system.market_review",
        "examples": [
            "MARKET_REVIEW_REGION=cn",
            "MARKET_REVIEW_REGION=both",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "MARKET_REVIEW_COLOR_SCHEME": {
        "title": "Market Review Color Scheme",
        "description": "Index change color style in market-review tables: green_up (green for gains, red for losses) or red_up (red for gains, green for losses).",
        "category": "system",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "green_up",
        "options": [
            {"label": "Green Up / Red Down", "value": "green_up"},
            {"label": "Red Up / Green Down", "value": "red_up"},
        ],
        "validation": {"enum": ["green_up", "red_up"]},
        "display_order": 49,
        "help_key": "settings.system.market_review",
        "examples": [
            "MARKET_REVIEW_COLOR_SCHEME=green_up",
            "MARKET_REVIEW_COLOR_SCHEME=red_up",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "MAX_WORKERS": {
        "title": "Max Workers",
        "description": "Maximum concurrent analysis threads. Keep low to avoid API rate limits.",
        "category": "system",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "3",
        "options": [],
        "validation": {"min": 1, "max": 20},
        "display_order": 50,
        "help_key": "settings.system.MAX_WORKERS",
        "examples": [
            "MAX_WORKERS=3",
            "MAX_WORKERS=5",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "ANALYSIS_DELAY": {
        "title": "Analysis Delay",
        "description": "Delay in seconds between individual stock analyses (for API rate limiting).",
        "category": "system",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "0",
        "options": [],
        "validation": {"min": 0, "max": 60},
        "display_order": 51,
        "help_key": "settings.system.ANALYSIS_DELAY",
        "examples": [
            "ANALYSIS_DELAY=0",
            "ANALYSIS_DELAY=5",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "SAVE_CONTEXT_SNAPSHOT": {
        "title": "Save Context Snapshot",
        "description": "Persist the full analysis_history.context_snapshot for history/API/Web transparency. Disable only to stop storing snapshots; it does not disable AnalysisContextPack prompt summaries during the current run.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 52,
        "help_key": "settings.system.SAVE_CONTEXT_SNAPSHOT",
        "examples": [
            "SAVE_CONTEXT_SNAPSHOT=true",
            "SAVE_CONTEXT_SNAPSHOT=false",
            "python main.py --no-context-snapshot",
        ],
        "docs": [
            {
                "label": "AnalysisContextPack P6 文档、迁移与回滚",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/analysis-context-pack.md#p6-文档迁移与回滚",
            },
            {
                "label": "完整指南：其他配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#其他配置",
            },
        ],
        "warning_codes": [],
    },
    "DEBUG": {
        "title": "Debug Mode",
        "description": "Enable debug mode with verbose logging.",
        "category": "system",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 55,
        "help_key": "settings.system.DEBUG",
        "examples": [
            "DEBUG=true",
            "DEBUG=false",
        ],
        "docs": [
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "BACKTEST_ENABLED": {
        "title": "Backtest Enabled",
        "description": "Whether backtest is enabled.",
        "category": "backtest",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 10,
        "help_key": "settings.backtest.BACKTEST_ENABLED",
        "examples": [
            "BACKTEST_ENABLED=true",
            "BACKTEST_ENABLED=false",
        ],
        "docs": [
            {
                "label": "完整指南：回测配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#回测功能",
            },
        ],
        "warning_codes": [],
    },
    "BACKTEST_EVAL_WINDOW_DAYS": {
        "title": "Backtest Eval Window Days",
        "description": "Backtest evaluation window in trading days.",
        "category": "backtest",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "10",
        "options": [],
        "validation": {"min": 1, "max": 365},
        "display_order": 20,
        "help_key": "settings.backtest.eval_params",
        "examples": [
            "BACKTEST_EVAL_WINDOW_DAYS=10",
            "BACKTEST_MIN_AGE_DAYS=14",
            "BACKTEST_NEUTRAL_BAND_PCT=2.0",
        ],
        "docs": [
            {
                "label": "完整指南：回测配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#回测功能",
            },
        ],
        "warning_codes": [],
    },
    "BACKTEST_MIN_AGE_DAYS": {
        "title": "Backtest Min Age Days",
        "description": "Only evaluate analysis records older than this threshold.",
        "category": "backtest",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "14",
        "options": [],
        "validation": {"min": 0, "max": 3650},
        "display_order": 30,
        "help_key": "settings.backtest.eval_params",
        "examples": [
            "BACKTEST_MIN_AGE_DAYS=14",
            "BACKTEST_EVAL_WINDOW_DAYS=10",
        ],
        "docs": [
            {
                "label": "完整指南：回测配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#回测功能",
            },
        ],
        "warning_codes": [],
    },
    "BACKTEST_ENGINE_VERSION": {
        "title": "Backtest Engine Version",
        "description": "Backtest engine version label.",
        "category": "backtest",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "v1",
        "options": [],
        "validation": {},
        "display_order": 40,
        "help_key": "settings.backtest.BACKTEST_ENGINE_VERSION",
        "examples": [
            "BACKTEST_ENGINE_VERSION=v1",
        ],
        "docs": [
            {
                "label": "完整指南：回测配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#回测功能",
            },
        ],
        "warning_codes": [],
    },
    "BACKTEST_NEUTRAL_BAND_PCT": {
        "title": "Backtest Neutral Band Pct",
        "description": "Neutral return band percentage for outcome labeling.",
        "category": "backtest",
        "data_type": "number",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "2.0",
        "options": [],
        "validation": {"min": 0.0, "max": 100.0},
        "display_order": 50,
        "help_key": "settings.backtest.eval_params",
        "examples": [
            "BACKTEST_NEUTRAL_BAND_PCT=2.0",
            "BACKTEST_EVAL_WINDOW_DAYS=10",
        ],
        "docs": [
            {
                "label": "完整指南：回测配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#回测功能",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_MODE": {
        "title": "Agent Mode",
        "description": "Enable ReAct Agent for stock analysis.",
        "category": "agent",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 10,
        "help_key": "settings.agent.AGENT_MODE",
        "examples": [
            "AGENT_MODE=true",
            "AGENT_MODE=false",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_GENERATION_BACKEND": {
        "title": "Ask-Stock Generation Method",
        "description": "Generation method used by the ask-stock assistant to generate replies and use tools.",
        "category": "agent",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "auto",
        "options": [
            {"label": "Auto", "value": "auto"},
            {"label": "Default model settings", "value": "litellm"},
        ],
        "validation": {"enum": ["auto", "litellm"]},
        "display_order": 2,
        "help_key": "settings.agent.AGENT_GENERATION_BACKEND",
        "examples": [
            "AGENT_GENERATION_BACKEND=auto",
            "AGENT_GENERATION_BACKEND=litellm",
        ],
        "docs": [
            {
                "label": "LLM 配置指南",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_MAX_STEPS": {
        "title": "Agent Max Steps",
        "description": f"Maximum reasoning-step limit for Agent mode. At the default ({AGENT_MAX_STEPS_DEFAULT}), each sub-agent keeps its own preset. When raised above {AGENT_MAX_STEPS_DEFAULT}, all sub-agents adopt this value. When lowered below a sub-agent's preset, that sub-agent is capped at this value.",
        "category": "agent",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": str(AGENT_MAX_STEPS_DEFAULT),
        "options": [],
        "validation": {"min": 1, "max": 50},
        "display_order": 20,
        "help_key": "settings.agent.AGENT_MAX_STEPS",
        "examples": [
            f"AGENT_MAX_STEPS={AGENT_MAX_STEPS_DEFAULT}",
            "AGENT_MAX_STEPS=25",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_SKILLS": {
        "title": "Agent Strategies",
        "description": "Comma-separated list of active agent strategy skills. Leave empty to use the primary default strategy skill declared in metadata (built-in default: bull_trend). When set to specific skills (not 'all'), scheduled tasks will automatically use the Agent pipeline.",
        "category": "agent",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [],
        "validation": {},
        "display_order": 30,
        "help_key": "settings.agent.AGENT_SKILLS",
        "examples": [
            "AGENT_SKILLS=",
            "AGENT_SKILLS=bull_trend,mean_reversion",
            "AGENT_SKILLS=all",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_SKILL_DIR": {
        "title": "Agent Strategy Dir",
        "description": "Directory containing agent strategy-skill definition files (YAML or SKILL.md bundles).",
        "category": "agent",
        "data_type": "string",
        "ui_control": "text",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "strategies",
        "options": [],
        "validation": {},
        "display_order": 40,
        "help_key": "settings.agent.AGENT_SKILL_DIR",
        "examples": [
            "AGENT_SKILL_DIR=strategies",
            "AGENT_SKILL_DIR=my_strategies",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_NL_ROUTING": {
        "title": "Agent NL Routing",
        "description": "Enable natural-language routing in bot dispatcher. When on, high-confidence stock queries in private chat (or @mentions) are routed to the agent even without an explicit command.",
        "category": "agent",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 50,
        "help_key": "settings.agent.AGENT_NL_ROUTING",
        "examples": [
            "AGENT_NL_ROUTING=true",
            "AGENT_NL_ROUTING=false",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_ARCH": {
        "title": "Agent Architecture",
        "description": "Agent execution architecture. 'single' uses the classic ReAct executor; 'multi' uses the orchestrator pipeline with specialised sub-agents.",
        "category": "agent",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "single",
        "options": [
            {"label": "Single Agent", "value": "single"},
            {"label": "Multi Agent (Orchestrator)", "value": "multi"},
        ],
        "validation": {},
        "display_order": 60,
        "help_key": "settings.agent.AGENT_ARCH",
        "examples": [
            "AGENT_ARCH=single",
            "AGENT_ARCH=multi",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_ORCHESTRATOR_MODE": {
        "title": "Orchestrator Mode",
        "description": "Pipeline mode when AGENT_ARCH=multi. 'quick' (tech→decision), 'standard' (tech→intel→decision), 'full' (tech→intel→risk→decision), 'specialist' (full + per-strategy specialist agents).",
        "category": "agent",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "standard",
        "options": [
            {"label": "Quick", "value": "quick"},
            {"label": "Standard", "value": "standard"},
            {"label": "Full", "value": "full"},
            {"label": "Specialist", "value": "specialist"},
        ],
        "validation": {"enum": ["quick", "standard", "full", "specialist", "strategy", "skill"]},
        "display_order": 61,
        "help_key": "settings.agent.AGENT_ORCHESTRATOR_MODE",
        "examples": [
            "AGENT_ORCHESTRATOR_MODE=standard",
            "AGENT_ORCHESTRATOR_MODE=full",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_ORCHESTRATOR_TIMEOUT_S": {
        "title": "Agent Timeout",
        "description": "Shared timeout budget in seconds for Agent execution. Single-agent runs use it as the overall ReAct loop budget; multi-agent mode uses it as the cooperative pipeline budget. Set to 0 to disable.",
        "category": "agent",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "600",
        "options": [],
        "validation": {"min": 0, "max": 3600},
        "display_order": 62,
        "help_key": "settings.agent.AGENT_ORCHESTRATOR_TIMEOUT_S",
        "examples": [
            "AGENT_ORCHESTRATOR_TIMEOUT_S=600",
            "AGENT_ORCHESTRATOR_TIMEOUT_S=0",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_RISK_OVERRIDE": {
        "title": "Risk Agent Override",
        "description": "Allow the risk agent to veto buy signals when critical risk flags are detected.",
        "category": "agent",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 63,
        "help_key": "settings.agent.AGENT_RISK_OVERRIDE",
        "examples": [
            "AGENT_RISK_OVERRIDE=true",
            "AGENT_RISK_OVERRIDE=false",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_DEEP_RESEARCH_BUDGET": {
        "title": "Deep Research Token Budget",
        "description": "Maximum token budget for Deep Research planning, follow-up research, and final synthesis.",
        "category": "agent",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "30000",
        "options": [],
        "validation": {"min": 5000, "max": 100000},
        "display_order": 64,
        "help_key": "settings.agent.DEEP_RESEARCH",
        "examples": [
            "AGENT_DEEP_RESEARCH_BUDGET=30000",
            "AGENT_DEEP_RESEARCH_BUDGET=50000",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_DEEP_RESEARCH_TIMEOUT": {
        "title": "Deep Research Timeout",
        "description": "Maximum seconds allowed for a Deep Research request before returning a timeout response.",
        "category": "agent",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "180",
        "options": [],
        "validation": {"min": 30, "max": 600},
        "display_order": 65,
        "help_key": "settings.agent.DEEP_RESEARCH",
        "examples": [
            "AGENT_DEEP_RESEARCH_TIMEOUT=180",
            "AGENT_DEEP_RESEARCH_TIMEOUT=300",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_MEMORY_ENABLED": {
        "title": "Agent Memory",
        "description": "Enable the memory & calibration system. Tracks prediction accuracy and adjusts agent confidence over time.",
        "category": "agent",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 66,
        "help_key": "settings.agent.AGENT_MEMORY_ENABLED",
        "examples": [
            "AGENT_MEMORY_ENABLED=true",
            "AGENT_MEMORY_ENABLED=false",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_SKILL_AUTOWEIGHT": {
        "title": "Auto-Weight Strategies",
        "description": "Automatically weight strategy-skill opinions by their historical backtest performance.",
        "category": "agent",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "true",
        "options": [],
        "validation": {},
        "display_order": 67,
        "help_key": "settings.agent.AGENT_SKILL_AUTOWEIGHT",
        "examples": [
            "AGENT_SKILL_AUTOWEIGHT=true",
            "AGENT_SKILL_AUTOWEIGHT=false",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_SKILL_ROUTING": {
        "title": "Strategy Routing",
        "description": "Strategy-skill selection mode. 'auto' detects market regime and picks relevant skills; 'manual' uses AGENT_SKILLS list only.",
        "category": "agent",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "auto",
        "options": [
            {"label": "Auto (Regime-based)", "value": "auto"},
            {"label": "Manual (Use AGENT_SKILLS)", "value": "manual"},
        ],
        "validation": {},
        "display_order": 68,
        "help_key": "settings.agent.AGENT_SKILL_ROUTING",
        "examples": [
            "AGENT_SKILL_ROUTING=auto",
            "AGENT_SKILL_ROUTING=manual",
        ],
        "docs": [
            {
                "label": "完整指南：Agent 配置",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_CONTEXT_COMPRESSION_ENABLED": {
        "title": "Agent Context Compression",
        "description": "Enable rolling compression of visible Agent chat history. Default is off to preserve existing behavior.",
        "category": "agent",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 72,
        "help_key": "settings.agent.context_compression",
        "examples": [
            "AGENT_CONTEXT_COMPRESSION_ENABLED=false",
            "AGENT_CONTEXT_COMPRESSION_ENABLED=true",
        ],
        "docs": [
            {
                "label": "LLM 配置指南：问股可见对话上下文压缩",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md#问股可见对话上下文压缩",
            },
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_CONTEXT_COMPRESSION_PROFILE": {
        "title": "Context Compression Profile",
        "description": "Preset for visible chat history compression. Trigger/protected-turn fields can be left empty to follow the selected profile.",
        "category": "agent",
        "data_type": "string",
        "ui_control": "select",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "balanced",
        "options": [
            {"label": "成本优先", "value": "cost"},
            {"label": "均衡推荐", "value": "balanced"},
            {"label": "长上下文原文优先", "value": "long_context_raw_first"},
        ],
        "validation": {"enum": list(AGENT_CONTEXT_COMPRESSION_PROFILES.keys())},
        "display_order": 73,
        "help_key": "settings.agent.context_compression",
        "examples": [
            "AGENT_CONTEXT_COMPRESSION_PROFILE=balanced",
            "AGENT_CONTEXT_COMPRESSION_PROFILE=cost",
            "AGENT_CONTEXT_COMPRESSION_PROFILE=long_context_raw_first",
        ],
        "docs": [
            {
                "label": "LLM 配置指南：问股可见对话上下文压缩",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md#问股可见对话上下文压缩",
            },
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_CONTEXT_COMPRESSION_TRIGGER_TOKENS": {
        "title": "Context Compression Trigger Tokens",
        "description": "Token threshold for visible chat history compression. Leave empty to follow the selected compression profile preset.",
        "category": "agent",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [],
        "validation": {"min": 1000, "max": 200000},
        "display_order": 74,
        "help_key": "settings.agent.context_compression",
        "examples": [
            "AGENT_CONTEXT_COMPRESSION_TRIGGER_TOKENS=",
            "AGENT_CONTEXT_COMPRESSION_TRIGGER_TOKENS=12000",
        ],
        "docs": [
            {
                "label": "LLM 配置指南：问股可见对话上下文压缩",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md#问股可见对话上下文压缩",
            },
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_CONTEXT_PROTECTED_TURNS": {
        "title": "Context Protected Turns",
        "description": "Recent user turns preserved verbatim during visible chat history compression. Leave empty to follow the selected compression profile preset.",
        "category": "agent",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [],
        "validation": {"min": 1, "max": 20},
        "display_order": 75,
        "help_key": "settings.agent.context_compression",
        "examples": [
            "AGENT_CONTEXT_PROTECTED_TURNS=",
            "AGENT_CONTEXT_PROTECTED_TURNS=4",
        ],
        "docs": [
            {
                "label": "LLM 配置指南：问股可见对话上下文压缩",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md#问股可见对话上下文压缩",
            },
            {
                "label": "完整指南：环境变量完整列表",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_EVENT_MONITOR_ENABLED": {
        "title": "Event Monitor",
        "description": "Enable background Event Monitor polling in schedule mode.",
        "category": "agent",
        "data_type": "boolean",
        "ui_control": "switch",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "false",
        "options": [],
        "validation": {},
        "display_order": 69,
        "help_key": "settings.agent.event_monitor",
        "examples": [
            "AGENT_EVENT_MONITOR_ENABLED=true",
            "AGENT_EVENT_MONITOR_INTERVAL_MINUTES=5",
        ],
        "docs": [
            {
                "label": "告警中心文档",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/alerts.md",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_EVENT_MONITOR_INTERVAL_MINUTES": {
        "title": "Event Monitor Interval",
        "description": "Polling interval, in minutes, for background Event Monitor checks.",
        "category": "agent",
        "data_type": "integer",
        "ui_control": "number",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "5",
        "options": [],
        "validation": {"min": 1, "max": 1440},
        "display_order": 70,
        "help_key": "settings.agent.event_monitor",
        "examples": [
            "AGENT_EVENT_MONITOR_INTERVAL_MINUTES=5",
            "AGENT_EVENT_MONITOR_INTERVAL_MINUTES=15",
        ],
        "docs": [
            {
                "label": "告警中心文档",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/alerts.md",
            },
        ],
        "warning_codes": [],
    },
    "AGENT_EVENT_ALERT_RULES_JSON": {
        "title": "Event Alert Rules",
        "description": (
            "JSON array of Event Monitor rules loaded by schedule mode. "
            "Legacy JSON supports only price_cross, price_change_percent, and volume_spike. "
            "Technical indicator, watchlist, portfolio, and market light alert rules "
            "are available through the Alert API/Web center."
        ),
        "category": "agent",
        "data_type": "json",
        "ui_control": "textarea",
        "is_sensitive": False,
        "is_required": False,
        "is_editable": True,
        "default_value": "",
        "options": [],
        "validation": {},
        "display_order": 71,
        "help_key": "settings.agent.EVENT_ALERT_RULES_JSON",
        "examples": [
            'AGENT_EVENT_ALERT_RULES_JSON=[{"alert_type":"price_cross","stock_code":"600519","direction":"above","price":1800}]',
            'AGENT_EVENT_ALERT_RULES_JSON=[{"alert_type":"volume_spike","stock_code":"300750","multiplier":2.5}]',
        ],
        "docs": [
            {
                "label": "告警中心文档",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/alerts.md",
            },
        ],
        "warning_codes": ["legacy_json_only_basic_rules"],
    },
}

_DOC_FULL_GUIDE_ENV = [
    {
        "label": "完整指南：环境变量完整列表",
        "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#环境变量完整列表",
    },
]

_DOC_FULL_GUIDE_SEARCH = [
    {
        "label": "完整指南：搜索服务配置",
        "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#搜索服务配置",
    },
]

_DOC_FULL_GUIDE_DATA_SOURCE = [
    {
        "label": "完整指南：数据源配置",
        "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#数据源配置",
    },
]

_DOC_FULL_GUIDE_NOTIFICATION = [
    {
        "label": "完整指南：通知渠道配置",
        "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#通知渠道详细配置",
    },
]

_DOC_LLM_CONFIG = [
    {
        "label": "LLM 配置指南",
        "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/LLM_CONFIG_GUIDE.md",
    },
    {
        "label": "LLM 服务商配置速查",
        "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/llm-providers.md",
    },
]

_DOC_CUSTOM_WEBHOOK = [
    {
        "label": "完整指南：自定义 Webhook",
        "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/full-guide.md#自定义-webhook",
    },
]

_FIELD_HELP_METADATA: Dict[str, Dict[str, Any]] = {
    "ANSPIRE_LLM_ENABLED": {
        "help_key": "settings.ai_model.anspire_llm",
        "examples": [
            "ANSPIRE_LLM_ENABLED=true",
            "ANSPIRE_API_KEYS=your_anspire_key",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": [],
    },
    "ANSPIRE_LLM_BASE_URL": {
        "help_key": "settings.ai_model.anspire_llm",
        "examples": [
            "ANSPIRE_LLM_BASE_URL=https://open-gateway.anspire.cn/v6",
            "ANSPIRE_LLM_BASE_URL=https://open-gateway.anspire.ai/v6",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["base_url_must_match_provider"],
    },
    "ANSPIRE_LLM_MODEL": {
        "help_key": "settings.ai_model.anspire_llm",
        "examples": [
            "ANSPIRE_LLM_MODEL=Doubao-Seed-2.0-lite",
            "LITELLM_MODEL=openai/Doubao-Seed-2.0-lite",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": [],
    },
    "DEEPSEEK_API_KEYS": {
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "DEEPSEEK_API_KEYS=sk-xxxx,sk-yyyy",
            "LITELLM_MODEL=deepseek/deepseek-v4-flash",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "GEMINI_API_KEYS": {
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "GEMINI_API_KEYS=your_gemini_key_1,your_gemini_key_2",
            "LITELLM_MODEL=gemini/gemini-3.1-pro-preview",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "GEMINI_MODEL": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "GEMINI_MODEL=gemini-3.1-pro-preview",
            "LITELLM_MODEL=gemini/gemini-3.1-pro-preview",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "GEMINI_MODEL_FALLBACK": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "GEMINI_MODEL_FALLBACK=gemini-3-flash-preview",
            "LITELLM_FALLBACK_MODELS=gemini/gemini-3-flash-preview",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "GEMINI_TEMPERATURE": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "GEMINI_TEMPERATURE=0.7",
            "LLM_TEMPERATURE=0.7",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "OPENAI_API_KEYS": {
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "OPENAI_API_KEYS=sk-xxxx,sk-yyyy",
            "OPENAI_BASE_URL=https://api.example.com/v1",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "OPENAI_MODEL": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "OPENAI_MODEL=gpt-5.5",
            "LITELLM_MODEL=openai/gpt-5.5",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "OPENAI_VISION_MODEL": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "OPENAI_VISION_MODEL=gpt-5.5",
            "VISION_MODEL=openai/gpt-5.5",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "OPENAI_TEMPERATURE": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "OPENAI_TEMPERATURE=0.7",
            "LLM_TEMPERATURE=0.7",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "ANTHROPIC_API_KEYS": {
        "help_key": "settings.ai_model.provider_keys",
        "examples": [
            "ANTHROPIC_API_KEYS=sk-ant-xxxx,sk-ant-yyyy",
            "LITELLM_MODEL=anthropic/claude-sonnet-4-6",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "ANTHROPIC_MODEL": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "ANTHROPIC_MODEL=claude-sonnet-4-6",
            "LITELLM_MODEL=anthropic/claude-sonnet-4-6",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "ANTHROPIC_TEMPERATURE": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "ANTHROPIC_TEMPERATURE=0.7",
            "LLM_TEMPERATURE=0.7",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "ANTHROPIC_MAX_TOKENS": {
        "help_key": "settings.ai_model.legacy_provider_params",
        "examples": [
            "ANTHROPIC_MAX_TOKENS=8192",
        ],
        "docs": _DOC_LLM_CONFIG,
        "warning_codes": ["legacy_provider_setting"],
    },
    "TICKFLOW_API_KEY": {
        "help_key": "settings.data_source.TICKFLOW_API_KEY",
        "examples": [
            "TICKFLOW_API_KEY=your_tickflow_key",
        ],
        "docs": _DOC_FULL_GUIDE_DATA_SOURCE,
        "warning_codes": ["secret_value"],
    },
    "SERPAPI_API_KEYS": {
        "help_key": "settings.data_source.search_api_keys",
        "examples": [
            "SERPAPI_API_KEYS=serpapi_key_1,serpapi_key_2",
        ],
        "docs": _DOC_FULL_GUIDE_SEARCH,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "BRAVE_API_KEYS": {
        "help_key": "settings.data_source.search_api_keys",
        "examples": [
            "BRAVE_API_KEYS=brave_key_1,brave_key_2",
        ],
        "docs": _DOC_FULL_GUIDE_SEARCH,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "BOCHA_API_KEYS": {
        "help_key": "settings.data_source.search_api_keys",
        "examples": [
            "BOCHA_API_KEYS=bocha_key_1,bocha_key_2",
        ],
        "docs": _DOC_FULL_GUIDE_SEARCH,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "MINIMAX_API_KEYS": {
        "help_key": "settings.data_source.search_api_keys",
        "examples": [
            "MINIMAX_API_KEYS=minimax_key_1,minimax_key_2",
        ],
        "docs": _DOC_FULL_GUIDE_SEARCH,
        "warning_codes": ["secret_value", "comma_separated_keys"],
    },
    "SEARXNG_PUBLIC_INSTANCES_ENABLED": {
        "help_key": "settings.data_source.SEARXNG_BASE_URLS",
        "examples": [
            "SEARXNG_PUBLIC_INSTANCES_ENABLED=true",
            "SEARXNG_PUBLIC_INSTANCES_ENABLED=false",
        ],
        "docs": _DOC_FULL_GUIDE_SEARCH,
        "warning_codes": ["public_instance_stability"],
    },
    "BIAS_THRESHOLD": {
        "help_key": "settings.data_source.BIAS_THRESHOLD",
        "examples": [
            "BIAS_THRESHOLD=5.0",
            "BIAS_THRESHOLD=8.0",
        ],
        "docs": _DOC_FULL_GUIDE_DATA_SOURCE,
        "warning_codes": [],
    },
    "PYTDX_HOST": {
        "help_key": "settings.data_source.pytdx",
        "examples": [
            "PYTDX_HOST=119.147.212.81",
            "PYTDX_PORT=7709",
        ],
        "docs": _DOC_FULL_GUIDE_DATA_SOURCE,
        "warning_codes": [],
    },
    "PYTDX_PORT": {
        "help_key": "settings.data_source.pytdx",
        "examples": [
            "PYTDX_PORT=7709",
            "PYTDX_HOST=119.147.212.81",
        ],
        "docs": _DOC_FULL_GUIDE_DATA_SOURCE,
        "warning_codes": [],
    },
    "PYTDX_SERVERS": {
        "help_key": "settings.data_source.pytdx",
        "examples": [
            "PYTDX_SERVERS=119.147.212.81:7709,119.147.212.81:7711",
        ],
        "docs": _DOC_FULL_GUIDE_DATA_SOURCE,
        "warning_codes": ["overrides_pytdx_host_port"],
    },
    "DINGTALK_APP_KEY": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "DINGTALK_APP_KEY=your_dingtalk_app_key",
            "DINGTALK_APP_SECRET=your_dingtalk_app_secret",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "DINGTALK_APP_SECRET": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "DINGTALK_APP_SECRET=your_dingtalk_app_secret",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "PUSHPLUS_TOKEN": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "PUSHPLUS_TOKEN=your_pushplus_token",
            "PUSHPLUS_TOPIC=your_pushplus_topic",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "CUSTOM_WEBHOOK_BEARER_TOKEN": {
        "help_key": "settings.notification.CUSTOM_WEBHOOK_URLS",
        "examples": [
            "CUSTOM_WEBHOOK_BEARER_TOKEN=your_bearer_token",
        ],
        "docs": _DOC_CUSTOM_WEBHOOK,
        "warning_codes": ["secret_value"],
    },
    "CUSTOM_WEBHOOK_BODY_TEMPLATE": {
        "help_key": "settings.notification.CUSTOM_WEBHOOK_URLS",
        "examples": [
            'CUSTOM_WEBHOOK_BODY_TEMPLATE={"msg_type":"text","content":$content_json}',
            'CUSTOM_WEBHOOK_BODY_TEMPLATE={"title":$title_json,"text":$content_json}',
        ],
        "docs": _DOC_CUSTOM_WEBHOOK,
        "warning_codes": ["json_template_must_render_object"],
    },
    "FEISHU_WEBHOOK_SECRET": {
        "help_key": "settings.notification.FEISHU_WEBHOOK_URL",
        "examples": [
            "FEISHU_WEBHOOK_SECRET=your_feishu_webhook_secret",
        ],
        "docs": [
            *_DOC_FULL_GUIDE_NOTIFICATION,
            {
                "label": "飞书机器人配置专题",
                "href": "https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/bot/feishu-bot-config.md",
            },
        ],
        "warning_codes": ["secret_value"],
    },
    "FEISHU_WEBHOOK_KEYWORD": {
        "help_key": "settings.notification.FEISHU_WEBHOOK_URL",
        "examples": [
            "FEISHU_WEBHOOK_KEYWORD=股票日报",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": [],
    },
    "FEISHU_APP_ID": {
        "help_key": "settings.notification.FEISHU_WEBHOOK_URL",
        "examples": [
            "FEISHU_APP_ID=cli_xxxxx",
            "FEISHU_APP_SECRET=your_feishu_app_secret",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["not_webhook_delivery"],
    },
    "FEISHU_APP_SECRET": {
        "help_key": "settings.notification.FEISHU_WEBHOOK_URL",
        "examples": [
            "FEISHU_APP_SECRET=your_feishu_app_secret",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value", "not_webhook_delivery"],
    },
    "TELEGRAM_MESSAGE_THREAD_ID": {
        "help_key": "settings.notification.telegram",
        "examples": [
            "TELEGRAM_MESSAGE_THREAD_ID=123",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": [],
    },
    "DISCORD_MAIN_CHANNEL_ID": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "DISCORD_MAIN_CHANNEL_ID=123456789012345678",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": [],
    },
    "DISCORD_INTERACTIONS_PUBLIC_KEY": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "DISCORD_INTERACTIONS_PUBLIC_KEY=your_discord_public_key",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": [],
    },
    "SLACK_CHANNEL_ID": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "SLACK_CHANNEL_ID=C0123456789",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": [],
    },
    "SLACK_WEBHOOK_URL": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["webhook_secret_value"],
    },
    "PUSHOVER_USER_KEY": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "PUSHOVER_USER_KEY=your_pushover_user_key",
            "PUSHOVER_API_TOKEN=your_pushover_api_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "PUSHOVER_API_TOKEN": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "PUSHOVER_API_TOKEN=your_pushover_api_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "NTFY_URL": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "NTFY_URL=https://ntfy.sh/your_topic",
            "NTFY_TOKEN=your_ntfy_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["webhook_secret_value"],
    },
    "NTFY_TOKEN": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "NTFY_TOKEN=your_ntfy_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "GOTIFY_URL": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "GOTIFY_URL=https://gotify.example.com",
            "GOTIFY_TOKEN=your_gotify_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["webhook_secret_value"],
    },
    "GOTIFY_TOKEN": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "GOTIFY_TOKEN=your_gotify_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "PUSHPLUS_TOPIC": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "PUSHPLUS_TOPIC=your_pushplus_topic",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": [],
    },
    "SERVERCHAN3_SENDKEY": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "SERVERCHAN3_SENDKEY=your_serverchan3_sendkey",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
    "ASTRBOT_URL": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "ASTRBOT_URL=https://astrbot.example.com/webhook",
            "ASTRBOT_TOKEN=your_astrbot_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["webhook_secret_value"],
    },
    "ASTRBOT_TOKEN": {
        "help_key": "settings.notification.chat_bots",
        "examples": [
            "ASTRBOT_TOKEN=your_astrbot_token",
        ],
        "docs": _DOC_FULL_GUIDE_NOTIFICATION,
        "warning_codes": ["secret_value"],
    },
}


def get_category_definitions() -> List[Dict[str, Any]]:
    """Return deep-copied category metadata."""
    return deepcopy(_CATEGORY_DEFINITIONS)


def get_registered_field_keys() -> List[str]:
    """Return all explicitly registered keys."""
    return list(_FIELD_DEFINITIONS.keys())


def _extract_option_values(options: List[Any]) -> List[str]:
    """Extract canonical option values from string/object style select options."""
    values: List[str] = []
    for option in options:
        if isinstance(option, str):
            values.append(option)
            continue
        if isinstance(option, dict):
            value = option.get("value")
            if isinstance(value, str) and value:
                values.append(value)
    return values


def get_field_definition(key: str, value_hint: Optional[str] = None) -> Dict[str, Any]:
    """Return field definition for key, including inferred fallback metadata."""
    key_upper = key.upper()
    if key_upper in _FIELD_DEFINITIONS:
        field = deepcopy(_FIELD_DEFINITIONS[key_upper])
        if key_upper in _FIELD_HELP_METADATA:
            field.update(deepcopy(_FIELD_HELP_METADATA[key_upper]))
        field["key"] = key_upper
        validation = deepcopy(field.get("validation") or {})
        option_values = _extract_option_values(field.get("options", []))
        if field.get("ui_control") == "select" and option_values and "enum" not in validation:
            validation["enum"] = option_values
        field["validation"] = validation
        return field

    category = _infer_category(key_upper)
    data_type = _infer_data_type(key_upper, value_hint)
    field = {
        "key": key_upper,
        "title": key_upper.replace("_", " ").title(),
        "description": "Auto-inferred field metadata.",
        "category": category,
        "data_type": data_type,
        "ui_control": _infer_ui_control(data_type, key_upper),
        "is_sensitive": _is_sensitive_key(key_upper),
        "is_required": False,
        "is_editable": True,
        "default_value": None,
        "options": [],
        "validation": {},
        "display_order": 9000,
    }
    return field


def build_schema_response() -> Dict[str, Any]:
    """Build schema payload grouped by category."""
    category_map: Dict[str, Dict[str, Any]] = {}
    for category in get_category_definitions():
        category_map[category["category"]] = {**category, "fields": []}

    for key in sorted(_FIELD_DEFINITIONS.keys()):
        field = get_field_definition(key)
        category_map[field["category"]]["fields"].append(field)

    categories = sorted(category_map.values(), key=lambda item: item["display_order"])
    for category in categories:
        category["fields"] = sorted(
            category["fields"],
            key=lambda item: (item.get("display_order", 9999), item["key"]),
        )

    return {
        "schema_version": SCHEMA_VERSION,
        "categories": categories,
    }


def _is_sensitive_key(key: str) -> bool:
    markers = ("KEY", "TOKEN", "SECRET", "PASSWORD")
    return any(marker in key for marker in markers)


def _infer_category(key: str) -> str:
    if key == "STOCK_LIST":
        return "base"
    if key.startswith("BACKTEST_"):
        return "backtest"
    if key.startswith(("GEMINI_", "OPENAI_", "ANTHROPIC_", "LITELLM_", "AIHUBMIX_", "DEEPSEEK_", "LLM_")):
        return "ai_model"
    if key.endswith("_PRIORITY") or key.startswith(
        (
            "TUSHARE",
            "TICKFLOW",
            "AKSHARE",
            "EFINANCE",
            "PYTDX",
            "BAOSTOCK",
            "YFINANCE",
            "TAVILY",
            "SERPAPI",
            "BRAVE",
            "BOCHA",
            "ANSPIRE",
            "SEARXNG",
            "NEWS_",
            "BIAS_",
        )
    ) or key in ("ENABLE_REALTIME_QUOTE", "ENABLE_CHIP_DISTRIBUTION"):
        return "data_source"
    if key.startswith((
        "WECHAT",
        "FEISHU",
        "TELEGRAM",
        "EMAIL",
        "PUSHOVER",
        "NTFY",
        "GOTIFY",
        "PUSHPLUS",
        "SERVERCHAN",
        "DINGTALK",
        "DISCORD",
        "SLACK",
        "CUSTOM_WEBHOOK",
        "WECOM",
        "ASTRBOT",
    )) or "WEBHOOK" in key:
        return "notification"
    if key.startswith(("LOG_", "SCHEDULE_", "WEBUI_", "HTTP_", "HTTPS_", "MAX_", "DEBUG", "MARKET_REVIEW_", "TRADING_DAY_", "ANALYSIS_DELAY")):
        return "system"
    return "uncategorized"


def _infer_data_type(key: str, value_hint: Optional[str]) -> str:
    if key.endswith("_TIME"):
        return "time"
    if value_hint is None:
        return "string"

    lowered = value_hint.strip().lower()
    if lowered in {"true", "false"}:
        return "boolean"

    try:
        int(value_hint)
        return "integer"
    except (TypeError, ValueError):
        pass

    try:
        float(value_hint)
        return "number"
    except (TypeError, ValueError):
        pass

    if key in {"STOCK_LIST", "EMAIL_RECEIVERS", "CUSTOM_WEBHOOK_URLS"}:
        return "array"
    return "string"


def _infer_ui_control(data_type: str, key: str) -> str:
    if _is_sensitive_key(key):
        return "password"
    if data_type == "boolean":
        return "switch"
    if data_type in {"integer", "number"}:
        return "number"
    if data_type == "time":
        return "time"
    if data_type == "array":
        return "textarea"
    return "text"
