diff --git a/backend/app/services/ml/tagger.py b/backend/app/services/ml/tagger.py index 0ee33eb..40ad21c 100644 --- a/backend/app/services/ml/tagger.py +++ b/backend/app/services/ml/tagger.py @@ -15,9 +15,13 @@ from dataclasses import dataclass from pathlib import Path import numpy as np -import onnxruntime as ort from PIL import Image, ImageFile +# onnxruntime lives in requirements-ml.txt only — it is NOT installed in the +# lean web image or in CI. Imported lazily inside Tagger.load() so this module +# imports fine without it (the suggestion service imports SURFACED_CATEGORIES +# from here in the web container, and CI collects the pure-logic tests). + # Tolerate minutely-truncated source images (same rationale as IR's wd14.py: # a few missing bytes at the JPEG EOI shouldn't block tagging the whole image). ImageFile.LOAD_TRUNCATED_IMAGES = True @@ -43,7 +47,7 @@ class TagPrediction: class Tagger: def __init__(self, model_dir: Path | None = None): self._model_dir = model_dir or _MODEL_DIR - self._session: ort.InferenceSession | None = None + self._session = None # onnxruntime.InferenceSession once load()ed self._tag_meta: list[dict] | None = None self._input_name: str | None = None self._output_name: str | None = None @@ -73,6 +77,11 @@ class Tagger: {"name": row["name"], "category": row["category"]} ) + # Lazy import — kept after the file-existence checks so the + # missing-model RuntimeError still fires first in environments + # without onnxruntime (CI / lean web image). + import onnxruntime as ort + session = ort.InferenceSession( str(model_path), providers=["CPUExecutionProvider"] )