"""配置加载:backend/config.json(可从 config.example.json 复制修改)。 未配置 api_key 时,AI 玩家自动回退为内置启发式 AI,游戏依然完整可玩。 支持环境变量覆盖:WEREWOLF_LLM_BASE_URL / WEREWOLF_LLM_API_KEY / WEREWOLF_LLM_MODEL。 """ from __future__ import annotations import json import os from dataclasses import dataclass, field from pathlib import Path BACKEND_DIR = Path(__file__).resolve().parent.parent CONFIG_PATH = BACKEND_DIR / "config.json" EXAMPLE_PATH = BACKEND_DIR / "config.example.json" @dataclass class LLMConfig: base_url: str = "" api_key: str = "" model: str = "" temperature: float = 0.8 @property def configured(self) -> bool: return bool(self.base_url and self.api_key and self.model) @dataclass class ServerConfig: host: str = "0.0.0.0" port: int = 8000 @dataclass class AppConfig: llm: LLMConfig = field(default_factory=LLMConfig) server: ServerConfig = field(default_factory=ServerConfig) def load_config() -> AppConfig: cfg = AppConfig() source = CONFIG_PATH if not source.exists() and EXAMPLE_PATH.exists(): source = EXAMPLE_PATH if source.exists(): try: raw = json.loads(source.read_text(encoding="utf-8")) except (json.JSONDecodeError, OSError): raw = {} llm = raw.get("llm", {}) cfg.llm.base_url = llm.get("base_url", cfg.llm.base_url) cfg.llm.api_key = llm.get("api_key", cfg.llm.api_key) cfg.llm.model = llm.get("model", cfg.llm.model) cfg.llm.temperature = float(llm.get("temperature", cfg.llm.temperature)) srv = raw.get("server", {}) cfg.server.host = srv.get("host", cfg.server.host) cfg.server.port = int(srv.get("port", cfg.server.port)) cfg.llm.base_url = os.environ.get("WEREWOLF_LLM_BASE_URL", cfg.llm.base_url).strip() cfg.llm.api_key = os.environ.get("WEREWOLF_LLM_API_KEY", cfg.llm.api_key).strip() cfg.llm.model = os.environ.get("WEREWOLF_LLM_MODEL", cfg.llm.model).strip() return cfg CONFIG = load_config()