Migrer des applications Node.js avec le SDK OpenAI : Base URL, streaming et sorties structurées
Un guide technique pour les développeurs Node.js migrant leurs applications avec le SDK OpenAI : configuration d'une base URL personnalisée, gestion de l'authentification, streaming de sorties structurées et diagnostic des erreurs d'API.
Sur cette page
Introduction
Migrer une application Node.js existante qui utilise le SDK officiel openai ne nécessite pas de réécrire votre logique métier. Dans la mesure où de nombreux fournisseurs de modèles unifiés respectent scrupuleusement les spécifications REST d'OpenAI, adapter une application existante pour transiter par une couche de routage compatible OpenAI — telle que OneMux — requiert uniquement d'ajuster les paramètres d'initialisation de votre client.
Cependant, les pipelines en production reposent fréquemment sur des fonctionnalités avancées : respect strict des schémas pour l'extraction de données, streaming token par token et reprise robuste sur erreur. Ce guide explique comment reconfigurer le SDK OpenAI pour Node.js, exécuter une extraction structurée via JSON Schema, traiter les réponses en streaming de façon fiable et diagnostiquer les erreurs d'exécution.
Étape 1 : Configuration de la Base URL et de l'authentification
Le SDK officiel OpenAI pour Node.js (openai v4+) accepte des surcharges de configuration directement dans le constructeur de la classe OpenAI. Pour migrer votre couche de transport vers un point de terminaison compatible OpenAI, configurez la propriété baseURL et définissez votre clé d'API.
Initialisation du client SDK
import OpenAI from "openai";
// Initialize the client pointing to an OpenAI-compatible gateway
const client = new OpenAI({
apiKey: process.env.ONEMUX_API_KEY,
baseURL: "https://api.onemux.net/v1",
});
Prérequis de migration
- Variables d'environnement : Veillez à bien séparer les clés de vos fournisseurs dans votre configuration
.env(par exemple,ONEMUX_API_KEYcontreOPENAI_API_KEY). - Suffixe de chemin : Le SDK OpenAI ajoute automatiquement les chemins de route comme
/chat/completionsà labaseURL. Assurez-vous systématiquement que votrebaseURLinclut le suffixe/v1(ex.https://api.onemux.net/v1), à moins que votre passerelle ne prenne en charge le routage racine. - Version du SDK : Vérifiez que vous utilisez
openaien version^4.0.0ou supérieure, car les anciennes versions v3 s'appuyaient sur une structure de configuration différente (la classeConfiguration).
Pour consulter les règles de routage prises en charge et démarrer avec vos identifiants d'API, rendez-vous sur le Guide de démarrage rapide OneMux.
Étape 2 : Extraction structurée avec JSON Schema strict
Les sorties structurées forcent les modèles à renvoyer un résultat strictement conforme au schéma JSON fourni. Selon la documentation d'OpenAI relative aux sorties structurées, définir "strict": true impose que :
- Tous les champs de l'objet soient répertoriés dans le tableau
required. additionalProperties: falsesoit explicitement défini sur tous les schémas d'objets.- Les champs optionnels soient modélisés sous forme de types union avec
null(ex.["string", "null"]).
Implémentation d'une complétion structurée standard
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.ONEMUX_API_KEY,
baseURL: "https://api.onemux.net/v1",
});
// Define strict JSON Schema for invoice extraction
const invoiceExtractionSchema = {
type: "object",
properties: {
vendorName: { type: "string" },
invoiceId: { type: "string" },
totalAmount: { type: "number" },
currency: { type: "string" },
items: {
type: "array",
items: {
type: "object",
properties: {
description: { type: "string" },
quantity: { type: "number" },
unitPrice: { type: "number" },
},
required: ["description", "quantity", "unitPrice"],
additionalProperties: false,
},
},
},
required: ["vendorName", "invoiceId", "totalAmount", "currency", "items"],
additionalProperties: false,
} as const;
async function extractInvoice(rawDocumentText: string) {
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "Extract normalized invoice records from the provided unstructured input.",
},
{
role: "user",
content: rawDocumentText,
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "invoice_extraction",
strict: true,
schema: invoiceExtractionSchema,
},
},
});
const choice = response.choices[0];
// Check for safety or policy refusals
if (choice.message.refusal) {
throw new Error(`Model refused extraction: ${choice.message.refusal}`);
}
const rawJson = choice.message.content;
if (!rawJson) {
throw new Error("Received empty response payload");
}
return JSON.parse(rawJson);
}
La compatibilité avec les passerelles sous-jacentes dépend de la prise en charge ou non du mode json_schema avec strict: true par le point de terminaison du fournisseur sélectionné. Pour plus de précisions sur les fonctionnalités des modèles selon les plateformes, consultez le Catalogue des modèles OneMux.
Étape 3 : Gestion du streaming avec des sorties structurées
Lorsque stream: true est activé en conjonction avec response_format, le modèle diffuse des fragments de chaînes partielles du payload JSON. À la différence d'un chat textuel standard, les blocs individuels ne peuvent pas être directement analysés avec JSON.parse(), car ils correspondent à des jetons JSON incomplets.
Accumulation des blocs JSON reçus en streaming
Pour diffuser correctement des sorties structurées, accumulez les fragments de deltas jusqu'à la fin du flux, ou redirigez les blocs entrants vers un parseur de flux JSON incrémental.
async function streamInvoiceExtraction(rawDocumentText: string) {
const stream = await client.chat.completions.create({
model: "gpt-4o",
stream: true,
messages: [
{
role: "system",
content: "Extract normalized invoice records from the provided text.",
},
{
role: "user",
content: rawDocumentText,
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "invoice_extraction",
strict: true,
schema: invoiceExtractionSchema,
},
},
});
let accumulatedJson = "";
let refusalReason: string | null = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.refusal) {
refusalReason = (refusalReason || "") + delta.refusal;
}
if (delta?.content) {
accumulatedJson += delta.content;
// Emit partial events or log progress if building a reactive UI
}
}
if (refusalReason) {
throw new Error(`Extraction was refused during stream: ${refusalReason}`);
}
try {
return JSON.parse(accumulatedJson);
} catch (err) {
throw new Error(`Failed to parse streamed JSON payload: ${accumulatedJson}`);
}
}
Si votre pipeline exploite le streaming uniquement pour réduire le délai d'affichage du premier octet (TTFB) dans les interfaces utilisateur, vérifiez que le point de terminaison sous-jacent prend bien en charge les événements Server-Sent Events (SSE) continus à travers votre proxy réseau.
Étape 4 : Diagnostic des erreurs et dépannage
Lors de la migration d'appels SDK vers une baseURL personnalisée, le diagnostic d'erreurs doit faire la distinction entre les défaillances de proxy réseau, les schémas mal configurés, les limites de débit et les refus du modèle.
Erreurs fréquentes et stratégies de résolution
| Code / Type d'erreur | Cause racine | Diagnostic et résolution |
|---|---|---|
401 Unauthorized | Clé d'API invalide ou manquante | Vérifiez que votre apiKey contient un identifiant valide reconnu par la passerelle ciblée (par exemple, contrôlez ONEMUX_API_KEY). |
400 BadRequestError | Violation de schéma (strict: true) | Inspectez le message d'erreur pour détecter les mots-clés de schéma JSON non supportés (ex. absence de additionalProperties: false ou champs manquants dans required). |
404 NotFound | Chemin ou identifiant de modèle incorrect | Assurez-vous que la baseURL se termine par /v1. Vérifiez que le modèle demandé est bien présent dans la liste des modèles actifs du fournisseur. |
429 RateLimitError | Plafond de concurrence ou de budget atteint | Implémentez un backoff exponentiel. Surveillez vos limites de quota depuis le tableau de bord de votre fournisseur. |
Refusal (Corps du choice) | Garde-fous de sécurité du modèle déclenchés | Le modèle a émis un refus explicite. Inspectez choice.message.refusal plutôt que de lire content. |
Enveloppeur structuré pour la gestion d'erreurs
import OpenAI from "openai";
async function safeExtractWrapper(fn: () => Promise<any>) {
try {
return await fn();
} catch (error: any) {
if (error instanceof OpenAI.APIError) {
console.error(`OpenAI API Error: status=${error.status}, type=${error.type}`);
console.error(`Message: ${error.message}`);
if (error.status === 400) {
console.error("Verify that your schema complies strictly with JSON Schema constraints.");
} else if (error.status === 401) {
console.error("Authentication failed: Check your bearer token and proxy baseURL.");
} else if (error.status === 429) {
console.warn("Rate limit reached. Queue request for delayed retry.");
}
} else {
console.error("Unexpected application error:", error);
}
throw error;
}
}
Étape 5 : Consolider les fournisseurs via OneMux
Routier vos requêtes SDK OpenAI via OneMux simplifie les opérations multi-modèles sans imposer de bibliothèques tierces ni d'adaptateurs de protocole sur mesure.
- Point de terminaison unifié : Conservez une interface client unique tout en routant vos requêtes vers divers modèles grâce à la sémantique standard du SDK OpenAI.
- Visibilité et gestion des dépenses : Centralisez la facturation, suivez l'utilisation par modèle et définissez des règles de dépenses sans devoir gérer de multiples factures de prestataires.
- Paiement à l'usage flexible : Approvisionnez votre compte via des recharges de crédits souples afin de prévenir toute interruption imprévue de service.
Pour connaître les tarifs en vigueur et comparer les options de routage entre modèles, consultez le Guide des tarifs OneMux ainsi que la présentation technique dans la Documentation OneMux.
Sources
FAQ
Puis-je utiliser Zod avec le SDK OpenAI lors de l'utilisation d'une base URL personnalisée ?
Oui. Dès lors que votre passerelle implémente intégralement la spécification des sorties structurées pour `/chat/completions`, les méthodes utilitaires telles que `client.beta.chat.completions.parse()` et `zodResponseFormat()` fonctionnent sans friction en traduisant les schémas Zod dans le format JSON Schema strict attendu.
Pourquoi le modèle renvoie-t-il une erreur 400 lors de l'utilisation de 'strict: true' ?
Le moteur de sorties structurées d'OpenAI exige que chaque définition d'objet comporte explicitement `additionalProperties: false` et référence l'ensemble des propriétés déclarées dans le tableau `required`. Si une clé manque dans `required`, ou si des mots-clés de schéma JSON non supportés sont présents, l'API rejette la requête avec une erreur de validation 400.
Le changement de la baseURL affecte-t-il la télémétrie ou les délais d'expiration (timeouts) du SDK ?
La surcharge de `baseURL` redirige l'intégralité du transport HTTP vers la nouvelle URL. Les options de délai d'attente et de nouvelle tentative (comme `maxRetries` ou `timeout` dans le constructeur OpenAI) continuent de fonctionner à l'identique côté client.
Articles liés
Guides
Coût et guide du contexte de GPT 6.1 Terra pour une extraction JSON fiable
Découvrez comment estimer le coût des charges GPT 6.1 Terra et définir des limites pour le contexte, les nouvelles tentatives et les jetons de sortie lors d’une extraction structurée avec validation JSON Schema via OneMux.
Guides
GPT-5.6 Terra : pourquoi la baisse du prix des tokens d'entrée change votre stratégie token
OpenAI a baissé les prix d'entrée de GPT-5.6 Terra et Luna. Découvrez comment les tokens à faible coût affectent la compression de prompts, les dépenses API et le routage — et comment OneMux simplifie l'accès.
Guides
GPT-5.6 Sol pour le support ecommerce : comment exploiter l'API LLM la plus récente pour le service client
Découvrez comment GPT-5.6 Sol, le dernier LLM d'OpenAI, peut transformer votre support ecommerce avec l'API unifiée d'OneMux. Explorez les tarifs, l'intégration et les cas d'usage pratiques.
Guides
OpenAI assouplit les limites de GPT-5.6 Sol : ce que les indie hackers doivent savoir sur l'accès à l'API IA
OpenAI a récemment assoupli les limites de débit sur GPT-5.6 Sol. Mais ne parlez pas d'illimité. Découvrez comment les indie hackers peuvent tirer parti de GPT-5.6 Luna via OneMux pour un accès flexible et économique à l'API IA.