Preskoči na sadržaj
Streaming

Streaming

Postavite stream na true i odgovor stiže kao server-sent events dok ga model piše: prvo rezonovanje, zatim odgovor, pa poslednji deo sa razlogom završetka i upotrebom. Koristite ga kad god osoba čeka tekst, i za duge odgovore.

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

OpenAI SDK-ovi vam daju delove jedan po jedan. Čitajte svaki deo prema onome što nosi: deo rezonovanja, deo odgovora, poziv alata ili kraj odgovora.

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

Kako je stream uokviren

  • Odgovor ima status 200 i zaglavlja content-type: text/event-stream i cache-control: no-cache.
  • Svaki događaj je jedna linija koja počinje sa data: i iza koje sledi prazna linija. Tekst posle data: je jedan JSON deo. Nema linija event: ni id:.
  • Linija koja počinje dvotačkom, kao što je : ping, je komentar koji drži vezu otvorenom. Preskočite je.
  • Stream se završava sa data: [DONE]. Stream koji stane bez toga je nepotpun.

Delovi redom

Svaki deo je objekat 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
    }
  ]
}

Svi delovi jednog streama nose isti id, created i model. choices uvek sadrži jedan unos. Njegov delta sadrži ono što je novo, a finish_reason je null do poslednjeg dela.

Ključ u delta Tip Opis Šalje
role string Uvek assistant. Stiže jednom, na početku streama. Hostovani open-weight modeli, shannon-2-lite, shannon-2-pro
reasoning_content string Deo rezonovanja modela. Spojite delove redom. Hostovani open-weight modeli, porodica Shannon 3, shannon-1.6-pro, shannon-coder-1
content string Deo odgovora. Spojite delove redom. Svi modeli
tool_calls array Jedan kompletan poziv alata. Pogledajte Delta-e poziva alata. Svi modeli

Prvo dolazi rezonovanje, zatim odgovor, zatim eventualni pozivi alata. Čitajte deo po ključevima u njegovom delta, a ne po mestu u streamu: delta može da sadrži role zajedno sa prvim delom teksta, a poslednji je prazan.

Ceo stream onako kako putuje. Da linije ostanu kratke, id, object, created i model izostavljeni su iz svakog dela ovde.

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-e rezonovanja

Model koji rezonuje šalje svoje rezonovanje u delta.reasoning_content pre odgovora. Držite ta dva teksta odvojeno: prikažite rezonovanje kao poseban deo koji se može sklopiti, ili ga izostavite.

Hostovani open-weight modeli ne šalju rezonovanje kada je reasoning_effort off, niti kada zahtev ima response_format.

shannon-2-lite i shannon-2-pro takođe rezonuju pre odgovora. Na ovom endpointu šalju komentarske linije : thinking dok to rade, a zatim odgovor.

reasoning_content nije u tipiziranim modelima OpenAI SDK-ova. U Pythonu ga čitajte pomoću getattr(delta, "reasoning_content", None); u JavaScriptu delta.reasoning_content radi kakav jeste.

Delta-e poziva alata

Poziv alata stiže ceo: jedan deo po pozivu, čiji delta sadrži poziv sa kompletnim stringom arguments. index numeriše pozive odgovora od 0. Kod koji spaja arguments deo po deo radi nepromenjen, jer spaja jedan deo.

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

Posle poziva, poslednji deo ima finish_reason tool_calls.

Kada zahtev ima tools, shannon-1.6-lite, shannon-1.6-pro i shannon-coder-1 šalju tekst odgovora kao jedan delta na kraju odgovora.

Poslednji deo

Poslednji deo pre data: [DONE] ima prazan delta, finish_reason i usage zahteva, sve u istom delu. Svaki stream ga ima; ne trebaju vam 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
  }
}

Na hostovanim open-weight modelima usage ima i prompt_tokens_details.cached_tokens i completion_tokens_details.reasoning_tokens.

Posle zahteva sa web_search: true čija je pretraga nešto našla, poslednji deo nosi i sources, a jedan deo pre njega nosi oznake citata kao delta.annotations. Ugrađena web pretraga

finish_reason Opis Šalje
stop Model je završio odgovor, ili se pojavio stop string. Svi modeli
tool_calls Odgovor sadrži jedan ili više poziva alata. Svi modeli
length Odgovor je presečen na ograničenju izlaza. Porodica Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

Polja usage opisana su uz endpoint. Chat Completions

Keep-alive linije

Dok model radi i još nema šta da pošalje, stream nosi komentarske linije. One ne sadrže podatke. SSE parser ih sam preskače; kod koji čita sirove linije mora da preskoči svaku liniju koja počinje dvotačkom.

Linija Šalje Kada
: ping Porodica Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 Na svakih 15 sekundi.
: keepalive Hostovani open-weight modeli Na svakih 15 sekundi dok model radi.
: thinking shannon-2-lite, shannon-2-pro Dok model rezonuje.

Greške unutar streama

Kada stream počne, njegov status je 200, pa kvar stiže unutar streama: deo sa članom error na mestu gde bi bio 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]

Posle dela sa greškom i dalje slede poslednji deo, sa finish_reason i usage, i data: [DONE]. Proverite svaki deo na error. Bez te provere odgovor koji je pao na pola izgleda kompletno.

Tip Kod Poruka Kada
api_error The model backend failed to answer. Please retry. Model nije uspeo, ili nije napisao ništa. Ponovo pošaljite zahtev.
rate_limit_error Shannon routes are temporarily busy. Please retry. Model je u ovom trenutku zauzet. Sačekajte nekoliko sekundi i ponovo pošaljite zahtev.
invalid_request_error context_length_exceeded Razgovor je duži od kontekstnog prozora modela. Skratite ga pre ponovnog slanja. Tekst poruke varira.

Hostovani open-weight modeli šalju član error samo sa type i message.

OpenAI Python i JavaScript SDK-ovi podižu deo sa greškom kao API grešku dok iterirate, zato petlju obmotajte uobičajenim rukovanjem greškama.

Statusni kodovi, telo greške i šta ponoviti imaju svoju stranicu. Upravljanje greškama

Zatvaranje veze

Zatvaranje veze zaustavlja isporuku, a ne zahtev. Model piše odgovor do kraja i zahtev se naplaćuje kao da ste pročitali sve. Da platite manje, tražite manje: postavite niži max_tokens na modelu koji ga primenjuje.

Stream se može i prerano prekinuti na putu do vas, na primer pri prekidu mreže ili dok se server ažurira. Tada staje bez poslednjeg dela i bez data: [DONE]. Smatrajte takav odgovor nepotpunim i ponovo pošaljite zahtev.

Čitanje streama bez SDK-a

Ispravan čitač radi pet stvari: greške koje stignu pre streama šalje svom rukovanju greškama, deli telo na linije, preskače komentarske linije, staje na data: [DONE] i proverava svaki deo na 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))

Python primer koristi paket requests. JavaScript primer radi na Node.js 18 ili novijem.

Streamovi drugih formata

Responses i Messages endpointi takođe strimuju. Njihovi frameovi nose liniju event: sa imenom događaja i liniju data: sa njegovim JSON-om. Svaka stranica navodi svoje događaje redom.

U streamu Chat CompletionsResponsesMessages
Frameovi Linije data:Linije event: i data:Linije event: i data:
Rezonovanje stiže u delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
Tekst odgovora stiže u delta.contentresponse.output_text.deltatext_delta
Upotreba stiže u Poslednji deoresponse.completedmessage_delta
Stream se završava sa Poslednji deo, zatim data: [DONE]response.completedmessage_stop