doc2gl / intern_vl.py
Doc2GL Deploy
Deploy Doc2GL to HuggingFace Space
eaa2438
import os
import requests
import json
import time
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def generate_mermaid_from_intern(base64_image):
"""
Génère un diagramme Mermaid à partir d'une image en base64
en utilisant l'API InternVL (Intern AI).
Version avec retry automatique et gestion DNS robuste.
"""
# Token API InternVL
api_key = os.environ.get("INTERNVL_API_KEY", "")
# URL de l'API Intern AI (selon doc officielle)
url = 'https://chat.intern-ai.org.cn/api/v1/chat/completions'
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {api_key}'
}
# Prompt pour gΓ©nΓ©rer le Mermaid
prompt = """Analyze this mindmap image and convert it into a Mermaid diagram format.
IMPORTANT RULES:
1. Use ONLY 'graph TD' (top-down) direction
2. Format: graph TD
3. Each node: ID[Label]
4. Each edge: Parent --> Child
5. Extract ALL nodes and relationships visible in the image
6. Preserve the hierarchical structure
7. Use simple, clear node IDs (A, B, C, etc.)
Return ONLY the Mermaid code, nothing else."""
# Liste de modèles multimodaux disponibles
models_to_try = [
'intern-latest', # Meilleur modèle (alias)
'internvl3.5-latest', # InternVL 3.5 (alias)
'internvl3-latest', # InternVL 3 (alias)
'internvl-latest', # InternVL (alias)
'internvl3.5-241b-a28b', # InternVL 3.5 spΓ©cifique
'internvl3-78b', # InternVL 3 78B
'intern-s1', # Intern S1
'intern-s1-mini', # Intern S1 mini (fallback)
]
# ═════════════════════════════════════════════════════════════════════
# CONFIGURATION RETRY ROBUSTE
# ═════════════════════════════════════════════════════════════════════
# Configurer une session avec retry automatique
session = requests.Session()
retry_strategy = Retry(
total=5, # 5 tentatives au total
backoff_factor=2, # Attente exponentielle (2s, 4s, 8s, 16s, 32s)
status_forcelist=[429, 500, 502, 503, 504], # Codes Γ  retry
allowed_methods=["POST"], # Retry sur POST
raise_on_status=False # Ne pas lever d'exception immΓ©diatement
)
adapter = HTTPAdapter(
max_retries=retry_strategy,
pool_connections=10,
pool_maxsize=10
)
session.mount("https://", adapter)
session.mount("http://", adapter)
last_error = None
for model_name in models_to_try:
# ═════════════════════════════════════════════════════════════════
# RETRY MANUEL POUR LES ERREURS DNS
# ═════════════════════════════════════════════════════════════════
max_dns_retries = 3
for dns_attempt in range(max_dns_retries):
try:
# Construction du payload
data = {
'model': model_name,
'messages': [
{
'role': 'user',
'content': [
{
'type': 'text',
'text': prompt
},
{
'type': 'image_url',
'image_url': {
'url': f"data:image/jpeg;base64,{base64_image}"
}
}
]
}
],
'temperature': 0.1,
'top_p': 0.9,
'max_tokens': 2000
}
# Appel de l'API avec timeout augmentΓ©
response = session.post(
url,
headers=headers,
json=data,
timeout=90 # Timeout augmentΓ© Γ  90 secondes
)
# Si succès, traiter la réponse
if response.status_code == 200:
result = response.json()
# Extraire le contenu
if 'choices' in result and len(result['choices']) > 0:
raw_text = result['choices'][0]['message']['content']
# Nettoyer le code Mermaid
clean_lines = []
for line in raw_text.split('\n'):
clean_line = line.replace('```mermaid', '').replace('```', '').strip()
if clean_line and not clean_line.startswith("style"):
clean_lines.append(clean_line)
mermaid_code = "\n".join(clean_lines)
# VΓ©rifier que le rΓ©sultat contient du Mermaid
if mermaid_code and ('graph' in mermaid_code.lower() or '-->' in mermaid_code):
print(f"βœ… ModΓ¨le InternVL utilisΓ© avec succΓ¨s : {model_name}")
session.close()
return mermaid_code
else:
raise ValueError(f"Réponse invalide pour le modèle {model_name}")
else:
raise ValueError(f"Format de réponse invalide pour le modèle {model_name}")
# Si échec 401 avec ce modèle, essayer le suivant
elif response.status_code == 401:
last_error = f"401 Unauthorized avec modèle {model_name}"
break # Pas la peine de retry DNS, c'est un problème de token
# Si échec 400 (mauvais modèle), essayer le suivant
elif response.status_code == 400:
try:
error_data = response.json()
last_error = f"400 Bad Request avec modèle {model_name}: {error_data.get('message', error_data.get('msg', 'Unknown error'))}"
except:
last_error = f"400 Bad Request avec modèle {model_name}"
break # Pas la peine de retry DNS
# Si échec 404 (modèle non trouvé), essayer le suivant
elif response.status_code == 404:
last_error = f"404 Not Found avec modèle {model_name}"
break # Pas la peine de retry DNS
# Autres erreurs
else:
response.raise_for_status()
# ═════════════════════════════════════════════════════════════
# GESTION SPÉCIFIQUE DES ERREURS DNS
# ═════════════════════════════════════════════════════════════
except (requests.exceptions.ConnectionError,
requests.exceptions.Timeout,
ConnectionResetError) as e:
error_str = str(e).lower()
# Erreur DNS dΓ©tectΓ©e
if 'getaddrinfo' in error_str or 'resolve' in error_str or 'name resolution' in error_str:
if dns_attempt < max_dns_retries - 1:
wait_time = (dns_attempt + 1) * 5 # 5s, 10s, 15s
print(
f"⚠️ Erreur DNS pour {model_name}, retry {dns_attempt + 1}/{max_dns_retries} dans {wait_time}s...")
time.sleep(wait_time)
continue # Retry
else:
last_error = f"Erreur DNS persistante avec modèle {model_name} après {max_dns_retries} tentatives: {e}"
break # Abandonner ce modèle
# Timeout rΓ©seau
elif 'timeout' in error_str:
if dns_attempt < max_dns_retries - 1:
wait_time = (dns_attempt + 1) * 3
print(
f"⏱️ Timeout pour {model_name}, retry {dns_attempt + 1}/{max_dns_retries} dans {wait_time}s...")
time.sleep(wait_time)
continue
else:
last_error = f"Timeout persistant avec modèle {model_name}: {e}"
break
# Autres erreurs de connexion
else:
last_error = f"Erreur de connexion avec modèle {model_name}: {e}"
break
except requests.exceptions.RequestException as e:
last_error = f"Erreur requΓͺte avec modΓ¨le {model_name}: {e}"
break # Pas de retry pour les autres erreurs requests
except Exception as e:
last_error = f"Erreur inattendue avec modèle {model_name}: {e}"
break
# Si on arrive ici, c'est qu'on a rΓ©ussi Γ  faire la requΓͺte
break # Sortir de la boucle de retry DNS
# Fermer la session
session.close()
# Si aucun modèle n'a fonctionné
raise RuntimeError(f"Échec de tous les modèles InternVL. Dernière erreur: {last_error}")