GuidesOneMux

Migration von Node.js-Anwendungen mit dem OpenAI-SDK: Base-URL, Streaming und Structured Outputs

Ein technischer Leitfaden für Node.js-Entwickler zur Migration von OpenAI-SDK-Anwendungen: Konfiguration benutzerdefinierter Base-URLs, Authentifizierung, Streaming von Structured Outputs und Diagnose von API-Fehlern.

Auf dieser Seite

Einführung

Die Migration einer bestehenden Node.js-Anwendung, die das offizielle openai-SDK verwendet, erfordert kein Umschreiben Ihrer Geschäftslogik. Da viele Anbieter vereinheitlichter Modellplattformen die OpenAI-REST-Spezifikation exakt einhalten, genügt für die Umstellung auf eine OpenAI-kompatible Routing-Schicht – wie OneMux – lediglich die Anpassung der Initialisierungsparameter Ihres Clients.

In Produktionspipelines kommen jedoch häufig erweiterte Funktionen zum Einsatz: strikte Schemakonformität bei der Datenextraktion, Token-weises Streaming und eine verlässliche Fehlerbehandlung. Dieser Leitfaden beschreibt, wie Sie das Node.js OpenAI-SDK rekonfigurieren, strukturierte JSON-Schema-Extraktionen durchführen, Streaming-Antworten zuverlässig verarbeiten und Ausführungsfehler diagnostizieren.


Schritt 1: Konfiguration von Base-URL und Authentifizierung

Das offizielle OpenAI Node.js-SDK (openai v4+) ermöglicht Konfigurationsüberschreibungen direkt im Konstruktor der OpenAI-Klasse. Um Ihre Transportschicht auf einen OpenAI-kompatiblen Endpunkt zu migrieren, konfigurieren Sie die Eigenschaft baseURL und setzen Ihren API-Schlüssel.

SDK-Client-Initialisierung

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",
});

Voraussetzungen für die Migration

  1. Umgebungsvariablen: Achten Sie darauf, API-Schlüssel verschiedener Anbieter in Ihrer .env-Konfiguration getrennt zu halten (beispielsweise ONEMUX_API_KEY anstelle von OPENAI_API_KEY).
  2. Pfadsuffix: Das OpenAI-SDK hängt Routenpfade wie /chat/completions automatisch an die baseURL an. Stellen Sie sicher, dass Ihre baseURL das Suffix /v1 enthält (z. B. https://api.onemux.net/v1), sofern Ihr Gateway kein Root-Routing anwendet.
  3. SDK-Version: Prüfen Sie, ob Sie die Version openai ^4.0.0 oder höher einsetzen. Ältere v3-Versionen verwendeten eine abweichende Konfigurationsstruktur (Configuration-Klasse).

Informationen zu unterstützten Routing-Regeln und den ersten Schritten mit API-Zugangsdaten finden Sie im OneMux Quickstart.


Schritt 2: Strukturierte Extraktion mit striktem JSON-Schema

Structured Outputs zwingen Modelle dazu, Ausgaben exakt gemäß einem vorgegebenen JSON-Schema zu liefern. Gemäß der OpenAI-Dokumentation zu Structured Outputs erfordert das Setzen von "strict": true Folgendes:

  • Alle Objektfelder müssen im required-Array aufgeführt sein.
  • additionalProperties: false muss für alle Objektschemata explizit definiert werden.
  • Optionale Felder müssen als Union-Typen mit null abgebildet werden (z. B. ["string", "null"]).

Implementierung einer regulären strukturierten Completion

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);
}

Die Kompatibilität mit nachgelagerten Gateways hängt davon ab, ob der jeweilige Provider-Endpunkt den Modus json_schema mit strict: true unterstützt. Einzelheiten zu den Modellfähigkeiten auf verschiedenen Plattformen finden Sie im OneMux-Modellkatalog.


Schritt 3: Streaming mit Structured Outputs verarbeiten

Wenn stream: true in Kombination mit response_format aktiviert ist, streamt das Modell unvollständige String-Fragmente der JSON-Payload. Anders als bei einfachem Text-Chat können die einzelnen Chunks nicht direkt mit JSON.parse() verarbeitet werden, da sie unvollständige JSON-Tokens darstellen.

Akkumulieren gestreamter JSON-Chunks

Um strukturierte Ausgaben korrekt zu streamen, sammeln Sie die Delta-Fragmente an, bis der Stream abgeschlossen ist, oder leiten Sie eingehende Chunks durch einen inkrementellen JSON-Stream-Parser.

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}`);
  }
}

Falls Ihre Pipeline Streaming primär nutzt, um die Time-to-First-Byte (TTFB) in Benutzeroberflächen zu minimieren, stellen Sie sicher, dass der nachgelagerte Endpunkt kontinuierliche Server-Sent Events (SSE) durch Ihren Netzwerk-Proxy unterstützt.


Schritt 4: Fehlerdiagnose und Fehlerbehebung

Bei der Migration von SDK-Aufrufen auf eine benutzerdefinierte baseURL sollte die Fehlerdiagnose sauber zwischen Netzwerk-Proxy-Fehlern, Schemakonfigurationsfehlern, Ratenbegrenzungen und Verweigerungen durch das Modell unterscheiden.

Typische Fehler und Lösungsansätze

Fehlercode / TypUrsacheDiagnose & Behebung
401 UnauthorizedUngültiger oder fehlender API-SchlüsselStellen Sie sicher, dass Ihr apiKey gültige Zugangsdaten enthält, die vom Ziel-Gateway akzeptiert werden (z. B. ONEMUX_API_KEY prüfen).
400 BadRequestErrorSchema-Verletzung (strict: true)Prüfen Sie die Fehlermeldung auf nicht unterstützte JSON-Schema-Keywords (z. B. fehlendes additionalProperties: false oder nicht in required enthaltene Felder).
404 NotFoundFalscher Pfad oder falsche Modell-IDPrüfen Sie, ob die baseURL auf /v1 endet. Vergewissern Sie sich, dass das angeforderte Modell in der aktiven Modellliste des Anbieters existiert.
429 RateLimitErrorNebenläufigkeits- oder Ausgabenlimit erreichtImplementieren Sie exponentielles Backoff. Überwachen Sie Kontingentgrenzen über Ihr Provider-Dashboard.
Refusal (Choice-Body)Sicherheitsrichtlinien des Modells ausgelöstDas Modell hat die Ausgabe explizit verweigert. Prüfen Sie choice.message.refusal, anstatt content auszuwerten.

Strukturierter Wrapper zur Fehlerbehandlung

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;
  }
}

Schritt 5: Anbieter über OneMux konsolidieren

Die Weiterleitung von OpenAI-SDK-Anfragen über OneMux vereinfacht den Multi-Modell-Betrieb, ohne dass zusätzliche Drittanbieter-Bibliotheken oder eigene Protokolladapter erforderlich sind.

  • Einheitlicher Endpunkt: Behalten Sie eine konsistente Client-Schnittstelle bei, während Sie Anfragen an verschiedene Modelle mit Standard-OpenAI-SDK-Semantik weiterleiten.
  • Ausgabenverwaltung & Transparenz: Zentralisieren Sie die Abrechnung, analysieren Sie die Nutzung pro Modell und setzen Sie Budgetregeln durch, ohne separate Rechnungen mehrerer Anbieter verwalten zu müssen.
  • Flexibles Pay-as-You-Go: Laden Sie Ihr Konto über flexible Guthabenaufladungen auf und verhindern Sie so unerwartete Dienstunterbrechungen.

Aktuelle Nutzungstarife und Routing-Optionen für verschiedene Modelle finden Sie in der OneMux-Preisübersicht sowie in der technischen Übersicht der OneMux-Dokumentation.


Quellen

FAQ

Kann ich Zod mit dem OpenAI-SDK verwenden, wenn eine benutzerdefinierte Base-URL eingerichtet ist?

Ja. Sofern Ihr Gateway die Spezifikation für Structured Outputs unter `/chat/completions` vollständig implementiert, funktionieren Hilfsmethoden wie `client.beta.chat.completions.parse()` und `zodResponseFormat()` nahtlos, indem sie Zod-Schemas in die geforderte strikte JSON-Schema-Payload übersetzen.

Warum gibt das Modell bei der Verwendung von 'strict: true' einen 400-Fehler zurück?

Die Structured-Outputs-Engine von OpenAI verlangt, dass jede Objektdefinition explizit `additionalProperties: false` enthält und jede deklarierte Eigenschaft im `required`-Array aufgeführt ist. Fehlt ein Schlüssel in `required` oder werden nicht unterstützte JSON-Schema-Keywords übergeben, weist die API die Anfrage mit einem 400-Validierungsfehler ab.

Beeinflusst das Ändern der baseURL die Telemetrie oder Timeouts des SDKs?

Das Überschreiben von `baseURL` leitet den gesamten HTTP-Transport an die neue Adresse weiter. Timeout- und Wiederholungsoptionen (wie `maxRetries` oder `timeout` im OpenAI-Konstruktor) funktionieren auf Client-Seite unverändert weiter.

Ähnliche Artikel