Erreurs
Les erreurs typées que lève le SDK, les trois 402 différents, et tous les statuts et codes que renvoie l'API.
Le SDK lève trois sortes d'erreurs.
PaymentRequiredError
Levée quand le proxy ou getBalance() répond 402. Porte le lien de rechargement, quand
il y en a un.
import { PaymentRequiredError } from "@fleeex/sdk";
try {
await client.chat.completions.create({ model, messages });
} catch (err) {
if (err instanceof PaymentRequiredError) {
if (err.topupUrl) redirect(err.topupUrl);
else showSupportMessage(); // pas de lien, voir plus bas
}
}| Propriété | Type | Notes |
|---|---|---|
status | 402 | Toujours 402. |
code | string | undefined | Lequel des trois 402 il s'agit. |
topupUrl | string | undefined | Lien signé et de courte durée pour faire passer l'utilisateur final par le Checkout. Réellement optionnel : vérifiez-le toujours avant de rediriger. |
correlationId | string | undefined | Identifiant de trace pour le support et le débogage. |
message | string | Générique par conception, et ne révèle jamais l'existence d'un portefeuille. |
Le hook onPaymentRequired (voir les options) se
déclenche sur la même condition, avant que l'erreur ne soit levée.
Les trois 402
Le 402 ne recouvre pas une seule situation. Le code dit laquelle, et les trois
appellent des remèdes différents. Faire un branchement dessus, c'est la différence entre
dire à un utilisateur d'ajouter des fonds et lui annoncer une chose que payer ne réglera
pas.
| Code | Signification | topupUrl | Que faire |
|---|---|---|---|
PAYMENT_REQUIRED | Le couple (app, utilisateur) n'a pas de portefeuille autorisé, ou le solde ne couvre pas l'appel. | toujours | Envoyez l'utilisateur final vers topupUrl pour autoriser ou approvisionner. |
APP_SPEND_CAP_EXCEEDED | Votre application a atteint le plafond mensuel de dépense que l'utilisateur lui a fixé, et son portefeuille peut être encore plein. | le plus souvent | Dites-lui que cette application a atteint le plafond que vous avez fixé, qu'il peut relever, plutôt que « ajoutez des fonds ». Le plafond est vérifié une fois en amont puis revérifié à l'écriture de la facturation ; la seconde vérification perd rarement une course, et lève alors le même code sans lien. |
WALLET_SUSPENDED | Le portefeuille est bloqué après une sortie d'argent (un remboursement ou une opposition que le solde ne pouvait pas couvrir). | jamais | Arrêtez de réessayer et orientez l'utilisateur final vers le support. Approvisionner le portefeuille ne lève pas le blocage. |
Traitez donc topupUrl comme optionnel sur n'importe quel 402, quel que soit le code,
car deux des trois peuvent arriver sans.
Les deux premiers sont délibérément indiscernables l'un de l'autre dans le message : « pas de portefeuille » et « portefeuille vide » renvoient un corps identique, car les distinguer révélerait si un utilisateur donné possède un portefeuille Fleeex. Un utilisateur qui a révoqué votre application est indiscernable d'un utilisateur qui ne l'a jamais autorisée, pour la même raison.
WALLET_SUSPENDED ne porte ni montant, ni manque à couvrir, ni formulation de litige.
Votre application n'y est pas partie, et l'historique de paiement de l'utilisateur final
ne la regarde pas.
FleeexApiError
Tout autre code non 2xx renvoyé par une aide de facturation Fleeex (getConnection(),
getUsageSummary(), ou un code autre que 402 sur getBalance()), par exemple un 400
quand un redirectUri n'est pas dans la liste autorisée de l'application.
| Propriété | Type | Notes |
|---|---|---|
status | number | Statut HTTP. |
code | string | undefined | Code d'erreur issu de l'enveloppe. |
correlationId | string | undefined | Identifiant de trace. |
Erreurs OpenAI
Sur le chemin du proxy, toute erreur autre qu'un 402 est levée par le SDK OpenAI sous
forme d'OpenAI.APIError (et de ses sous-classes). Traitez-les exactement comme vous le
feriez avec le SDK OpenAI, mais lisez le code, car plusieurs sont propres à Fleeex et ont
des significations distinctes.
import OpenAI from "openai";
import { PaymentRequiredError } from "@fleeex/sdk";
try {
await client.chat.completions.create({ model, messages });
} catch (err) {
if (err instanceof PaymentRequiredError) {
if (err.topupUrl) redirect(err.topupUrl);
} else if (err instanceof OpenAI.APIError) {
console.error(err.status, err.code, err.message);
} else {
throw err;
}
}Tous les statuts et codes
Toute la surface du plan de données : appels relayés, GET /v1/models et les aides de
facturation.
| Statut | Code | Quand |
|---|---|---|
400 | BAD_REQUEST | Un model non pris en charge ou désactivé (le message renvoie en écho ce que vous avez envoyé, pas la cible d'un alias). Un paramètre non pris en charge, nommé, avec la liste des paramètres acceptés. Un corps malformé, ou imbriqué sur plus de 32 niveaux. Un x-fleeex-user absent ou malformé. Un redirectUri hors de la liste autorisée de l'application. |
401 | UNAUTHORIZED | La clé d'API est absente, invalide ou révoquée. |
402 | PAYMENT_REQUIRED | Pas de portefeuille autorisé, ou solde insuffisant. Porte toujours topupUrl. |
402 | APP_SPEND_CAP_EXCEEDED | Le plafond mensuel par application. Porte le plus souvent topupUrl, voir plus haut. |
402 | WALLET_SUSPENDED | Le portefeuille est bloqué après une reprise de fonds. Ne porte jamais topupUrl. |
403 | APP_SUSPENDED | Votre application a été suspendue par un opérateur Fleeex. Délibérément pas un 401 : l'identifiant est valide et nomme correctement votre application, donc faire tourner une clé ou réessayer ne change rien. Cessez d'appeler. Un flux en cours n'est pas coupé, mais la requête suivante est refusée. |
403 | MODEL_NOT_ENTITLED | Le modèle existe mais il est réservé à d'autres applications, voir Choisir un modèle. L'appel n'atteint aucun fournisseur et ne réserve rien, et GET /v1/models l'omet purement et simplement : un appelant qui liste d'abord ne rencontre donc jamais ce cas. |
404 | NOT_FOUND | GET /v1/models/{model} quand aucun modèle ni alias de ce nom n'est servi. |
413 | PAYLOAD_TOO_LARGE | Le corps de la requête dépasse 1 Mo. |
429 | TOO_MANY_REQUESTS | La limite de débit de Fleeex par application. Elle compte au niveau de votre application : toutes ses clés, y compris une clé de bac à sable, partagent une seule limite. |
429 | CONCURRENT_STREAM_LIMIT | Cet utilisateur final a déjà le nombre maximal de flux ouverts. Envoyé avec un en-tête Retry-After, et refusé avant toute réservation et tout appel au fournisseur : il ne coûte donc rien. Réessayez quand l'un des flux de cet utilisateur se termine ; le remède est de réduire le nombre de flux ouverts, pas la cadence des requêtes. |
429 | UPSTREAM_THROTTLED | Le fournisseur de modèle a bridé l'appel. Réessayable, contrairement à UPSTREAM_PROVIDER_ERROR. |
502 | UPSTREAM_PROVIDER_ERROR | Le fournisseur a rejeté l'appel (accès au modèle, invocation non prise en charge, schéma hors de son sous-ensemble, dépassement de délai, panne). La réservation est libérée et rien n'est facturé. |
500 | INTERNAL_SERVER_ERROR | Une faute de Fleeex. Le détail est journalisé côté serveur en regard du correlationId ; la réponse n'en dit pas plus. |
Un appel refusé ne coûte rien : un 400, un 402, un 403 ou la limite de flux
court-circuitent avant d'atteindre le fournisseur, si bien qu'aucune complétion n'est
jamais générée puis jetée.
Enveloppe des erreurs
Le backend de Fleeex émet un corps cohérent, exposé sous le type FleeexErrorBody.
interface FleeexErrorBody {
error?: { code?: string; message?: string; correlationId?: string };
topupUrl?: string;
}correlationId est la seule chose qui mérite d'être journalisée de votre côté : c'est la
prise qui relie votre appel en échec à sa trace côté serveur. Il est renvoyé en écho sur
chaque réponse, et pas seulement sur les erreurs, et vous pouvez fournir le vôtre via un
en-tête de requête x-correlation-id.
Un message n'est jamais une trace de pile, une erreur de SDK cloud ou un identifiant
interne, et pour un 500, jamais rien d'autre que le texte générique ci-dessus.