diff --git a/services/imajin-classifier/service/src/models/__init__.py b/services/imajin-classifier/service/src/models/__init__.py index abaa1678..00e0f367 100644 --- a/services/imajin-classifier/service/src/models/__init__.py +++ b/services/imajin-classifier/service/src/models/__init__.py @@ -1,10 +1,14 @@ """Data models for imajin-classifier service.""" from .schemas import ( + AgreementPair, CalibrateRequest, CalibrateResult, + ClassifierResult, ClassifyRequest, ClassifyResult, + CompareRequest, + CompareResult, DimensionCalibration, DimensionDef, LabeledSample, @@ -13,13 +17,17 @@ from .schemas import ( ) __all__ = [ - "DimensionDef", - "ClassifyRequest", - "ClassifyResult", - "LabeledSample", + "AgreementPair", "CalibrateRequest", "CalibrateResult", + "ClassifierResult", + "ClassifyRequest", + "ClassifyResult", + "CompareRequest", + "CompareResult", "DimensionCalibration", + "DimensionDef", + "LabeledSample", "PresetInfo", "PresetsResult", ] diff --git a/services/imajin-classifier/service/src/models/schemas.py b/services/imajin-classifier/service/src/models/schemas.py index 0a5fc1b1..9ae171a1 100644 --- a/services/imajin-classifier/service/src/models/schemas.py +++ b/services/imajin-classifier/service/src/models/schemas.py @@ -226,3 +226,64 @@ class PresetsResult(BaseModel): """List of available scoring presets.""" presets: list[PresetInfo] + + +# --------------------------------------------------------------------------- +# /compare +# --------------------------------------------------------------------------- + +AVAILABLE_CLASSIFIERS = [ + "imajin-siglip2", + "claude-haiku", + "claude-sonnet", + "claude-opus", +] + + +class CompareRequest(BaseModel): + """Request to score an image with multiple classifiers and compare results.""" + + image_base64: str = Field( + ..., + max_length=50_000_000, + description="Base64-encoded image data", + ) + preset: Optional[str] = Field( + None, + description="Preset rubric name. Required if no inline dimensions.", + ) + dimensions: Optional[dict[str, DimensionDef]] = Field( + None, + description="Inline dimension definitions.", + ) + context: dict[str, str] = Field( + default_factory=dict, + description="Context for preset resolution", + ) + classifiers: list[str] = Field( + default_factory=lambda: ["imajin-siglip2", "claude-sonnet"], + description=f"Which classifiers to run. Options: {', '.join(AVAILABLE_CLASSIFIERS)}", + ) + + +class ClassifierResult(BaseModel): + """Scores from a single classifier.""" + + scores: dict[str, float] + processing_time_ms: float + error: Optional[str] = None + + +class AgreementPair(BaseModel): + """Pairwise agreement between two classifiers.""" + + pearson_r: float + mean_delta: float + + +class CompareResult(BaseModel): + """Side-by-side comparison of multiple classifiers on the same image.""" + + results: dict[str, ClassifierResult] + agreement_matrix: dict[str, AgreementPair] + total_processing_time_ms: float