EU GPT logo
EU GPT

Public preview — Deze API is in public preview. Endpoints, schemas en limieten kunnen wijzigen vóór general availability.

API

Streams afhandelen

SSE parsen zonder SDK — voor custom clients, edge functions en taalbindings die nog niet bestaan.

Als de officiële OpenAI-SDK in je stack past, gebruik die — hij regelt SSE-parsing, reconnection-edge-cases en getypeerde events voor je. Deze gids is voor de gevallen waarin je het zelf moet rollen: een Go-service, een Cloudflare Worker, een Rust-client, een server die de stream forward’t naar een browser.

Het wire-format#

event: message
data: {"type":"response.created","sequence_number":0, …}

event: message
data: {"type":"response.output_text.delta","sequence_number":1,"delta":"He"}

event: message
data: {"type":"response.output_text.delta","sequence_number":2,"delta":"llo"}

event: message
data: {"type":"response.completed","sequence_number":3, …}

Elke event is één of meer regels, gescheiden door \n. Events zijn van elkaar gescheiden door een lege regel (\n\n). De data:-regel is JSON; alles anders is metadata die je kunt negeren.

Browser: fetch + ReadableStream#

De fetch in de browser retourneert een ReadableStream voor text/event-stream-responses. De EventSource-API werkt hier niet omdat hij geen body of headers kan meesturen — gebruik fetch.

const response = await fetch("/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ input: "Tell me a story." }),
});

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}

const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  let idx;
  while ((idx = buffer.indexOf("\n\n")) !== -1) {
    const raw = buffer.slice(0, idx);
    buffer = buffer.slice(idx + 2);
    handleEvent(raw);
  }
}

function handleEvent(raw) {
  for (const line of raw.split("\n")) {
    if (line.startsWith("data: ")) {
      const event = JSON.parse(line.slice(6));
      if (event.type === "response.output_text.delta") {
        renderDelta(event.delta);
      } else if (event.type === "response.completed") {
        renderDone();
      } else if (event.type === "error") {
        renderError(event.message);
      }
    }
  }
}

Node / server-side#

import { fetch } from "undici"; // of built-in node fetch op 18+

const response = await fetch("https://chat.eugpt.ai/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.EUGPT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ input: "Hi", stream: true }),
});

const decoder = new TextDecoder();
let buffer = "";

for await (const chunk of response.body) {
  buffer += decoder.decode(chunk, { stream: true });
  let idx;
  while ((idx = buffer.indexOf("\n\n")) !== -1) {
    const raw = buffer.slice(0, idx);
    buffer = buffer.slice(idx + 2);
    for (const line of raw.split("\n")) {
      if (line.startsWith("data: ")) {
        const event = JSON.parse(line.slice(6));
        // handle event
      }
    }
  }
}

Python zonder SDK#

import json
import os
import httpx

def stream_response(input_text: str):
    with httpx.stream(
        "POST",
        "https://chat.eugpt.ai/v1/responses",
        headers={
            "Authorization": f"Bearer {os.environ['EUGPT_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={"input": input_text, "stream": True},
        timeout=httpx.Timeout(connect=10, read=120, write=10, pool=10),
    ) as response:
        response.raise_for_status()
        buffer = ""
        for chunk in response.iter_text():
            buffer += chunk
            while "\n\n" in buffer:
                raw, buffer = buffer.split("\n\n", 1)
                for line in raw.splitlines():
                    if line.startswith("data: "):
                        yield json.loads(line[6:])

Gebruik het als:

for event in stream_response("Tell me a haiku."):
    if event["type"] == "response.output_text.delta":
        print(event["delta"], end="", flush=True)

Forwarden naar een browser#

Als je server EU GPT proxy’t naar een browser, is het schoonste patroon om niet te parsen — alleen de bytes door te pipen. De browser regelt SSE-parsing zelf.

// Cloudflare Worker / edge function
const upstream = await fetch("https://chat.eugpt.ai/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${env.EUGPT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: await request.text(),
});

return new Response(upstream.body, {
  headers: {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive",
  },
});

Strip of herschrijf geen events — de OpenAI-SDK aan de browser-kant verwacht de canonieke shape.

Mid-stream-errors afhandelen#

Een stream kan eindigen met een error-event in plaats van response.completed. Controleer altijd het laatste event dat je hebt ontvangen. Een stream die eindigt na een delta maar zonder terminal event duidt op een netwerkfailure, geen succesvolle response — behandel het als retry-kandidaat.

let terminal = null;
for await (const event of events) {
  if (event.type === "response.completed" || event.type === "error") {
    terminal = event;
  }
  // ... handle other events
}

if (!terminal) {
  throw new Error("Stream closed without terminal event");
}
if (terminal.type === "error") {
  throw new Error(`Stream errored: ${terminal.code} ${terminal.message}`);
}