"""运行时 LLM 配置:可由业务后端(管理端 AI 配置)动态下发,覆盖 .env。""" from __future__ import annotations from dataclasses import dataclass, field from threading import RLock from typing import Any @dataclass class RuntimeLlmConfig: """enabled=None 表示未由管理端覆盖,沿用 .env。""" enabled: bool | None = None api_base_url: str | None = None api_key: str | None = None model: str | None = None temperature: float | None = None source: str = "env" _lock = RLock() _runtime = RuntimeLlmConfig() def get_runtime_llm() -> RuntimeLlmConfig: with _lock: return RuntimeLlmConfig( enabled=_runtime.enabled, api_base_url=_runtime.api_base_url, api_key=_runtime.api_key, model=_runtime.model, temperature=_runtime.temperature, source=_runtime.source, ) def update_runtime_llm( *, enabled: bool | None = None, api_base_url: str | None = None, api_key: str | None = None, model: str | None = None, temperature: float | None = None, ) -> RuntimeLlmConfig: """ 更新运行时配置。 - api_key 为 None:不改密钥 - api_key 为非空字符串:覆盖 - api_key 为 "":清空运行时密钥(回退 .env) """ with _lock: if enabled is not None: _runtime.enabled = bool(enabled) if api_base_url is not None and api_base_url.strip(): _runtime.api_base_url = api_base_url.strip() if api_key is not None: _runtime.api_key = api_key.strip() if api_key.strip() else None if model is not None and model.strip(): _runtime.model = model.strip() if temperature is not None: t = float(temperature) _runtime.temperature = max(0.0, min(2.0, t)) _runtime.source = "runtime" return get_runtime_llm() def runtime_status(env_key_configured: bool, env_base: str, env_model: str) -> dict[str, Any]: rt = get_runtime_llm() key_ok = bool(rt.api_key) or env_key_configured enabled = False if key_ok: if rt.enabled is None: enabled = env_key_configured or bool(rt.api_key) else: enabled = bool(rt.enabled) return { "enabled": enabled, "configured": key_ok, "api_key_configured": key_ok, "api_base_url": rt.api_base_url or env_base, "model": rt.model or env_model, "temperature": rt.temperature, "source": rt.source if (rt.api_key or rt.enabled is not None or rt.api_base_url) else "env", }