Files
my-botty-tools/botty_next/config/models.py

60 lines
1.7 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import Literal
import yaml
from pydantic import BaseModel, ConfigDict, Field, field_validator
class CaptureConfig(BaseModel):
model_config = ConfigDict(extra="forbid")
backend: Literal["fixture", "mss", "dxcam"] = "fixture"
monitor: int = 1
fps_limit: int = Field(default=10, ge=1, le=240)
window_title: str | None = None
class VisionConfig(BaseModel):
model_config = ConfigDict(extra="forbid")
template_threshold: float = Field(default=0.85, ge=0.0, le=1.0)
debug_output_dir: Path = Path("botty_next/debug/output")
class InputConfig(BaseModel):
model_config = ConfigDict(extra="forbid")
enabled: bool = False
dry_run: bool = True
emergency_stop_key: str = "f12"
@field_validator("dry_run")
@classmethod
def dry_run_required_when_disabled(cls, value: bool) -> bool:
return value
class BottyNextConfig(BaseModel):
model_config = ConfigDict(extra="forbid")
profile_name: str = "local"
capture: CaptureConfig = Field(default_factory=CaptureConfig)
vision: VisionConfig = Field(default_factory=VisionConfig)
input: InputConfig = Field(default_factory=InputConfig)
@field_validator("input")
@classmethod
def input_must_be_explicit_and_dry_run_by_default(cls, value: InputConfig) -> InputConfig:
if value.enabled and value.dry_run is False:
raise ValueError("live input cannot be enabled without a future explicit safety gate")
return value
def load_config(path: str | Path) -> BottyNextConfig:
config_path = Path(path)
with config_path.open("r", encoding="utf-8") as handle:
raw = yaml.safe_load(handle) or {}
return BottyNextConfig.model_validate(raw)