- cloud_session.py: CLOUD_AUTH_BYPASS_IPS with CIDR support; X-Real-IP for Docker bridge NAT-aware client IP resolution; local-dev DB path under CLOUD_DATA_ROOT for bypass sessions - compose.cloud.yml: thread CLOUD_AUTH_BYPASS_IPS from shell env; document Docker bridge CIDR requirement in .env.example - nginx.cloud.conf + nginx.conf: client_max_body_size 20m for barcode uploads - barcode_scanner.py: EXIF orientation correction (PIL ImageOps.exif_transpose) before cv2 decode; rotation coverage extended to [90, 180, 270, 45, 135] to catch sideways barcodes the 270° case was missing - llm_recipe.py: CF-core VRAM lease acquire/release wrapping LLMRouter calls - tasks/runner.py + config.py: COORDINATOR_URL + recipe_llm VRAM budget (4GB) - recipes.py: per-request Store creation inside asyncio.to_thread worker to avoid SQLite check_same_thread violations - download_datasets.py: HF_PARQUET_FILES strategy for repos without dataset builders (lishuyang/recipepairs direct parquet download) - derive_substitutions.py: use recipepairs_recipes.parquet for ingredient lookup; numpy array detection; JSON category parsing - test_build_flavorgraph_index.py: rewritten for CSV-based index format - pyproject.toml: add Pillow>=10.0 for EXIF rotation support
62 lines
2 KiB
Python
62 lines
2 KiB
Python
"""
|
|
Kiwi application config.
|
|
Uses circuitforge-core for env loading; no pydantic-settings dependency.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from circuitforge_core.config.settings import load_env
|
|
|
|
# Load .env from the repo root (two levels up from app/core/)
|
|
_ROOT = Path(__file__).resolve().parents[2]
|
|
load_env(_ROOT / ".env")
|
|
|
|
|
|
class Settings:
|
|
# API
|
|
API_PREFIX: str = os.environ.get("API_PREFIX", "/api/v1")
|
|
PROJECT_NAME: str = "Kiwi — Pantry Intelligence"
|
|
|
|
# CORS
|
|
CORS_ORIGINS: list[str] = [
|
|
o.strip()
|
|
for o in os.environ.get("CORS_ORIGINS", "").split(",")
|
|
if o.strip()
|
|
]
|
|
|
|
# File storage
|
|
DATA_DIR: Path = Path(os.environ.get("DATA_DIR", str(_ROOT / "data")))
|
|
UPLOAD_DIR: Path = DATA_DIR / "uploads"
|
|
PROCESSING_DIR: Path = DATA_DIR / "processing"
|
|
ARCHIVE_DIR: Path = DATA_DIR / "archive"
|
|
|
|
# Database
|
|
DB_PATH: Path = Path(os.environ.get("DB_PATH", str(DATA_DIR / "kiwi.db")))
|
|
|
|
# Processing
|
|
MAX_CONCURRENT_JOBS: int = int(os.environ.get("MAX_CONCURRENT_JOBS", "4"))
|
|
USE_GPU: bool = os.environ.get("USE_GPU", "true").lower() in ("1", "true", "yes")
|
|
GPU_MEMORY_LIMIT: int = int(os.environ.get("GPU_MEMORY_LIMIT", "6144"))
|
|
|
|
# Quality
|
|
MIN_QUALITY_SCORE: float = float(os.environ.get("MIN_QUALITY_SCORE", "50.0"))
|
|
|
|
# CF-core resource coordinator (VRAM lease management)
|
|
COORDINATOR_URL: str = os.environ.get("COORDINATOR_URL", "http://localhost:7700")
|
|
|
|
# Feature flags
|
|
ENABLE_OCR: bool = os.environ.get("ENABLE_OCR", "false").lower() in ("1", "true", "yes")
|
|
|
|
# Runtime
|
|
DEBUG: bool = os.environ.get("DEBUG", "false").lower() in ("1", "true", "yes")
|
|
CLOUD_MODE: bool = os.environ.get("CLOUD_MODE", "false").lower() in ("1", "true", "yes")
|
|
DEMO_MODE: bool = os.environ.get("DEMO_MODE", "false").lower() in ("1", "true", "yes")
|
|
|
|
def ensure_dirs(self) -> None:
|
|
for d in (self.UPLOAD_DIR, self.PROCESSING_DIR, self.ARCHIVE_DIR):
|
|
d.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
settings = Settings()
|