from typing import Any, Dict, Tuple METRIC_KEYS = [ "hierarchy_strength", "focal_clarity", "typography_control", "font_conflict", "palette_control", "color_conflict", "visual_clutter", "template_genericity", "originality", "technical_quality", "visual_impact", ] REQUIRED_KEYS = { "content_present": bool, "hierarchy_strength": int, "focal_clarity": int, "typography_control": int, "font_conflict": int, "palette_control": int, "color_conflict": int, "visual_clutter": int, "template_genericity": int, "originality": int, "technical_quality": int, "visual_impact": int, "confidence": (float, int), "comment": str, } PRESENCE_SCHEMA = { "type": "object", "properties": { "content_present": {"type": "boolean"}, }, "required": ["content_present"], "additionalProperties": False, } def metric_schema(metric_name: str) -> Dict[str, Any]: return { "type": "object", "properties": { metric_name: {"type": "integer", "minimum": 1, "maximum": 5}, }, "required": [metric_name], "additionalProperties": False, } FINAL_JUDGE_SCHEMA = { "type": "object", "properties": { "label": {"type": "string", "enum": ["bad", "medium", "good"]}, "confidence": {"type": "number", "minimum": 0.0, "maximum": 1.0}, "rationale": {"type": "string"}, }, "required": ["label", "confidence", "rationale"], "additionalProperties": False, } OUTPUT_SCHEMA = { "type": "object", "properties": { "content_present": {"type": "boolean"}, "hierarchy_strength": {"type": "integer", "minimum": 1, "maximum": 5}, "focal_clarity": {"type": "integer", "minimum": 1, "maximum": 5}, "typography_control": {"type": "integer", "minimum": 1, "maximum": 5}, "font_conflict": {"type": "integer", "minimum": 1, "maximum": 5}, "palette_control": {"type": "integer", "minimum": 1, "maximum": 5}, "color_conflict": {"type": "integer", "minimum": 1, "maximum": 5}, "visual_clutter": {"type": "integer", "minimum": 1, "maximum": 5}, "template_genericity": {"type": "integer", "minimum": 1, "maximum": 5}, "originality": {"type": "integer", "minimum": 1, "maximum": 5}, "technical_quality": {"type": "integer", "minimum": 1, "maximum": 5}, "visual_impact": {"type": "integer", "minimum": 1, "maximum": 5}, "confidence": {"type": "number", "minimum": 0.0, "maximum": 1.0}, "comment": {"type": "string"}, }, "required": [ "content_present", "hierarchy_strength", "focal_clarity", "typography_control", "font_conflict", "palette_control", "color_conflict", "visual_clutter", "template_genericity", "originality", "technical_quality", "visual_impact", "confidence", "comment", ], "additionalProperties": False, } def validate_scores(data: Dict[str, Any]) -> Tuple[bool, str]: for key, expected_type in REQUIRED_KEYS.items(): if key not in data: return False, f"Missing key: {key}" if not isinstance(data[key], expected_type): return False, f"Invalid type for key: {key}" for key in METRIC_KEYS: value = data[key] if not isinstance(value, int): return False, f"{key} must be int" if value < 1 or value > 5: return False, f"{key} must be in range 1..5" confidence = float(data["confidence"]) if confidence < 0.0 or confidence > 1.0: return False, "confidence must be in range 0.0..1.0" comment = data["comment"].strip() if not comment: return False, "comment must not be empty" return True, "ok"