Preskoči na vsebino
Pretakanje

Pretakanje

Nastavite stream na true in odgovor prispe kot dogodki, ki jih pošilja strežnik (server-sent events), medtem ko ga model piše: najprej razmišljanje, nato odgovor, nato zadnji kos z razlogom zaključka in porabo (usage). Uporabite ga vedno, ko oseba čaka na besedilo, in za dolge odgovore.

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

SDK-ji OpenAI vam dajejo kose enega za drugim. Vsak kos preberite po tem, kar nosi: košček razmišljanja, košček odgovora, klic orodja ali konec 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 tok oblikovan

  • Odgovor ima status 200 ter glavi content-type: text/event-stream in cache-control: no-cache.
  • Vsak dogodek je ena vrstica, ki se začne z data: in ji sledi prazna vrstica. Besedilo za data: je en kos JSON. Vrstic event: ali id: ni.
  • Vrstica, ki se začne z dvopičjem, kot je : ping, je komentar, ki ohranja povezavo odprto. Preskočite jo.
  • Tok se konča z data: [DONE]. Tok, ki se ustavi brez tega, je nepopoln.

Kosi po vrsti

Vsak kos 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
    }
  ]
}

Vsi kosi enega toka nosijo isti id, created in model. choices vedno vsebuje en vnos. Njegov delta vsebuje, kar je novega, finish_reason pa je null do zadnjega kosa.

Ključ v delta Tip Opis Pošlje
role string Vedno assistant. Prispe enkrat, na začetku toka. Gostovani modeli z odprtimi utežmi, shannon-2-lite, shannon-2-pro
reasoning_content string Košček razmišljanja modela. Koščke združite po vrsti. Gostovani modeli z odprtimi utežmi, družina Shannon 3, shannon-1.6-pro, shannon-coder-1
content string Košček odgovora. Koščke združite po vrsti. Vsi modeli
tool_calls array En popoln klic orodja. Glejte Delte klicev orodij. Vsi modeli

Najprej pride razmišljanje, nato odgovor, nato morebitni klici orodij. Kos preberite po ključih v njegovem delta, ne po mestu v toku: delta lahko vsebuje role skupaj s prvim koščkom besedila, zadnji pa je prazen.

Celoten tok, kakor potuje. Da so vrstice kratke, so id, object, created in model tu iz vsakega kosa izpuščeni.

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]

Delte razmišljanja

Model, ki razmišlja, pošlje svoje razmišljanje v delta.reasoning_content pred odgovorom. Obe besedili ločite: razmišljanje prikažite kot ločen del, ki ga je mogoče zložiti, ali pa ga izpustite.

Gostovani modeli z odprtimi utežmi ne pošljejo razmišljanja, kadar je reasoning_effort off, in ne, kadar zahtevek vsebuje response_format.

Tudi shannon-2-lite in shannon-2-pro razmišljata, preden odgovorita. Na tej končni točki medtem pošiljata komentarne vrstice : thinking, nato odgovor.

reasoning_content ni v tipiziranih modelih SDK-jev OpenAI. V Pythonu ga preberite z getattr(delta, "reasoning_content", None); v JavaScriptu delta.reasoning_content deluje takoj.

Delte klicev orodij

Klic orodja prispe celoten: en kos na klic, katerega delta vsebuje klic s popolnim nizom arguments. index šteje klice odgovora od 0. Koda, ki arguments združuje košček za koščkom, deluje nespremenjena, ker združi en košček.

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

Po klicih ima zadnji kos finish_reason tool_calls.

Kadar zahtevek vsebuje tools, shannon-1.6-lite, shannon-1.6-pro in shannon-coder-1 pošljejo besedilo odgovora kot en delta ob koncu odgovora.

Zadnji kos

Zadnji kos pred data: [DONE] ima prazen delta, finish_reason in usage zahtevka, vse v istem kosu. Vsak tok ga ima; stream_options ne potrebujete.

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

Pri gostovanih modelih z odprtimi utežmi ima usage tudi prompt_tokens_details.cached_tokens in completion_tokens_details.reasoning_tokens.

Po zahtevku z web_search: true, katerega iskanje je kaj našlo, zadnji kos nosi tudi sources, en kos pred njim pa nosi oznake navedkov kot delta.annotations. Vgrajeno spletno iskanje

finish_reason Opis Pošlje
stop Model je končal svoj odgovor ali pa se je pojavil niz stop. Vsi modeli
tool_calls Odgovor vsebuje enega ali več klicev orodij. Vsi modeli
length Odgovor je bil odrezan na omejitvi izhoda. Družina Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

Polja usage so opisana pri končni točki. Chat Completions

Vrstice za ohranjanje povezave

Medtem ko model dela in še nima kaj poslati, tok nosi komentarne vrstice. Ne vsebujejo podatkov. Razčlenjevalnik SSE jih preskoči sam; koda, ki bere surove vrstice, mora preskočiti vsako vrstico, ki se začne z dvopičjem.

Vrstica Pošlje Kdaj
: ping Družina Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 Vsakih 15 sekund.
: keepalive Gostovani modeli z odprtimi utežmi Vsakih 15 sekund, medtem ko model dela.
: thinking shannon-2-lite, shannon-2-pro Medtem ko model razmišlja.

Napake znotraj toka

Ko se tok začne, je njegov status 200, zato napaka prispe znotraj toka: kos s članom error tam, kjer bi bil 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]

Za kosom z napako še vedno sledita zadnji kos, s finish_reason in usage, ter data: [DONE]. V vsakem kosu preverite error. Brez tega preverjanja videti odgovor, ki je odpovedal na pol poti, popoln.

Tip Koda Sporočilo Kdaj
api_error The model backend failed to answer. Please retry. Model je odpovedal ali ni napisal ničesar. Pošljite zahtevek znova.
rate_limit_error Shannon routes are temporarily busy. Please retry. Model je v tem trenutku zaseden. Počakajte nekaj sekund in zahtevek pošljite znova.
invalid_request_error context_length_exceeded Pogovor je daljši od kontekstnega okna modela. Skrajšajte ga, preden ga pošljete znova. Besedilo sporočila se razlikuje.

Gostovani modeli z odprtimi utežmi pošljejo člana error samo s type in message.

SDK-ja OpenAI za Python in JavaScript kos z napako med iteracijo sprožita kot napako API, zato zanko ovijte v svoje običajno obravnavanje napak.

Statusne kode, telo napake in kaj ponoviti imajo svojo stran. Obravnava napak

Zapiranje povezave

Zaprtje povezave ustavi dostavo, ne zahtevka. Model odgovor napiše do konca, zahtevek pa se obračuna, kot da bi ga prebrali v celoti. Če želite plačati manj, zahtevajte manj: nastavite nižji max_tokens pri modelu, ki ga upošteva.

Tok se lahko na poti do vas konča tudi predčasno, na primer ob prekinitvi omrežja ali med posodabljanjem strežnika. Takrat se ustavi brez zadnjega kosa in brez data: [DONE]. Tak odgovor obravnavajte kot nepopoln in zahtevek pošljite znova.

Branje toka brez SDK-ja

Pravilen bralnik naredi pet stvari: napake, ki prispejo pred tokom, preusmeri v svoje obravnavanje napak, telo razdeli na vrstice, preskoči komentarne vrstice, ustavi se pri data: [DONE] in v vsakem kosu preveri 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))

Primer v Pythonu uporablja paket requests. Primer v JavaScriptu teče na Node.js 18 ali novejšem.

Tokovi drugih formatov

Končni točki Responses in Messages prav tako pretakata. Njuni okvirji nosijo vrstico event: z imenom dogodka in vrstico data: z njegovim JSON. Vsaka stran navaja svoje dogodke po vrsti.

V toku Chat CompletionsResponsesMessages
Okvirji Vrstice data:Vrstice event: in data:Vrstice event: in data:
Razmišljanje prispe v delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
Besedilo odgovora prispe v delta.contentresponse.output_text.deltatext_delta
Poraba (usage) prispe v Zadnji kosresponse.completedmessage_delta
Tok se konča z Zadnji kos, nato data: [DONE]response.completedmessage_stop