"""Non-secret local TOML configuration.""" from __future__ import annotations import os import tomllib from collections.abc import Mapping from pathlib import Path class LocalConfigError(ValueError): """A local configuration file is malformed or unsafe to replace.""" def _mapping(value: object, section: str) -> Mapping[str, object]: if value is None: return {} if not isinstance(value, dict): raise LocalConfigError(f"TOML-Abschnitt [{section}] muss eine Tabelle sein.") return value def _value(values: Mapping[str, object], name: str) -> str | None: value = values.get(name) if value is None: return None if isinstance(value, bool): return "true" if value else "false" if isinstance(value, (str, int, float)): return str(value) raise LocalConfigError(f"TOML-Wert '{name}' hat einen nicht unterstützten Typ.") def load_local_defaults(path: Path) -> dict[str, str]: """Translate the supported non-secret TOML fields to settings inputs.""" if not path.exists(): return {} try: with path.open("rb") as handle: document = tomllib.load(handle) except (OSError, tomllib.TOMLDecodeError) as exc: raise LocalConfigError(f"Lokale Konfiguration ist ungültig: {path}") from exc providers = _mapping(document.get("providers"), "providers") gemini = _mapping(providers.get("gemini"), "providers.gemini") ollama = _mapping(providers.get("ollama"), "providers.ollama") translated: dict[str, str] = {} fields = ( (providers, "active", "JAVIS_PROVIDER_MODE"), (ollama, "model", "JAVIS_MODEL"), (ollama, "base_url", "JAVIS_OLLAMA_URL"), (ollama, "timeout_seconds", "JAVIS_MODEL_TIMEOUT"), (gemini, "enabled", "JAVIS_GEMINI_ENABLED"), (gemini, "model", "JAVIS_GEMINI_MODEL"), (gemini, "optional_model", "JAVIS_GEMINI_OPTIONAL_MODEL"), (gemini, "free_only", "JAVIS_FREE_ONLY"), ( gemini, "billing_confirmed_disabled", "JAVIS_GEMINI_BILLING_CONFIRMED_DISABLED", ), (gemini, "max_requests_per_day", "JAVIS_MAX_CLOUD_REQUESTS_PER_DAY"), (gemini, "max_input_chars", "JAVIS_MAX_CLOUD_INPUT_CHARS"), (gemini, "max_output_tokens", "JAVIS_MAX_CLOUD_OUTPUT_TOKENS"), (gemini, "timeout_seconds", "JAVIS_GEMINI_TIMEOUT"), (gemini, "max_attempts", "JAVIS_GEMINI_MAX_ATTEMPTS"), (gemini, "max_context_messages", "JAVIS_MAX_CLOUD_CONTEXT_MESSAGES"), ) for table, toml_name, setting_name in fields: value = _value(table, toml_name) if value is not None: translated[setting_name] = value return translated def write_gemini_activation(path: Path) -> None: """Create a conservative non-secret config without replacing existing data.""" if path.exists(): raise LocalConfigError( f"Lokale Konfiguration existiert bereits und wurde nicht überschrieben: {path}" ) content = """# Javis local configuration. This file must never contain secrets. [providers] active = "auto" [providers.ollama] model = "qwen3:8b" base_url = "http://127.0.0.1:11434" timeout_seconds = 180 [providers.gemini] enabled = true model = "gemini-3.6-flash" optional_model = "gemini-3.5-flash-lite" free_only = true billing_confirmed_disabled = true max_requests_per_day = 25 max_input_chars = 12000 max_output_tokens = 1024 timeout_seconds = 30 max_attempts = 2 max_context_messages = 6 """ try: path.parent.mkdir(parents=True, exist_ok=True) temporary_path = path.with_suffix(path.suffix + ".tmp") temporary_path.write_text(content, encoding="utf-8") os.replace(temporary_path, path) except OSError as exc: raise LocalConfigError( f"Lokale Konfiguration konnte nicht geschrieben werden: {path}" ) from exc