"""configs/ 目录下 YAML 配置加载器。 目前支持加载 configs/llm_models.yaml 的场景配置(scenes.)。 配置优先级(从高到低): 1. 代码 / 命令行显式参数(如 --provider qwen --model qwen-plus) 2. 本文件 YAML 场景配置(scenes.) 3. 环境变量 / .env(LLM_PROVIDER、DEEPSEEK_MODEL 等,向后兼容) 4. 代码内置默认值 说明:API Key 一律放 .env,本文件只保存环境变量名(api_key_env),禁止写密钥。 """ from __future__ import annotations from functools import lru_cache from pathlib import Path from loguru import logger DEFAULT_CONFIG_PATH = Path("configs/llm_models.yaml") @lru_cache(maxsize=8) def _load_yaml(path: Path) -> dict: """读取 YAML 文件为 dict;文件缺失或解析失败返回空 dict(走兜底配置)。""" if not path.is_file(): logger.debug("配置文件不存在,使用内置/环境变量兜底: {}", path) return {} try: import yaml data = yaml.safe_load(path.read_text(encoding="utf-8")) or {} except Exception as e: # noqa: BLE001 - YAML 语法错误等 logger.error("解析 {} 失败: {}", path, e) return {} return data if isinstance(data, dict) else {} def load_scene_config(scene: str) -> dict: """读取 llm_models.yaml 中 scenes. 的配置 dict。 场景不存在或未配置时返回空 dict(调用方回退 .env / 内置默认)。 scene 为空字符串同样返回空 dict。 """ if not scene: return {} data = _load_yaml(DEFAULT_CONFIG_PATH) scenes = data.get("scenes") or {} cfg = scenes.get(scene) if cfg is None: logger.debug("llm_models.yaml 未配置场景 {!r},使用 .env 兜底", scene) return {} if not isinstance(cfg, dict): logger.warning("llm_models.yaml 场景 {!r} 应为 map,已忽略", scene) return {} return cfg def load_defaults() -> dict: """读取 llm_models.yaml 顶层 defaults(全局默认参数)。""" data = _load_yaml(DEFAULT_CONFIG_PATH) d = data.get("defaults") or {} return d if isinstance(d, dict) else {} def clear_cache() -> None: """清空 YAML 缓存(测试或热更新配置时使用)。""" _load_yaml.cache_clear()