Pilih Auto, TokenLab Verified, atau Official untuk setiap permintaan, dengan harga yang ditampilkan di awal. Lihat yang baru

Melakukan Retry pada Streaming Respons LLM Tanpa Menduplikasi Output

CryptoCrypto
·28 September 2026·10 menit baca·Diperbarui 28 September 2026·33 tampilan
#streaming#api respons#reliabilitas#websocket
Melakukan Retry pada Streaming Respons LLM Tanpa Menduplikasi Output

Permintaan streaming hanya aman untuk diulang (replay) jika tiga kondisi terpenuhi secara bersamaan. Tidak ada yang sampai ke klien Anda. Tidak ada yang terukur (metered) secara teramati. Dan permintaan tersebut tidak membawa status sisi server (server-side state). Setelah event output pertama, langkah yang tepat adalah melaporkan kegagalan alih-alih mengulanginya.

TokenLab menerapkan aturan tersebut pada gateway-nya untuk streaming Responses API, baik melalui HTTP maupun WebSocket. Jalur WebSocket telah diubah pada 2026-09-28 agar sesuai dengan HTTP.

Mengapa stream berbeda dari permintaan biasa

Panggilan non-streaming mengembalikan body atau error. Anda dapat melakukan retry pada error tersebut karena Anda tidak mendapatkan apa pun kembali.

Sebuah stream memberikan output kepada Anda sebelum permintaan selesai. Event output pertama adalah titik tanpa jalan kembali (point of no return). Jika koneksi terputus setelah itu, Anda memegang teks parsial. Mengulang permintaan berarti menghasilkan jawaban yang sama lagi dan membayarnya dua kali. Anda mungkin juga menduplikasi pemanggilan tool yang telah dijalankan oleh agen Anda.

Panduan streaming TokenLab menyatakannya secara langsung:

Setelah event pertama tiba, stream yang terputus tidak lengkap dan tidak dimulai ulang secara otomatis.

Jadi, klien Anda memerlukan satu bit status lokal: saw_output. Status ini berubah menjadi true saat ada output yang mencapai kode Anda. Setiap keputusan untuk melakukan retry membaca bit tersebut terlebih dahulu.

Stream yang berakhir tanpa response.completed adalah kegagalan. Jangan berasumsi bahwa teks yang Anda miliki sudah lengkap. Tangani event response.failed, response.incomplete, dan error.

Keputusan replay, poin demi poin

TokenLab mengulang permintaan sekali pada rute lain yang tersedia jika semua kondisi berikut terpenuhi. Permintaan bersifat stateless. Tidak ada yang sampai ke klien. Tidak ada hasil atau penggunaan yang teramati untuk percobaan yang gagal. Dan kegagalan tersebut berupa event pra-output yang dapat di-retry atau error pembacaan upstream sebelum event pertama. Paling banyak satu replay terjadi per permintaan. Jika penggantinya juga gagal sebelum output, kegagalan tersebut tidak akan diulang kembali.

Sumber: Panduan streaming dan perilaku gateway TokenLab, diamati pada 2026-09-28.

Titik kegagalan Di-replay oleh TokenLab? Alasan
Event pra-output yang dapat di-retry (response.failed, atau event error yang ditandai dapat di-retry, seperti error upstream yang kelebihan beban atau internal) Ya, sekali, jika permintaan bersifat stateless Tidak ada yang sampai ke klien dan tidak ada penggunaan yang teramati, sehingga eksekusi kedua tidak terlihat.
Stream upstream terputus (read error) sebelum event pertama Ya, sekali, jika permintaan bersifat stateless Jendela yang sama. Klien tidak memegang output dan tidak ada biaya.
Kegagalan kedua sebelum output, setelah satu replay Tidak Anggarannya adalah satu replay per permintaan.
Kegagalan apa pun setelah output sampai ke klien Tidak Klien sudah memegang teks parsial. Replay akan menduplikasi output dan biaya.
Respons tersimpan (store), kelanjutan (previous_response_id), atau permintaan yang terikat origin Tidak Eksekusi kedua dapat membuat respons tersimpan kedua atau mengacaukan status percakapan.
Timeout event pertama Tidak Upstream mungkin masih melakukan pembuatan. Replay dapat menjalankan pekerjaan yang sama dua kali sementara percobaan pertama berlanjut.
Buffer overflow pra-output Tidak Batasnya bersifat lokal pada gateway. Awalan yang terlalu besar yang sama kemungkinan besar akan terkena batas yang sama lagi pada rute berikutnya.
Klien terputus Tidak Klien berhenti mendengarkan.
Kegagalan deterministik, misalnya permintaan tidak valid Tidak Melakukan retry tidak dapat mengubah hasil. Dikirim tanpa perubahan.
Kegagalan yang sudah membawa penggunaan Tidak Percobaan tersebut telah diukur (metered). Dikirim tanpa perubahan.
Tidak ada rute lain yang tersisa Tidak Tidak ada tempat untuk mengirimnya. Klien mendapatkan kegagalan dengan kodenya sendiri.

Ketika kegagalan tidak di-replay, atau tidak ada rute lain yang tersisa, Anda menerimanya dengan kode error-nya sendiri. Contoh publik: stream_read_error saat stream upstream terputus, dan upstream_stream_buffer_limit saat buffer overflow. Jika pemilihan rute itu sendiri gagal setelah keputusan replay, giliran WebSocket berakhir dengan websocket_response_failed (status 500) dan biaya yang dicadangkan akan dikembalikan.

Penagihan mengikuti aturan yang sama. Anda membayar hanya untuk percobaan yang berhasil dikirimkan. Permintaan yang di-replay mungkin telah dieksekusi di upstream dua kali, dan biaya upstream tambahan tersebut adalah tanggungan TokenLab, karena tidak ada yang sampai kepada Anda dari percobaan pertama. Giliran yang gagal dan tidak mengirimkan apa pun akan dikembalikan dananya.

Satu detail waktu penting untuk penanganan error Anda. Sebelum output dimulai, gateway menahan response.created dan response.in_progress hingga event output pertama atau kegagalan tiba, paling lama 10 detik. Event yang ditahan tersebut kemudian sampai kepada Anda bersamaan dengan output pertama, atau dengan event terminal. Urutan dan konten tidak berubah. Anda hanya melihatnya sedikit lebih lambat. 10 detik tersebut adalah batas maksimum, bukan penundaan yang umum.

Apa yang berubah untuk WebSocket pada 2026-09-28

TokenLab melayani Responses API melalui streaming HTTP ("stream": true, server-sent events) dan melalui WebSocket di wss://api.tokenlab.sh/v1/responses, di mana klien mengirim event response.create. Respons WebSocket selalu di-stream. Mereka tidak mendukung background atau response.cancel. Setiap koneksi menangani satu respons aktif dalam satu waktu hingga 60 menit.

Sebelum perubahan, kedua jalur tersebut tidak sinkron. HTTP menahan event siklus hidup dan mengulang kegagalan pra-output yang stateless. WebSocket meneruskan response.created segera dan mengirimkan kegagalan pra-output ke klien, serta mengembalikan dananya. Gangguan upstream yang sama menghasilkan jawaban bersih pada HTTP dan error pada WebSocket.

Jalur WebSocket sekarang mengikuti aturan HTTP, termasuk replay stream yang terputus sebelum ada event yang tiba. Secara internal, sebagian besar kegagalan upstream yang terlihat pada giliran WebSocket terjadi sebelum ada output. Itu adalah jendela yang tepat di mana replay aman dilakukan.

Gateway meningkatkan kasus kegagalan pra-output. Ini tidak menjamin bahwa stream akan selesai.

Bagaimana perubahan dirilis tanpa merusak perilaku lain

Pekerjaan ini mengikuti proses yang dibangun untuk menangkap perubahan perilaku yang tidak disengaja.

  • Kunci perilaku. Sebelum perubahan, setiap skenario giliran WebSocket direkam sebagai fixture: frame yang diterima klien, panggilan upstream yang dilakukan, dan hasil penagihan. Rangkaian ini berkembang menjadi 63 skenario yang direkam selama pekerjaan ini. Perubahan perilaku harus dinyatakan di awal. Hanya fixture yang disebutkan dalam deklarasi tersebut yang boleh berubah. Setiap fixture lainnya harus tetap identik secara byte.
  • Pemeriksaan mutasi. Setiap aturan keputusan baru diuji dengan sengaja membaliknya, seperti mengulang timeout event pertama atau tidak mengulang kegagalan pembaca, dan mengonfirmasi bahwa kunci gagal.
  • Tangkapan ulasan. Versi pertama juga membuat kasus buffer-overflow dapat di-replay, dengan klaim paritas dengan HTTP. Ulasan menunjukkan bahwa HTTP tidak pernah mengulang kasus tersebut, karena alasan dalam tabel. Perbaikan mengembalikan perilaku lama dan menambahkan skenario batas: kegagalan pembaca kedua tidak di-replay, tidak ada rute tersisa, kegagalan setelah response.created ditahan, dan stream pengganti yang kemudian terputus.

Log permintaan dari giliran yang berhasil setelah replay sekarang juga mencatat percobaan gagal sebelumnya, seperti yang sudah dilakukan HTTP.

Kode klien yang memiliki keputusan retry

Atur retry otomatis SDK ke 0 untuk panggilan streaming. Itu menjaga keputusan tetap berada di kode Anda. Simpan keputusan replay di satu tempat, jangan disebar di berbagai handler. Untuk error HTTP, patuhi retryable dan retry_after sebagaimana dijelaskan dalam panduan penanganan error, dan simpan ID permintaan.

SSE melalui 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,  # own the retry decision instead of resending a half-read stream
) as client:
    completed, saw_output = False, False
    with client.responses.create(
        model="gpt-5.6-terra",
        input="Reply with one short sentence about retries.",
        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 closed before response.completed, after_output={saw_output}")
    print()

Contoh ini menggunakan OpenAI SDK 2.15.0 terhadap https://api.tokenlab.sh/v1 dengan max_retries=0. Kode ini melacak saw_output, dan memunculkan error pada event response.failed, response.incomplete, dan error, serta pada stream yang ditutup sebelum response.completed. Diverifikasi terhadap produksi pada 2026-09-28 dengan gpt-5.6-terra.

Jika kegagalan tiba dengan saw_output == false dan permintaan memenuhi syarat (stateless, dengan kegagalan yang dapat di-replay), TokenLab telah mengulanginya sekali; respons tersimpan, kelanjutan, dan timeout event pertama tidak di-replay sama sekali. Putuskan di tingkat aplikasi apakah permintaan baru dapat diterima, karena permintaan baru berarti pembuatan baru. Jika saw_output == true, laporkan kegagalan dan tunjukkan apa yang Anda miliki, atau buang teks parsial tersebut dengan sengaja.

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":
                    # After output has started, a failure is final for this turn.
                    # Resend only if your app can discard the partial text.
                    raise RuntimeError(f"{kind} after_output={saw_output}: {json.dumps(event)[:300]}")
                return "".join(text)
        raise RuntimeError(f"socket closed before a terminal event, after_output={saw_output}")


print(asyncio.run(run_turn("Reply with one short sentence about retries.")))

Contoh ini menggunakan websockets 16.0, terhubung ke wss://api.tokenlab.sh/v1/responses dengan header Bearer, mengirim satu response.create dengan store: false, dan mengumpulkan response.output_text.delta. Kode ini memunculkan error dengan after_output pada event terminal apa pun yang tidak selesai atau penutupan awal. Diverifikasi terhadap produksi pada 2026-09-28 dengan gpt-5.6-terra.

Flag after_output memiliki ide yang sama dengan saw_output. Flag ini memberi tahu kode pemanggil Anda apakah giliran baru dimungkinkan tanpa menduplikasi efek samping.

Daftar periksa untuk logika retry Anda sendiri

  • Perlakukan stream yang berakhir tanpa response.completed sebagai kegagalan, setiap saat.
  • Lacak satu boolean untuk mengetahui apakah output telah mencapai kode Anda. Ubah statusnya pada event output pertama, bukan pada event siklus hidup pertama.
  • Kegagalan pra-output pada permintaan yang memenuhi syarat telah melalui satu kali replay gateway; upaya lebih lanjut adalah keputusan Anda.
  • Setelah output parsial, kirim ulang hanya jika aplikasi Anda dapat membuang teks parsial tersebut dan menerima biaya untuk dua kali pembuatan.
  • Dalam loop agen, periksa apakah stream parsial sudah berisi pemanggilan tool yang telah ditindaklanjuti oleh kode Anda. Jangan mengulang giliran yang efek sampingnya tidak dapat Anda batalkan.
  • Untuk respons tersimpan dan kelanjutan previous_response_id, periksa status apa yang ada sebelum Anda mengirim ulang apa pun.
  • Atur retry streaming ke 0 di SDK Anda dan simpan keputusan replay di satu fungsi.
  • Catat ID permintaan agar Anda dapat mencocokkan jawaban yang dikirimkan dengan percobaan di baliknya.

FAQ

Apakah TokenLab memulai ulang stream setelah output parsial?

Tidak. Setelah output mencapai klien Anda, kegagalan dilaporkan dan tidak pernah di-replay. Anda memegang teks parsial, jadi memulai ulang akan menduplikasi output dan biaya. Aplikasi Anda memutuskan apakah akan menampilkan, memotong, atau membuang apa yang dimilikinya.

Apakah saya akan ditagih dua kali jika gateway mengulang permintaan saya?

Tidak. Anda membayar hanya untuk percobaan yang berhasil dikirimkan. Permintaan yang di-replay mungkin telah dieksekusi di upstream dua kali, tetapi tidak ada yang sampai kepada Anda dari percobaan pertama, dan biaya upstream tambahan tersebut adalah tanggungan TokenLab. Giliran yang gagal dan tidak mengirimkan apa pun akan dikembalikan dananya.

Mengapa timeout event pertama tidak di-retry?

Karena upstream mungkin masih melakukan pembuatan. Replay dapat menjalankan pekerjaan yang sama dua kali sementara percobaan pertama berlanjut. Timeout event pertama diperlakukan berbeda dari error pembacaan yang memutus stream sebelum event pertama.

Bisakah saya melakukan retry pada respons tersimpan atau kelanjutan previous_response_id?

Tidak secara otomatis. TokenLab tidak pernah mengulang respons tersimpan, kelanjutan, atau permintaan yang terikat origin, karena eksekusi kedua dapat membuat respons tersimpan kedua atau mengacaukan status percakapan. Periksa status apa yang ada sebelum Anda mengirim ulang, dan hanya kirim ulang jika aplikasi Anda dapat merekonsiliasi status tersebut.

Jika Anda ingin memantau stream event mentah sendiri, buat API key dan catat setiap jenis event yang diterima klien Anda. Panduan streaming dan panduan penanganan error mencakup seluruh set event. Untuk latar belakang tentang bagaimana gateway melakukan routing dan pemulihan, lihat infrastruktur keandalan API TokenLab AI dan Responses API vs Chat Completions untuk agen.

Sumber

Model terkait

Model yang baru dirilis

Bangun dengan model dalam panduan ini

Bandingkan harga, uji rute, dan ubah riset menjadi panggilan API yang berjalan.