Aller au contenu principal

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

CodeStatutDescription
200OKRequête réussie
400Bad RequestParamètres de requête invalides
401UnauthorizedClé API manquante ou invalide
403ForbiddenAccès refusé ou permissions insuffisantes
404Not FoundEndpoint ou ressource introuvable
429Too Many RequestsLimite de taux dépassée
500Internal Server ErrorErreur serveur interne
502Bad GatewayProblème de passerelle
503Service UnavailableService temporairement indisponible

Structure des erreurs

{
"message": "CODE_ERREUR",
"error": true,
"data": null,
"status": 4XX
}

Champs de l'erreur

ChampTypeDescription
messagestringCode d'erreur spécifique (ex: INVALID_API_KEY)
errorbooleantrue pour une erreur
datanullToujours null en cas d'erreur
statusintegerCode 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-1
  • yodi-1-32k
  • yodi-embed
  • yodi-code
  • yodi-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.0
  • top_p: 0.0 - 1.0
  • max_tokens: 1 - limite du modèle
  • frequency_penalty: -2.0 - 2.0
  • presence_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

Prochaines étapes