SPB Git forge

spb/wp14_uqo

Public
2commits 1branches 1releases
909.0 KBsize
maindefault branch
1 h agolast push
Python 57% TeX 42.9%
3.8 KB · 66 lines python
Raw Blame History
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