#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Test : analyse d'images immobilières via Claude Haiku 4.5 (REST, sans SDK).""" import json, os, urllib.request, urllib.error for line in open(".env", encoding="utf-8"): if line.startswith("ANTHROPIC_API_KEY="): KEY = line.split("=", 1)[1].strip() SCHEMA = { "type": "object", "additionalProperties": False, "properties": { "shot_type": {"type": "string", "enum": ["exterieur_facade","cuisine","salle_bain","salon","chambre","sous_sol","cour_terrain","vue","autre_interieur","autre"]}, "condition_score": {"type": "integer", "description": "1=très délabré, 10=neuf/luxueux"}, "renovation_level": {"type": "string", "enum": ["a_renover","date","standard","renove","haut_de_gamme","inconnu"]}, "natural_light": {"type": "string", "enum": ["sombre","moyen","lumineux","tres_lumineux"]}, "clutter": {"type": "string", "enum": ["vide","epure","normal","encombre"]}, "curb_appeal": {"type": "integer", "description": "attrait extérieur 1-10; 0 si non applicable"}, "materials": {"type": "array", "items": {"type": "string", "enum": ["bois_franc","ceramique","granit_quartz","stainless","brique","pierre","vinyle","beton","tapis"]}}, "view_quality": {"type": "string", "enum": ["aucune","urbaine","verdure","eau","montagne","inconnu"]}, "staged": {"type": "boolean", "description": "mise en scène pro / home staging"}, "photo_quality": {"type": "integer", "description": "qualité de la photo elle-même 1-10"} }, "required": ["shot_type","condition_score","renovation_level","natural_light","clutter","curb_appeal","materials","view_quality","staged","photo_quality"] } SYS = ("Tu es un évaluateur immobilier objectif. Analyse la photo et renvoie des " "attributs FACTUELS et reproductibles au format JSON. Sois objectif : base " "les scores sur des critères concrets (état des surfaces, finitions, " "luminosité), pas sur un goût personnel.") def analyze(url): body = {"model": "claude-haiku-4-5", "max_tokens": 400, "system": SYS, "messages": [{"role": "user", "content": [ {"type": "image", "source": {"type": "url", "url": url}}, {"type": "text", "text": "Analyse cette photo de propriété."}]}], "output_config": {"format": {"type": "json_schema", "schema": SCHEMA}}} req = urllib.request.Request( "https://api.anthropic.com/v1/messages", data=json.dumps(body).encode(), headers={"x-api-key": KEY, "anthropic-version": "2023-06-01", "content-type": "application/json"}) with urllib.request.urlopen(req, timeout=90) as r: out = json.load(r) txt = next(b["text"] for b in out["content"] if b["type"] == "text") return json.loads(txt), out.get("usage") data_r = json.load(open("data/raw/dataset_realtor-property-crawler_2026-07-03_10-36-26-001.json", encoding="utf-8")) data_d = json.load(open("data/raw/dataset_duproprio-property-crawler_2026-07-03_00-37-49-449.json", encoding="utf-8")) # realtor: 1re image = façade ; duproprio: image dont caption ~ Kitchen tests = [] r0 = next(d for d in data_r if d.get("images")) tests.append(("REALTOR 1re photo (façade attendue)", r0["images"][0]["url"])) d0 = next(d for d in data_d if d.get("images")) kit = next((im for im in d0["images"] if "kitchen" in (im.get("caption") or "").lower()), d0["images"][0]) tests.append(("DUPROPRIO (caption=%s)" % (kit.get("caption","?")[:30]), kit["url"])) for label, url in tests: try: specs, usage = analyze(url) print(f"\n=== {label} ===") print("URL:", url[:90]) print("tokens:", usage) print(json.dumps(specs, ensure_ascii=False, indent=1)) except urllib.error.HTTPError as e: print("HTTP", e.code, e.read().decode()[:500])