Pereiti prie turinio
Srautinė transliacija

Srautinė transliacija

Nustatykite stream į true, ir atsakymas ateis kaip serverio siunčiami įvykiai, kol modelis jį rašo: pirmiausia mąstymas, paskui atsakymas, paskui paskutinis gabalas su baigimo priežastimi ir naudojimu. Naudokite tai visada, kai tekstą laukia žmogus, ir ilgiems atsakymams.

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

OpenAI SDK gabalus pateikia po vieną. Kiekvieną gabalą skaitykite pagal tai, ką jis neša: mąstymo dalį, atsakymo dalį, įrankio kreipinį arba atsakymo pabaigą.

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

Kaip suskirstytas srautas

  • Atsakymas turi būseną 200 ir antraštes content-type: text/event-stream bei cache-control: no-cache.
  • Kiekvienas įvykis yra viena eilutė, prasidedanti data: , po kurios eina tuščia eilutė. Tekstas po data: yra vienas JSON gabalas. Eilučių event: ar id: nėra.
  • Eilutė, prasidedanti dvitaškiu, pavyzdžiui, : ping, yra komentaras, palaikantis ryšį atvirą. Praleiskite ją.
  • Srautas baigiasi data: [DONE]. Srautas, sustojęs be jo, yra neužbaigtas.

Gabalai iš eilės

Kiekvienas gabalas yra objektas 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
    }
  ]
}

Visi vieno srauto gabalai turi tą patį id, created ir model. choices visada turi vieną įrašą. Jo delta talpina tai, kas nauja, o finish_reason yra null iki paskutinio gabalo.

Raktas delta objekte Tipas Aprašymas Siunčia
role string Visada assistant. Ateina vieną kartą, srauto pradžioje. Talpinami atvirų svorių modeliai, shannon-2-lite, shannon-2-pro
reasoning_content string Modelio mąstymo dalis. Dalis sujunkite iš eilės. Talpinami atvirų svorių modeliai, Shannon 3 šeima, shannon-1.6-pro, shannon-coder-1
content string Atsakymo dalis. Dalis sujunkite iš eilės. Visi modeliai
tool_calls array Vienas pilnas įrankio kreipinys. Žr. Įrankių kreipinių delta. Visi modeliai

Pirmiausia eina mąstymas, paskui atsakymas, paskui įrankių kreipiniai. Gabalą skaitykite pagal jo delta raktus, o ne pagal vietą sraute: delta gali talpinti role kartu su pirmąja teksto dalimi, o paskutinė yra tuščia.

Visas srautas taip, kaip jis keliauja. Kad eilutės būtų trumpesnės, čia iš kiekvieno gabalo praleisti id, object, created ir model.

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]

Mąstymo delta

Modelis, kuris mąsto, savo mąstymą prieš atsakymą siunčia delta.reasoning_content. Du tekstus laikykite atskirai: mąstymą rodykite kaip atskirą suskleidžiamą dalį arba praleiskite.

Talpinami atvirų svorių modeliai mąstymo nesiunčia, kai reasoning_effort yra off, ir nesiunčia, kai užklausoje yra response_format.

shannon-2-lite ir shannon-2-pro taip pat mąsto prieš atsakydami. Šiame galiniame taške, kol mąsto, jie siunčia komentarų eilutes : thinking, o paskui atsakymą.

reasoning_content nėra OpenAI SDK tipizuotuose modeliuose. Python kalboje skaitykite jį su getattr(delta, "reasoning_content", None); JavaScript kalboje delta.reasoning_content veikia toks, koks yra.

Įrankių kreipinių delta

Įrankio kreipinys ateina visas: vienas gabalas kiekvienam kreipiniui, kurio delta talpina kreipinį su visa arguments eilute. index sunumeruoja atsakymo kreipinius nuo 0. Kodas, kuris arguments jungia dalimis, veikia nepakitęs, nes jungia vieną dalį.

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

Po kreipinių paskutinis gabalas turi finish_reason tool_calls.

Kai užklausoje yra tools, shannon-1.6-lite, shannon-1.6-pro ir shannon-coder-1 atsakymo tekstą siunčia kaip vieną delta atsakymo pabaigoje.

Paskutinis gabalas

Paskutinis gabalas prieš data: [DONE] turi tuščią delta, finish_reason ir užklausos usage, visa tai tame pačiame gabale. Jį turi kiekvienas srautas; stream_options nereikia.

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

Talpinamuose atvirų svorių modeliuose usage taip pat turi prompt_tokens_details.cached_tokens ir completion_tokens_details.reasoning_tokens.

Po užklausos su web_search: true, kurios paieška ką nors rado, paskutiniame gabale taip pat yra sources, o vienas gabalas prieš jį talpina citavimo žymeklius kaip delta.annotations. Žiniatinklio paieška

finish_reason Aprašymas Siunčia
stop Modelis baigė atsakymą arba pasirodė stop eilutė. Visi modeliai
tool_calls Atsakyme yra vienas ar daugiau įrankių kreipinių. Visi modeliai
length Atsakymas nutrauktas pasiekus išvesties ribą. Shannon 3 šeima, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

usage laukai aprašyti galinio taško puslapyje. Chat Completions

Palaikymo eilutės

Kol modelis dirba ir dar nėra ką siųsti, sraute eina komentarų eilutės. Jose duomenų nėra. SSE analizatorius jas praleidžia pats; kodas, skaitantis neapdorotas eilutes, turi praleisti kiekvieną eilutę, prasidedančią dvitaškiu.

Eilutė Siunčia Kada
: ping Shannon 3 šeima, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 Kas 15 sekundžių.
: keepalive Talpinami atvirų svorių modeliai Kas 15 sekundžių, kol modelis dirba.
: thinking shannon-2-lite, shannon-2-pro Kol modelis mąsto.

Klaidos sraute

Kai srautas jau prasidėjo, jo būsena yra 200, todėl nesėkmė ateina sraute: gabalas su nariu error ten, kur būtų 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 klaidos gabalo vis tiek eina paskutinis gabalas su finish_reason ir usage bei data: [DONE]. Kiekviename gabale tikrinkite error. Be šios patikros atsakymas, nepavykęs viduryje, atrodo užbaigtas.

Tipas Kodas Pranešimas Kada
api_error The model backend failed to answer. Please retry. Modelis nepavyko arba nieko neparašė. Siųskite užklausą dar kartą.
rate_limit_error Shannon routes are temporarily busy. Please retry. Modelis šiuo metu užimtas. Palaukite kelias sekundes ir siųskite užklausą dar kartą.
invalid_request_error context_length_exceeded Pokalbis ilgesnis už modelio konteksto langą. Prieš siųsdami iš naujo, jį sutrumpinkite. Pranešimo tekstas skiriasi.

Talpinami atvirų svorių modeliai narį error siunčia tik su type ir message.

OpenAI Python ir JavaScript SDK, kol iteruojate, klaidos gabalą iškelia kaip API klaidą, todėl apgaubkite ciklą savo įprastu klaidų tvarkymu.

Būsenos kodai, klaidos turinys ir ką kartoti turi savo puslapį. Klaidų tvarkymas

Ryšio uždarymas

Uždarius ryšį sustoja pristatymas, bet ne užklausa. Modelis atsakymą parašo iki galo, o užklausa apmokestinama taip, tarsi būtumėte perskaitę viską. Norėdami mokėti mažiau, prašykite mažiau: modeliui, kuris jį taiko, nustatykite mažesnį max_tokens.

Srautas taip pat gali baigtis anksčiau pakeliui pas jus, pavyzdžiui, nutrūkus tinklui arba atnaujinant serverį. Tada jis sustoja be paskutinio gabalo ir be data: [DONE]. Tokį atsakymą laikykite neužbaigtu ir siųskite užklausą dar kartą.

Srauto skaitymas be SDK

Teisingas skaitytuvas daro penkis dalykus: prieš srautą atėjusias klaidas perduoda savo klaidų tvarkymui, padalija turinį į eilutes, praleidžia komentarų eilutes, sustoja ties data: [DONE] ir kiekviename gabale tikrina 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 pavyzdys naudoja paketą requests. JavaScript pavyzdys veikia su Node.js 18 ar naujesne.

Kitų formatų srautai

Galiniai taškai Responses ir Messages taip pat siunčia srautu. Jų kadruose yra eilutė event: su įvykio pavadinimu ir eilutė data: su jo JSON. Kiekvienas puslapis išvardija savo įvykius iš eilės.

Sraute Chat CompletionsResponsesMessages
Kadrai data: eilutėsevent: ir data: eilutėsevent: ir data: eilutės
Mąstymas ateina delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
Atsakymo tekstas ateina delta.contentresponse.output_text.deltatext_delta
Naudojimas ateina Paskutiniame gabaleresponse.completedmessage_delta
Srautas baigiasi Paskutinis gabalas, tada data: [DONE]response.completedmessage_stop