from __future__ import annotations import hashlib import json from abc import ABC, abstractmethod from typing import Any from app.storage.models import VisualAssetRecord, VisualViewRecord from app.storage.repositories import new_id, now_ts def canonical_hash(parameters: dict[str, Any]) -> str: canonical = json.dumps(parameters, sort_keys=True, separators=(",", ":")) return hashlib.sha256(canonical.encode()).hexdigest() class Lens(ABC): name: str version: str = "v1" view_type: str @abstractmethod async def execute(self, asset: VisualAssetRecord, image_bytes: bytes, parameters: dict[str, Any]) -> tuple[dict[str, Any], float, list[dict[str, Any]]]: raise NotImplementedError async def run(self, asset: VisualAssetRecord, image_bytes: bytes, parameters: dict[str, Any]) -> VisualViewRecord: payload, confidence, provenance = await self.execute(asset, image_bytes, parameters) ts = now_ts() return VisualViewRecord( id=new_id("view"), asset_id=asset.id, sha256=asset.sha256, view_type=self.view_type, lens_name=self.name, lens_version=self.version, parameters_hash=canonical_hash(parameters), status="ready", payload_json=payload, confidence=confidence, provenance_json=provenance, created_at=ts, updated_at=ts, )