from __future__ import annotations from pathlib import Path from typing import Any import yaml from pydantic import BaseModel from app.ai_provider.config.models import ModelConfig, ProviderConfig from app.core.config import settings class _ModelsFile(BaseModel): version: int = 1 models: list[ModelConfig] class _ProvidersFile(BaseModel): version: int = 1 providers: dict[str, ProviderConfig] class _PricingFile(BaseModel): """pricing.yaml 顶层结构。""" version: int = 1 pricing: dict[str, dict] = {} def get_models_config_path() -> Path: """返回已解析的 models.yaml 路径(project_root/config/models.yaml)。""" return settings.project_root / "config" / "models.yaml" def get_providers_config_path() -> Path: """返回已解析的 providers.yaml 路径(project_root/config/providers.yaml)。""" return settings.project_root / "config" / "providers.yaml" def load_models_config(path: Path | None = None) -> list[ModelConfig]: """从 YAML 文件加载模型配置。如果未指定路径,使用配置中的默认路径。""" if path is None: path = get_models_config_path() if not path.exists(): raise FileNotFoundError(f"Models config not found: {path}") raw: dict[str, Any] = yaml.safe_load(path.read_text(encoding="utf-8")) or {} parsed = _ModelsFile.model_validate(raw) return parsed.models def load_providers_config(path: Path | None = None) -> dict[str, ProviderConfig]: """从 YAML 文件加载提供商配置。如果未指定路径,使用配置中的默认路径。""" if path is None: path = get_providers_config_path() if not path.exists(): raise FileNotFoundError(f"Providers config not found: {path}") raw: dict[str, Any] = yaml.safe_load(path.read_text(encoding="utf-8")) or {} # 验证前将字典键注入为 provider 的 name 字段 providers_raw = raw.get("providers", {}) for name, cfg in providers_raw.items(): if isinstance(cfg, dict) and "name" not in cfg: cfg["name"] = name parsed = _ProvidersFile.model_validate(raw) return parsed.providers def get_pricing_config_path() -> Path: """返回 pricing.yaml 路径(project_root/config/pricing.yaml)。""" return settings.project_root / "config" / "pricing.yaml" def load_pricing_config(path: Path | None = None) -> dict[str, dict]: """从 YAML 文件加载定价配置。""" if path is None: path = get_pricing_config_path() if not path.exists(): return {} raw: dict[str, Any] = yaml.safe_load(path.read_text(encoding="utf-8")) or {} parsed = _PricingFile.model_validate(raw) return parsed.pricing