Se connecterContactCommencer gratuitement
Base URL and SDK Migration27 juillet 2026Flatkey Team

Migration du client OpenAI : préserver RPM, TPM et le comportement des tentatives

Migrez un client de type OpenAI vers une passerelle unifiée sans provoquer de tempêtes de tentatives, en passant en revue RPM, TPM, les timeouts, le streaming, le backoff, les canaris et le rollback.

Migration du client OpenAI : préserver RPM, TPM et le comportement des tentatives

Une migration du client OpenAI peut sembler terminée après deux changements de configuration : remplacer la clé API et pointer le SDK vers une nouvelle URL de base. La première requête réussit, la forme de la réponse semble familière, et la pull request paraît prête à être fusionnée.

Cela prouve la compatibilité de l’interface. Cela ne prouve pas le comportement en production.

La partie la plus difficile d’une migration du client OpenAI consiste à préserver ce qui se passe lorsque le trafic devient irrégulier : les requêtes arrivent par rafales, les prompts s’allongent, les flux durent plus longtemps que prévu, un fournisseur renvoie 429, ou une réponse expire après que le travail a peut-être déjà commencé. Si le SDK, votre application et une file de tâches réessaient tous indépendamment, un appel en échec peut se transformer en plusieurs tentatives quasi simultanées.

Ce tutoriel montre comment migrer une intégration Python ou TypeScript existante de type OpenAI vers une passerelle unifiée tout en rendant explicites le comportement de limite de débit et de réessai. Les exemples utilisent l’URL de base compatible OpenAI de Flatkey, mais la méthode d’examen s’applique à toute migration de passerelle.

Réponse rapide : que faut-il changer ?

Pour une migration du client OpenAI sûre, examinez ces paramètres ensemble plutôt que de considérer l’URL de base comme l’unique changement.

Surface de migration À examiner Décision de départ sûre
Point de terminaison API URL de base et authentification Modifier via des variables d’environnement, pas avec des littéraux dispersés
Sélection du modèle Identifiants exacts des modèles et paramètres pris en charge Épingler un modèle connu pour le canari
Réessais du SDK Nombre de réessais automatiques et codes d’état réessayables Choisir si les réessais relèvent du SDK ou de votre application
Réessais de l’application Backoff, jitter, limite du nombre de tentatives et budget de réessai Conserver un seul responsable des réessais et journaliser chaque tentative
Contrôle du RPM Taux d’arrivée des requêtes et taille des rafales Ajouter une limite de concurrence ou de file d’attente avant le basculement
Contrôle du TPM Prompt plus nombre attendu de jetons de sortie Tester des prompts volumineux réalistes, pas seulement un test de fumée sur une seule ligne
Délais d’attente Durée de connexion, de lecture et durée totale de la requête Définir des valeurs explicites pour les appels synchrones et en streaming
Observabilité Identifiants de requête, tentatives, jetons, latence et résultat final Comparer les journaux du client avec les journaux d’utilisation de la passerelle

Si vous avez d’abord besoin de l’explication des acronymes, lisez Les limites de débit des LLM expliquées : RPM, TPM et réessais. Ce guide commence là où cette explication se termine : au diff de migration et au plan de test en production.

Pourquoi un remplacement de l’URL de base est nécessaire mais insuffisant

Le guide de démarrage rapide de Flatkey documente le changement minimal côté client : conserver le schéma de requête du SDK OpenAI et définir l’URL de base sur https://router.flatkey.ai/v1. Il recommande aussi de vérifier les journaux d’utilisation après la requête afin de pouvoir confirmer le modèle, les volumes de jetons, la latence et le coût.

C’est le bon test de fumée. Une migration du client OpenAI en production nécessite quatre questions supplémentaires :

  1. Le SDK retente-t-il automatiquement les erreurs 429, les timeouts ou les erreurs serveur ?
  2. Une autre couche retente-t-elle également la même opération ayant échoué ?
  3. La concurrence est-elle limitée par le débit de requêtes, le débit de jetons, ou les deux ?
  4. Pouvez-vous distinguer une opération logique unique de ses tentatives individuelles ?

La documentation officielle des SDK Python et Node d’OpenAI indique actuellement que certaines erreurs sont retentées deux fois par défaut, y compris les réponses 429, les erreurs de connexion, les timeouts et certaines erreurs serveur. Les deux SDK exposent une configuration de retry et de timeout. Ce comportement par défaut est pratique pour une intégration directe, mais il peut devenir une amplification invisible lorsque votre propre code met déjà en œuvre un backoff.

L’objectif de la migration n’est pas de « désactiver tous les retries ». L’objectif est de « savoir quelle couche est responsable du retry ».

Étape 1 : inventorier chaque couche de retry avant de modifier le code

Commencez par tracer le chemin d’appel réel.

action utilisateur ou tâche
  -> wrapper de retry de l’application
  -> retry de livraison de la file d’attente
  -> retry du SDK OpenAI
  -> passerelle
  -> fournisseur

Pour chaque couche, consignez :

  • Quelles erreurs déclenchent une nouvelle tentative.
  • Le nombre maximal de tentatives.
  • Si le délai utilise un sleep fixe, un backoff exponentiel ou du jitter.
  • Si une valeur Retry-After fournie par le serveur est respectée.
  • Si le même identifiant d’opération est conservé entre les tentatives.
  • Si une requête ayant expiré est considérée comme ayant échoué avant que tout travail n’ait eu lieu.

Cette dernière hypothèse est risquée. Un timeout côté client indique seulement que le client a cessé d’attendre. Le système en amont a peut-être encore accepté ou terminé la requête. Pour du contenu généré, une nouvelle tentative peut donc créer un autre résultat et une autre requête facturable, même lorsque votre application n’a observé qu’une seule tâche logique.

Estimer l’amplification au pire cas

Supposons qu’une file d’attente puisse livrer une tâche trois fois, que le wrapper applicatif autorise trois tentatives et que le SDK effectue l’appel initial plus deux retries. Dans le pire des cas, une tâche logique peut déclencher :

3 livraisons de file d’attente × 3 tentatives applicatives × 3 tentatives SDK = 27 tentatives HTTP

Vous n’atteindrez peut-être jamais ce nombre maximal, mais la multiplication explique pourquoi un bref 429 peut se transformer en tempête de retries. Inscrivez ce chiffre dans la revue de migration. Cela rend visibles les valeurs par défaut cachées.

Étape 2 : déplacer les paramètres de l’endpoint dans la configuration

Gardez le diff de migration réversible. Ne remplacez pas les chaînes d’endpoint partout dans la base de code.

Python avant et après

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["LLM_API_KEY"],
    base_url=os.environ.get("LLM_BASE_URL", "https://api.openai.com/v1"),
    max_retries=0,
    timeout=45.0,
)

Pour le canari Flatkey, configurez :

export LLM_API_KEY="$FLATKEY_API_KEY"
export LLM_BASE_URL="https://router.flatkey.ai/v1"

TypeScript avant et après

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.LLM_API_KEY,
  baseURL: process.env.LLM_BASE_URL ?? "https://api.openai.com/v1",
  maxRetries: 0,
  timeout: 45_000,
});

Ces exemples définissent les tentatives de l’SDK à zéro, car la section suivante confie explicitement à l’application la responsabilité des nouvelles tentatives. Si votre application ne possède aucune couche de retry, vous pouvez à la place conserver des retries SDK bornés. Ne gardez pas les deux par erreur.

Pour la checklist de compatibilité plus large, voir OpenAI Compatible API Gateway: Migration Checklist for Minimal Code Changes.

Étape 3 : attribuer explicitement une seule couche à la responsabilité des retries

Une politique de retry utile comporte cinq éléments :

  1. Une courte liste d’échecs pouvant être retentés.
  2. Un plafond strict sur le nombre de tentatives.
  3. Un temps total maximal de retry.
  4. Un backoff exponentiel avec jitter.
  5. Des logs structurés pour chaque tentative.

Voici un petit wrapper Python pour un appel de chat synchrone :

import random
import time
from openai import APITimeoutError, APIConnectionError, APIStatusError, RateLimitError

RETRYABLE_STATUS_CODES = {408, 409, 429, 500, 502, 503, 504}


def create_chat_with_retry(client, *, model, messages, max_attempts=4):
    started_at = time.monotonic()

    for attempt in range(1, max_attempts + 1):
        try:
            return client.chat.completions.create(
                model=model,
                messages=messages,
            )
        except (RateLimitError, APITimeoutError, APIConnectionError) as error:
            retryable = True
            caught_error = error
        except APIStatusError as error:
            retryable = error.status_code in RETRYABLE_STATUS_CODES
            caught_error = error

        if not retryable or attempt == max_attempts:
            raise caught_error

        exponential_delay = min(2 ** (attempt - 1), 16)
        jitter = random.uniform(0, 0.5 * exponential_delay)
        sleep_seconds = exponential_delay + jitter

        print({
            "event": "llm_retry",
            "attempt": attempt,
            "next_delay_seconds": round(sleep_seconds, 2),
            "elapsed_seconds": round(time.monotonic() - started_at, 2),
            "error_type": type(caught_error).__name__,
        })
        time.sleep(sleep_seconds)

    raise RuntimeError("unreachable")

Traitez ceci comme un point de départ à examiner, et non comme une politique universelle. En production, analysez et respectez un en-tête de réponse Retry-After valide avant de revenir à un délai calculé localement. Ajoutez un budget de durée totale écoulée afin que les retries ne dépassent pas la latence que votre produit peut tolérer.

Ne retryez pas toutes les erreurs

Échec Action par défaut Pourquoi
400 requête invalide Ne pas retenter à l’identique Le payload doit changer
401 authentification Ne pas retenter à l’identique La clé ou l’en-tête doit changer
404 modèle introuvable Ne pas retenter à l’identique L’identifiant du modèle ou l’accès doit changer
429 limite de débit Retenter avec délai et jitter Une capacité peut devenir disponible
500 ou 503 Retenter dans un petit budget L’échec peut être temporaire
Délai d’attente client Retenter avec prudence La requête en amont a peut-être déjà été exécutée

Le guide de démarrage rapide de Flatkey donne la même recommandation de haut niveau pour 429 : réessayer avec un backoff exponentiel et du jitter. L’ajout spécifique à la migration consiste à s’assurer qu’une seule couche applique cette politique.

Étape 4 : dimensionner la concurrence pour RPM et TPM

Une migration du client OpenAI peut préserver la syntaxe des requêtes tout en modifiant l’enveloppe de capacité. Le RPM et le TPM contraignent des charges de travail différentes :

  • Le RPM devient le goulot d’étranglement lorsque vous envoyez de nombreuses petites requêtes.
  • Le TPM devient le goulot d’étranglement lorsque les prompts, les sorties ou les évaluations parallèles sont volumineux.

Appuyez-vous sur le trafic observé plutôt que sur une moyenne unique. Collectez au minimum :

  • Le nombre de requêtes par minute au médian et au pic.
  • Les jetons d’entrée aux p50, p95 et maximum.
  • Les jetons de sortie aux p50 et p95.
  • La durée médiane et p95 des requêtes.
  • Le nombre de flux simultanés.

Un plafond de concurrence approximatif peut être estimé à partir de chaque limite :

Concurrence basée sur le RPM ≈ (RPM / 60) × durée moyenne d’une requête en secondes

Concurrence basée sur le TPM ≈ (TPM / jetons moyens par requête / 60)
                                × durée moyenne d’une requête en secondes

Utilisez le résultat le plus faible comme plafond initial, puis laissez une marge pour les pics et les tentatives.

Exemple : supposons qu’une route autorise 600 RPM et 300 000 TPM, que la requête moyenne utilise 1 500 jetons au total, et que la durée moyenne soit de 3 secondes.

Plafond RPM : (600 / 60) × 3 = 30 requêtes simultanées
Plafond TPM : (300,000 / 1,500 / 60) × 3 = 10 requêtes simultanées

Le TPM est la contrainte la plus serrée dans cet exemple. Démarrer à 30 requêtes simultanées parce que le RPM semblait généreux entraînerait des réponses 429 évitables.

Ce calcul donne une indication, il ne constitue pas une garantie du fournisseur. Les fournisseurs peuvent utiliser des fenêtres glissantes, des seaux de jetons, des limites séparées pour les jetons d’entrée et de sortie, des pools spécifiques aux modèles ou des contrôles d’accélération. Le plan de test doit vérifier le comportement réel pour le modèle et le compte sélectionnés.

Étape 5 : tester séparément le streaming et le comportement des délais d’attente

Ne considérez pas un appel non streamé réussi comme la preuve que le streaming est sans risque.

Pour les requêtes en streaming, testez :

  • Le délai jusqu’au premier jeton.
  • L’intervalle de silence maximal entre les blocs.
  • Le délai d’attente de lecture du client.
  • Le comportement lorsque le consommateur se déconnecte.
  • Si votre wrapper de retry peut accidentellement démarrer un second flux.
  • Si la sortie partielle est conservée, ignorée ou affichée à l’utilisateur.

Un flux qui échoue après une sortie partielle n’est pas équivalent à une requête ayant échoué avant toute sortie. Le relancer automatiquement peut afficher du texte dupliqué ou produire une continuation différente. Décidez si le produit doit réessayer, demander à l’utilisateur ou exposer le résultat partiel.

N’oubliez pas non plus que les délais d’attente du SDK et ceux de l’infrastructure peuvent différer. Un proxy inverse, une plateforme serverless, un worker de tâches ou une connexion navigateur peut se terminer avant que la bibliothèque cliente n’atteigne son propre délai d’attente. Pendant la migration du client OpenAI, enregistrez le délai d’attente le plus court dans l’ensemble du chemin de la requête.

Étape 6 : exécuter une matrice de canari avant un trafic large

Utilisez un modèle figé et un faible pourcentage du trafic. Le premier canari doit répondre à la question de savoir si la nouvelle route préserve le comportement, et non si chaque modèle fonctionne.

Cas de test Entrée Preuve attendue
Authentification Clés valides et invalides Succès plus un 401 non retenté
Validation du modèle IDs de modèle valides et mal orthographiés Succès plus une erreur de modèle non retentée
Petite rafale de requêtes De nombreux prompts courts Mise en file d’attente contrôlée sans pic de tentatives
Grande rafale de prompts Moins de prompts à fort volume de tokens La pression sur le TPM est visible et bornée
429 forcé Dépasser temporairement la limite canari Un seul propriétaire de tentative, délais avec jitter, nombre maximal de tentatives plafonné
Expiration forcée Définir un délai client volontairement court Expiration consignée sans relecture non bornée
Interruption du streaming Se déconnecter pendant un flux Comportement explicite pour la sortie partielle
Erreur serveur Injecter ou simuler 503 Tentatives bornées et rapport final d’erreur
Rollback Restaurer l’URL de base précédente Le rollback par configuration seule réussit

Pour chaque opération logique, consignez :

operation_id
attempt_number
base_url_name
model_requested
http_status
input_tokens
output_tokens
latency_ms
retry_delay_ms
final_outcome

Comparez ensuite les journaux de l’application avec les journaux d’utilisation de Flatkey. Les volumes doivent être cohérents entre eux. Si une opération applicative correspond à plusieurs requêtes passerelle, votre instrumentation des tentatives doit expliquer pourquoi.

Étape 7 : définir les seuils de déploiement et de rollback

Une migration du client OpenAI doit disposer de conditions d’arrêt numériques avant le lancement du premier canari.

Exemples de seuils :

  • Effectuer un rollback si le taux d’erreur final augmente de plus d’un point de pourcentage convenu.
  • Mettre en pause si le nombre de tentatives par opération dépasse le budget de tentatives attendu.
  • Mettre en pause si le p95 de latence dépasse le budget de délai du produit.
  • Mettre en pause si l’utilisation de tokens par opération réussie change de manière inattendue.
  • Étendre le trafic uniquement après que les chemins streaming et non streaming aient tous deux réussi.

Évitez de comparer uniquement les volumes bruts de 429. Une bonne file d’attente peut réduire les erreurs finales tout en augmentant temporairement les requêtes différées. Suivez à la fois les résultats au niveau des tentatives et au niveau des opérations.

Checklist de la pull request de migration

Copiez cette checklist dans la PR d’implémentation.

  • L’URL de base et la clé proviennent de variables d’environnement.
  • Le canari utilise un identifiant de modèle exact et vérifié.
  • Une seule couche gère les tentatives.
  • Les valeurs par défaut de retry du SDK sont documentées dans la PR.
  • Le comportement pour 429, les expirations et 5xx a un nombre de tentatives borné.
  • Le backoff inclut du jitter et respecte Retry-After lorsqu’il est présent.
  • Les plafonds RPM et TPM sont estimés à partir du trafic observé.
  • Le streaming dispose d’un test d’échec séparé.
  • Chaque tentative partage un unique operation_id logique.
  • Les journaux d’utilisation et les journaux de l’application sont comparés.
  • Les seuils de déploiement et de rollback sont rédigés avant le lancement.
  • Le point de terminaison précédent peut être restauré sans autre modification du code.

Erreurs courantes de migration

Conserver les retries du SDK et ceux de l’application sans calculer le total

C’est la conclusion de revue la plus importante. Les valeurs par défaut restent un comportement, même lorsqu’elles ne sont pas visibles dans la fonction locale.

Tester seulement une minuscule invite

Une requête en une ligne prouve les identifiants et la compatibilité des réponses. Elle ne dit presque rien sur la pression TPM, les limites de sortie, les longs flux ou la latence p95.

Réessayer les erreurs d’authentification et de validation

Le backoff ne peut pas réparer une clé invalide, un paramètre non pris en charge ou un modèle mal orthographié. Réessayer des charges utiles inchangées gaspille de la capacité et masque le vrai défaut.

Considérer qu’un timeout prouve qu’aucune requête n’a été exécutée

Le client peut cesser d’attendre après que l’amont a accepté l’appel. Concevez les tentatives et la comptabilisation en gardant cette ambiguïté à l’esprit.

Modifier l’endpoint, les modèles, les invites et la politique de retry dans une seule version

Cela rend les échecs difficiles à attribuer. Migrez d’abord une forme de requête connue, puis élargissez le choix du modèle une fois que la route est observable.

Une définition plus sûre de « compatible OpenAI »

Pour la planification de la migration, « compatible OpenAI » devrait signifier que le schéma d’interaction est suffisamment familier pour réduire les changements de code. Cela ne doit pas être interprété comme la promesse que chaque fournisseur partage des quotas identiques, le comptage des jetons, la sémantique des erreurs, la latence, le comportement du streaming ou la prise en charge des paramètres.

Cette distinction facilite la revue d’une migration du client OpenAI. Conservez l’interface stable là où cela aide, mais testez le contrat opérationnel là où les fournisseurs et les routes peuvent différer.

Flatkey centralise l’accès et la facturation derrière une seule URL de base compatible OpenAI, ce qui peut simplifier le diff du client et l’extension ultérieure des modèles. Le travail d’ingénierie consiste toujours à rendre explicites les retries, le débit et l’observabilité avant le basculement du trafic de production.

C’est le standard qu’une migration du client OpenAI de production doit atteindre : un petit changement d’interface étayé par des preuves opérationnelles explicites.

Consultez la page de tarification de Flatkey lors du choix des modèles pour votre canari, puis approuvez la migration uniquement après que la checklist a été validée en revue de code et que le comportement de la route est visible dans les journaux.

Questions fréquemment posées

Dois-je désactiver les retries du SDK OpenAI pendant la migration ?

Désactivez-les si votre application ou votre file d’attente gère déjà les retries. Si aucune autre couche ne retente, des retries bornés du SDK peuvent être raisonnables. La règle importante est d’éviter plusieurs responsables de retries indépendants.

Quelle est la différence entre RPM et TPM pendant une migration ?

RPM limite la fréquence des requêtes, tandis que TPM limite le débit de jetons. De petites requêtes fréquentes peuvent atteindre d’abord la limite RPM ; moins d’invites ou de sorties volumineuses peuvent atteindre d’abord la limite TPM. Testez les deux formes de charge.

Un 429 doit-il toujours être retenté ?

Uniquement dans une fenêtre bornée de retries et de latence. Respectez Retry-After lorsqu’il est disponible, sinon utilisez un backoff exponentiel avec jitter. Arrêtez-vous si l’opération ne peut plus respecter l’objectif de latence du produit.

Puis-je retenter en toute sécurité une génération ayant expiré ?

Pas avec certitude. La requête amont a peut-être été exécutée même si le client a expiré. Traitez la tentative comme une possible requête en double et journalisez la relation entre les essais.

Quel est le canari minimal sûr ?

Utilisez un seul modèle figé, une seule forme de requête, une responsabilité explicite des tentatives, une limite de concurrence et des tests pour 429, les délais d’attente, l’interruption du streaming et le rollback. Comparez les tentatives côté client avec les journaux d’utilisation de la passerelle avant d’augmenter le trafic.

Sources