Troubleshooting guide
This guide helps you diagnose and resolve common issues encountered with the YODI API.
đ Initial checksâ
Basic checksâ
Before digging deeper, perform these essential checks:
# 1. Test connectivity
curl -I https://api.yodi.tg/v1/models
# 2. Verify your API key
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.yodi.tg/v1/models
# 3. Test a simple request
curl -X POST https://api.yodi.tg/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "yodi-1", "messages": [{"role": "user", "content": "test"}], "max_tokens": 5}'
Required system informationâ
For effective debugging, collect the following information:
- SDK version in use
- Operating system and version
- Programming language and version
- Full error message with stack trace
- Minimal reproducer code
- Timestamp of the error
- Request ID if available
đš Erreurs d'authentificationâ
Erreur 401 - Unauthorizedâ
SymptĂŽmes :
{
"message": "INVALID_API_KEY",
"error": true,
"data": null,
"status": 401
}
Causes possibles :
- Clé API invalide ou expirée
# Incorrect
headers = {"Authorization": "your-api-key"}
# Correct
headers = {"Authorization": "Bearer your-api-key"}
- Format d'authentification incorrect
// Incorrect
fetch('/api', {
headers: { 'Auth': 'Bearer key' }
});
// Correct
fetch('/api', {
headers: { 'Authorization': 'Bearer key' }
});
- Clé API révoquée ou suspendue
Solutions :
- Vérifiez votre clé API :
# Test simple de validation
curl -H "Authorization: Bearer YOUR_KEY" \
https://api.yodi.tg/v1/models
- Régénérez votre clé API :
- Connectez-vous Ă votre dashboard
- Allez dans "API Keys"
- Créez une nouvelle clé
- Mettez Ă jour votre code
- Script de validation :
import requests
import os
def validate_api_key(api_key):
headers = {"Authorization": f"Bearer {api_key}"}
response = requests.get(
"https://api.yodi.tg/v1/models",
headers=headers
)
if response.status_code == 200:
print(" Clé API valide")
return True
else:
print(f" Clé API invalide: {response.status_code}")
print(response.text)
return False
# Test
api_key = os.getenv("YODI_API_KEY")
validate_api_key(api_key)
Erreur 403 - Forbiddenâ
SymptĂŽmes :
{
"message": "INSUFFICIENT_PERMISSIONS",
"error": true,
"data": null,
"status": 403
}
Causes et solutions :
- Plan insuffisant pour la fonctionnalité
- Vérifiez les limites de votre plan
- Upgradez si nécessaire
- ModĂšle non accessible
- Certains modÚles sont réservés aux plans Pro+
- Utilisez un modĂšle compatible avec votre plan
Erreurs de limitation de tauxâ
Erreur 429 - Too Many Requestsâ
SymptĂŽmes :
{
"message": "RATE_LIMIT_EXCEEDED",
"error": true,
"data": null,
"status": 429
}
Implémentation d'un retry avec backoff :
import time
import random
import requests
from typing import Callable, Any
class RetryHandler:
def __init__(self, max_retries: int = 3, base_delay: float = 1.0):
self.max_retries = max_retries
self.base_delay = base_delay
def exponential_backoff(self, attempt: int) -> float:
"""Calcule le délai avec backoff exponentiel et jitter"""
delay = self.base_delay * (2 ** attempt)
jitter = random.uniform(0, 0.1) * delay
return delay + jitter
def retry_request(self, func: Callable, *args, **kwargs) -> Any:
"""Exécute une fonction avec retry automatique"""
last_exception = None
for attempt in range(self.max_retries + 1):
try:
response = func(*args, **kwargs)
# SuccĂšs
if response.status_code == 200:
return response
# Rate limit - on retry
elif response.status_code == 429:
if attempt < self.max_retries:
delay = self.exponential_backoff(attempt)
print(f"Rate limited. Retry {attempt + 1}/{self.max_retries} aprĂšs {delay:.2f}s")
time.sleep(delay)
continue
else:
raise Exception(f"Rate limit aprĂšs {self.max_retries} tentatives")
# Autres erreurs - pas de retry
else:
raise Exception(f"HTTP {response.status_code}: {response.text}")
except requests.exceptions.RequestException as e:
last_exception = e
if attempt < self.max_retries:
delay = self.exponential_backoff(attempt)
print(f"Erreur réseau. Retry {attempt + 1}/{self.max_retries} aprÚs {delay:.2f}s")
time.sleep(delay)
else:
raise last_exception
raise last_exception
# Utilisation
retry_handler = RetryHandler(max_retries=3)
def make_api_call():
return requests.post(
"https://api.yodi.tg/v1/chat/completions",
headers={"Authorization": f"Bearer {api_key}"},
json={
"model": "yodi-1",
"messages": [{"role": "user", "content": "Hello"}]
}
)
# Appel avec retry automatique
response = retry_handler.retry_request(make_api_call)
Optimisation des requĂȘtes :
import asyncio
import aiohttp
from asyncio import Semaphore
class RateLimitedClient:
def __init__(self, api_key: str, max_concurrent: int = 5):
self.api_key = api_key
self.semaphore = Semaphore(max_concurrent)
self.session = None
async def __aenter__(self):
self.session = aiohttp.ClientSession(
headers={"Authorization": f"Bearer {self.api_key}"}
)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.session.close()
async def make_request(self, prompt: str):
async with self.semaphore: # Limite la concurrence
async with self.session.post(
"https://api.yodi.tg/v1/chat/completions",
json={
"model": "yodi-1",
"messages": [{"role": "user", "content": prompt}]
}
) as response:
if response.status == 429:
# Attendre avant de retry
await asyncio.sleep(1)
return await self.make_request(prompt)
return await response.json()
# Utilisation pour traiter plusieurs requĂȘtes
async def process_batch(prompts):
async with RateLimitedClient(api_key) as client:
tasks = [client.make_request(prompt) for prompt in prompts]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
đ§ Erreurs de validationâ
Erreur 400 - Bad Requestâ
SymptĂŽmes :
{
"message": "INVALID_FORMAT",
"error": true,
"data": null,
"status": 400
}
Validation du format de requĂȘte :
import json
from typing import Dict, List, Any
class RequestValidator:
@staticmethod
def validate_chat_request(data: Dict[str, Any]) -> tuple[bool, str]:
"""Valide une requĂȘte de chat completion"""
# Champs requis
required_fields = ['model', 'messages']
for field in required_fields:
if field not in data:
return False, f"Champ requis manquant: {field}"
# Validation du modĂšle
valid_models = ['yodi-1', 'yodi-1-32k', 'yodi-instruct', 'yodi-code']
if data['model'] not in valid_models:
return False, f"ModĂšle invalide: {data['model']}"
# Validation des messages
if not isinstance(data['messages'], list) or len(data['messages']) == 0:
return False, "Messages doit ĂȘtre une liste non vide"
for i, message in enumerate(data['messages']):
if not isinstance(message, dict):
return False, f"Message {i} doit ĂȘtre un objet"
if 'role' not in message or 'content' not in message:
return False, f"Message {i} manque 'role' ou 'content'"
if message['role'] not in ['system', 'user', 'assistant']:
return False, f"RĂŽle invalide dans message {i}: {message['role']}"
# Validation des paramĂštres optionnels
if 'temperature' in data:
temp = data['temperature']
if not isinstance(temp, (int, float)) or temp < 0 or temp > 2:
return False, "Temperature doit ĂȘtre entre 0 et 2"
if 'max_tokens' in data:
max_tokens = data['max_tokens']
if not isinstance(max_tokens, int) or max_tokens < 1:
return False, "max_tokens doit ĂȘtre un entier positif"
return True, "RequĂȘte valide"
@staticmethod
def validate_embedding_request(data: Dict[str, Any]) -> tuple[bool, str]:
"""Valide une requĂȘte d'embedding"""
if 'model' not in data:
return False, "Champ 'model' requis"
if data['model'] != 'yodi-embed':
return False, f"ModĂšle invalide pour embeddings: {data['model']}"
if 'input' not in data:
return False, "Champ 'input' requis"
input_data = data['input']
if isinstance(input_data, str):
if len(input_data.strip()) == 0:
return False, "Input ne peut pas ĂȘtre vide"
elif isinstance(input_data, list):
if len(input_data) == 0:
return False, "Input ne peut pas ĂȘtre une liste vide"
for item in input_data:
if not isinstance(item, str) or len(item.strip()) == 0:
return False, "Tous les Ă©lĂ©ments de input doivent ĂȘtre des chaĂźnes non vides"
else:
return False, "Input doit ĂȘtre une chaĂźne ou une liste de chaĂźnes"
return True, "RequĂȘte valide"
# Utilisation
def safe_api_call(endpoint: str, data: Dict[str, Any]):
# Validation selon l'endpoint
if endpoint.endswith('/chat/completions'):
is_valid, message = RequestValidator.validate_chat_request(data)
elif endpoint.endswith('/embeddings'):
is_valid, message = RequestValidator.validate_embedding_request(data)
else:
is_valid, message = True, "Endpoint non validé"
if not is_valid:
print(f" Erreur de validation: {message}")
return None
# Faire l'appel API
response = requests.post(endpoint, json=data, headers=headers)
return response
Validation des tokens :
def estimate_tokens(text: str) -> int:
"""Estimation approximative du nombre de tokens"""
# RĂšgle approximative : 1 token â 3-4 caractĂšres pour le français
return len(text) // 3
def validate_token_limits(messages: List[Dict], model: str, max_tokens: int = None) -> bool:
"""Valide que la requĂȘte ne dĂ©passe pas les limites de tokens"""
model_limits = {
'yodi-1': 4096,
'yodi-1-32k': 32768,
'yodi-instruct': 4096,
'yodi-code': 8192
}
if model not in model_limits:
return False, f"ModĂšle inconnu: {model}"
# Calculer les tokens d'entrée
input_tokens = sum(estimate_tokens(msg['content']) for msg in messages)
# Ajouter les tokens de sortie demandés
output_tokens = max_tokens or 500 # Défaut si non spécifié
total_tokens = input_tokens + output_tokens
limit = model_limits[model]
if total_tokens > limit:
return False, f"Total tokens ({total_tokens}) dépasse la limite ({limit})"
return True, "Tokens OK"
đ Erreurs de connectivitĂ©â
Timeouts et erreurs rĂ©seauâ
SymptĂŽmes courants :
ConnectionTimeoutReadTimeoutConnectionErrorSSL/TLS errors
Configuration robuste des timeouts :
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
class RobustYodiClient:
def __init__(self, api_key: str):
self.api_key = api_key
self.session = self._create_session()
def _create_session(self):
"""Crée une session avec retry et timeout configurés"""
session = requests.Session()
# Configuration des retry
retry_strategy = Retry(
total=3, # Nombre total de retry
status_forcelist=[429, 500, 502, 503, 504], # Codes HTTP Ă retry
backoff_factor=1, # Délai entre retry (1s, 2s, 4s...)
respect_retry_after_header=True # Respecter les headers Retry-After
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
# Headers par défaut
session.headers.update({
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
"User-Agent": "YodiClient/1.0"
})
return session
def chat_completion(self, **kwargs):
"""Appel de chat completion avec gestion robuste des erreurs"""
try:
response = self.session.post(
"https://api.yodi.tg/v1/chat/completions",
json=kwargs,
timeout=(30, 60) # (connection_timeout, read_timeout)
)
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print(" Timeout - Le serveur met trop de temps à répondre")
raise
except requests.exceptions.ConnectionError:
print(" Erreur de connexion - Vérifiez votre réseau")
raise
except requests.exceptions.HTTPError as e:
print(f" Erreur HTTP {e.response.status_code}: {e.response.text}")
raise
except Exception as e:
print(f" Erreur inattendue: {e}")
raise
# Test de connectivité
def test_connectivity():
"""Test complet de la connectivité à l'API"""
tests = [
("DNS Resolution", lambda: socket.gethostbyname("api.yodi.tg")),
("HTTPS Connection", lambda: requests.get("https://api.yodi.tg", timeout=10)),
("API Authentication", lambda: requests.get(
"https://api.yodi.tg/v1/models",
headers={"Authorization": f"Bearer {api_key}"},
timeout=10
))
]
for test_name, test_func in tests:
try:
result = test_func()
print(f" {test_name}: OK")
except Exception as e:
print(f" {test_name}: {e}")
return False
return True
ProblĂšmes de proxy et firewallâ
Configuration proxy :
import os
import requests
# Configuration automatique du proxy
def get_proxy_config():
"""Détecte et configure automatiquement les proxies"""
proxy_config = {}
# Variables d'environnement standard
http_proxy = os.getenv('HTTP_PROXY') or os.getenv('http_proxy')
https_proxy = os.getenv('HTTPS_PROXY') or os.getenv('https_proxy')
if http_proxy:
proxy_config['http'] = http_proxy
if https_proxy:
proxy_config['https'] = https_proxy
return proxy_config
# Client avec support proxy
class ProxyAwareClient:
def __init__(self, api_key: str):
self.api_key = api_key
self.proxies = get_proxy_config()
def make_request(self, **kwargs):
response = requests.post(
"https://api.yodi.tg/v1/chat/completions",
headers={"Authorization": f"Bearer {self.api_key}"},
json=kwargs,
proxies=self.proxies,
verify=True # Garde la vĂ©rification SSL mĂȘme avec proxy
)
return response.json()
Whitelist pour firewalls d'entreprise :
Demandez à votre équipe IT d'autoriser ces domaines :
api.yodi.tg(port 443)*.yodi.tg(port 443)- Certificats SSL de Let's Encrypt ou DigiCert
đ ProblĂšmes de qualitĂ© de rĂ©ponseâ
RĂ©ponses incohĂ©rentesâ
Diagnostic des prompts :
class PromptDiagnostic:
@staticmethod
def analyze_prompt_quality(prompt: str) -> Dict[str, Any]:
"""Analyse la qualité d'un prompt"""
analysis = {
'length': len(prompt),
'word_count': len(prompt.split()),
'has_context': False,
'has_examples': False,
'has_constraints': False,
'clarity_score': 0,
'suggestions': []
}
# Vérifier la présence de contexte
context_indicators = ['contexte', 'background', 'situation', 'dans le cadre']
analysis['has_context'] = any(indicator in prompt.lower() for indicator in context_indicators)
# Vérifier la présence d'exemples
example_indicators = ['exemple', 'par exemple', 'comme', 'tel que']
analysis['has_examples'] = any(indicator in prompt.lower() for indicator in example_indicators)
# Vérifier les contraintes
constraint_indicators = ['doit', 'ne pas', 'maximum', 'minimum', 'format']
analysis['has_constraints'] = any(indicator in prompt.lower() for indicator in constraint_indicators)
# Score de clarté basique
clarity_score = 0
if analysis['has_context']: clarity_score += 25
if analysis['has_examples']: clarity_score += 25
if analysis['has_constraints']: clarity_score += 25
if 50 <= analysis['word_count'] <= 200: clarity_score += 25
analysis['clarity_score'] = clarity_score
# Suggestions d'amélioration
if not analysis['has_context']:
analysis['suggestions'].append("Ajoutez du contexte pour clarifier la situation")
if not analysis['has_examples']:
analysis['suggestions'].append("Incluez des exemples pour illustrer le format attendu")
if not analysis['has_constraints']:
analysis['suggestions'].append("Spécifiez des contraintes (longueur, format, style)")
if analysis['word_count'] < 20:
analysis['suggestions'].append("Prompt trop court - ajoutez plus de détails")
if analysis['word_count'] > 500:
analysis['suggestions'].append("Prompt trop long - condensez les informations essentielles")
return analysis
# Test d'amélioration de prompt
def improve_prompt_iteratively(original_prompt: str, test_cases: List[str]):
"""Teste et améliore un prompt de maniÚre itérative"""
current_prompt = original_prompt
best_score = 0
best_prompt = original_prompt
for iteration in range(3): # 3 itérations d'amélioration
print(f"\n=== Itération {iteration + 1} ===")
# Analyser le prompt actuel
analysis = PromptDiagnostic.analyze_prompt_quality(current_prompt)
print(f"Score de clarté: {analysis['clarity_score']}/100")
print(f"Suggestions: {analysis['suggestions']}")
# Tester avec des cas d'usage
responses = []
for test_case in test_cases:
response = client.chat.completions.create(
model="yodi-1",
messages=[
{"role": "system", "content": current_prompt},
{"role": "user", "content": test_case}
],
temperature=0.3
)
responses.append(response.choices[0].message.content)
# Ăvaluer la qualitĂ© des rĂ©ponses
consistency_score = evaluate_response_consistency(responses)
total_score = (analysis['clarity_score'] + consistency_score) / 2
print(f"Score de consistance: {consistency_score}/100")
print(f"Score total: {total_score}/100")
if total_score > best_score:
best_score = total_score
best_prompt = current_prompt
# Améliorer le prompt pour la prochaine itération
current_prompt = apply_prompt_improvements(current_prompt, analysis['suggestions'])
return best_prompt, best_score
def evaluate_response_consistency(responses: List[str]) -> int:
"""Ăvalue la consistance des rĂ©ponses"""
if len(responses) < 2:
return 100
# Calcul simple basé sur la longueur et la structure
lengths = [len(r) for r in responses]
length_variance = np.var(lengths) / np.mean(lengths) if np.mean(lengths) > 0 else 1
# Score basé sur la variance (moins de variance = plus consistant)
consistency_score = max(0, 100 - (length_variance * 100))
return int(consistency_score)
Optimisation des paramĂštres :
def find_optimal_parameters(prompt: str, test_cases: List[str]):
"""Trouve les paramÚtres optimaux pour un prompt donné"""
parameter_grid = {
'temperature': [0.1, 0.3, 0.5, 0.7, 0.9],
'max_tokens': [100, 200, 300, 500],
'top_p': [0.8, 0.9, 1.0]
}
best_config = {}
best_score = 0
results = []
# Test de toutes les combinaisons
for temp in parameter_grid['temperature']:
for max_tok in parameter_grid['max_tokens']:
for top_p in parameter_grid['top_p']:
config = {
'temperature': temp,
'max_tokens': max_tok,
'top_p': top_p
}
# Tester avec tous les cas d'usage
responses = []
for test_case in test_cases:
response = client.chat.completions.create(
model="yodi-1",
messages=[{"role": "user", "content": f"{prompt}\n\n{test_case}"}],
**config
)
responses.append(response.choices[0].message.content)
# Ăvaluer la qualitĂ©
score = evaluate_response_quality(responses, test_cases)
results.append({
'config': config,
'score': score,
'responses': responses
})
if score > best_score:
best_score = score
best_config = config
print(f"Config {config}: Score {score:.2f}")
return best_config, best_score, results
def evaluate_response_quality(responses: List[str], test_cases: List[str]) -> float:
"""Ăvalue la qualitĂ© globale des rĂ©ponses"""
scores = []
for response, test_case in zip(responses, test_cases):
# CritÚres d'évaluation
relevance_score = evaluate_relevance(response, test_case)
completeness_score = evaluate_completeness(response)
clarity_score = evaluate_clarity(response)
total_score = (relevance_score + completeness_score + clarity_score) / 3
scores.append(total_score)
return sum(scores) / len(scores)
def evaluate_relevance(response: str, test_case: str) -> float:
"""Ăvalue la pertinence de la rĂ©ponse"""
# Analyse simple basée sur les mots-clés communs
response_words = set(response.lower().split())
test_words = set(test_case.lower().split())
common_words = response_words.intersection(test_words)
relevance = len(common_words) / len(test_words) if test_words else 0
return min(100, relevance * 100)
def evaluate_completeness(response: str) -> float:
"""Ăvalue la complĂ©tude de la rĂ©ponse"""
# CritĂšres simples
min_length = 50
ideal_length = 200
length = len(response)
if length < min_length:
return (length / min_length) * 100
elif length <= ideal_length:
return 100
else:
# Pénalité légÚre pour les réponses trop longues
return max(80, 100 - ((length - ideal_length) / ideal_length) * 20)
def evaluate_clarity(response: str) -> float:
"""Ăvalue la clartĂ© de la rĂ©ponse"""
# Analyse basée sur la structure
sentences = response.split('.')
avg_sentence_length = sum(len(s.split()) for s in sentences) / len(sentences)
# Score basé sur la longueur moyenne des phrases (8-20 mots = optimal)
if 8 <= avg_sentence_length <= 20:
return 100
elif avg_sentence_length < 8:
return 70 # Phrases trop courtes
else:
return max(50, 100 - (avg_sentence_length - 20) * 2) # Phrases trop longues
đ§ Outils de diagnosticâ
Script de diagnostic completâ
#!/bin/bash
# diagnostic-yodi.sh - Script de diagnostic complet pour l'API YODI
echo "đ DIAGNOSTIC API YODI"
echo "====================="
# Variables
API_KEY="${YODI_API_KEY}"
BASE_URL="https://api.yodi.tg/v1"
if [ -z "$API_KEY" ]; then
echo " Variable YODI_API_KEY non définie"
echo " Exportez votre clé API: export YODI_API_KEY='your-key'"
exit 1
fi
echo " Clé API détectée: ${API_KEY:0:8}..."
# Test 1: Connectivité réseau
echo -e "\nđĄ Test de connectivitĂ© rĂ©seau..."
if ping -c 1 api.yodi.tg > /dev/null 2>&1; then
echo " DNS et connectivité OK"
else
echo " ProblÚme de connectivité réseau"
echo " Vérifiez votre connexion internet et vos paramÚtres de proxy"
fi
# Test 2: Certificat SSL
echo -e "\nđ Test du certificat SSL..."
SSL_CHECK=$(echo | openssl s_client -connect api.yodi.tg:443 -servername api.yodi.tg 2>/dev/null | openssl x509 -noout -dates 2>/dev/null)
if [ $? -eq 0 ]; then
echo " Certificat SSL valide"
echo " $SSL_CHECK"
else
echo " ProblĂšme de certificat SSL"
fi
# Test 3: Authentification
echo -e "\nđ Test d'authentification..."
AUTH_RESPONSE=$(curl -s -w "%{http_code}" -o /tmp/auth_test.json \
-H "Authorization: Bearer $API_KEY" \
"$BASE_URL/models")
HTTP_CODE="${AUTH_RESPONSE: -3}"
if [ "$HTTP_CODE" = "200" ]; then
echo " Authentification réussie"
MODELS_COUNT=$(jq '.data | length' /tmp/auth_test.json 2>/dev/null || echo "N/A")
echo " ModĂšles disponibles: $MODELS_COUNT"
else
echo " Ăchec d'authentification (HTTP $HTTP_CODE)"
cat /tmp/auth_test.json 2>/dev/null
fi
# Test 4: Test de requĂȘte basique
echo -e "\nđŹ Test de requĂȘte chat..."
CHAT_RESPONSE=$(curl -s -w "%{http_code}" -o /tmp/chat_test.json \
-X POST "$BASE_URL/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "yodi-1",
"messages": [{"role": "user", "content": "Test de diagnostic"}],
"max_tokens": 10
}')
HTTP_CODE="${CHAT_RESPONSE: -3}"
if [ "$HTTP_CODE" = "200" ]; then
echo " RequĂȘte chat rĂ©ussie"
RESPONSE_TEXT=$(jq -r '.choices[0].message.content' /tmp/chat_test.json 2>/dev/null)
echo " Réponse: $RESPONSE_TEXT"
else
echo " Ăchec de requĂȘte chat (HTTP $HTTP_CODE)"
cat /tmp/chat_test.json 2>/dev/null
fi
# Test 5: Test d'embeddings
echo -e "\nđ Test d'embeddings..."
EMBED_RESPONSE=$(curl -s -w "%{http_code}" -o /tmp/embed_test.json \
-X POST "$BASE_URL/embeddings" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "yodi-embed",
"input": "Test embedding"
}')
HTTP_CODE="${EMBED_RESPONSE: -3}"
if [ "$HTTP_CODE" = "200" ]; then
echo " RequĂȘte embedding rĂ©ussie"
EMBED_DIM=$(jq '.data[0].embedding | length' /tmp/embed_test.json 2>/dev/null)
echo " Dimension: $EMBED_DIM"
else
echo " Ăchec de requĂȘte embedding (HTTP $HTTP_CODE)"
cat /tmp/embed_test.json 2>/dev/null
fi
# Test 6: Performance et latence
echo -e "\n Test de performance..."
START_TIME=$(date +%s.%N)
PERF_RESPONSE=$(curl -s -w "%{http_code}" -o /dev/null \
-X POST "$BASE_URL/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "yodi-1",
"messages": [{"role": "user", "content": "Performance test"}],
"max_tokens": 5
}')
END_TIME=$(date +%s.%N)
LATENCY=$(echo "$END_TIME - $START_TIME" | bc)
HTTP_CODE="${PERF_RESPONSE: -3}"
if [ "$HTTP_CODE" = "200" ]; then
echo " Test de performance réussi"
echo " Latence: ${LATENCY}s"
if (( $(echo "$LATENCY < 2.0" | bc -l) )); then
echo " đ Performance excellente"
elif (( $(echo "$LATENCY < 5.0" | bc -l) )); then
echo " Performance acceptable"
else
echo " đ Performance lente"
fi
else
echo " Ăchec du test de performance"
fi
# Nettoyage
rm -f /tmp/auth_test.json /tmp/chat_test.json /tmp/embed_test.json
echo -e "\nđŻ RĂSUMĂ DU DIAGNOSTIC"
echo "======================="
echo "Si tous les tests sont , votre configuration est correcte."
echo "Si certains tests échouent , consultez le guide de dépannage."
echo ""
echo "Pour un support technique: support@yodi.tg"
Dashboard de monitoringâ
import time
import json
import threading
from datetime import datetime, timedelta
from typing import Dict, List
import matplotlib.pyplot as plt
import pandas as pd
class YodiMonitor:
def __init__(self, api_key: str):
self.api_key = api_key
self.metrics = {
'requests': [],
'latencies': [],
'errors': [],
'tokens_used': []
}
self.monitoring = False
def start_monitoring(self, interval: int = 60):
"""Démarre le monitoring en arriÚre-plan"""
self.monitoring = True
def monitor_loop():
while self.monitoring:
self._collect_metrics()
time.sleep(interval)
monitor_thread = threading.Thread(target=monitor_loop)
monitor_thread.daemon = True
monitor_thread.start()
print(f" Monitoring démarré (intervalle: {interval}s)")
def stop_monitoring(self):
"""ArrĂȘte le monitoring"""
self.monitoring = False
print("đ Monitoring arrĂȘtĂ©")
def _collect_metrics(self):
"""Collecte les métriques de performance"""
timestamp = datetime.now()
try:
# Test de latence
start_time = time.time()
response = requests.post(
"https://api.yodi.tg/v1/chat/completions",
headers={"Authorization": f"Bearer {self.api_key}"},
json={
"model": "yodi-1",
"messages": [{"role": "user", "content": "ping"}],
"max_tokens": 1
},
timeout=30
)
latency = (time.time() - start_time) * 1000 # en ms
# Enregistrer les métriques
self.metrics['requests'].append({
'timestamp': timestamp,
'status_code': response.status_code,
'success': response.status_code == 200
})
self.metrics['latencies'].append({
'timestamp': timestamp,
'latency_ms': latency
})
if response.status_code != 200:
self.metrics['errors'].append({
'timestamp': timestamp,
'error_code': response.status_code,
'error_message': response.text
})
except Exception as e:
self.metrics['errors'].append({
'timestamp': timestamp,
'error_code': 'NETWORK_ERROR',
'error_message': str(e)
})
def generate_report(self, hours_back: int = 24) -> Dict:
"""GénÚre un rapport de performance"""
cutoff_time = datetime.now() - timedelta(hours=hours_back)
# Filtrer les données récentes
recent_requests = [
r for r in self.metrics['requests']
if r['timestamp'] > cutoff_time
]
recent_latencies = [
l for l in self.metrics['latencies']
if l['timestamp'] > cutoff_time
]
recent_errors = [
e for e in self.metrics['errors']
if e['timestamp'] > cutoff_time
]
# Calculer les statistiques
total_requests = len(recent_requests)
successful_requests = sum(1 for r in recent_requests if r['success'])
success_rate = (successful_requests / total_requests * 100) if total_requests > 0 else 0
latencies = [l['latency_ms'] for l in recent_latencies]
avg_latency = sum(latencies) / len(latencies) if latencies else 0
p95_latency = np.percentile(latencies, 95) if latencies else 0
# Analyse des erreurs
error_distribution = {}
for error in recent_errors:
code = error['error_code']
error_distribution[code] = error_distribution.get(code, 0) + 1
return {
'period': f"DerniĂšres {hours_back} heures",
'total_requests': total_requests,
'success_rate': f"{success_rate:.2f}%",
'avg_latency_ms': f"{avg_latency:.2f}",
'p95_latency_ms': f"{p95_latency:.2f}",
'error_count': len(recent_errors),
'error_distribution': error_distribution,
'recommendations': self._generate_recommendations(
success_rate, avg_latency, recent_errors
)
}
def _generate_recommendations(self, success_rate: float, avg_latency: float,
errors: List[Dict]) -> List[str]:
"""GénÚre des recommandations basées sur les métriques"""
recommendations = []
if success_rate < 95:
recommendations.append("Taux de succÚs faible - Vérifiez votre implémentation des retry")
if avg_latency > 2000:
recommendations.append("Latence élevée - Optimisez vos prompts et réduisez max_tokens")
error_codes = [e['error_code'] for e in errors]
if error_codes.count('429') > len(errors) * 0.1:
recommendations.append("Beaucoup d'erreurs 429 - Implémentez un rate limiting")
if error_codes.count('500') > 0:
recommendations.append("Erreurs serveur détectées - Contactez le support si persistant")
if not recommendations:
recommendations.append("Performance normale - Continuez le monitoring")
return recommendations
def plot_metrics(self, hours_back: int = 24):
"""GénÚre des graphiques de performance"""
cutoff_time = datetime.now() - timedelta(hours=hours_back)
# Préparer les données
latency_data = [
(l['timestamp'], l['latency_ms'])
for l in self.metrics['latencies']
if l['timestamp'] > cutoff_time
]
if not latency_data:
print("Pas assez de données pour générer des graphiques")
return
# Créer les graphiques
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
# Graphique de latence
timestamps, latencies = zip(*latency_data)
ax1.plot(timestamps, latencies, 'b-', alpha=0.7)
ax1.set_title('Latence de l\'API YODI')
ax1.set_ylabel('Latence (ms)')
ax1.grid(True)
# Graphique de taux de succĂšs (par heure)
success_by_hour = {}
for request in self.metrics['requests']:
if request['timestamp'] > cutoff_time:
hour = request['timestamp'].replace(minute=0, second=0, microsecond=0)
if hour not in success_by_hour:
success_by_hour[hour] = {'total': 0, 'success': 0}
success_by_hour[hour]['total'] += 1
if request['success']:
success_by_hour[hour]['success'] += 1
hours = sorted(success_by_hour.keys())
success_rates = [
(success_by_hour[h]['success'] / success_by_hour[h]['total'] * 100)
for h in hours
]
ax2.bar(hours, success_rates, alpha=0.7, color='green')
ax2.set_title('Taux de succĂšs par heure')
ax2.set_ylabel('Taux de succĂšs (%)')
ax2.set_ylim(0, 100)
plt.tight_layout()
plt.show()
# Utilisation du monitoring
def setup_monitoring():
monitor = YodiMonitor(api_key)
# Démarrer le monitoring
monitor.start_monitoring(interval=30) # Toutes les 30 secondes
# Laisser tourner pendant quelques minutes
time.sleep(300) # 5 minutes
# Générer un rapport
report = monitor.generate_report(hours_back=1)
print("\nđ RAPPORT DE PERFORMANCE")
print("========================")
for key, value in report.items():
if key != 'recommendations':
print(f"{key}: {value}")
print("\nđĄ RECOMMANDATIONS:")
for rec in report['recommendations']:
print(f"- {rec}")
# ArrĂȘter le monitoring
monitor.stop_monitoring()
# setup_monitoring()
Pour plus d'aide :
- đ§ Support technique : support@yodi.tg
- đ Documentation : docs.yodi.tg
- đŹ CommunautĂ© : community.yodi.tg
- đ Signaler un bug : github.com/yodi-ai/issues
Ce guide est mis à jour réguliÚrement. N'hésitez pas à nous faire part de vos retours pour l'améliorer.