Se connecterContactCommencer gratuitement
Reliability and Routing30 juillet 2026Flatkey Team

Observabilité des API LLM : métriques, traces, logs et coût

Un guide de production pour surveiller les API LLM avec des métriques de succès validées, des traces distribuées, des logs structurés sûrs, des SLO, des alertes et le coût par tâche acceptée.

Observabilité des API LLM : métriques, traces, logs et coût

L’observabilité des API LLM consiste à transformer chaque appel de modèle en suffisamment de preuves structurées pour répondre à quatre questions de production :

  1. La requête a-t-elle abouti ?
  2. Combien de temps l’utilisateur a-t-il attendu ?
  3. Qu’a consommé la requête et quel en a été le coût ?
  4. Pourquoi le système a-t-il choisi ce modèle, cette nouvelle tentative ou ce repli ?

La surveillance HTTP classique est nécessaire, mais elle n’est pas suffisante. Un 200 OK peut toujours contenir un JSON invalide, une réponse vide, une réponse refusée, un appel d’outil cassé ou une sortie qui viole le contrat de l’application. Une requête peut aussi réussir après trois tentatives et coûter en silence quatre fois plus que prévu.

L’objectif pratique n’est pas de journaliser chaque prompt. Il s’agit de créer un petit contrat de télémétrie cohérent qui relie les résultats de l’application au modèle, au fournisseur, à l’itinéraire, à la latence, à l’utilisation des tokens, aux nouvelles tentatives et au coût—sans divulguer de données utilisateur.

Ce guide montre comment construire ce contrat avec des métriques, des traces, des logs structurés, des objectifs de niveau de service, des tableaux de bord et des alertes.

Ce que l’observabilité des API LLM doit expliquer

Un système d’observabilité utile permet à un ingénieur d’astreinte de passer rapidement d’un symptôme à une cause.

Question de production Preuves nécessaires
Pourquoi la latence a-t-elle augmenté ? Durée de bout en bout, durée du fournisseur, temps d’attente en file, temps jusqu’au premier token, modèle, région, nombre de nouvelles tentatives
Pourquoi le coût a-t-il augmenté ? Tokens d’entrée, tokens de sortie, tokens mis en cache lorsqu’ils sont disponibles, instantané du prix du modèle, tentatives, taux de tâches acceptées
Pourquoi les utilisateurs voient-ils de mauvais résultats ? Résultat du validateur de sortie, erreurs de schéma, état de refus, résultat de l’appel d’outil, score d’évaluation, version du prompt
Pourquoi le trafic a-t-il basculé vers un autre modèle ? Politique de routage, cible sélectionnée, raison du repli, état du disjoncteur, erreurs du fournisseur
L’incident est-il spécifique au fournisseur ? Fournisseur, modèle, compte ou déploiement, région, code de statut, ID de requête du fournisseur
Peut-on reproduire une requête ? ID de requête interne, ID de trace, empreinte d’entrée anonymisée, version du prompt, paramètres du modèle

La première règle de conception est simple : mesurer le contrat de l’application, pas seulement le contrat de transport.

Les cinq couches de télémétrie

La surveillance des API LLM devient plus simple lorsque vous séparez cinq couches au lieu de forcer chaque signal dans un seul tableau de bord.

1. Métriques de requête

Les métriques montrent les tendances et alimentent les alertes. Enregistrez des compteurs et des histogrammes pour :

  • Nombre de requêtes
  • Latence de bout en bout
  • Temps jusqu’au premier token pour les réponses en streaming
  • Latence du fournisseur ou de l’appel au modèle
  • Requêtes réussies, échouées, annulées et ayant expiré
  • Réponses HTTP 429 et 5xx
  • Nouvelles tentatives et tentatives de repli
  • Tokens d’entrée, de sortie et mis en cache
  • Coût estimé et rapproché

Les métriques doivent avoir des étiquettes bornées. De bonnes étiquettes incluent provider, model, route, environment, status et error_type. Évitez les étiquettes à forte cardinalité telles que les ID utilisateur, les ID de requête, le texte du prompt ou les URL complètes.

2. Traces distribuées

Une trace explique le parcours d’une requête à travers votre API, la file d’attente, la couche de récupération, les appels d’outils, la passerelle et le fournisseur de modèle.

Une hiérarchie de trace pratique ressemble à ceci :

POST /support/reply
├── retrieve_customer_context
├── llm.route
│   ├── llm.attempt provider_a/model_primary
│   └── llm.attempt provider_b/model_fallback
├── validate_structured_output
└── persist_draft

Chaque tentative de modèle doit avoir son propre span. Si deux fournisseurs sont essayés, la trace doit montrer deux tentatives au lieu de masquer les deux dans un seul span opaque llm.call.

Les conventions sémantiques de l’IA générative d’OpenTelemetry fournissent un vocabulaire partagé utile pour les spans, métriques et événements liés à l’IA générative. Traitez la version de la convention comme faisant partie de votre schéma de télémétrie afin de pouvoir migrer délibérément lorsque les attributs évoluent.

3. Journaux structurés

Les journaux capturent des décisions discrètes et un contexte de diagnostic qui seraient trop coûteux ou trop détaillés pour des libellés de métriques.

Les événements utiles incluent :

  • llm.request.started
  • llm.route.selected
  • llm.retry.scheduled
  • llm.fallback.selected
  • llm.response.validated
  • llm.request.completed
  • llm.request.failed

Chaque événement doit inclure les mêmes champs de corrélation : request_id, trace_id, route, model, provider, prompt_version et attempt.

4. Signaux de qualité et de contrat

La qualité ne peut pas être déduite des codes de statut. Ajoutez autant que possible des validateurs déterministes :

  • JSON analysé avec succès
  • Les champs de schéma requis existent
  • Le nom et les arguments de l’outil sont autorisés
  • La liste des citations est présente lorsque requis
  • La longueur de sortie est dans les limites du produit
  • Le refus ou l’état de sécurité est reconnu
  • Les vérifications des règles métier réussissent

Pour les tâches subjectives, joignez plus tard des résultats d’évaluation échantillonnés. Conservez la télémétrie des requêtes en ligne et l’évaluation hors ligne reliées par un identifiant de requête ou d’échantillon stable.

Avant de remplacer un modèle en production, utilisez un workflow d’évaluation de modèle IA répétable plutôt que de vous fier uniquement à la latence agrégée et au prix des jetons.

5. Coût et résultats business

Les comptes de jetons sont des संकेतaux d’utilisation, pas des résultats business. Reliez l’utilisation du modèle à l’unité qui compte pour votre produit :

  • Coût par réponse de support acceptée
  • Coût par tâche de codage terminée
  • Coût par image de produit générée approuvée par un réviseur
  • Coût par lead qualifié enrichi
  • Coût par extraction structurée réussie

La formule la plus utile est :

coût effectif par tâche acceptée = coût total du modèle / tâches acceptées

Cela met en évidence les fausses économies. Un modèle moins cher qui provoque davantage de tentatives, d’échecs de validation ou de retraitement humain peut augmenter le coût effectif.

Un contrat de télémétrie minimal pour chaque appel de modèle

Commencez par un schéma d’événement versionné. Les noms de champs exacts peuvent suivre votre pile d’observabilité, mais les concepts doivent rester stables.

{
  "schema_version": "llm-observability.v1",
  "timestamp": "2026-07-30T09:00:00Z",
  "request_id": "req_internal_01",
  "trace_id": "7c4b...",
  "environment": "production",
  "feature": "support_reply",
  "route": "support-default",
  "provider": "provider-a",
  "model": "model-primary",
  "prompt_version": "support-reply-v12",
  "attempt": 1,
  "stream": true,
  "status": "success",
  "http_status": 200,
  "latency_ms": 1840,
  "time_to_first_token_ms": 410,
  "input_tokens": 1640,
  "output_tokens": 284,
  "cached_input_tokens": 900,
  "estimated_cost_usd": 0.0068,
  "validator": "passed",
  "fallback_reason": null,
  "provider_request_id": "redacted-or-scoped-value"
}

Ne rendez pas les prompts et réponses bruts obligatoires. Ne les stockez que lorsqu’il existe un besoin défini, une politique de conservation approuvée, des contrôles d’accès appropriés et un chemin de masquage sûr.

Métriques à placer sur le premier tableau de bord

Ne commencez pas avec 40 panneaux. Créez un tableau de bord opérationnel unique qui répond à la question de savoir si les utilisateurs reçoivent des résultats valides dans le respect du budget de latence et de coût.

Trafic et succès

  • Requêtes par minute
  • Taux de succès du transport
  • Taux de succès validé
  • Taux d’annulation
  • Taux de timeout
  • Ratio d’amplification des retries
  • Taux de repli

Taux de succès validé doit être le signal principal de disponibilité :

taux de succès validé = requêtes qui passent le contrat de l’application / requêtes éligibles

C’est plus strict et plus utile que réponses 2xx / requêtes.

Latence

Suivez les distributions plutôt que les moyennes :

  • Latence de bout en bout p50, p95 et p99
  • Latence d’appel au fournisseur p50, p95 et p99
  • Temps jusqu’au premier token p50 et p95
  • Attente en file p95
  • Exécution des outils p95
  • Durée de validation p95

Séparez les routes en streaming et non streaming. Une requête en streaming peut sembler réactive avec un bon temps jusqu’au premier token, même lorsque le temps total de complétion est long.

Fiabilité

  • Taux de 429 par fournisseur et modèle
  • Taux de 5xx par fournisseur et modèle
  • Taux d’erreur réseau
  • Taux de réponse mal formée ou non valide selon le schéma
  • Taux d’échec des appels d’outil
  • État ouvert du disjoncteur
  • Taux d’épuisement du budget de retries

Si les limites de débit sont une cause fréquente, utilisez une stratégie de retry LLM bornée pour les limites RPM et TPM plutôt que des retries non coordonnés dans chaque worker d’application.

Utilisation et coût

  • Tokens d’entrée et de sortie par fonctionnalité
  • Tokens par tâche acceptée
  • Coût estimé par requête
  • Coût par tâche acceptée
  • Coût des retries
  • Delta de coût du repli
  • Dépense quotidienne par rapport au budget
  • Estimation des coûts par rapport à la facture du fournisseur ou à l’export d’utilisation

Conservez à la fois estimated_cost et reconciled_cost. Le premier permet une surveillance quasi en temps réel ; le second corrige les estimations après l’arrivée des données de facturation faisant autorité.

How to trace retries and fallback routing

Les nouvelles tentatives et les mécanismes de repli sont l’endroit où la supervision de base se brise généralement. Si toutes les tentatives partagent un seul champ de statut, une requête coûteuse et dégradée peut sembler saine.

Enregistrez ces champs pour chaque tentative :

Field Why it matters
attempt Montre l’amplification et l’ordre des décisions
target_id Identifie le fournisseur, le déploiement, la région et le modèle sans secrets
reason Distingue un timeout, un 429, un 5xx, un échec de validation et le routage par politique
remaining_budget_ms Prouve que le routeur a respecté la date limite visible par l’utilisateur
safe_to_repeat Rend explicites les décisions d’idempotence
output_started Empêche un fallback non sûr après que la sortie en streaming a atteint le client
contract_compatible Confirme que la cible suivante prend en charge le schéma, les outils et la modalité requis

Un LLM API fallback routing playbook de production doit définir la politique de décision. L’observabilité doit ensuite prouver que le routeur l’a suivie.

TypeScript instrumentation pattern

L’exemple suivant garde la télémétrie indépendante d’un SDK de modèle spécifique. Il enregistre un span parent de route et un span enfant pour chaque tentative.

import { context, SpanStatusCode, trace } from "@opentelemetry/api";

const tracer = trace.getTracer("ai-gateway");

type ModelAttempt = {
  provider: string;
  model: string;
  reason: "primary" | "retry" | "fallback";
};

export async function runModelRoute(
  attempts: ModelAttempt[],
  callModel: (attempt: ModelAttempt) => Promise<{
    text: string;
    usage?: { inputTokens?: number; outputTokens?: number };
    providerRequestId?: string;
  }>,
) {
  return tracer.startActiveSpan("llm.route", async (routeSpan) => {
    routeSpan.setAttribute("app.llm.route", "support-default");
    routeSpan.setAttribute("app.llm.attempt_limit", attempts.length);

    try {
      for (const [index, attempt] of attempts.entries()) {
        const result = await tracer.startActiveSpan(
          "llm.attempt",
          { attributes: {
            "gen_ai.system": attempt.provider,
            "gen_ai.request.model": attempt.model,
            "app.llm.attempt": index + 1,
            "app.llm.reason": attempt.reason,
          } },
          context.active(),
          async (attemptSpan) => {
            const startedAt = performance.now();

            try {
              const response = await callModel(attempt);
              const valid = response.text.trim().length > 0;

              attemptSpan.setAttribute("app.llm.validated", valid);
              attemptSpan.setAttribute(
                "gen_ai.usage.input_tokens",
                response.usage?.inputTokens ?? 0,
              );
              attemptSpan.setAttribute(
                "gen_ai.usage.output_tokens",
                response.usage?.outputTokens ?? 0,
              );
              attemptSpan.setAttribute(
                "app.llm.latency_ms",
                performance.now() - startedAt,
              );

              if (!valid) {
                throw new Error("response_validation_failed");
              }

              attemptSpan.setStatus({ code: SpanStatusCode.OK });
              return response;
            } catch (error) {
              attemptSpan.recordException(error as Error);
              attemptSpan.setStatus({
                code: SpanStatusCode.ERROR,
                message: (error as Error).message,
              });
              return null;
            } finally {
              attemptSpan.end();
            }
          },
        );

        if (result) {
          routeSpan.setAttribute("app.llm.selected_attempt", index + 1);
          routeSpan.setStatus({ code: SpanStatusCode.OK });
          return result;
        }
      }

      throw new Error("llm_route_exhausted");
    } catch (error) {
      routeSpan.recordException(error as Error);
      routeSpan.setStatus({
        code: SpanStatusCode.ERROR,
        message: (error as Error).message,
      });
      throw error;
    } finally {
      routeSpan.end();
    }
  });
}

En production, ajoutez vos compteurs de métriques et vos événements de logs structurés en parallèle des spans. Capturez également les identifiants de requête du fournisseur lorsqu’ils sont disponibles ; ils sont souvent essentiels lorsqu’il faut escalader un incident auprès d’un fournisseur de modèle. Conservez ces identifiants hors des messages d’erreur publics.

Logs sans fuite de prompt

Le réglage par défaut le plus sûr est une journalisation axée d’abord sur les métadonnées.

Journaliser par défaut

  • Identifiants internes de requête et de trace
  • ID de requête du fournisseur
  • Nom de la fonctionnalité et de la route
  • Fournisseur, modèle et alias de déploiement
  • Version du modèle de prompt
  • Paramètres tels que la température et le nombre maximal de jetons de sortie
  • Utilisation des jetons
  • Latence et temps jusqu’au premier jeton
  • Classe d’erreur et décision de réessai
  • Résultat du validateur
  • Noms d’outils assainis

Ne pas journaliser par défaut

  • Prompts ou réponses bruts
  • Clés API ou en-têtes d’autorisation
  • Secrets clients
  • Documents récupérés
  • Arguments d’outils contenant des données personnelles ou réglementées
  • Chemins de fichiers complets ou enregistrements de base de données
  • URL signées

Lorsque la capture de contenu est nécessaire pour le débogage ou l’évaluation, échantillonnez-la séparément, expurgez-la avant stockage, restreignez l’accès, chiffrez-la et définissez une courte période de conservation. Le guide de gestion sécurisée des clés API couvre les contrôles associés pour les secrets, la journalisation, la rotation et la réponse aux incidents.

Des SLO pour les fonctionnalités reposant sur des LLM

Un objectif de niveau de service pour un LLM doit décrire la fonctionnalité visible par l’utilisateur, et non le compte du fournisseur.

Exemples de SLO pour une fonctionnalité de réponse d’assistance structurée :

SLO Objectif exemple
Disponibilité validée 99,5 % des requêtes éligibles renvoient une sortie conforme au contrat
Latence interactive 95 % produisent le premier jeton en moins de 1,5 seconde
Latence d’achèvement 95 % se terminent en moins de 8 secondes
Garde-fou de coût 99 % restent en dessous du plafond de coût par requête
Confinement du repli Moins de 3 % nécessitent un repli au cours d’une heure glissante

Ces chiffres sont des exemples, pas des objectifs universels. Définissez-les à partir des attentes des utilisateurs, de la complexité de la tâche, du comportement du fournisseur et des coûts unitaires.

Utilisez les budgets d’erreur pour décider quand ralentir les mises en production de fonctionnalités, renforcer la politique de route ou déplacer le trafic. Un fournisseur peut atteindre son propre objectif de disponibilité tandis que votre produit manque son SLO parce que la mise en file d’attente, les outils, la validation ou le comportement de repli ajoutent des échecs.

Alerter sur les symptômes, diagnostiquer avec les causes

Alertez un opérateur en cas d’impact utilisateur. Utilisez des alertes de gravité inférieure ou des annotations de tableau de bord pour les causes probables.

Symptômes justifiant une alerte

  • Le taux de succès validé enfreint le SLO
  • Le p95 du temps jusqu’au premier jeton dépasse le seuil visible par l’utilisateur
  • Le taux de routes épuisées augmente fortement
  • Le coût par tâche acceptée dépasse le garde-fou
  • Une fonctionnalité critique n’a aucune cible saine compatible avec le contrat

Signaux de diagnostic

  • Le taux de 429 d’un fournisseur augmente
  • Le taux d’échec de schéma d’un modèle change
  • L’amplification des réessais augmente
  • Le temps d’attente en file augmente
  • Le disjoncteur s’ouvre
  • L’utilisation des jetons évolue après une nouvelle version du prompt

Évitez d’alerter pour chaque 5xx d’un fournisseur. Si le repli fonctionne et que les utilisateurs reçoivent toujours des réponses valides dans le budget de latence, l’événement peut nécessiter une investigation sans réveiller l’ingénieur d’astreinte.

Un modèle opérationnel à trois tableaux de bord

Tableau de bord 1 : expérience utilisateur

Affichez la disponibilité validée, la latence, le temps jusqu’au premier jeton, l’achèvement des tâches et les erreurs visibles par l’utilisateur par fonctionnalité.

Tableau de bord 2 : routage et fournisseurs

Afficher la part du trafic, les erreurs des fournisseurs, les retries, les fallbacks, les coupe-circuits, l’épuisement des routes et la latence par modèle et cible.

Tableau de bord 3 : usage et économie

Afficher les tokens, les dépenses estimées, les dépenses rapprochées, le coût par tâche acceptée, l’écart budgétaire et les principales fonctionnalités par coût.

Conservez les annotations de déploiement et de version de prompt sur les trois tableaux. Sinon, une régression qui commence immédiatement après une mise en production peut ressembler à une variance aléatoire du fournisseur.

Liste de contrôle du déploiement

  1. Définir un schéma d’événement versionné unique.
  2. Générer un identifiant de requête interne à la frontière du produit.
  3. Propager le contexte de trace à travers les files d’attente, les outils et les appels au modèle.
  4. Créer un span enfant pour chaque tentative de modèle.
  5. Enregistrer les identifiants de requête du fournisseur lorsqu’ils sont renvoyés.
  6. Ajouter une validation déterministe des sorties.
  7. Suivre explicitement les raisons des retries et des fallbacks.
  8. Calculer le coût estimé à partir d’une table de prix versionnée.
  9. Rapprocher les estimations avec l’utilisation autoritative ou les exports de facturation.
  10. Construire un tableau de bord des résultats utilisateur avant les tableaux de bord des fournisseurs.
  11. Définir un SLO pour le succès validé et la latence.
  12. Masquer ou exclure les prompts, les réponses, les secrets et les données sensibles des outils.
  13. Exécuter des tests d’échec pour les timeouts, 429, 5xx, sorties mal formées et l’épuisement des routes.
  14. Vérifier la cardinalité des labels avant d’activer les métriques en production.
  15. Échantillonner les traces selon le risque : conserver davantage les erreurs et les requêtes lentes que les succès de routine.

Erreurs courantes d’observabilité

Considérer chaque 200 comme un succès

Ajoutez des validateurs de contrat et signalez le succès validé séparément.

Journaliser les prompts bruts pour chaque requête

Cela crée des problèmes de confidentialité, de sécurité, de rétention et de coût. Préférez les métadonnées et un échantillonnage contrôlé.

Masquer les retries dans une seule durée

Créez un span et un événement par tentative afin que les opérateurs puissent voir l’amplification.

Utiliser les noms de modèle comme seule identité de route

Suivez le fournisseur, l’alias de déploiement ou de compte, la région et la politique de routage. Un même modèle peut se comporter différemment selon les cibles.

Alerter sur la latence moyenne

Les moyennes masquent les problèmes de queue de distribution. Utilisez p95 et p99, et séparez le temps jusqu’au premier token du temps total de complétion.

Faire confiance indéfiniment au coût estimé

Les tables de prix, le traitement du cache et la comptabilité du fournisseur peuvent changer. Rapprochez les estimations des données de facturation et enregistrez la version de la table de prix.

Permettre aux labels de télémétrie de croître sans limites

Les identifiants de requête et les identifiants client doivent figurer dans les traces ou les logs, pas dans les labels des métriques.

Où une passerelle d’API IA aide

Les applications multi-fournisseurs ont sinon besoin d’adaptateurs séparés pour l’authentification, le nommage des modèles, les retries, les champs d’usage, les erreurs et les exports de facturation. Une passerelle peut réduire cette surface d’intégration en donnant à l’application une frontière d’API stable tout en conservant les détails du fournisseur et du modèle dans la télémétrie interne.

Flatkey fournit une clé API unique, un endpoint compatible avec OpenAI et un accès aux modèles des principaux fournisseurs. Cela permet de centraliser le contrat de télémétrie côté application même lorsque les charges de travail utilisent différents modèles de texte, d’image ou de vidéo. La passerelle ne remplace pas l’observabilité au niveau produit : votre application doit toujours enregistrer la fonctionnalité, la version du prompt, le résultat de validation, la latence visible par l’utilisateur et le résultat de la tâche acceptée.

Si votre équipe consolide des fournisseurs, commencez par le guide d’architecture de passerelle d’API IA, puis ajoutez le contrat de télémétrie de cet article avant de transférer le trafic de production.

Questions fréquemment posées

Qu’est-ce que l’observabilité des API LLM ?

L’observabilité des API LLM est la collecte et la corrélation des métriques, des traces, des logs, des contrôles de qualité, de l’utilisation et des données de coût pour les fonctionnalités alimentées par un modèle. Elle explique à la fois le comportement du fournisseur et si l’application a renvoyé un résultat utilisateur valide.

Que dois-je surveiller pour une API LLM ?

Surveillez le taux de réussite validée, la latence de bout en bout, le temps jusqu’au premier jeton, la latence du fournisseur, les taux de 429 et 5xx, les nouvelles tentatives, les basculements, l’utilisation des jetons, le coût estimé, le coût par tâche acceptée et les échecs du contrat de sortie.

Les prompts et les réponses doivent-ils être stockés dans les traces ?

Pas par défaut. Stockez d’abord les métadonnées. Ne capturez le contenu que pour un objectif défini de débogage ou d’évaluation, avec anonymisation, contrôles d’accès, chiffrement, échantillonnage et politique de rétention.

Quelle est la différence entre la surveillance LLM et l’observabilité LLM ?

La surveillance vous indique qu’une métrique connue a dépassé un seuil. L’observabilité vous fournit suffisamment d’éléments corrélés pour enquêter sur de nouveaux modes de défaillance à travers l’application, la route, le fournisseur, le modèle, les outils et le contrat de sortie.

Comment calculer le coût par requête d’une API LLM ?

Multipliez les unités facturables d’entrée, de sortie, d’entrée mise en cache, de médias ou autres par un barème de prix versionné, puis ajoutez le coût des tentatives de nouvelle exécution et de secours. Réconciliez l’estimation avec les données de facturation du fournisseur ou de la passerelle.

Quel ID de requête dois-je stocker ?

Créez votre propre ID de requête interne et votre propre ID de trace, puis stockez également l’ID de requête du fournisseur lorsque l’API en renvoie un. Les ID internes relient vos systèmes ; l’ID du fournisseur aide pour le support externe et l’escalade d’incident.

Construisez le contrat de télémétrie avant l’incident

Le meilleur moment pour décider de ce qu’un appel de modèle doit enregistrer est avant l’arrivée du trafic de production. Commencez par la réussite validée, les distributions de latence, un span par tentative, des libellés de métriques bornés, des logs axés d’abord sur les métadonnées et le coût par tâche acceptée. Testez ensuite le système en provoquant les échecs que vous attendez du routeur.

Cette base transforme un rapport vague — « la fonctionnalité IA est lente et coûteuse » — en une décision traçable : quelle fonctionnalité, quelle route, quel modèle, quelle tentative, quel échec, combien de délai et combien de coût.

Découvrez les tarifs Flatkey lorsque vous êtes prêt à comparer des routes multi-modèles derrière une seule API compatible OpenAI.