init
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
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 = "<ctrl>+<shift>+a"
|
||||
toggle: str = "<ctrl>+<shift>+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
|
||||
Reference in New Issue
Block a user