Python 57%
TeX 42.9%
1#!/usr/bin/env python32# -*- coding: utf-8 -*-3"""Test : extraction de specs via GPT-4o-mini (API REST, sans dépendance)."""4import json, os, urllib.request, urllib.error56# charge la clé depuis .env7for line in open(".env", encoding="utf-8"):8 if line.startswith("OPENAI_API_KEY="):9 os.environ["OPENAI_API_KEY"] = line.split("=", 1)[1].strip()10KEY = os.environ["OPENAI_API_KEY"]1112SCHEMA = {13 "type": "object",14 "additionalProperties": False,15 "properties": {16 "condition": {"type": "string", "enum": ["a_renover","original","bon","renove","refait_neuf","neuf","inconnu"]},17 "turnkey": {"type": "boolean"},18 "recently_renovated": {"type": "boolean"},19 "flooring": {"type": "array", "items": {"type": "string", "enum": ["bois_franc","ceramique","plancher_flottant","tapis","vinyle","beton","autre"]}},20 "countertop": {"type": "string", "enum": ["granit","quartz","marbre","stratifie","bois","autre","inconnu"]},21 "heating": {"type": "array", "items": {"type": "string", "enum": ["electrique","plinthes","thermopompe","bois","gaz","mazout","radiant","geothermie","autre"]}},22 "garage": {"type": "boolean"},23 "pool": {"type": "boolean"},24 "waterfront": {"type": "boolean"},25 "view": {"type": "string", "enum": ["eau","montagne","ville","aucune","inconnu"]},26 "finished_basement":{"type": "boolean"},27 "fireplace": {"type": "boolean"},28 "central_ac": {"type": "boolean"},29 "new_construction":{"type": "boolean"},30 "income_property": {"type": "boolean"},31 "highlights": {"type": "array", "items": {"type": "string"}, "description": "3-6 mots-clés de vente saillants"}32 },33 "required": ["condition","turnkey","recently_renovated","flooring","countertop",34 "heating","garage","pool","waterfront","view","finished_basement",35 "fireplace","central_ac","new_construction","income_property","highlights"]36}3738SYS = ("Tu es un extracteur d'attributs immobiliers. À partir de la description "39 "d'une propriété (français ou anglais), renvoie les specs au format JSON "40 "strict. Base-toi UNIQUEMENT sur le texte ; si une info est absente, mets "41 "false / 'inconnu' / [] selon le champ. highlights = 3 à 6 mots-clés.")4243def extract(desc):44 body = {45 "model": "gpt-4o-mini",46 "temperature": 0,47 "messages": [{"role": "system", "content": SYS},48 {"role": "user", "content": desc[:4000]}],49 "response_format": {"type": "json_schema", "json_schema":50 {"name": "specs", "strict": True, "schema": SCHEMA}},51 }52 req = urllib.request.Request(53 "https://api.openai.com/v1/chat/completions",54 data=json.dumps(body).encode(),55 headers={"Authorization": f"Bearer {KEY}",56 "Content-Type": "application/json"})57 with urllib.request.urlopen(req, timeout=60) as r:58 out = json.load(r)59 return out["choices"][0]["message"]["content"], out.get("usage")6061# 3 descriptions du dataset62data = json.load(open("data/processed/maisons_avec_prix.json", encoding="utf-8"))63descs = [d["description"] for d in data if d.get("description")][:3]64for i, dsc in enumerate(descs):65 try:66 content, usage = extract(dsc)67 print(f"\n=== Exemple {i+1} (tokens: {usage}) ===")68 print("DESC:", dsc[:160].replace("\n", " "), "...")69 print("SPECS:", json.dumps(json.loads(content), ensure_ascii=False))70 except urllib.error.HTTPError as e:71 print("HTTP", e.code, e.read().decode()[:400]); break72