import os import sys import tomllib from dataclasses import dataclass, field from pathlib import Path from dotenv import load_dotenv @dataclass class HotkeyCfg: capture: str = "++a" toggle: str = "++h" @dataclass class OverlayCfg: width: int = 460 height: int = 360 margin_right: int = 20 margin_bottom: int = 40 alpha: float = 0.93 font_size: int = 13 font_family: str = "Courier" bg_color: str = "#0f0f1a" fg_color: str = "#e2e2e2" accent_color: str = "#4ade80" @dataclass class ScreenshotCfg: delay_ms: int = 200 tmp_path: str = "/tmp/support_agent_screenshot.png" @dataclass class AICfg: api_key: str = "" base_url: str = "" model: str = "gpt-4o" max_tokens: int = 1500 system_prompt: str = "你是一个答题助手,请分析截图中的题目,给出答案和解析。" @dataclass class Config: hotkeys: HotkeyCfg = field(default_factory=HotkeyCfg) overlay: OverlayCfg = field(default_factory=OverlayCfg) screenshot: ScreenshotCfg = field(default_factory=ScreenshotCfg) ai: AICfg = field(default_factory=AICfg) def _find_config_toml() -> Path | None: candidates = [ Path.cwd() / "config.toml", Path(__file__).parent.parent / "config.toml", Path.home() / ".config" / "support-agent" / "config.toml", ] for p in candidates: if p.exists(): return p return None def load_config() -> Config: load_dotenv() cfg = Config() toml_path = _find_config_toml() if toml_path: with open(toml_path, "rb") as f: data = tomllib.load(f) if "hotkeys" in data: h = data["hotkeys"] cfg.hotkeys.capture = h.get("capture", cfg.hotkeys.capture) cfg.hotkeys.toggle = h.get("toggle", cfg.hotkeys.toggle) if "overlay" in data: o = data["overlay"] cfg.overlay.width = o.get("width", cfg.overlay.width) cfg.overlay.height = o.get("height", cfg.overlay.height) cfg.overlay.margin_right = o.get("margin_right", cfg.overlay.margin_right) cfg.overlay.margin_bottom = o.get("margin_bottom", cfg.overlay.margin_bottom) cfg.overlay.alpha = o.get("alpha", cfg.overlay.alpha) cfg.overlay.font_size = o.get("font_size", cfg.overlay.font_size) cfg.overlay.font_family = o.get("font_family", cfg.overlay.font_family) cfg.overlay.bg_color = o.get("bg_color", cfg.overlay.bg_color) cfg.overlay.fg_color = o.get("fg_color", cfg.overlay.fg_color) cfg.overlay.accent_color = o.get("accent_color", cfg.overlay.accent_color) if "screenshot" in data: s = data["screenshot"] cfg.screenshot.delay_ms = s.get("delay_ms", cfg.screenshot.delay_ms) cfg.screenshot.tmp_path = s.get("tmp_path", cfg.screenshot.tmp_path) if "ai" in data: a = data["ai"] cfg.ai.base_url = a.get("base_url", cfg.ai.base_url) cfg.ai.model = a.get("model", cfg.ai.model) cfg.ai.max_tokens = a.get("max_tokens", cfg.ai.max_tokens) cfg.ai.system_prompt = a.get("system_prompt", cfg.ai.system_prompt) # 环境变量优先级高于 config.toml cfg.ai.api_key = os.environ.get("OPENAI_API_KEY", "") env_base_url = os.environ.get("OPENAI_BASE_URL", "") if env_base_url: cfg.ai.base_url = env_base_url if not cfg.ai.api_key: print("警告:未设置 OPENAI_API_KEY,请在 .env 文件中配置", file=sys.stderr) return cfg