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