Preskočiť na obsah
Streamovanie

Streamovanie

Nastavte stream na true a odpoveď prichádza ako server-sent events, kým ju model píše: najprv uvažovanie, potom odpoveď, potom posledný chunk s dôvodom ukončenia a usage. Používajte to vždy, keď text čaká človek, a pri dlhých odpovediach.

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

SDK OpenAI vám dávajú chunky jeden po druhom. Každý chunk čítajte podľa toho, čo nesie: kúsok uvažovania, kúsok odpovede, volanie nástroja alebo koniec odpovede.

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

Ako je stream členený

  • Odpoveď má status 200 a hlavičky content-type: text/event-stream a cache-control: no-cache.
  • Každá udalosť je jeden riadok, ktorý začína data: a nasleduje prázdny riadok. Text za data: je jeden chunk JSON. Riadky event: ani id: tam nie sú.
  • Riadok, ktorý začína dvojbodkou, napríklad : ping, je komentár, ktorý drží spojenie otvorené. Preskočte ho.
  • Stream sa končí data: [DONE]. Stream, ktorý sa zastaví bez neho, je neúplný.

Chunky v poradí

Každý chunk je objekt 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
    }
  ]
}

Všetky chunky jedného streamu nesú rovnaké id, created a model. choices vždy obsahuje jeden záznam. Jeho delta obsahuje, čo je nové, a finish_reason je null až do posledného chunku.

Kľúč v delta Typ Popis Posiela
role string Vždy assistant. Príde raz, na začiatku streamu. Hostované open-weight modely, shannon-2-lite, shannon-2-pro
reasoning_content string Kúsok uvažovania modelu. Kúsky spájajte v poradí. Hostované open-weight modely, rodina Shannon 3, shannon-1.6-pro, shannon-coder-1
content string Kúsok odpovede. Kúsky spájajte v poradí. Všetky modely
tool_calls array Jedno úplné volanie nástroja. Pozri Delty volania nástroja. Všetky modely

Najprv prichádza uvažovanie, potom odpoveď, potom prípadné volania nástrojov. Chunk čítajte podľa kľúčov v jeho delta, nie podľa jeho miesta v streame: delta môže obsahovať role spolu s prvým kúskom textu a posledná je prázdna.

Celý stream tak, ako putuje. Aby boli riadky krátke, id, object, created a model sú tu v každom chunku vynechané.

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]

Delty uvažovania

Model, ktorý uvažuje, posiela svoje uvažovanie v delta.reasoning_content pred odpoveďou. Oba texty držte oddelene: uvažovanie zobrazte ako samostatnú, zbaliteľnú časť, alebo ho vynechajte.

Hostované open-weight modely neposielajú žiadne uvažovanie, keď je reasoning_effort off, ani keď má request response_format.

shannon-2-lite a shannon-2-pro tiež uvažujú, než odpovedia. Na tomto endpointe počas toho posielajú komentárové riadky : thinking a potom odpoveď.

reasoning_content nie je v typovaných modeloch SDK OpenAI. V Pythone ho čítajte cez getattr(delta, "reasoning_content", None); v JavaScripte funguje delta.reasoning_content tak, ako je.

Delty volania nástroja

Volanie nástroja prichádza celé: jeden chunk na volanie, ktorého delta obsahuje volanie s úplným reťazcom arguments. index čísluje volania odpovede od 0. Kód, ktorý spája arguments po kúskoch, funguje bez zmeny, lebo spojí jeden kúsok.

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

Po volaniach má posledný chunk finish_reason tool_calls.

Keď má request tools, shannon-1.6-lite, shannon-1.6-pro a shannon-coder-1 posielajú text odpovede ako jednu deltu na konci odpovede.

Posledný chunk

Posledný chunk pred data: [DONE] má prázdnu delta, finish_reason a usage requestu, všetko v tom istom chunku. Má ho každý stream; stream_options nepotrebujete.

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

Na hostovaných open-weight modeloch má usage aj prompt_tokens_details.cached_tokens a completion_tokens_details.reasoning_tokens.

Po requeste s web_search: true, ktorého vyhľadávanie niečo našlo, nesie posledný chunk aj sources a jeden chunk pred ním nesie citačné značky ako delta.annotations. Vstavané webové vyhľadávanie

finish_reason Popis Posiela
stop Model dokončil odpoveď, alebo sa objavil reťazec stop. Všetky modely
tool_calls Odpoveď obsahuje jedno alebo viac volaní nástrojov. Všetky modely
length Odpoveď bola prerušená na limite výstupu. Rodina Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

Polia usage sú popísané pri endpointe. Chat Completions

Riadky keep-alive

Kým model pracuje a ešte nemá čo poslať, stream nesie komentárové riadky. Neobsahujú žiadne dáta. Parser SSE ich preskočí sám; kód, ktorý číta surové riadky, musí preskočiť každý riadok začínajúci dvojbodkou.

Riadok Posiela Kedy
: ping Rodina Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 Každých 15 sekúnd.
: keepalive Hostované open-weight modely Každých 15 sekúnd, kým model pracuje.
: thinking shannon-2-lite, shannon-2-pro Kým model uvažuje.

Chyby vo vnútri streamu

Keď stream začne, jeho status je 200, takže zlyhanie príde vo vnútri streamu: chunk s členom error na mieste, kde by boli choices.

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]

Po chybovom chunku stále nasledujú posledný chunk s finish_reason a usage a data: [DONE]. Kontrolujte v každom chunku error. Bez tejto kontroly vyzerá odpoveď, ktorá zlyhala v polovici, ako úplná.

Typ Kód Správa Kedy
api_error The model backend failed to answer. Please retry. Model zlyhal alebo nenapísal nič. Pošlite request znova.
rate_limit_error Shannon routes are temporarily busy. Please retry. Model je v tejto chvíli vyťažený. Počkajte niekoľko sekúnd a pošlite request znova.
invalid_request_error context_length_exceeded Konverzácia je dlhšia než kontextové okno modelu. Skráťte ju, než ju pošlete znova. Text správy sa líši.

Hostované open-weight modely posielajú člena error len s type a message.

SDK OpenAI pre Python a JavaScript vyvolajú chybový chunk ako chybu API počas iterovania, takže slučku obaľte svojím obvyklým spracovaním chýb.

Stavové kódy, telo chyby a čo zopakovať majú vlastnú stránku. Spracovanie chýb

Zatvorenie spojenia

Zatvorenie spojenia zastaví doručovanie, nie request. Model dopíše odpoveď do konca a request sa účtuje, ako keby ste ju prečítali celú. Aby ste zaplatili menej, žiadajte menej: nastavte nižšie max_tokens na modeli, ktorý ho uplatňuje.

Stream sa môže predčasne skončiť aj na ceste k vám, napríklad pri výpadku siete alebo počas aktualizácie servera. Zastaví sa potom bez posledného chunku a bez data: [DONE]. Takúto odpoveď považujte za neúplnú a pošlite request znova.

Čítanie streamu bez SDK

Správny čítač robí päť vecí: chyby, ktoré prídu pred streamom, posiela do svojho spracovania chýb, rozdelí telo na riadky, preskočí komentárové riadky, zastaví sa pri data: [DONE] a v každom chunku kontroluje 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))

Ukážka v Pythone používa balík requests. Ukážka v JavaScripte beží na Node.js 18 alebo novšom.

Streamy ostatných formátov

Endpointy Responses a Messages tiež streamujú. Ich frames nesú riadok event: s názvom udalosti a riadok data: s jej JSON. Každá stránka uvádza svoje udalosti v poradí.

V streame Chat CompletionsResponsesMessages
Frames Riadky data:Riadky event: a data:Riadky event: a data:
Uvažovanie prichádza v delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
Text odpovede prichádza v delta.contentresponse.output_text.deltatext_delta
Usage prichádza v Posledný chunkresponse.completedmessage_delta
Stream sa končí Posledný chunk, potom data: [DONE]response.completedmessage_stop