meeting_memory/core_agent/config.py

105 lines
3.5 KiB
Python
Raw Normal View History

2026-06-24 07:05:19 +00:00
from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
try:
from dotenv import load_dotenv
except Exception: # pragma: no cover
load_dotenv = None
@dataclass(slots=True)
class CoreAgentConfig:
model: str
api_key: Optional[str]
base_url: Optional[str]
timeout: float = 120.0
temperature: float = 0.2
max_iterations: int = 12
user_name: str = "User"
agent_name: str = "MeetingAgent"
def load_core_agent_env(env_dir: str | Path | None = None) -> Optional[Path]:
candidates = []
if env_dir:
candidates.append(Path(env_dir) / ".env")
candidates.append(Path.cwd() / ".env")
for path in candidates:
if not path.exists():
continue
if load_dotenv is not None:
load_dotenv(path, override=False)
else:
_load_env_without_dependency(path)
return path
return None
def apply_compat_env_aliases() -> None:
if not os.getenv("OPENAI_API_KEY") and os.getenv("API_KEY"):
os.environ["OPENAI_API_KEY"] = os.environ["API_KEY"]
if not os.getenv("OPENAI_BASE_URL") and os.getenv("BASE_URL"):
os.environ["OPENAI_BASE_URL"] = os.environ["BASE_URL"]
if not os.getenv("CORE_AGENT_MODEL") and os.getenv("MODEL_NAME"):
os.environ["CORE_AGENT_MODEL"] = os.environ["MODEL_NAME"]
def build_core_agent_config() -> CoreAgentConfig:
return CoreAgentConfig(
model=os.getenv("CORE_AGENT_MODEL") or os.getenv("MODEL_NAME") or os.getenv("MODEL") or "",
api_key=os.getenv("OPENAI_API_KEY") or os.getenv("API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL") or os.getenv("BASE_URL"),
timeout=float(os.getenv("OPENAI_TIMEOUT", "120")),
temperature=float(os.getenv("OPENAI_TEMPERATURE", "0.2")),
max_iterations=int(os.getenv("CORE_AGENT_MAX_ITERATIONS", "12")),
user_name=os.getenv("USER_NAME", "User"),
agent_name=os.getenv("AGENT_NAME", "MeetingAgent"),
)
def require_model_config(config: CoreAgentConfig) -> None:
missing = []
if _is_missing_or_placeholder(config.api_key):
missing.append("OPENAI_API_KEY")
if _is_missing_or_placeholder(config.base_url):
missing.append("OPENAI_BASE_URL")
if _is_missing_or_placeholder(config.model):
missing.append("MODEL_NAME or CORE_AGENT_MODEL")
if missing:
joined = ", ".join(missing)
raise RuntimeError(
"缺少大模型配置:" + joined + "。请复制 .env.example 为 .env并填写 OpenAI-compatible API 配置;"
"如果只想本地演示工具流程,请加 --offline。"
)
def _load_env_without_dependency(path: Path) -> None:
for raw_line in path.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
def _is_missing_or_placeholder(value: Optional[str]) -> bool:
if value is None or not str(value).strip():
return True
normalized = str(value).strip().lower()
placeholders = {
"your-api-key-here",
"your-base-url-here",
"your-model-name-here",
"sk-xxx",
"xxx",
"changeme",
}
return normalized in placeholders or normalized.startswith("your-")