Files

1 line
25 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "choco-robot/homebot", "files": {"software/src/configs/secrets.py": {"2b5eb736e6f8ad8fb3402ab778bcdb6229864f38": {"t": 1785674269.117423, "n": 10238, "h": "57bbad16a6dff62e", "c": "# -*- coding: utf-8 -*-\n\"\"\"密钥管理模块 - 安全加载和管理API密钥\n\n支持从环境变量和.env.local文件加载敏感配置\n确保密钥不会意外提交到版本控制\n\"\"\"\nimport os\nimport sys\nfrom pathlib import Path\nfrom typing import Optional\nfrom dataclasses import dataclass, field\n\nfrom common.logging import get_logger\n\nlogger = get_logger(__name__)\n\n\n# 项目根目录(software/)\nPROJECT_ROOT = Path(__file__).parent.parent.parent\n\n\ndef _load_env_file(env_path: Path) -> None:\n \"\"\"加载.env文件到环境变量\n \n Args:\n env_path: .env文件路径\n \"\"\"\n if not env_path.exists():\n return\n \n try:\n with open(env_path, 'r', encoding='utf-8') as f:\n for line in f:\n line = line.strip()\n # 跳过注释和空行\n if not line or line.startswith('#'):\n continue\n # 解析KEY=VALUE\n if '=' in line:\n key, value = line.split('=', 1)\n key = key.strip()\n value = value.strip().strip('\"\\'') # 去除引号\n # 只设置尚未存在的环境变量(允许用户通过系统环境变量覆盖)\n if key not in os.environ:\n os.environ[key] = value\n logger.debug(f\"已加载环境变量文件: {env_path}\")\n except Exception as e:\n logger.warning(f\"加载环境变量文件失败: {e}\")\n\n\ndef _load_all_env_files() -> None:\n \"\"\"按优先级加载所有环境变量文件\"\"\"\n # 优先级:.env.local > .env.development > .env.production > .env\n env_files = [\n PROJECT_ROOT / \".env.local\",\n PROJECT_ROOT / \".env.development\",\n PROJECT_ROOT / \".env.production\",\n PROJECT_ROOT / \".env\",\n ]\n \n for env_file in env_files:\n if env_file.exists():\n _load_env_file(env_file)\n\n\n# 模块加载时自动执行\n_load_all_env_files()\n\n\n@dataclass\nclass TTSSecrets:\n \"\"\"TTS 密钥配置\"\"\"\n appid: str = \"\"\n access_token: str = \"\"\n resource_id: str = \"seed-tts-2.0\"\n voice_type: str = \"zh_female_vv_uranus_bigtts\"\n\n\n@dataclass\nclass LLMSecrets:\n \"\"\"LLM 密钥配置\"\"\"\n api_key: str = \"\"\n api_url: str = \"https://ark.cn-beijing.volces.com/api/v3\" # 默认使用火山Ark\n model: str = \"\" # 火山Ark需要填写模型ID,如 ep-20250324123456-abcdef\n\n\n@dataclass\nclass VisionSecrets:\n \"\"\"图片理解/Vision API 密钥配置\"\"\"\n provider: str = \"deepseek\" # deepseek/qwen/openai\n api_key: str = \"\"\n api_url: str = \"\"\n model: str = \"\"\n\n\n@dataclass\nclass Secrets:\n \"\"\"所有密钥配置\"\"\"\n tts: TTSSecrets = field(default_factory=TTSSecrets)\n llm: LLMSecrets = field(default_factory=LLMSecrets)\n vision: VisionSecrets = field(default_factory=VisionSecrets)\n\n\n# 全局密钥实例\n_secrets_instance: Optional[Secrets] = None\n\n\ndef _get_env(key: str, default: str = \"\") -> str:\n \"\"\"安全获取环境变量\n \n Args:\n key: 环境变量名\n default: 默认值\n \n Returns:\n 环境变量值或默认值\n \"\"\"\n return os.environ.get(key, default)\n\n\ndef _mask_key(key: str, visible_start: int = 4, visible_end: int = 4) -> str:\n \"\"\"脱敏显示API密钥\n \n Args:\n key: 原始密钥\n visible_start: 开头显示的字符数\n visible_end: 结尾显示的字符数\n \n Returns:\n 脱敏后的密钥,如 sk-****1234\n \"\"\"\n if len(key) <= visible_start + visible_end:\n return \"*\" * len(key) if key else \"(未设置)\"\n return f\"{key[:visible_start]}****{key[-visible_end:]}\"\n\n\ndef load_secrets() -> Secrets:\n \"\"\"加载所有密钥配置\n \n 从环境变量加载敏感配置,支持多种命名方式\n \n Returns:\n Secrets: 密钥配置对象\n \"\"\"\n # TTS 配置 - 支持多种环境变量名\n tts = TTSSecrets(\n appid=_get_env(\"VOLCANO_APPID\", _get_env(\"TTS_APPID\", \"\")),\n access_token=_get_env(\"VOLCANO_ACCESS_TOKEN\", _get_env(\"TTS_ACCESS_TOKEN\", \"\")),\n resource_id=_get_env(\"VOLCANO_RESOURCE_ID\", \"seed-tts-2.0\"),\n voice_type=_get_env(\"VOLCANO_VOICE_TYPE\", \"zh_female_vv_uranus_bigtts\"),\n )\n \n # LLM 配置 - 优先使用火山Ark,兼容DeepSeek\n llm = LLMSecrets(\n api_key=_get_env(\"ARK_API_KEY\", _get_env(\"VOLCANO_API_KEY\", _get_env(\"DEEPSEEK_API_KEY\", _get_env(\"LLM_API_KEY\", \"\")))),\n api_url=_get_env(\"ARK_API_URL\", _get_env(\"LLM_API_URL\", \"https://ark.cn-beijing.volces.com/api/v3\")),\n model=_get_env(\"ARK_MODEL_ID\", _get_env(\"VOLCANO_MODEL_ID\", _get_env(\"DEEPSEEK_MODEL\", _get_env(\"LLM_MODEL\", \"\")))),\n )\n \n # Vision 配置\n vision_provider = _get_env(\"VISION_PROVIDER\", \"deepseek\")\n \n # 根据provider获取对应的配置\n vision_api_key = _get_env(\"VISION_API_KEY\", \"\")\n vision_api_url = _get_env(\"VISION_API_URL\", \"\")\n vision_model = _get_env(\"VISION_MODEL\", \"\")\n \n # 如果没有单独设置VISION配置,使用LLM的配置\n if not vision_api_key and vision_provider == \"deepseek\":\n vision_api_key = llm.api_key\n vision_api_url = llm.api_url or \"https://api.deepseek.com/v1\"\n vision_model = vision_model or \"deepseek-chat\"\n \n vision = VisionSecrets(\n provider=vision_provider,\n api_key=vision_api_key,\n api_url=vision_api_url,\n model=vision_model,\n )\n \n return Secrets(tts=tts, llm=llm, vision=vision)\n\n\ndef get_secrets() -> Secrets:\n \"\"\"获取全局密钥实例\n \n Returns:\n Secrets: 全局密钥配置实例\n \"\"\"\n global _secrets_instance\n if _secrets_instance is None:\n _secrets_instance = load_secrets()\n return _secrets_instance\n\n\ndef reload_secrets() -> Secrets:\n \"\"\"重新加载密钥配置\n \n 用于在运行时重新读取环境变量\n \n Returns:\n Secrets: 新的密钥配置实例\n \"\"\"\n global _secrets_instance\n _load_all_env_files()\n _secrets_instance = load_secrets()\n return _secrets_instance\n\n\ndef check_secrets(verbose: bool = True) -> dict:\n \"\"\"检查密钥配置状态\n \n 检查各项密钥是否已配置,并返回状态报告\n \n Args:\n verbose: 是否打印详细信息\n \n Returns:\n dict: 各服务的配置状态\n \"\"\"\n secrets = get_secrets()\n \n status = {\n \"tts\": {\n \"configured\": bool(secrets.tts.appid and secrets.tts.access_token),\n \"appid\": secrets.tts.appid[:6] + \"...\" if secrets.tts.appid else \"(未设置)\",\n \"access_token\": _mask_key(secrets.tts.access_token),\n },\n \"llm\": {\n \"configured\": bool(secrets.llm.api_key),\n \"api_key\": _mask_key(secrets.llm.api_key),\n \"model\": secrets.llm.model,\n },\n \"vision\": {\n \"configured\": bool(secrets.vision.api_key),\n \"provider\": secrets.vision.provider,\n \"api_key\": _mask_key(secrets.vision.api_key),\n },\n }\n \n if verbose:\n print(\"\\n\" + \"=\" * 50)\n print(\"HomeBot API Key Configuration Status\")\n print(\"=\" * 50)\n \n # TTS 状态\n tts_ok = status[\"tts\"][\"configured\"]\n status_icon = \"[OK]\" if tts_ok else \"[MISSING]\"\n print(f\"\\n[TTS] 火山引擎 TTS: {status_icon}\")\n if tts_ok:\n print(f\" AppID: {status['tts']['appid']}\")\n print(f\" Access Token: {status['tts']['access_token']}\")\n else:\n print(\" [提示] 设置 VOLCANO_APPID 和 VOLCANO_ACCESS_TOKEN 环境变量\")\n \n # LLM 状态\n llm_ok = status[\"llm\"][\"configured\"]\n status_icon = \"[OK]\" if llm_ok else \"[MISSING]\"\n print(f\"\\n[LLM] 火山Ark LLM: {status_icon}\")\n if llm_ok:\n print(f\" API Key: {status['llm']['api_key']}\")\n print(f\" Model: {status['llm']['model'] if status['llm']['model'] else '(未设置,必填)'}\")\n else:\n print(\" [提示] 设置 ARK_API_KEY 和 ARK_MODEL_ID 环境变量\")\n print(\" ARK_MODEL_ID 格式: ep-20250324123456-abcdef\")\n \n # Vision 状态\n vision_ok = status[\"vision\"][\"configured\"]\n status_icon = \"[OK]\" if vision_ok else \"[MISSING]\"\n print(f\"\\n[Vision] 图片理解: {status_icon}\")\n print(f\" Provider: {status['vision']['provider']}\")\n if vision_ok:\n print(f\" API Key: {status['vision']['api_key']}\")\n elif status[\"vision\"][\"provider\"] == \"deepseek\" and llm_ok:\n print(\" [提示] 将复用 DeepSeek LLM 的配置\")\n status[\"vision\"][\"configured\"] = True # 复用LLM配置\n else:\n print(\" [提示] 设置 VISION_API_KEY 环境变量\")\n \n # 配置文件路径提示\n local_env = PROJECT_ROOT / \".env.local\"\n print(f\"\\n[文件] 配置文件路径: {local_env}\")\n if not local_env.exists():\n example_env = PROJECT_ROOT / \".env.example\"\n print(f\"[提示] 复制 {example_env.name} 为 {local_env.name} 并填入密钥\")\n \n print(\"=\" * 50 + \"\\n\")\n \n return status\n\n\ndef require_secrets(service: str) -> None:\n \"\"\"检查特定服务的密钥是否已配置\n \n 如果未配置,打印帮助信息并退出程序\n \n Args:\n service: 服务名称 (tts/llm/vision)\n \"\"\"\n secrets = get_secrets()\n \n if service == \"tts\":\n if not (secrets.tts.appid and secrets.tts.access_token):\n logger.error(\"火山引擎 TTS 密钥未配置\")\n print(\"\\n[错误] 火山引擎 TTS 密钥未配置\")\n print(\"\\n请设置以下环境变量之一:\")\n print(\" 1. VOLCANO_APPID and VOLCANO_ACCESS_TOKEN\")\n print(\" 2. TTS_APPID and TTS_ACCESS_TOKEN\")\n print(f\"\\n或创建 {PROJECT_ROOT / '.env.local'} 文件,格式如下:\")\n print(\" VOLCANO_APPID=your_appid\")\n print(\" VOLCANO_ACCESS_TOKEN=your_token\")\n sys.exit(1)\n \n elif service == \"llm\":\n if not secrets.llm.api_key:\n logger.error(\"火山Ark LLM API Key 未配置\")\n print(\"\\n[错误] 火山Ark LLM API Key 未配置\")\n print(\"\\n请设置以下环境变量:\")\n print(\" ARK_API_KEY=your_api_key\")\n print(\" ARK_MODEL_ID=ep-your_model_id\")\n print(f\"\\n或创建 {PROJECT_ROOT / '.env.local'} 文件,格式如下:\")\n print(\" ARK_API_KEY=your_api_key\")\n print(\" ARK_MODEL_ID=ep-20250324123456-abcdef\")\n sys.exit(1)\n if not secrets.llm.model:\n logger.error(\"火山Ark 模型ID未配置\")\n print(\"\\n[错误] 火山Ark 模型ID未配置\")\n print(\"\\n请在 .env.local 文件中设置 ARK_MODEL_ID:\")\n print(\" ARK_MODEL_ID=ep-20250324123456-abcdef\")\n print(\"\\n注意: 需要在火山方舟控制台创建推理接入点并复制模型ID\")\n sys.exit(1)\n \n elif service == \"vision\":\n # Vision 可以复用 DeepSeek 的配置\n if not secrets.vision.api_key:\n if secrets.llm.api_key and secrets.vision.provider == \"deepseek\":\n return # 允许复用LLM配置\n logger.error(\"Vision API Key 未配置\")\n print(\"\\n[错误] Vision API Key 未配置\")\n print(\"\\n请设置以下环境变量:\")\n print(\" VISION_API_KEY\")\n print(f\"\\n或创建 {PROJECT_ROOT / '.env.local'} 文件\")\n sys.exit(1)\n\n\nif __name__ == \"__main__\":\n # 命令行检查密钥配置\n check_secrets(verbose=True)\n"}, "599258e1d553f8f6bd2dde99a005c381928cf9b7": {"t": 1785753286.8075523, "n": 10238, "h": "57bbad16a6dff62e", "c": "# -*- coding: utf-8 -*-\n\"\"\"密钥管理模块 - 安全加载和管理API密钥\n\n支持从环境变量和.env.local文件加载敏感配置\n确保密钥不会意外提交到版本控制\n\"\"\"\nimport os\nimport sys\nfrom pathlib import Path\nfrom typing import Optional\nfrom dataclasses import dataclass, field\n\nfrom common.logging import get_logger\n\nlogger = get_logger(__name__)\n\n\n# 项目根目录(software/)\nPROJECT_ROOT = Path(__file__).parent.parent.parent\n\n\ndef _load_env_file(env_path: Path) -> None:\n \"\"\"加载.env文件到环境变量\n \n Args:\n env_path: .env文件路径\n \"\"\"\n if not env_path.exists():\n return\n \n try:\n with open(env_path, 'r', encoding='utf-8') as f:\n for line in f:\n line = line.strip()\n # 跳过注释和空行\n if not line or line.startswith('#'):\n continue\n # 解析KEY=VALUE\n if '=' in line:\n key, value = line.split('=', 1)\n key = key.strip()\n value = value.strip().strip('\"\\'') # 去除引号\n # 只设置尚未存在的环境变量(允许用户通过系统环境变量覆盖)\n if key not in os.environ:\n os.environ[key] = value\n logger.debug(f\"已加载环境变量文件: {env_path}\")\n except Exception as e:\n logger.warning(f\"加载环境变量文件失败: {e}\")\n\n\ndef _load_all_env_files() -> None:\n \"\"\"按优先级加载所有环境变量文件\"\"\"\n # 优先级:.env.local > .env.development > .env.production > .env\n env_files = [\n PROJECT_ROOT / \".env.local\",\n PROJECT_ROOT / \".env.development\",\n PROJECT_ROOT / \".env.production\",\n PROJECT_ROOT / \".env\",\n ]\n \n for env_file in env_files:\n if env_file.exists():\n _load_env_file(env_file)\n\n\n# 模块加载时自动执行\n_load_all_env_files()\n\n\n@dataclass\nclass TTSSecrets:\n \"\"\"TTS 密钥配置\"\"\"\n appid: str = \"\"\n access_token: str = \"\"\n resource_id: str = \"seed-tts-2.0\"\n voice_type: str = \"zh_female_vv_uranus_bigtts\"\n\n\n@dataclass\nclass LLMSecrets:\n \"\"\"LLM 密钥配置\"\"\"\n api_key: str = \"\"\n api_url: str = \"https://ark.cn-beijing.volces.com/api/v3\" # 默认使用火山Ark\n model: str = \"\" # 火山Ark需要填写模型ID,如 ep-20250324123456-abcdef\n\n\n@dataclass\nclass VisionSecrets:\n \"\"\"图片理解/Vision API 密钥配置\"\"\"\n provider: str = \"deepseek\" # deepseek/qwen/openai\n api_key: str = \"\"\n api_url: str = \"\"\n model: str = \"\"\n\n\n@dataclass\nclass Secrets:\n \"\"\"所有密钥配置\"\"\"\n tts: TTSSecrets = field(default_factory=TTSSecrets)\n llm: LLMSecrets = field(default_factory=LLMSecrets)\n vision: VisionSecrets = field(default_factory=VisionSecrets)\n\n\n# 全局密钥实例\n_secrets_instance: Optional[Secrets] = None\n\n\ndef _get_env(key: str, default: str = \"\") -> str:\n \"\"\"安全获取环境变量\n \n Args:\n key: 环境变量名\n default: 默认值\n \n Returns:\n 环境变量值或默认值\n \"\"\"\n return os.environ.get(key, default)\n\n\ndef _mask_key(key: str, visible_start: int = 4, visible_end: int = 4) -> str:\n \"\"\"脱敏显示API密钥\n \n Args:\n key: 原始密钥\n visible_start: 开头显示的字符数\n visible_end: 结尾显示的字符数\n \n Returns:\n 脱敏后的密钥,如 sk-****1234\n \"\"\"\n if len(key) <= visible_start + visible_end:\n return \"*\" * len(key) if key else \"(未设置)\"\n return f\"{key[:visible_start]}****{key[-visible_end:]}\"\n\n\ndef load_secrets() -> Secrets:\n \"\"\"加载所有密钥配置\n \n 从环境变量加载敏感配置,支持多种命名方式\n \n Returns:\n Secrets: 密钥配置对象\n \"\"\"\n # TTS 配置 - 支持多种环境变量名\n tts = TTSSecrets(\n appid=_get_env(\"VOLCANO_APPID\", _get_env(\"TTS_APPID\", \"\")),\n access_token=_get_env(\"VOLCANO_ACCESS_TOKEN\", _get_env(\"TTS_ACCESS_TOKEN\", \"\")),\n resource_id=_get_env(\"VOLCANO_RESOURCE_ID\", \"seed-tts-2.0\"),\n voice_type=_get_env(\"VOLCANO_VOICE_TYPE\", \"zh_female_vv_uranus_bigtts\"),\n )\n \n # LLM 配置 - 优先使用火山Ark,兼容DeepSeek\n llm = LLMSecrets(\n api_key=_get_env(\"ARK_API_KEY\", _get_env(\"VOLCANO_API_KEY\", _get_env(\"DEEPSEEK_API_KEY\", _get_env(\"LLM_API_KEY\", \"\")))),\n api_url=_get_env(\"ARK_API_URL\", _get_env(\"LLM_API_URL\", \"https://ark.cn-beijing.volces.com/api/v3\")),\n model=_get_env(\"ARK_MODEL_ID\", _get_env(\"VOLCANO_MODEL_ID\", _get_env(\"DEEPSEEK_MODEL\", _get_env(\"LLM_MODEL\", \"\")))),\n )\n \n # Vision 配置\n vision_provider = _get_env(\"VISION_PROVIDER\", \"deepseek\")\n \n # 根据provider获取对应的配置\n vision_api_key = _get_env(\"VISION_API_KEY\", \"\")\n vision_api_url = _get_env(\"VISION_API_URL\", \"\")\n vision_model = _get_env(\"VISION_MODEL\", \"\")\n \n # 如果没有单独设置VISION配置,使用LLM的配置\n if not vision_api_key and vision_provider == \"deepseek\":\n vision_api_key = llm.api_key\n vision_api_url = llm.api_url or \"https://api.deepseek.com/v1\"\n vision_model = vision_model or \"deepseek-chat\"\n \n vision = VisionSecrets(\n provider=vision_provider,\n api_key=vision_api_key,\n api_url=vision_api_url,\n model=vision_model,\n )\n \n return Secrets(tts=tts, llm=llm, vision=vision)\n\n\ndef get_secrets() -> Secrets:\n \"\"\"获取全局密钥实例\n \n Returns:\n Secrets: 全局密钥配置实例\n \"\"\"\n global _secrets_instance\n if _secrets_instance is None:\n _secrets_instance = load_secrets()\n return _secrets_instance\n\n\ndef reload_secrets() -> Secrets:\n \"\"\"重新加载密钥配置\n \n 用于在运行时重新读取环境变量\n \n Returns:\n Secrets: 新的密钥配置实例\n \"\"\"\n global _secrets_instance\n _load_all_env_files()\n _secrets_instance = load_secrets()\n return _secrets_instance\n\n\ndef check_secrets(verbose: bool = True) -> dict:\n \"\"\"检查密钥配置状态\n \n 检查各项密钥是否已配置,并返回状态报告\n \n Args:\n verbose: 是否打印详细信息\n \n Returns:\n dict: 各服务的配置状态\n \"\"\"\n secrets = get_secrets()\n \n status = {\n \"tts\": {\n \"configured\": bool(secrets.tts.appid and secrets.tts.access_token),\n \"appid\": secrets.tts.appid[:6] + \"...\" if secrets.tts.appid else \"(未设置)\",\n \"access_token\": _mask_key(secrets.tts.access_token),\n },\n \"llm\": {\n \"configured\": bool(secrets.llm.api_key),\n \"api_key\": _mask_key(secrets.llm.api_key),\n \"model\": secrets.llm.model,\n },\n \"vision\": {\n \"configured\": bool(secrets.vision.api_key),\n \"provider\": secrets.vision.provider,\n \"api_key\": _mask_key(secrets.vision.api_key),\n },\n }\n \n if verbose:\n print(\"\\n\" + \"=\" * 50)\n print(\"HomeBot API Key Configuration Status\")\n print(\"=\" * 50)\n \n # TTS 状态\n tts_ok = status[\"tts\"][\"configured\"]\n status_icon = \"[OK]\" if tts_ok else \"[MISSING]\"\n print(f\"\\n[TTS] 火山引擎 TTS: {status_icon}\")\n if tts_ok:\n print(f\" AppID: {status['tts']['appid']}\")\n print(f\" Access Token: {status['tts']['access_token']}\")\n else:\n print(\" [提示] 设置 VOLCANO_APPID 和 VOLCANO_ACCESS_TOKEN 环境变量\")\n \n # LLM 状态\n llm_ok = status[\"llm\"][\"configured\"]\n status_icon = \"[OK]\" if llm_ok else \"[MISSING]\"\n print(f\"\\n[LLM] 火山Ark LLM: {status_icon}\")\n if llm_ok:\n print(f\" API Key: {status['llm']['api_key']}\")\n print(f\" Model: {status['llm']['model'] if status['llm']['model'] else '(未设置,必填)'}\")\n else:\n print(\" [提示] 设置 ARK_API_KEY 和 ARK_MODEL_ID 环境变量\")\n print(\" ARK_MODEL_ID 格式: ep-20250324123456-abcdef\")\n \n # Vision 状态\n vision_ok = status[\"vision\"][\"configured\"]\n status_icon = \"[OK]\" if vision_ok else \"[MISSING]\"\n print(f\"\\n[Vision] 图片理解: {status_icon}\")\n print(f\" Provider: {status['vision']['provider']}\")\n if vision_ok:\n print(f\" API Key: {status['vision']['api_key']}\")\n elif status[\"vision\"][\"provider\"] == \"deepseek\" and llm_ok:\n print(\" [提示] 将复用 DeepSeek LLM 的配置\")\n status[\"vision\"][\"configured\"] = True # 复用LLM配置\n else:\n print(\" [提示] 设置 VISION_API_KEY 环境变量\")\n \n # 配置文件路径提示\n local_env = PROJECT_ROOT / \".env.local\"\n print(f\"\\n[文件] 配置文件路径: {local_env}\")\n if not local_env.exists():\n example_env = PROJECT_ROOT / \".env.example\"\n print(f\"[提示] 复制 {example_env.name} 为 {local_env.name} 并填入密钥\")\n \n print(\"=\" * 50 + \"\\n\")\n \n return status\n\n\ndef require_secrets(service: str) -> None:\n \"\"\"检查特定服务的密钥是否已配置\n \n 如果未配置,打印帮助信息并退出程序\n \n Args:\n service: 服务名称 (tts/llm/vision)\n \"\"\"\n secrets = get_secrets()\n \n if service == \"tts\":\n if not (secrets.tts.appid and secrets.tts.access_token):\n logger.error(\"火山引擎 TTS 密钥未配置\")\n print(\"\\n[错误] 火山引擎 TTS 密钥未配置\")\n print(\"\\n请设置以下环境变量之一:\")\n print(\" 1. VOLCANO_APPID and VOLCANO_ACCESS_TOKEN\")\n print(\" 2. TTS_APPID and TTS_ACCESS_TOKEN\")\n print(f\"\\n或创建 {PROJECT_ROOT / '.env.local'} 文件,格式如下:\")\n print(\" VOLCANO_APPID=your_appid\")\n print(\" VOLCANO_ACCESS_TOKEN=your_token\")\n sys.exit(1)\n \n elif service == \"llm\":\n if not secrets.llm.api_key:\n logger.error(\"火山Ark LLM API Key 未配置\")\n print(\"\\n[错误] 火山Ark LLM API Key 未配置\")\n print(\"\\n请设置以下环境变量:\")\n print(\" ARK_API_KEY=your_api_key\")\n print(\" ARK_MODEL_ID=ep-your_model_id\")\n print(f\"\\n或创建 {PROJECT_ROOT / '.env.local'} 文件,格式如下:\")\n print(\" ARK_API_KEY=your_api_key\")\n print(\" ARK_MODEL_ID=ep-20250324123456-abcdef\")\n sys.exit(1)\n if not secrets.llm.model:\n logger.error(\"火山Ark 模型ID未配置\")\n print(\"\\n[错误] 火山Ark 模型ID未配置\")\n print(\"\\n请在 .env.local 文件中设置 ARK_MODEL_ID:\")\n print(\" ARK_MODEL_ID=ep-20250324123456-abcdef\")\n print(\"\\n注意: 需要在火山方舟控制台创建推理接入点并复制模型ID\")\n sys.exit(1)\n \n elif service == \"vision\":\n # Vision 可以复用 DeepSeek 的配置\n if not secrets.vision.api_key:\n if secrets.llm.api_key and secrets.vision.provider == \"deepseek\":\n return # 允许复用LLM配置\n logger.error(\"Vision API Key 未配置\")\n print(\"\\n[错误] Vision API Key 未配置\")\n print(\"\\n请设置以下环境变量:\")\n print(\" VISION_API_KEY\")\n print(f\"\\n或创建 {PROJECT_ROOT / '.env.local'} 文件\")\n sys.exit(1)\n\n\nif __name__ == \"__main__\":\n # 命令行检查密钥配置\n check_secrets(verbose=True)\n"}}}}