Codes d'erreur
L'API YODI utilise des codes d'erreur HTTP standard et des codes d'erreur spécifiques pour vous aider à diagnostiquer et résoudre les problèmes.
Codes d'erreur HTTP
| Code | Statut | Description |
|---|---|---|
200 | OK | Requête réussie |
400 | Bad Request | Paramètres de requête invalides |
401 | Unauthorized | Clé API manquante ou invalide |
403 | Forbidden | Accès refusé ou permissions insuffisantes |
404 | Not Found | Endpoint ou ressource introuvable |
429 | Too Many Requests | Limite de taux dépassée |
500 | Internal Server Error | Erreur serveur interne |
502 | Bad Gateway | Problème de passerelle |
503 | Service Unavailable | Service temporairement indisponible |
Structure des erreurs
{
"message": "CODE_ERREUR",
"error": true,
"data": null,
"status": 4XX
}
Champs de l'erreur
| Champ | Type | Description |
|---|---|---|
message | string | Code d'erreur spécifique (ex: INVALID_API_KEY) |
error | boolean | true pour une erreur |
data | null | Toujours null en cas d'erreur |
status | integer | Code HTTP (400, 401, 403, 429, 500, etc.) |
Types d'erreurs
invalid_request_error
Erreurs liées aux paramètres de la requête.
invalid_api_key
{
"message": "INVALID_API_KEY",
"error": true,
"data": null,
"status": 401
}
Causes :
- Clé API incorrecte ou malformée
- Clé API révoquée ou expirée
- Format d'autorisation incorrect
Solutions :
- Vérifiez votre clé API dans le tableau de bord
- Assurez-vous d'utiliser le format
Bearer your_api_key - Générez une nouvelle clé si nécessaire
missing_api_key
{
"message": "MISSING_API_KEY",
"error": true,
"data": null,
"status": 401
}
Solution :
# Incorrect
response = requests.post("https://api.yodi.tg/v1/chat/completions", json=data)
# Correct
headers = {"Authorization": "Bearer your_api_key_here"}
response = requests.post("https://api.yodi.tg/v1/chat/completions", headers=headers, json=data)
invalid_model
{
"message": "INVALID_MODEL",
"error": true,
"data": null,
"status": 400
}
Modèles valides :
yodi-1yodi-1-32kyodi-embedyodi-codeyodi-instruct
context_length_exceeded
{
"message": "CONTEXT_LENGTH_EXCEEDED",
"error": true,
"data": null,
"status": 400
}
Solutions :
def handle_context_limit(messages, max_tokens=7000):
"""Réduit le contexte si nécessaire"""
# Estimation grossière des tokens
total_tokens = sum(len(msg["content"].split()) * 1.3 for msg in messages)
if total_tokens > max_tokens:
# Garder le message système et les messages récents
system_msgs = [msg for msg in messages if msg["role"] == "system"]
other_msgs = [msg for msg in messages if msg["role"] != "system"]
# Garder seulement les derniers messages
while total_tokens > max_tokens and len(other_msgs) > 2:
other_msgs.pop(0)
total_tokens = sum(len(msg["content"].split()) * 1.3 for msg in system_msgs + other_msgs)
return system_msgs + other_msgs
return messages
# Utilisation
try:
response = client.chat.completions.create(
model="yodi-1",
messages=messages
)
except Exception as e:
if "context_length_exceeded" in str(e):
# Réduire le contexte et réessayer
reduced_messages = handle_context_limit(messages)
response = client.chat.completions.create(
model="yodi-1-32k", # ou utiliser le modèle 32k
messages=reduced_messages
)
invalid_parameter
{
"message": "INVALID_PARAMETER",
"error": true,
"data": null,
"status": 400
}
Paramètres valides :
temperature: 0.0 - 2.0top_p: 0.0 - 1.0max_tokens: 1 - limite du modèlefrequency_penalty: -2.0 - 2.0presence_penalty: -2.0 - 2.0
rate_limit_error
Erreurs liées aux limites de taux.
rate_limit_exceeded
{
"message": "RATE_LIMIT_EXCEEDED",
"error": true,
"data": null,
"status": 429
}
Gestion avec backoff exponentiel :
import time
import random
def exponential_backoff(func, max_retries=5):
"""Implémente un backoff exponentiel avec jitter"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if "rate_limit" in str(e).lower() and attempt < max_retries - 1:
# Calcul du délai avec jitter
base_delay = 2 ** attempt
jitter = random.uniform(0, 1)
delay = base_delay + jitter
print(f"Rate limit atteint, attente {delay:.1f}s (tentative {attempt + 1})")
time.sleep(delay)
else:
raise e
raise Exception("Nombre maximum de tentatives atteint")
# Utilisation
def make_api_call():
return client.chat.completions.create(
model="yodi-1",
messages=[{"role": "user", "content": "Hello"}]
)
response = exponential_backoff(make_api_call)
quota_exceeded
{
"message": "QUOTA_EXCEEDED",
"error": true,
"data": null,
"status": 429
}
Solutions :
- Vérifiez votre usage dans le tableau de bord
- Mettez à niveau votre plan si nécessaire
- Optimisez vos requêtes pour réduire la consommation
server_error
Erreurs côté serveur.
internal_error
{
"message": "INTERNAL_ERROR",
"error": true,
"data": null,
"status": 500
}
Gestion :
def robust_api_call(func, max_retries=3):
"""Gère les erreurs serveur avec retry automatique"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if any(code in str(e) for code in ["500", "502", "503", "internal_error"]):
if attempt < max_retries - 1:
wait_time = (attempt + 1) * 2
print(f"Erreur serveur, nouvelle tentative dans {wait_time}s...")
time.sleep(wait_time)
continue
raise e
Gestion d'erreurs complète
Classe de gestion d'erreurs
import logging
from enum import Enum
from typing import Optional, Callable
class YodiErrorType(Enum):
AUTHENTICATION = "authentication"
RATE_LIMIT = "rate_limit"
VALIDATION = "validation"
SERVER = "server"
NETWORK = "network"
class YodiErrorHandler:
def __init__(self, logger: Optional[logging.Logger] = None):
self.logger = logger or logging.getLogger(__name__)
def classify_error(self, error: Exception) -> YodiErrorType:
"""Classifie le type d'erreur"""
error_str = str(error).lower()
if any(code in error_str for code in ["401", "403", "invalid_api_key", "missing_api_key"]):
return YodiErrorType.AUTHENTICATION
elif any(code in error_str for code in ["429", "rate_limit", "quota"]):
return YodiErrorType.RATE_LIMIT
elif any(code in error_str for code in ["400", "invalid_parameter", "context_length"]):
return YodiErrorType.VALIDATION
elif any(code in error_str for code in ["500", "502", "503", "internal_error"]):
return YodiErrorType.SERVER
else:
return YodiErrorType.NETWORK
def handle_error(self, error: Exception, context: str = "") -> str:
"""Gère une erreur et retourne un message utilisateur"""
error_type = self.classify_error(error)
if error_type == YodiErrorType.AUTHENTICATION:
message = "Erreur d'authentification. Vérifiez votre clé API."
self.logger.error(f"Auth error in {context}: {error}")
elif error_type == YodiErrorType.RATE_LIMIT:
message = "Limite de taux atteinte. Veuillez patienter avant de réessayer."
self.logger.warning(f"Rate limit in {context}: {error}")
elif error_type == YodiErrorType.VALIDATION:
message = f"Paramètres invalides: {error}"
self.logger.error(f"Validation error in {context}: {error}")
elif error_type == YodiErrorType.SERVER:
message = "Erreur serveur temporaire. Réessayez dans quelques instants."
self.logger.error(f"Server error in {context}: {error}")
else:
message = f"Erreur de connexion: {error}"
self.logger.error(f"Network error in {context}: {error}")
return message
# Utilisation
error_handler = YodiErrorHandler()
def safe_api_call(func: Callable, context: str = ""):
"""Appel API avec gestion d'erreurs complète"""
try:
return func(), None
except Exception as e:
error_message = error_handler.handle_error(e, context)
return None, error_message
# Exemple
result, error = safe_api_call(
lambda: client.chat.completions.create(
model="yodi-1",
messages=[{"role": "user", "content": "Hello"}]
),
context="chat_completion"
)
if error:
print(f"Erreur: {error}")
else:
print(f"Succès: {result.choices[0].message.content}")
Décorateur pour retry automatique
from functools import wraps
import time
import random
def retry_on_error(max_retries=3, backoff_factor=2, retry_codes=None):
"""Décorateur pour retry automatique sur certaines erreurs"""
if retry_codes is None:
retry_codes = ["429", "500", "502", "503", "rate_limit", "internal_error"]
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
should_retry = any(code in str(e).lower() for code in retry_codes)
if should_retry and attempt < max_retries - 1:
# Calcul du délai avec jitter
delay = (backoff_factor ** attempt) + random.uniform(0, 1)
print(f"Tentative {attempt + 1} échouée, retry dans {delay:.1f}s")
time.sleep(delay)
else:
raise e
return None
return wrapper
return decorator
# Utilisation
@retry_on_error(max_retries=5, backoff_factor=1.5)
def create_completion(messages):
return client.chat.completions.create(
model="yodi-1",
messages=messages
)
Monitoring et alertes
Système de monitoring
from collections import defaultdict
from datetime import datetime, timedelta
class ErrorMonitor:
def __init__(self, alert_threshold=10, time_window_minutes=5):
self.errors = defaultdict(list)
self.alert_threshold = alert_threshold
self.time_window = timedelta(minutes=time_window_minutes)
def log_error(self, error_type: str, error_message: str):
"""Enregistre une erreur"""
timestamp = datetime.now()
self.errors[error_type].append({
'timestamp': timestamp,
'message': error_message
})
# Nettoyer les erreurs anciennes
self._cleanup_old_errors(error_type)
# Vérifier les seuils d'alerte
if len(self.errors[error_type]) >= self.alert_threshold:
self._trigger_alert(error_type)
def _cleanup_old_errors(self, error_type: str):
"""Supprime les erreurs trop anciennes"""
cutoff = datetime.now() - self.time_window
self.errors[error_type] = [
error for error in self.errors[error_type]
if error['timestamp'] > cutoff
]
def _trigger_alert(self, error_type: str):
"""Déclenche une alerte"""
count = len(self.errors[error_type])
print(f"� ALERTE: {count} erreurs de type '{error_type}' en {self.time_window.total_seconds()/60:.0f} minutes")
def get_error_summary(self) -> dict:
"""Retourne un résumé des erreurs récentes"""
summary = {}
for error_type, error_list in self.errors.items():
summary[error_type] = {
'count': len(error_list),
'latest': error_list[-1]['timestamp'] if error_list else None
}
return summary
monitor = ErrorMonitor()
def monitored_api_call(func, operation_name="api_call"):
"""Appel API avec monitoring d'erreurs"""
try:
return func()
except Exception as e:
error_type = YodiErrorHandler().classify_error(e).value
monitor.log_error(error_type, str(e))
raise
Debugging et logs
Configuration de logging avancée
import logging
import json
class YodiAPILogger:
def __init__(self, log_level=logging.INFO):
self.logger = logging.getLogger("yodi_api")
self.logger.setLevel(log_level)
# Handler pour fichier
file_handler = logging.FileHandler("yodi_api.log")
file_handler.setLevel(logging.DEBUG)
# Handler pour console
console_handler = logging.StreamHandler()
console_handler.setLevel(log_level)
# Format des logs
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)
def log_request(self, endpoint, params, response_time=None, error=None):
"""Log une requête API"""
log_data = {
'endpoint': endpoint,
'params': params,
'response_time': response_time,
'error': str(error) if error else None
}
if error:
self.logger.error(f"API Error: {json.dumps(log_data)}")
else:
self.logger.info(f"API Success: {json.dumps(log_data)}")
# Utilisation
api_logger = YodiAPILogger()
def logged_api_call(func, endpoint, params):
"""Appel API avec logging complet"""
start_time = time.time()
try:
result = func()
response_time = time.time() - start_time
api_logger.log_request(endpoint, params, response_time)
return result
except Exception as e:
response_time = time.time() - start_time
api_logger.log_request(endpoint, params, response_time, e)
raise