import base64, io from typing import Any, Dict from PIL import Image import torch from transformers import AutoImageProcessor, AutoModel class EndpointHandler: """DINOv2 image -> 768-dim instance embedding (CLS token). The repo ships the full DINOv2 model, so `path` (the deployed repo dir) is a complete model and loads locally with no runtime download. Custom handler because the endpoint 'feature-extraction' task loads a TEXT pipeline and breaks on this vision model. """ def __init__(self, path: str = ""): self.processor = AutoImageProcessor.from_pretrained(path) self.model = AutoModel.from_pretrained(path).eval() def _image(self, inp: Any) -> Image.Image: if isinstance(inp, Image.Image): return inp.convert("RGB") if isinstance(inp, (bytes, bytearray)): return Image.open(io.BytesIO(bytes(inp))).convert("RGB") if isinstance(inp, str): return Image.open(io.BytesIO(base64.b64decode(inp))).convert("RGB") raise ValueError(f"unsupported input type: {type(inp)}") def __call__(self, data: Dict[str, Any]): image = self._image(data.get("inputs")) inputs = self.processor(images=image, return_tensors="pt") with torch.no_grad(): out = self.model(**inputs) pooled = getattr(out, "pooler_output", None) emb = pooled[0] if pooled is not None else out.last_hidden_state[:, 0].squeeze(0) return emb.tolist()