🔬 NatureVision API
API d'inférence vision IA pour l'identification de végétaux et d'animaux.
Serveur MLX local sur Mac Mini M4 — accès réseau local uniquement.
Pas de clé API
JSON
multipart/form-data
Modèle : chargement…
Vue d'ensemble
L'API NatureVision expose le serveur d'inférence MLX sur le Mac Mini. Elle permet d'envoyer une image et d'obtenir une analyse naturaliste structurée en JSON : identification de l'espèce, état de santé, toxicité, conseils d'entretien.
Architecture : deux serveurs distincts.
•
•
•
Mac Mini :8080 — serveur MLX d'inférence (accès réseau local)•
Ubuntu :80 — frontend web + proxy API (accès depuis internet)
URL de base
| Serveur | URL | Usage |
|---|---|---|
| Mac Mini (MLX) | http://IP_MAC_MINI:8080 |
Inférence directe — réseau local uniquement |
| Ubuntu (proxy) | http://IP_UBUNTU/api |
API publique via nginx — upload + analyse complète |
Authentification
Aucune authentification requise. L'API est accessible librement sur le réseau local. Veillez à ne pas l'exposer directement sur internet sans protection.
Limites
| Paramètre | Valeur |
|---|---|
| Taille max image | 20 MB |
| Formats acceptés | JPEG, PNG, WebP, HEIC |
| Requêtes simultanées | 1 (file d'attente automatique) |
| Timeout analyse | 180 secondes |
| Tokens max générés | 2048 |
Le serveur MLX traite une seule analyse à la fois. Les requêtes simultanées reçoivent une erreur 429 et doivent être retentées.
Endpoints
GET
/health
État du serveur
Vérifie que le serveur MLX est démarré et que le modèle est chargé en mémoire.
Exemple de requête
curl http://IP_MAC_MINI:8080/health
Réponse — modèle prêt
{
"status": "ok", // "ok" | "loading"
"model": "mlx-community/Qwen3-VL-8B-Instruct-4bit",
"analyses": 42 // nombre d'analyses effectuées depuis démarrage
}
POST
/generate
Analyser une image
Envoie une image encodée en base64 et retourne l'analyse naturaliste complète en JSON.
Il est recommandé de redimensionner l'image à max 1280px avant envoi pour réduire le temps d'analyse et la consommation mémoire.
Corps de la requête — application/json
| Champ | Type | Requis | Description |
|---|---|---|---|
| prompt | string | requis | Instruction d'analyse pour le modèle IA |
| image_b64 | string | requis | Image encodée en base64 (JPEG recommandé) |
| max_tokens | integer | optionnel | Tokens max à générer (défaut: 2048, max: 4096) |
| temperature | float | optionnel | Créativité du modèle 0.0–1.0 (défaut: 0.05) |
Réponse
| Champ | Type | Description |
|---|---|---|
| response | string | Réponse brute du modèle (JSON stringifié ou texte) |
| duration_ms | integer | Durée de l'inférence en millisecondes |
Exemples de code
# Encoder l'image en base64 IMAGE_B64=$(base64 -i photo.jpg | tr -d '\n') # Appeler l'API curl -X POST http://IP_MAC_MINI:8080/generate \ -H "Content-Type: application/json" \ -d "{ \"prompt\": \"Identifie le végétal ou l'animal visible. Réponds en JSON.\", \"image_b64\": \"$IMAGE_B64\", \"max_tokens\": 1024, \"temperature\": 0.05 }"
Structure JSON de réponse
Le champ response contient un objet JSON stringifié à parser. Voici la structure complète.
{
"sujet_principal": "vegetal", // "vegetal" | "animal"
"vegetaux": [{
"nom_commun": "Amanite phalloïde",
"nom_scientifique": "Amanita phalloides",
"type": "champignon", // arbre|arbuste|plante_ornementale|mauvaise_herbe|fleur|...
"variete_ou_cultivar": "",
"etat_sante": "excellent", // excellent|bon|moyen|mauvais|critique
"score_sante": 90, // 0–100
"description": "Chapeau vert olive, lames blanches...",
"taille_adulte": "8-15 cm de hauteur",
"floraison": "Juillet à octobre",
"origine": "Europe, Amérique du Nord",
"ensoleillement": "mi_ombre", // plein_soleil|mi_ombre|ombre|indifferent
"besoin_eau": "modere", // faible|modere|eleve|tres_eleve
"frequence_arrosage": "",
"sol_ideal": "Sol calcaire, sous feuillus",
"problemes_detectes": [],
"diagnostic": [],
"solutions_traitement": [],
"methodes_elimination": [],
"conseils_entretien": [],
"toxicite_humain": "mortelle", // aucune|faible|moderee|elevee|mortelle
"toxicite_animaux": "mortelle",
"animaux_concernes": "tous les animaux",
"parties_toxiques": "toute la plante",
"symptomes_intoxication": ["Douleurs abdominales intenses", "Insuffisance hépatique"],
"conduite_urgence": "Appeler le 15 (SAMU) immédiatement",
"photo_url": "https://upload.wikimedia.org/..." // Wikipedia (peut être vide)
}],
"animaux": [{
"nom_commun": "Chat domestique",
"nom_scientifique": "Felis catus",
"type": "mammifere", // mammifere|oiseau|reptile|amphibien|poisson|insecte|arachnide|...
"race_ou_variete": "British Shorthair (probable)",
"statut_conservation": "LC", // LC|NT|VU|EN|CR|DD|non_evalue
"description": "Pelage gris bleuté dense...",
"taille_moyenne": "25-30 cm au garrot",
"poids_moyen": "4-8 kg",
"esperance_vie": "12-17 ans",
"caractere": "Calme, affectueux, indépendant",
"comportement_observe": "Au repos, regard vers l'objectif",
"habitat_naturel": "Domestique, territoire ~200m²",
"alimentation": "Carnivore strict",
"reproduction": "2-3 portées/an, 3-5 chatons",
"niveau_danger": "aucun", // aucun|faible|modere|eleve
"toxicite_humain": "aucune",
"toxicite_animaux": "aucune",
"venin_ou_poison": "",
"symptomes_contact": [],
"conduite_urgence": "",
"que_faire_si_rencontre": "Animal domestique, approche normale",
"protection_legale": "",
"faits_interessants": ["Peut tourner ses oreilles à 180°"],
"photo_url": "https://upload.wikimedia.org/..."
}],
"resume_general": "Chat British Shorthair adulte au repos...",
"conseil_immediat": "Aucune intervention urgente",
"saison_recommandee": "Toute l'année pour cet animal"
}
Codes d'erreur
| Code HTTP | Signification | Solution |
|---|---|---|
| 400 | Image invalide ou trop volumineuse | Vérifier le format (JPEG/PNG) et la taille (< 20MB) |
| 422 | Le modèle n'a pas retourné du JSON valide | Réessayer — le modèle a généré du texte libre |
| 429 | Serveur occupé (analyse en cours) | Attendre 5–10 secondes et réessayer |
| 500 | Erreur interne lors de l'inférence | Vérifier les logs du Mac Mini |
| 503 | Modèle en cours de chargement | Attendre 30–60s après le démarrage du serveur |
| 504 | Timeout — analyse trop longue | Réduire la taille de l'image ou augmenter le timeout |
Exemple complet — Python
Exemple fonctionnel avec gestion d'erreurs, retry automatique et parsing JSON robuste.
import base64, json, time, io, re import requests from PIL import Image NATURE_VISION_URL = "http://IP_MAC_MINI:8080" def encode_image(path_or_bytes, max_px=1280): if isinstance(path_or_bytes, (str, bytes)): img = Image.open(path_or_bytes if isinstance(path_or_bytes, str) else io.BytesIO(path_or_bytes)) else: img = path_or_bytes img = img.convert("RGB") if max(img.size) > max_px: r = max_px / max(img.size) img = img.resize((int(img.width*r), int(img.height*r)), Image.LANCZOS) buf = io.BytesIO() img.save(buf, "JPEG", quality=88) return base64.b64encode(buf.getvalue()).decode() def extract_json(text): text = re.sub(r'<think>[\s\S]*?</think>', '', text).strip() try: return json.loads(text) except: pass m = re.search(r'```(?:json)?\s*([\s\S]*?)\s*```', text) if m: try: return json.loads(m.group(1)) except: pass s, e = text.find('{'), text.rfind('}') if s != -1 and e > s: return json.loads(text[s:e+1]) raise ValueError("JSON introuvable dans la réponse") def analyze(image_path, prompt=None, retries=2): prompt = prompt or ( "Tu es un naturaliste expert. Identifie le sujet principal " "au premier plan et réponds UNIQUEMENT en JSON." ) image_b64 = encode_image(image_path) for attempt in range(retries + 1): try: r = requests.post( f"{NATURE_VISION_URL}/generate", json={"prompt": prompt, "image_b64": image_b64, "max_tokens": 1024, "temperature": 0.05}, timeout=180 ) if r.status_code == 429: time.sleep(8); continue r.raise_for_status() data = r.json() result = extract_json(data["response"]) print(f"✓ Analyse en {data['duration_ms']}ms") return result except Exception as e: if attempt == retries: raise print(f"Tentative {attempt+1} échouée : {e}") time.sleep(3) # Usage result = analyze("photo_plante.jpg") for plant in result.get("vegetaux", []): print(f"Plante : {plant['nom_commun']} ({plant['nom_scientifique']})") print(f"Santé : {plant['etat_sante']} — {plant['score_sante']}/100") if plant['toxicite_humain'] != 'aucune': print(f"⚠️ TOXICITÉ : {plant['toxicite_humain']}")
NatureVision API — Serveur MLX local · Mac Mini M4 ·
Retour à l'application