| from __future__ import annotations |
|
|
| from pathlib import Path |
|
|
| from pydantic_settings import BaseSettings, SettingsConfigDict |
|
|
| ROOT = Path(__file__).resolve().parent.parent |
|
|
| CATEGORIES = ( |
| "groceries", |
| "dining", |
| "transport", |
| "household", |
| "health", |
| "entertainment", |
| "utilities", |
| "office", |
| "travel", |
| "other", |
| ) |
|
|
| DOC_KINDS = ("receipt", "invoice", "document") |
|
|
| ACCEPTED_SUFFIXES = { |
| ".jpg", |
| ".jpeg", |
| ".png", |
| ".webp", |
| ".heic", |
| ".heif", |
| ".tif", |
| ".tiff", |
| ".pdf", |
| ".txt", |
| } |
|
|
|
|
| class Settings(BaseSettings): |
| model_config = SettingsConfigDict( |
| env_prefix="RECEIPT_", |
| env_file=str(ROOT / ".env"), |
| env_file_encoding="utf-8", |
| extra="ignore", |
| ) |
|
|
| root_dir: Path = ROOT |
| data_dir: Path = ROOT / "data" |
| inbox_dir: Path = ROOT / "inbox" |
| processing_dir: Path = ROOT / "processing" |
| processed_dir: Path = ROOT / "processed" |
| failed_dir: Path = ROOT / "failed" |
| exports_dir: Path = ROOT / "exports" |
|
|
| idle_seconds: float = 30.0 |
|
|
| |
| |
| llm_backend: str = "gemma" |
| llm_base_url: str = "http://127.0.0.1:8080/v1" |
| llm_model: str = "google/gemma-4-12B-it" |
| llm_api_key: str = "local" |
| llm_accepts_images: bool = True |
| llm_max_tokens: int = 8192 |
| llm_timeout_s: float = 180.0 |
| |
| |
| llm_route: str = "auto" |
| studio_url: str = "" |
| gpu_host: str = "" |
| hermes_base_url: str = "" |
| hermes_model: str = "google/gemma-4-12B-it" |
| hermes_config_path: Path = Path.home() / ".hermes" / "config.yaml" |
|
|
| embed_backend: str = "omni" |
| embed_base_url: str = "" |
| embed_model: str = "google/gemma-4-12B-it" |
| embed_dim: int = 3840 |
| embed_api_key: str = "local" |
| embed_timeout_s: float = 120.0 |
| embed_prefix: bool = True |
|
|
| ocr_backend: str = "none" |
| ocr_base_url: str = "http://127.0.0.1:11434/v1" |
| ocr_model: str = "" |
| ocr_api_key: str = "local" |
|
|
| camera_url: str = "http://127.0.0.1:5001" |
| snapshot_width: int = 1280 |
| snapshot_quality: int = 85 |
|
|
| sku_auto: float = 0.88 |
| sku_review: float = 0.72 |
| vendor_auto: float = 0.82 |
| vendor_review: float = 0.65 |
|
|
| ui_host: str = "127.0.0.1" |
| ui_port: int = 7860 |
| ui_share_lan: bool = False |
|
|
| jpeg_max_edge: int = 2048 |
|
|
| def model_post_init(self, _context: object) -> None: |
| if not self.embed_base_url: |
| object.__setattr__(self, "embed_base_url", self.llm_base_url) |
| if not self.embed_model: |
| object.__setattr__(self, "embed_model", self.llm_model) |
|
|
| @property |
| def db_path(self) -> Path: |
| return self.data_dir / "receipts.db" |
|
|
| def ensure_dirs(self) -> None: |
| for path in ( |
| self.data_dir, |
| self.inbox_dir, |
| self.processing_dir, |
| self.processed_dir, |
| self.failed_dir, |
| self.exports_dir, |
| ): |
| path.mkdir(parents=True, exist_ok=True) |
|
|
|
|
| def load_settings(**overrides: object) -> Settings: |
| settings = Settings(**overrides) |
| settings.ensure_dirs() |
| return settings |
|
|