"""JSON schemas for structured extraction (strict: every field required, additionalProperties false). IMAGE_SCHEMA — one call per image. Flat object; fields not applicable to the scene take "not_applicable". TEXT_SCHEMA — one call per listing on the masked text (description + features + broker details). Both schemas are versioned; the version string is stored with every raw response. """ from __future__ import annotations SCHEMA_VERSION = "wp20-schema-1.0" NV = "not_visible" NA = "not_applicable" SCENES = ["exterior_front", "exterior_rear_or_side", "exterior_aerial_or_street", "kitchen", "bathroom", "living_or_dining", "bedroom", "basement", "garage", "mechanical_or_utility", "yard_pool_or_deck", "floor_plan_or_document", "other", "unusable"] CLADDING = ["brick", "stone", "vinyl", "wood", "aluminum_or_steel", "stucco_or_acrylic", "fiber_cement", "other"] DEFECTS = ["water_staining", "cracking", "mold_like_staining", "peeling_paint_or_finish", "worn_roof", "damaged_cladding", "sagging_or_unevenness", "none_visible"] def _enum(vals, extra=()): return {"type": "string", "enum": list(vals) + list(extra)} def _scale(desc): return {"type": "integer", "enum": [0, 1, 2, 3, 4, 5], "description": desc + " 0 = not applicable / not assessable."} IMAGE_SCHEMA = { "type": "object", "additionalProperties": False, "properties": { "scene": _enum(SCENES), "photo_quality": {"type": "integer", "enum": [1, 2, 3, 4, 5], "description": "Technical quality: 1 blurry/dark/cropped, 3 adequate, 5 professional."}, "staging_or_rendering": _enum(["none_apparent", "staged", "virtually_staged_or_rendered", "unsure"]), # exterior "building_type_visual": _enum(["detached", "semi_detached", "row_or_townhouse", "other"], [NV, NA]), "storeys_visible": _enum(["1", "1.5", "2", "2.5", "3+"], [NV, NA]), "cladding": {"type": "array", "description": "Up to 3 materials.", "items": _enum(CLADDING, [NV, NA])}, "roof_form": _enum(["gable", "hip", "flat_or_low_slope", "mansard", "mixed_or_other"], [NV, NA]), "roof_covering": _enum(["asphalt_shingle", "metal", "membrane_flat", "other"], [NV, NA]), "garage_visible": _enum(["attached", "detached", "carport", "none_visible"], [NV, NA]), "exterior_condition": _scale("Exterior condition 1 = severe visible deterioration, 3 = average wear for an occupied house, 5 = like new."), # interior "kitchen_grade": _enum(["basic", "standard", "upgraded", "luxury"], [NV, NA]), "kitchen_counters": _enum(["laminate", "quartz_granite_or_stone", "wood_or_butcher_block", "other"], [NV, NA]), "kitchen_cabinets": _enum(["melamine_or_laminate", "painted_or_thermofoil", "wood", "modern_high_gloss", "other"], [NV, NA]), "bathroom_grade": _enum(["basic", "standard", "upgraded", "luxury"], [NV, NA]), "flooring_main": _enum(["hardwood", "laminate_or_vinyl_plank", "ceramic_or_porcelain", "carpet", "concrete_or_other"], [NV, NA]), "basement_finish": _enum(["finished", "partially_finished", "unfinished"], [NV, NA]), "interior_condition": _scale("Interior condition 1 = heavily worn/damaged, 3 = average, 5 = like new."), "interior_finish_quality": _scale("Finish quality of materials visible 1 = minimal builder grade, 3 = standard, 5 = luxury."), # cross-cutting "renovation_evidence": _enum(["none_apparent", "partial", "extensive", "unsure"]), "visible_defects": {"type": "array", "description": "Up to 4 labels.", "items": _enum(DEFECTS)}, "confidence": {"type": "number", "description": "Your confidence between 0 and 1 that the scene-relevant attributes above are correct."}, "evidence": {"type": "string", "description": "At most 20 words naming the visual cues used (no speculation about hidden features)."}, }, "required": ["scene", "photo_quality", "staging_or_rendering", "building_type_visual", "storeys_visible", "cladding", "roof_form", "roof_covering", "garage_visible", "exterior_condition", "kitchen_grade", "kitchen_counters", "kitchen_cabinets", "bathroom_grade", "flooring_main", "basement_finish", "interior_condition", "interior_finish_quality", "renovation_evidence", "visible_defects", "confidence", "evidence"], } UNK = "not_stated" TEXT_SCHEMA = { "type": "object", "additionalProperties": False, "properties": { "building_type": _enum(["detached", "semi_detached", "row_or_townhouse", "other"], [UNK]), "storeys": _enum(["1", "1.5", "2", "2.5", "3+"], [UNK]), "cladding": {"type": "array", "description": "Up to 3 materials.", "items": _enum(CLADDING, [UNK])}, "roof_covering": _enum(["asphalt_shingle", "metal", "membrane_flat", "other"], [UNK]), "garage": _enum(["attached", "detached", "carport", "none"], [UNK]), "basement_finish": _enum(["finished", "partially_finished", "unfinished", "no_basement"], [UNK]), "kitchen_grade": _enum(["basic", "standard", "upgraded", "luxury"], [UNK]), "kitchen_counters": _enum(["laminate", "quartz_granite_or_stone", "wood_or_butcher_block", "other"], [UNK]), "kitchen_cabinets": _enum(["melamine_or_laminate", "painted_or_thermofoil", "wood", "modern_high_gloss", "other"], [UNK]), "bathroom_grade": _enum(["basic", "standard", "upgraded", "luxury"], [UNK]), "flooring_main": _enum(["hardwood", "laminate_or_vinyl_plank", "ceramic_or_porcelain", "carpet", "concrete_or_other"], [UNK]), "condition_claim": _enum(["needs_work", "average", "well_maintained", "renovated_or_like_new", "new_construction"], [UNK]), "renovation_claim": _enum(["none", "partial", "extensive"], [UNK]), "renovation_items": {"type": "array", "items": {"type": "string"}, "description": "Renovated components with year if stated, e.g. 'roof 2019', 'kitchen 2021'."}, "year_built_claim": {"type": "integer", "description": "Four-digit year if stated, else 0."}, "living_area_claim_sqft": {"type": "integer", "description": "Living/habitable area in sq ft if stated (convert m²), else 0."}, "building_footprint_claim_sqft": {"type": "integer", "description": "Footprint from stated building dimensions (e.g. 30x40 ft = 1200), else 0."}, "heating_energy": _enum(["electricity", "natural_gas", "oil", "wood_or_pellet", "heat_pump_mentioned", "other"], [UNK]), "pool": _enum(["inground", "above_ground", "none"], [UNK]), "defects_disclosed": {"type": "array", "items": {"type": "string"}, "description": "Explicitly disclosed problems (e.g. 'pyrite', 'vermiculite', 'roof to redo'); empty if none."}, "price_or_assessment_mentioned": {"type": "boolean", "description": "True if any price, tax or municipal assessment amount survives in the text."}, "confidence": {"type": "number", "description": "Between 0 and 1."}, }, "required": ["building_type", "storeys", "cladding", "roof_covering", "garage", "basement_finish", "kitchen_grade", "kitchen_counters", "kitchen_cabinets", "bathroom_grade", "flooring_main", "condition_claim", "renovation_claim", "renovation_items", "year_built_claim", "living_area_claim_sqft", "building_footprint_claim_sqft", "heating_energy", "pool", "defects_disclosed", "price_or_assessment_mentioned", "confidence"], }