Add settings history and quick AI chain
This commit is contained in:
parent
328dcec458
commit
a0c3b02ab1
13 changed files with 1627 additions and 23 deletions
|
|
@ -11,6 +11,7 @@ def _parse_bool(val: str) -> bool:
|
|||
@dataclass
|
||||
class Config:
|
||||
hotkey: str = "Cmd+m"
|
||||
edit_hotkey: str = "Cmd+n"
|
||||
ffmpeg_input: str = "pulse:default"
|
||||
ffmpeg_path: str = ""
|
||||
|
||||
|
|
@ -19,8 +20,10 @@ class Config:
|
|||
whisper_device: str = "cpu"
|
||||
|
||||
record_timeout_sec: int = 120
|
||||
edit_record_timeout_sec: int = 120
|
||||
|
||||
injection_backend: str = "clipboard"
|
||||
edit_injection_backend: str = "clipboard"
|
||||
|
||||
ai_enabled: bool = False
|
||||
ai_model: str = "llama3.2:3b"
|
||||
|
|
@ -29,10 +32,22 @@ class Config:
|
|||
ai_base_url: str = "http://localhost:11434/v1/chat/completions"
|
||||
ai_api_key: str = ""
|
||||
ai_timeout_sec: int = 20
|
||||
edit_ai_enabled: bool = True
|
||||
edit_ai_temperature: float = 0.0
|
||||
edit_ai_system_prompt_file: str = ""
|
||||
edit_window: dict = field(default_factory=lambda: {"width": 800, "height": 400})
|
||||
|
||||
context_capture: dict = field(default_factory=lambda: {"provider": "i3ipc", "on_focus_change": "abort"})
|
||||
context_rules: list[dict] = field(default_factory=list)
|
||||
|
||||
languages: dict = field(
|
||||
default_factory=lambda: {
|
||||
"en": {"code": "en", "hotkey": "Cmd+m", "label": "English"},
|
||||
"ptBR": {"code": "pt-BR", "hotkey": "Cmd+b", "label": "Português (Brasil)"},
|
||||
}
|
||||
)
|
||||
edit_language_detection: dict = field(default_factory=lambda: {"enabled": True, "provider": "langdetect", "fallback_code": "en"})
|
||||
|
||||
|
||||
def default_path() -> Path:
|
||||
return Path.home() / ".config" / "lel" / "config.json"
|
||||
|
|
@ -66,10 +81,16 @@ def load(path: str | None) -> Config:
|
|||
cfg.ffmpeg_path = os.environ["LEL_FFMPEG_PATH"]
|
||||
if os.getenv("LEL_RECORD_TIMEOUT_SEC"):
|
||||
cfg.record_timeout_sec = int(os.environ["LEL_RECORD_TIMEOUT_SEC"])
|
||||
if os.getenv("LEL_EDIT_RECORD_TIMEOUT_SEC"):
|
||||
cfg.edit_record_timeout_sec = int(os.environ["LEL_EDIT_RECORD_TIMEOUT_SEC"])
|
||||
if os.getenv("LEL_HOTKEY"):
|
||||
cfg.hotkey = os.environ["LEL_HOTKEY"]
|
||||
if os.getenv("LEL_EDIT_HOTKEY"):
|
||||
cfg.edit_hotkey = os.environ["LEL_EDIT_HOTKEY"]
|
||||
if os.getenv("LEL_INJECTION_BACKEND"):
|
||||
cfg.injection_backend = os.environ["LEL_INJECTION_BACKEND"]
|
||||
if os.getenv("LEL_EDIT_INJECTION_BACKEND"):
|
||||
cfg.edit_injection_backend = os.environ["LEL_EDIT_INJECTION_BACKEND"]
|
||||
|
||||
if os.getenv("LEL_AI_ENABLED"):
|
||||
cfg.ai_enabled = _parse_bool(os.environ["LEL_AI_ENABLED"])
|
||||
|
|
@ -85,22 +106,24 @@ def load(path: str | None) -> Config:
|
|||
cfg.ai_api_key = os.environ["LEL_AI_API_KEY"]
|
||||
if os.getenv("LEL_AI_TIMEOUT_SEC"):
|
||||
cfg.ai_timeout_sec = int(os.environ["LEL_AI_TIMEOUT_SEC"])
|
||||
if os.getenv("LEL_EDIT_AI_ENABLED"):
|
||||
cfg.edit_ai_enabled = _parse_bool(os.environ["LEL_EDIT_AI_ENABLED"])
|
||||
if os.getenv("LEL_EDIT_AI_TEMPERATURE"):
|
||||
cfg.edit_ai_temperature = float(os.environ["LEL_EDIT_AI_TEMPERATURE"])
|
||||
if os.getenv("LEL_EDIT_AI_SYSTEM_PROMPT_FILE"):
|
||||
cfg.edit_ai_system_prompt_file = os.environ["LEL_EDIT_AI_SYSTEM_PROMPT_FILE"]
|
||||
|
||||
if os.getenv("LEL_LANGUAGES_JSON"):
|
||||
cfg.languages = json.loads(os.environ["LEL_LANGUAGES_JSON"])
|
||||
if os.getenv("LEL_EDIT_LANG_FALLBACK"):
|
||||
cfg.edit_language_detection["fallback_code"] = os.environ["LEL_EDIT_LANG_FALLBACK"]
|
||||
|
||||
if os.getenv("LEL_CONTEXT_PROVIDER"):
|
||||
cfg.context_capture["provider"] = os.environ["LEL_CONTEXT_PROVIDER"]
|
||||
if os.getenv("LEL_CONTEXT_ON_FOCUS_CHANGE"):
|
||||
cfg.context_capture["on_focus_change"] = os.environ["LEL_CONTEXT_ON_FOCUS_CHANGE"]
|
||||
|
||||
if not cfg.hotkey:
|
||||
raise ValueError("hotkey cannot be empty")
|
||||
if cfg.record_timeout_sec <= 0:
|
||||
raise ValueError("record_timeout_sec must be > 0")
|
||||
if cfg.context_capture.get("provider") not in {"i3ipc"}:
|
||||
raise ValueError("context_capture.provider must be i3ipc")
|
||||
if cfg.context_capture.get("on_focus_change") not in {"abort"}:
|
||||
raise ValueError("context_capture.on_focus_change must be abort")
|
||||
if not isinstance(cfg.context_rules, list):
|
||||
cfg.context_rules = []
|
||||
validate(cfg)
|
||||
return cfg
|
||||
|
||||
|
||||
|
|
@ -108,3 +131,39 @@ def redacted_dict(cfg: Config) -> dict:
|
|||
d = cfg.__dict__.copy()
|
||||
d["ai_api_key"] = ""
|
||||
return d
|
||||
|
||||
|
||||
def validate(cfg: Config) -> None:
|
||||
if not cfg.hotkey:
|
||||
raise ValueError("hotkey cannot be empty")
|
||||
if not cfg.edit_hotkey:
|
||||
raise ValueError("edit_hotkey cannot be empty")
|
||||
if cfg.record_timeout_sec <= 0:
|
||||
raise ValueError("record_timeout_sec must be > 0")
|
||||
if cfg.edit_record_timeout_sec <= 0:
|
||||
raise ValueError("edit_record_timeout_sec must be > 0")
|
||||
if cfg.context_capture.get("provider") not in {"i3ipc"}:
|
||||
raise ValueError("context_capture.provider must be i3ipc")
|
||||
if cfg.context_capture.get("on_focus_change") not in {"abort"}:
|
||||
raise ValueError("context_capture.on_focus_change must be abort")
|
||||
if not isinstance(cfg.context_rules, list):
|
||||
cfg.context_rules = []
|
||||
if not isinstance(cfg.edit_window, dict):
|
||||
cfg.edit_window = {"width": 800, "height": 400}
|
||||
if not isinstance(cfg.languages, dict) or not cfg.languages:
|
||||
raise ValueError("languages must be a non-empty map")
|
||||
seen_hotkeys = set()
|
||||
for name, info in cfg.languages.items():
|
||||
if not isinstance(info, dict):
|
||||
raise ValueError(f"languages[{name}] must be an object")
|
||||
code = info.get("code")
|
||||
hotkey = info.get("hotkey")
|
||||
if not code or not hotkey:
|
||||
raise ValueError(f"languages[{name}] must include code and hotkey")
|
||||
if hotkey in seen_hotkeys:
|
||||
raise ValueError(f"duplicate hotkey in languages: {hotkey}")
|
||||
seen_hotkeys.add(hotkey)
|
||||
if not isinstance(cfg.edit_language_detection, dict):
|
||||
cfg.edit_language_detection = {"enabled": True, "provider": "langdetect", "fallback_code": "en"}
|
||||
if cfg.edit_language_detection.get("provider") not in {"langdetect"}:
|
||||
raise ValueError("edit_language_detection.provider must be langdetect")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue