"""Central paths and constants for the Küchenpass-Agent project.""" from __future__ import annotations import os from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parent.parent DATA_DIR = PROJECT_ROOT / "data" RAW_DIR = DATA_DIR / "raw" PROCESSED_DIR = DATA_DIR / "processed" SAMPLES_DIR = DATA_DIR / "samples" MODELS_DIR = PROJECT_ROOT / "models" DOCS_DIR = PROJECT_ROOT / "docs" SCREENSHOTS_DIR = DOCS_DIR / "screenshots" ML_MODEL_PATH = MODELS_DIR / "prep_time_xgb.joblib" ML_PIPELINE_PATH = MODELS_DIR / "prep_time_pipeline.joblib" ML_METRICS_PATH = MODELS_DIR / "ml_metrics.json" CV_MODEL_PATH = MODELS_DIR / "food_classifier.pth" CV_CLASSES_PATH = MODELS_DIR / "cv_classes.json" CV_METRICS_PATH = MODELS_DIR / "cv_metrics.json" # Geographic plausibility bounds for the Food Delivery dataset (India). Latitude/ # longitude outside these ranges are treated as noise and set to NaN during # cleaning. Kept deliberately wide so valid rows are never dropped. GEO_LAT_BOUNDS = (6.0, 38.0) GEO_LON_BOUNDS = (68.0, 98.0) # Restaurant-relevant subset of Food-101 (10 dishes that map to typical orders) CV_TARGET_CLASSES: list[str] = [ "pizza", "hamburger", "spaghetti_bolognese", "caesar_salad", "french_fries", "ramen", "steak", "sushi", "lasagna", "tiramisu", ] STATIONS = ["grill", "pasta", "salad", "fryer", "pizza", "dessert", "cold"] DISH_TO_STATION: dict[str, str] = { "pizza": "pizza", "hamburger": "grill", "burger": "grill", "steak": "grill", "spaghetti_bolognese": "pasta", "spaghetti": "pasta", "lasagna": "pasta", "pasta": "pasta", "ramen": "pasta", "caesar_salad": "salad", "salad": "salad", "french_fries": "fryer", "fries": "fryer", "sushi": "cold", "tiramisu": "dessert", "dessert": "dessert", } OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini") DECISION_CONFIDENCE_THRESHOLD = 0.6 # Maps NLP order dishes (which may carry style suffixes like "pizza_margherita" # or synonyms like "cheeseburger") onto one of the 10 CV_TARGET_CLASSES so the # pass-decision can compare the photographed dish with the order. Keys are # matched first exactly, then as substrings (see canonical_cv_dish). DISH_TO_CV_CLASS: dict[str, str] = { "pizza": "pizza", "margherita": "pizza", "hamburger": "hamburger", "burger": "hamburger", "cheeseburger": "hamburger", "spaghetti_bolognese": "spaghetti_bolognese", "spaghetti": "spaghetti_bolognese", "bolognese": "spaghetti_bolognese", "lasagna": "lasagna", "lasagne": "lasagna", "caesar_salad": "caesar_salad", "salad": "caesar_salad", "salat": "caesar_salad", "french_fries": "french_fries", "fries": "french_fries", "pommes": "french_fries", "ramen": "ramen", "steak": "steak", "sushi": "sushi", "tiramisu": "tiramisu", } # Per-class top-1 confidence thresholds for the pass decision. Derived from the # threshold sweep in src/cv/evaluate.py (precision target 0.95 per class). # Classes that are easily confused (lasagna<->pizza, spaghetti<->lasagna) need a # higher bar before we send the plate to the guest. Missing classes fall back to # DECISION_CONFIDENCE_THRESHOLD. PER_CLASS_THRESHOLDS: dict[str, float] = { "lasagna": 0.85, "pizza": 0.75, "spaghetti_bolognese": 0.80, "hamburger": 0.75, "ramen": 0.70, "steak": 0.70, "caesar_salad": 0.60, "sushi": 0.60, "tiramisu": 0.55, "french_fries": 0.55, } for _d in (DATA_DIR, RAW_DIR, PROCESSED_DIR, SAMPLES_DIR, MODELS_DIR): _d.mkdir(parents=True, exist_ok=True)