Eine Streaming-Anfrage kann nur dann sicher wiederholt werden, wenn drei Bedingungen gleichzeitig erfüllt sind: Es hat noch nichts den Client erreicht. Es wurde nichts Beobachtbares berechnet. Und die Anfrage enthält keinen serverseitigen Status. Nach dem ersten Ausgabeereignis ist es die richtige Entscheidung, den Fehler zu melden, anstatt die Anfrage zu wiederholen.
TokenLab wendet diese Regel auf seinem Gateway für das Responses API-Streaming an, sowohl über HTTP als auch über WebSocket. Der WebSocket-Pfad wurde am 28.09.2026 geändert, um dem HTTP-Verhalten zu entsprechen.
Warum sich ein Stream von einer normalen Anfrage unterscheidet
Ein Nicht-Streaming-Aufruf gibt einen Body oder einen Fehler zurück. Sie können den Fehler wiederholen, da Sie nichts erhalten haben.
Ein Stream liefert Ihnen Ausgaben, bevor die Anfrage abgeschlossen ist. Das erste Ausgabeereignis ist der Punkt ohne Wiederkehr. Wenn die Verbindung danach abbricht, halten Sie einen Teiltext in den Händen. Die Wiederholung der Anfrage bedeutet, dieselbe Antwort erneut zu generieren und doppelt dafür zu bezahlen. Möglicherweise duplizieren Sie auch einen Tool-Aufruf, den Ihr Agent bereits ausgeführt hat.
Der TokenLab-Streaming-Leitfaden drückt es direkt aus:
Nachdem das erste Ereignis eingetroffen ist, ist ein unterbrochener Stream unvollständig und wird nicht automatisch neu gestartet.
Ihr Client benötigt also ein lokales Status-Bit: saw_output. Es springt auf „true“, sobald eine Ausgabe Ihren Code erreicht. Jede Entscheidung zur Wiederholung liest zuerst dieses Bit.
Ein Stream, der ohne response.completed endet, ist ein Fehler. Gehen Sie nicht davon aus, dass der Text, den Sie haben, vollständig ist. Behandeln Sie die Ereignisse response.failed, response.incomplete und error.
Die Entscheidung zur Wiederholung im Detail
TokenLab wiederholt eine Anfrage einmal auf einer anderen verfügbaren Route, wenn alle folgenden Punkte zutreffen: Die Anfrage ist zustandslos. Nichts hat den Client erreicht. Für den fehlgeschlagenen Versuch wurde kein Ergebnis oder Verbrauch beobachtet. Und der Fehler ist entweder ein wiederholbares Ereignis vor der Ausgabe oder ein Upstream-Lesefehler vor dem ersten Ereignis. Pro Anfrage erfolgt maximal eine Wiederholung. Wenn der Ersatz ebenfalls vor der Ausgabe fehlschlägt, wird dieser Fehler nicht erneut wiederholt.
Quelle: TokenLab-Streaming-Leitfaden und Gateway-Verhalten, beobachtet am 28.09.2026.
| Fehlerpunkt | Wiederholt durch TokenLab? | Grund |
|---|---|---|
Wiederholbares Ereignis vor der Ausgabe (response.failed oder ein als wiederholbar markiertes error-Ereignis, wie z. B. ein überlasteter oder interner Upstream-Fehler) |
Ja, einmal, wenn die Anfrage zustandslos ist | Nichts hat den Client erreicht und es wurde kein Verbrauch beobachtet, daher ist eine zweite Ausführung unsichtbar. |
| Upstream-Stream-Abbruch (Lesefehler) vor dem ersten Ereignis | Ja, einmal, wenn die Anfrage zustandslos ist | Dasselbe Zeitfenster. Der Client hält keine Ausgabe und es gab keine Kosten. |
| Zweiter Fehler vor der Ausgabe, nach einer Wiederholung | Nein | Das Limit liegt bei einer Wiederholung pro Anfrage. |
| Jeder Fehler, nachdem die Ausgabe den Client erreicht hat | Nein | Der Client hält bereits Teiltext. Eine Wiederholung würde Ausgabe und Kosten duplizieren. |
Gespeicherte Antwort (store), Fortsetzung (previous_response_id) oder eine ursprungsgebundene Anfrage |
Nein | Eine zweite Ausführung könnte eine zweite gespeicherte Antwort erstellen oder den Konversationsstatus verfälschen. |
| Timeout beim ersten Ereignis | Nein | Der Upstream generiert möglicherweise noch. Eine Wiederholung könnte dieselbe Arbeit zweimal ausführen, während der erste Versuch weiterläuft. |
| Pufferüberlauf vor der Ausgabe | Nein | Das Limit ist lokal auf dem Gateway. Dasselbe zu große Präfix würde auf der nächsten Route sehr wahrscheinlich erneut darauf stoßen. |
| Client getrennt | Nein | Der Client hat aufgehört zuzuhören. |
| Deterministischer Fehler, z. B. eine ungültige Anfrage | Nein | Eine Wiederholung kann das Ergebnis nicht ändern. Unverändert zugestellt. |
| Fehler, der bereits Verbrauch verursacht hat | Nein | Der Versuch wurde berechnet. Unverändert zugestellt. |
| Keine andere Route mehr verfügbar | Nein | Es gibt keinen Ort, an den sie gesendet werden könnte. Der Client erhält den Fehler mit seinem eigenen Code. |
Wenn ein Fehler nicht wiederholt wird oder keine andere Route verfügbar ist, erhalten Sie ihn mit seinem eigenen Fehlercode. Öffentliche Beispiele: stream_read_error, wenn der Upstream-Stream unterbrochen wurde, und upstream_stream_buffer_limit bei Pufferüberlauf. Wenn die Routenauswahl selbst nach einer Wiederholungsentscheidung fehlschlägt, endet der WebSocket-Turn mit websocket_response_failed (Status 500) und die reservierte Gebühr wird erstattet.
Die Abrechnung folgt derselben Linie. Sie zahlen nur für den zugestellten Versuch. Eine wiederholte Anfrage wurde möglicherweise zweimal im Upstream ausgeführt, und diese zusätzlichen Upstream-Kosten trägt TokenLab, da Sie aus dem ersten Versuch nichts erhalten haben. Ein fehlgeschlagener Turn, der nichts liefert, wird erstattet.
Ein Timing-Detail ist für Ihre Fehlerbehandlung wichtig. Bevor die Ausgabe beginnt, hält das Gateway response.created und response.in_progress bis zum ersten Ausgabeereignis oder einem Fehler für maximal 10 Sekunden. Diese gehaltenen Ereignisse erreichen Sie dann zusammen mit der ersten Ausgabe oder mit dem terminalen Ereignis. Reihenfolge und Inhalt sind unverändert. Sie sehen sie nur etwas später. Diese 10 Sekunden sind ein Maximum, keine typische Verzögerung.
Was sich am 28.09.2026 für WebSocket geändert hat
TokenLab stellt die Responses API über HTTP-Streaming ("stream": true, server-sent events) und über WebSocket unter wss://api.tokenlab.sh/v1/responses bereit, wobei der Client response.create-Ereignisse sendet. WebSocket-Antworten werden immer gestreamt. Sie unterstützen kein background oder response.cancel. Jede Verbindung verarbeitet jeweils eine aktive Antwort für bis zu 60 Minuten.
Vor der Änderung unterschieden sich die beiden Pfade. HTTP hielt die Lebenszyklusereignisse zurück und wiederholte zustandslose Fehler vor der Ausgabe. WebSocket leitete response.created sofort weiter und lieferte Fehler vor der Ausgabe an den Client, wobei diese erstattet wurden. Derselbe Upstream-Hickup führte zu einer sauberen Antwort bei HTTP und einem Fehler bei WebSocket.
Der WebSocket-Pfad folgt nun der HTTP-Regel, einschließlich der Wiederholung eines Streams, der abbricht, bevor ein Ereignis eintrifft. Intern traten die meisten Upstream-Fehler bei WebSocket-Turns vor jeder Ausgabe auf. Das ist genau das Fenster, in dem eine Wiederholung sicher ist.
Das Gateway verbessert den Fall eines Fehlers vor der Ausgabe. Es garantiert nicht, dass ein Stream abgeschlossen wird.
Wie die Änderung ohne Beeinträchtigung anderer Verhaltensweisen veröffentlicht wurde
Die Arbeit folgte einem Prozess, der darauf ausgelegt ist, stille Verhaltensänderungen zu erkennen.
- Verhaltenssperre. Vor der Änderung wurde jedes WebSocket-Turn-Szenario als Fixture aufgezeichnet: die Frames, die der Client empfängt, die Upstream-Aufrufe und das Abrechnungsergebnis. Die Suite wuchs während dieser Arbeit auf 63 aufgezeichnete Szenarien an. Eine Verhaltensänderung muss im Voraus deklariert werden. Nur die in dieser Deklaration genannten Fixtures dürfen sich ändern. Jedes andere Fixture muss Byte-identisch bleiben.
- Mutationsprüfungen. Jede neue Entscheidungsregel wurde getestet, indem sie absichtlich umgekehrt wurde, z. B. durch Wiederholung eines Timeouts beim ersten Ereignis oder Nicht-Wiederholung eines Lesefehlers, und durch Bestätigung, dass die Sperre fehlschlägt.
- Eine Überprüfungsschleife. Die erste Version machte auch den Fall des Pufferüberlaufs wiederholbar, mit dem Anspruch auf Parität mit HTTP. Die Überprüfung ergab, dass HTTP diesen Fall aus dem im Tabellengrund genannten Grund niemals wiederholt. Ein Follow-up stellte das alte Verhalten wieder her und fügte Grenzfallszenarien hinzu: ein zweiter Lesefehler wird nicht wiederholt, keine Route mehr übrig, ein Fehler nach einem gehaltenen
response.createdund ein Ersatz-Stream, der dann abbricht.
Anfrageprotokolle eines Turns, der nach einer Wiederholung erfolgreich war, zeichnen nun auch den früheren fehlgeschlagenen Versuch auf, wie es bei HTTP bereits der Fall war.
Client-Code, der die Entscheidung zur Wiederholung trifft
Setzen Sie automatische SDK-Wiederholungen für Streaming-Aufrufe auf 0. Das behält die Entscheidung in Ihrem Code. Behalten Sie die Entscheidung zur Wiederholung an einer Stelle bei, nicht über Handler verteilt. Respektieren Sie bei HTTP-Fehlern retryable und retry_after wie im Fehlerbehandlungs-Leitfaden beschrieben und behalten Sie Anfrage-IDs bei.
SSE über HTTP
import os
from openai import OpenAI
with OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
timeout=30.0,
max_retries=0, # Entscheidung zur Wiederholung selbst treffen, anstatt einen halb gelesenen Stream erneut zu senden
) as client:
completed, saw_output = False, False
with client.responses.create(
model="gpt-5.6-terra",
input="Antworte mit einem kurzen Satz über Wiederholungen.",
stream=True,
) as stream:
for event in stream:
if event.type == "response.output_text.delta":
saw_output = True
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
completed = True
elif event.type in {"response.failed", "response.incomplete", "error"}:
raise RuntimeError(f"{event.type} after_output={saw_output}")
if not completed:
raise RuntimeError(f"Stream vor response.completed geschlossen, after_output={saw_output}")
print()
Das Beispiel verwendet das OpenAI SDK 2.15.0 gegen https://api.tokenlab.sh/v1 mit max_retries=0. Es verfolgt saw_output und löst bei response.failed-, response.incomplete- und error-Ereignissen sowie bei einem Stream, der vor response.completed schließt, einen Fehler aus. Verifiziert gegen die Produktion am 28.09.2026 mit gpt-5.6-terra.
Wenn der Fehler mit saw_output == false eintrifft und die Anfrage berechtigt war (zustandslos, mit einem wiederholbaren Fehler), hat TokenLab sie bereits einmal wiederholt; gespeicherte Antworten, Fortsetzungen und Timeouts beim ersten Ereignis wurden überhaupt nicht wiederholt. Entscheiden Sie auf App-Ebene, ob eine neue Anfrage akzeptabel ist, da eine neue Anfrage eine neue Generierung darstellt. Wenn saw_output == true, melden Sie den Fehler und zeigen Sie, was Sie haben, oder verwerfen Sie den Teiltext bewusst.
WebSocket
import asyncio
import json
import os
import websockets
URL = "wss://api.tokenlab.sh/v1/responses"
TERMINAL = {"response.completed", "response.failed", "response.incomplete", "error"}
async def run_turn(prompt: str) -> str:
headers = {"Authorization": f"Bearer {os.environ['TOKENLAB_API_KEY']}"}
async with websockets.connect(URL, additional_headers=headers, max_size=None) as ws:
await ws.send(json.dumps({
"type": "response.create",
"model": "gpt-5.6-terra",
"input": prompt,
"store": False,
}))
text, saw_output = [], False
async for raw in ws:
event = json.loads(raw)
kind = event.get("type")
if kind == "response.output_text.delta":
saw_output = True
text.append(event["delta"])
elif kind in TERMINAL:
if kind != "response.completed":
# Nachdem die Ausgabe begonnen hat, ist ein Fehler für diesen Turn endgültig.
# Nur erneut senden, wenn Ihre App den Teiltext verwerfen kann.
raise RuntimeError(f"{kind} after_output={saw_output}: {json.dumps(event)[:300]}")
return "".join(text)
raise RuntimeError(f"Socket vor einem terminalen Ereignis geschlossen, after_output={saw_output}")
print(asyncio.run(run_turn("Antworte mit einem kurzen Satz über Wiederholungen.")))
Das Beispiel verwendet websockets 16.0, verbindet sich mit wss://api.tokenlab.sh/v1/responses mit einem Bearer-Header, sendet ein response.create mit store: false und sammelt response.output_text.delta. Es löst bei jedem nicht abgeschlossenen terminalen Ereignis oder einem vorzeitigen Schließen mit after_output einen Fehler aus. Verifiziert gegen die Produktion am 28.09.2026 mit gpt-5.6-terra.
Das after_output-Flag entspricht der Idee von saw_output. Es teilt Ihrem aufrufenden Code mit, ob ein neuer Turn überhaupt möglich ist, ohne Seiteneffekte zu duplizieren.
Checkliste für Ihre eigene Wiederholungslogik
- Behandeln Sie einen Stream, der ohne
response.completedendet, jedes Mal als Fehler. - Verfolgen Sie einen booleschen Wert, ob eine Ausgabe Ihren Code erreicht hat. Schalten Sie ihn beim ersten Ausgabeereignis um, nicht beim ersten Lebenszyklusereignis.
- Ein Fehler vor der Ausgabe bei einer berechtigten Anfrage hat bereits seine eine Gateway-Wiederholung erhalten; ein weiterer Versuch liegt in Ihrer Entscheidung.
- Senden Sie nach einer Teilausgabe nur dann erneut, wenn Ihre App den Teiltext verwerfen und die Kosten für zwei Generierungen akzeptieren kann.
- Prüfen Sie in Agenten-Schleifen, ob der Teil-Stream bereits einen Tool-Aufruf enthielt, auf den Ihr Code reagiert hat. Wiederholen Sie keinen Turn, dessen Seiteneffekte Sie nicht rückgängig machen können.
- Überprüfen Sie bei gespeicherten Antworten und
previous_response_id-Fortsetzungen, welcher Status existiert, bevor Sie etwas erneut senden. - Setzen Sie Streaming-Wiederholungen in Ihrem SDK auf 0 und behalten Sie die Entscheidung zur Wiederholung in einer Funktion.
- Protokollieren Sie Anfrage-IDs, damit Sie eine zugestellte Antwort den Versuchen dahinter zuordnen können.
FAQ
Startet TokenLab einen Stream nach einer Teilausgabe neu?
Nein. Sobald die Ausgabe Ihren Client erreicht hat, wird ein Fehler gemeldet und niemals wiederholt. Sie halten einen Teiltext, daher würde ein Neustart Ausgabe und Kosten duplizieren. Ihre App entscheidet, ob sie das Vorhandene anzeigt, kürzt oder verwirft.
Werde ich doppelt belastet, wenn das Gateway meine Anfrage wiederholt?
Nein. Sie zahlen nur für den zugestellten Versuch. Eine wiederholte Anfrage wurde möglicherweise zweimal im Upstream ausgeführt, aber vom ersten Versuch hat Sie nichts erreicht, und diese zusätzlichen Upstream-Kosten trägt TokenLab. Ein fehlgeschlagener Turn, der nichts liefert, wird erstattet.
Warum wird ein Timeout beim ersten Ereignis nicht wiederholt?
Weil der Upstream möglicherweise noch generiert. Eine Wiederholung könnte dieselbe Arbeit zweimal ausführen, während der erste Versuch weiterläuft. Ein Timeout beim ersten Ereignis wird anders behandelt als ein Lesefehler, der den Stream vor dem ersten Ereignis unterbricht.
Kann ich eine gespeicherte Antwort oder eine previous_response_id-Fortsetzung wiederholen?
Nicht automatisch. TokenLab wiederholt niemals gespeicherte Antworten, Fortsetzungen oder ursprungsgebundene Anfragen, da eine zweite Ausführung eine zweite gespeicherte Antwort erstellen oder den Konversationsstatus verfälschen könnte. Überprüfen Sie, welcher Status existiert, bevor Sie etwas erneut senden, und senden Sie nur dann erneut, wenn Ihre App diesen Status abgleichen kann.
Wenn Sie den rohen Ereignis-Stream selbst beobachten möchten, erstellen Sie einen API-Schlüssel und protokollieren Sie jeden Ereignistyp, den Ihr Client empfängt. Der Streaming-Leitfaden und der Fehlerbehandlungs-Leitfaden decken das gesamte Ereignisspektrum ab. Hintergrundinformationen dazu, wie das Gateway routet und wiederherstellt, finden Sie unter TokenLab AI API reliability infrastructure und Responses API vs Chat Completions for agents.
Quellen
- https://docs.tokenlab.sh/guides/streamingGeprüft am 2026-09-28
- https://docs.tokenlab.sh/guides/error-handlingGeprüft am 2026-09-28



