Langsung ke konten
Streaming

Streaming

Setel stream ke true dan balasan datang sebagai server-sent events saat model menuliskannya: penalaran lebih dulu, lalu jawaban, lalu chunk terakhir dengan finish reason dan usage. Gunakan kapan pun seseorang menunggu teksnya, dan untuk balasan yang panjang.

POST https://api.shannon-ai.com/v1/chat/completions

SDK OpenAI memberi Anda chunk satu per satu. Baca setiap chunk berdasarkan isinya: sepotong penalaran, sepotong jawaban, pemanggilan tool, atau akhir balasan.

from openai import OpenAI, APIError

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com/v1",
)

stream = client.chat.completions.create(
    model="shannon-3",
    messages=[{"role": "user", "content": "Explain server-sent events in three sentences."}],
    stream=True,
)

answer, reasoning, tool_calls = [], [], []
finish_reason = usage = None

try:
    for chunk in stream:
        if chunk.usage:
            usage = chunk.usage
        if not chunk.choices:
            continue
        choice = chunk.choices[0]
        delta = choice.delta

        thought = getattr(delta, "reasoning_content", None)
        if thought:
            reasoning.append(thought)
        if delta.content:
            answer.append(delta.content)
            print(delta.content, end="", flush=True)
        for call in delta.tool_calls or []:
            tool_calls.append((call.id, call.function.name, call.function.arguments))
        if choice.finish_reason:
            finish_reason = choice.finish_reason
except APIError as error:
    # A failure after the stream started arrives as an error chunk.
    print(f"\nstream failed: {error}")

print(f"\n{finish_reason} {usage}")

Cara stream dibingkai

  • Balasan berstatus 200 dengan header content-type: text/event-stream dan cache-control: no-cache.
  • Setiap event adalah satu baris yang diawali data: dan diikuti baris kosong. Teks setelah data: adalah satu chunk JSON. Tidak ada baris event: atau id:.
  • Baris yang diawali titik dua, seperti : ping, adalah komentar yang menjaga koneksi tetap terbuka. Lewati.
  • Stream berakhir dengan data: [DONE]. Stream yang berhenti tanpanya tidak lengkap.

Chunk secara berurutan

Setiap chunk adalah objek chat.completion.chunk:

JSON
{
  "id": "chatcmpl-5f0c1e7a9b3d4c62a8e1f07d2b46c9a3",
  "object": "chat.completion.chunk",
  "created": 1791625200,
  "model": "shannon-3",
  "choices": [
    {
      "index": 0,
      "delta": {
        "content": "Server-sent"
      },
      "finish_reason": null
    }
  ]
}

Semua chunk dalam satu stream membawa id, created, dan model yang sama. choices selalu berisi satu entri. delta-nya berisi yang baru, dan finish_reason adalah null sampai chunk terakhir.

Key di delta Tipe Deskripsi Dikirim oleh
role string Selalu assistant. Datang sekali, di awal stream. Model open-weight hosted, shannon-2-lite, shannon-2-pro
reasoning_content string Sepotong penalaran model. Gabungkan potongan secara berurutan. Model open-weight hosted, keluarga Shannon 3, shannon-1.6-pro, shannon-coder-1
content string Sepotong jawaban. Gabungkan potongan secara berurutan. Semua model
tool_calls array Satu pemanggilan tool yang lengkap. Lihat Tool call deltas. Semua model

Penalaran datang lebih dulu, lalu jawaban, lalu pemanggilan tool apa pun. Baca chunk berdasarkan key di delta-nya, bukan berdasarkan posisinya dalam stream: delta dapat berisi role bersama potongan teks pertama, dan yang terakhir kosong.

Satu stream utuh sebagaimana dikirim. Agar baris tetap pendek, id, object, created, dan model dihilangkan dari tiap chunk di sini.

200 text/event-stream
data: {"choices":[{"index":0,"delta":{"reasoning_content":"Three sentences:"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"reasoning_content":" what it is, how it is framed, why it is used."},"finish_reason":null}]}

: ping

data: {"choices":[{"index":0,"delta":{"content":"Server-sent"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"content":" events let a server push text to a client over one HTTP reply."},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":1190,"completion_tokens":84,"total_tokens":1274}}

data: [DONE]

Delta penalaran

Model yang bernalar mengirim penalarannya di delta.reasoning_content sebelum jawaban. Pisahkan kedua teks itu: tampilkan penalaran sebagai bagian terpisah yang dapat dilipat, atau abaikan.

Model open-weight hosted tidak mengirim penalaran ketika reasoning_effort adalah off, dan juga tidak ketika permintaan memiliki response_format.

shannon-2-lite dan shannon-2-pro juga bernalar sebelum menjawab. Pada endpoint ini, keduanya mengirim baris komentar : thinking selama bernalar, lalu jawabannya.

reasoning_content tidak ada di model bertipe pada SDK OpenAI. Di Python, bacalah dengan getattr(delta, "reasoning_content", None); di JavaScript, delta.reasoning_content berfungsi apa adanya.

Tool call deltas

Pemanggilan tool datang utuh: satu chunk per panggilan, yang delta-nya berisi panggilan dengan string arguments yang lengkap. index menomori panggilan dalam balasan mulai dari 0. Kode yang menggabungkan arguments potong demi potong berfungsi tanpa perubahan, karena ia menggabungkan satu potongan.

delta
{
  "tool_calls": [
    {
      "index": 0,
      "id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"city\":\"Paris\"}"
      }
    }
  ]
}

Setelah panggilan, chunk terakhir memiliki finish_reason tool_calls.

Ketika permintaan memiliki tools, shannon-1.6-lite, shannon-1.6-pro, dan shannon-coder-1 mengirim teks jawaban sebagai satu delta di akhir balasan.

Chunk terakhir

Chunk terakhir sebelum data: [DONE] memiliki delta kosong, finish_reason, dan usage permintaan, semuanya dalam chunk yang sama. Setiap stream memilikinya; Anda tidak memerlukan stream_options.

JSON
{
  "id": "chatcmpl-5f0c1e7a9b3d4c62a8e1f07d2b46c9a3",
  "object": "chat.completion.chunk",
  "created": 1791625200,
  "model": "shannon-3",
  "choices": [
    {
      "index": 0,
      "delta": {},
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1190,
    "completion_tokens": 84,
    "total_tokens": 1274
  }
}

Pada model open-weight hosted, usage juga memiliki prompt_tokens_details.cached_tokens dan completion_tokens_details.reasoning_tokens.

Setelah permintaan dengan web_search: true yang pencariannya menemukan sesuatu, chunk terakhir juga membawa sources, dan satu chunk sebelumnya membawa tanda kutipan sebagai delta.annotations. Pencarian Web Terintegrasi

finish_reason Deskripsi Dikirim oleh
stop Model menyelesaikan jawabannya, atau string stop muncul. Semua model
tool_calls Balasan berisi satu atau lebih pemanggilan tool. Semua model
length Balasan terpotong pada batas output. Keluarga Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

Field usage dijelaskan bersama endpoint. Chat Completions

Baris keep-alive

Selama model bekerja dan belum ada yang dikirim, stream membawa baris komentar. Baris itu tidak berisi data. Parser SSE melewatinya sendiri; kode yang membaca baris mentah harus melewati setiap baris yang diawali titik dua.

Baris Dikirim oleh Kapan
: ping Keluarga Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 Setiap 15 detik.
: keepalive Model open-weight hosted Setiap 15 detik selama model bekerja.
: thinking shannon-2-lite, shannon-2-pro Selama model bernalar.

Error di dalam stream

Setelah stream dimulai, statusnya 200, sehingga kegagalan datang di dalam stream: chunk dengan anggota error di tempat choices seharusnya berada.

200 text/event-stream
data: {"error":{"message":"The model backend failed to answer. Please retry.","type":"api_error","code":null,"param":null}}

data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":1190,"completion_tokens":12,"total_tokens":1202}}

data: [DONE]

Setelah chunk error, chunk terakhir, dengan finish_reason dan usage, serta data: [DONE] tetap menyusul. Periksa setiap chunk untuk error. Tanpa pemeriksaan itu, balasan yang gagal di tengah jalan tampak lengkap.

Type Code Pesan Kapan
api_error The model backend failed to answer. Please retry. Model gagal, atau tidak menulis apa pun. Kirim ulang permintaan.
rate_limit_error Shannon routes are temporarily busy. Please retry. Model sedang sibuk saat ini. Tunggu beberapa detik dan kirim ulang permintaan.
invalid_request_error context_length_exceeded Percakapan lebih panjang dari jendela konteks model. Persingkat sebelum mengirim lagi. Teks pesan bervariasi.

Model open-weight hosted mengirim anggota error hanya dengan type dan message.

SDK OpenAI Python dan JavaScript memunculkan chunk error sebagai API error saat Anda melakukan iterasi, jadi bungkus loop dengan penanganan error yang biasa Anda pakai.

Kode status, body error, dan apa yang perlu dicoba ulang ada di halamannya sendiri. Penanganan kesalahan

Menutup koneksi

Menutup koneksi menghentikan pengiriman, bukan permintaan. Model menulis balasan hingga selesai dan permintaan ditagih seolah-olah Anda telah membaca semuanya. Untuk membayar lebih sedikit, minta lebih sedikit: setel max_tokens yang lebih rendah pada model yang menerapkannya.

Stream juga dapat berakhir lebih awal di perjalanan ke Anda, misalnya karena jaringan terputus atau saat server sedang diperbarui. Stream kemudian berhenti tanpa chunk terakhir dan tanpa data: [DONE]. Anggap balasan itu tidak lengkap dan kirim ulang permintaan.

Membaca stream tanpa SDK

Pembaca yang benar melakukan lima hal: mengirim error yang datang sebelum stream ke penanganan errornya, memecah body menjadi baris, melewati baris komentar, berhenti pada data: [DONE], dan memeriksa setiap chunk untuk error.

import json
import requests

response = requests.post(
    "https://api.shannon-ai.com/v1/chat/completions",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "model": "shannon-3",
        "messages": [{"role": "user", "content": "Explain server-sent events in three sentences."}],
        "stream": True,
    },
    stream=True,
)

# 1. Errors before the stream are plain JSON with their own status.
if response.status_code != 200:
    raise RuntimeError(f"{response.status_code}: {response.text}")

answer, done = [], False
for raw in response.iter_lines():          # 2. one line at a time
    line = raw.decode("utf-8")
    if not line.startswith("data: "):      # 3. empty lines and ": ping" comments
        continue
    data = line[len("data: "):]
    if data == "[DONE]":                   # 4. the end of the stream
        done = True
        break
    chunk = json.loads(data)
    if "error" in chunk:                   # 5. a failure inside the stream
        raise RuntimeError(chunk["error"]["message"])
    delta = chunk["choices"][0]["delta"]
    if delta.get("content"):
        answer.append(delta["content"])
    if chunk.get("usage"):
        print(chunk["choices"][0]["finish_reason"], chunk["usage"])

if not done:
    raise RuntimeError("the stream ended before [DONE]")
print("".join(answer))

Contoh Python memakai paket requests. Contoh JavaScript berjalan di Node.js 18 atau lebih baru.

Stream format lain

Endpoint Responses dan Messages juga melakukan streaming. Frame-nya membawa baris event: berisi nama event dan baris data: berisi JSON-nya. Setiap halaman mencantumkan eventnya secara berurutan.

Dalam stream Chat CompletionsResponsesMessages
Frame Baris data:Baris event: dan data:Baris event: dan data:
Penalaran datang di delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
Teks jawaban datang di delta.contentresponse.output_text.deltatext_delta
Usage datang di Chunk terakhirresponse.completedmessage_delta
Stream berakhir dengan Chunk terakhir, lalu data: [DONE]response.completedmessage_stop