Ugrás a tartalomra
Streaming

Streaming

Állítsd a streamet true értékre, és a válasz server-sent eventként érkezik, miközben a modell írja: először a gondolkodás, utána a válasz, végül egy utolsó chunk a befejezés okával és a használati adatokkal. Használd, amikor ember vár a szövegre, és hosszú válaszoknál.

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

Az OpenAI SDK-k egyenként adják a chunkokat. Minden chunkot aszerint olvass, mit hordoz: gondolkodás egy darabját, a válasz egy darabját, eszközhívást vagy a válasz végét.

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

Hogyan keretezett a stream

  • A válasz státusza 200, a fejlécei pedig content-type: text/event-stream és cache-control: no-cache.
  • Minden esemény egy data: előtaggal kezdődő sor, amelyet egy üres sor követ. A data: utáni szöveg egy JSON-chunk. Nincsenek event: vagy id: sorok.
  • A kettősponttal kezdődő sor, például a : ping, megjegyzés, amely nyitva tartja a kapcsolatot. Ugord át.
  • A stream a data: [DONE] sorral ér véget. Az a stream, amely nélküle áll le, hiányos.

Chunkok sorrendben

Minden chunk egy chat.completion.chunk objektum:

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

Egy stream összes chunkja ugyanazt az id, created és model értéket hordozza. A choices mindig egy elemet tartalmaz. A delta azt tartalmazza, ami új, a finish_reason pedig null az utolsó chunkig.

Kulcs a delta-ban Típus Leírás Küldi
role string Mindig assistant. A stream elején egyszer érkezik. Hosztolt nyílt súlyú modellek, shannon-2-lite, shannon-2-pro
reasoning_content string A modell gondolkodásának egy darabja. Fűzd össze a darabokat sorrendben. Hosztolt nyílt súlyú modellek, Shannon 3 család, shannon-1.6-pro, shannon-coder-1
content string A válasz egy darabja. Fűzd össze a darabokat sorrendben. Minden modell
tool_calls array Egy teljes eszközhívás. Lásd: Eszközhívás-deltái. Minden modell

Először a gondolkodás jön, utána a válasz, majd az esetleges eszközhívások. A chunkot a delta kulcsai alapján olvasd, ne a stream-beli helye alapján: a delta hordozhat role mezőt az első szövegdarabbal együtt, az utolsó pedig üres.

Egy teljes stream úgy, ahogy utazik. A sorok röviden tartása végett az id, object, created és model mező itt minden chunkból ki van hagyva.

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]

Gondolkodási deltái

A gondolkodó modell a gondolkodását a delta.reasoning_content mezőben küldi a válasz előtt. Tartsd külön a két szöveget: mutasd a gondolkodást külön, összecsukható részként, vagy hagyd el.

A hosztolt nyílt súlyú modellek nem küldenek gondolkodást, ha a reasoning_effort értéke off, és akkor sem, ha a kérés response_format mezőt tartalmaz.

A shannon-2-lite és a shannon-2-pro is gondolkodik a válasz előtt. Ezen a végponton eközben : thinking megjegyzéssorokat küldenek, majd a választ.

A reasoning_content nincs benne az OpenAI SDK-k típusos modelljeiben. Pythonban a getattr(delta, "reasoning_content", None) hívással olvasd; JavaScriptben a delta.reasoning_content közvetlenül működik.

Eszközhívás-deltái

Az eszközhívás egészében érkezik: hívásonként egy chunk, amelynek delta mezője a hívást a teljes arguments stringgel tartalmazza. Az index a válasz hívásait számozza 0-tól. Az arguments mezőt darabonként összefűző kód változtatás nélkül működik, mert egy darabot fűz.

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

A hívások után az utolsó chunk finish_reason értéke tool_calls.

Ha a kérés tools mezőt tartalmaz, a shannon-1.6-lite, shannon-1.6-pro és shannon-coder-1 a válaszszöveget egyetlen deltában küldi a válasz végén.

Az utolsó chunk

A data: [DONE] előtti utolsó chunknak üres a delta mezője, és a kérés finish_reason és usage értékét tartalmazza, mindet ugyanabban a chunkban. Minden stream tartalmazza; nincs szükséged a stream_options mezőre.

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

A hosztolt nyílt súlyú modelleknél a usage a prompt_tokens_details.cached_tokens és a completion_tokens_details.reasoning_tokens mezőt is tartalmazza.

Az olyan kérés után, amely web_search: true értéket küld, és amelynek a keresése talált valamit, az utolsó chunk sources mezőt is tartalmaz, az előtte lévő chunk pedig a hivatkozási jelöléseket hordozza delta.annotations néven. Beépített webes keresés

finish_reason Leírás Küldi
stop A modell befejezte a válaszát, vagy megjelent egy stop string. Minden modell
tool_calls A válasz egy vagy több eszközhívást tartalmaz. Minden modell
length A választ a kimeneti korlátnál levágták. Shannon 3 család, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

A usage mezőit a végpont leírása tartalmazza. Chat Completions

Keep-alive sorok

Amíg a modell dolgozik, és még nincs mit küldenie, a stream megjegyzéssorokat hordoz. Nem tartalmaznak adatot. Az SSE-értelmező magától átugorja őket; a nyers sorokat olvasó kódnak át kell ugrania minden kettősponttal kezdődő sort.

Sor Küldi Mikor
: ping Shannon 3 család, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 15 másodpercenként.
: keepalive Hosztolt nyílt súlyú modellek 15 másodpercenként, amíg a modell dolgozik.
: thinking shannon-2-lite, shannon-2-pro Amíg a modell gondolkodik.

Hibák a streamen belül

Miután a stream elindult, a státusza 200, ezért a hiba a streamen belül érkezik: egy chunk error taggal ott, ahol a choices lenne.

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]

A hibás chunk után még követi az utolsó chunk, a finish_reason és a usage értékkel, valamint a data: [DONE]. Minden chunkban ellenőrizd az error mezőt. Ellenőrzés nélkül a félúton megbukott válasz teljesnek látszik.

Típus Kód Üzenet Mikor
api_error The model backend failed to answer. Please retry. A modell hibázott, vagy nem írt semmit. Küldd el újra a kérést.
rate_limit_error Shannon routes are temporarily busy. Please retry. A modell ebben a pillanatban foglalt. Várj néhány másodpercet, és küldd el újra a kérést.
invalid_request_error context_length_exceeded A beszélgetés hosszabb a modell kontextusablakánál. Rövidítsd, mielőtt újra elküldöd. Az üzenet szövege változó.

A hosztolt nyílt súlyú modellek az error tagot csak type és message mezővel küldik.

Az OpenAI Python és JavaScript SDK-k a hibás chunkot API-hibaként dobják, amíg iterálsz, ezért a ciklust vedd körbe a szokásos hibakezeléseddel.

A státuszkódok, a hibatörzs és az újrapróbálandó esetek külön oldalon vannak. Hibakezelés

A kapcsolat bezárása

A kapcsolat bezárása a kézbesítést állítja le, nem a kérést. A modell a válasz végéig megírja a választ, és a kérést úgy számlázza, mintha végigolvastad volna. Ha kevesebbet akarsz fizetni, kevesebbet kérj: állíts alacsonyabb max_tokens értéket olyan modellen, amely érvényesíti.

A stream hozzád vezető úton korán is véget érhet, például hálózati szakadáskor vagy szerverfrissítés közben. Ilyenkor az utolsó chunk és a data: [DONE] nélkül áll le. Tekintsd hiányosnak az ilyen választ, és küldd el újra a kérést.

Stream olvasása SDK nélkül

A helyes olvasó öt dolgot tesz: a stream előtt érkező hibákat a hibakezelésébe küldi, a törzset sorokra bontja, átugorja a megjegyzéssorokat, megáll a data: [DONE] soron, és minden chunkban ellenőrzi az error mezőt.

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

A Python minta a requests csomagot használja. A JavaScript minta Node.js 18 vagy újabb verzión fut.

A többi formátum streamjei

A Responses és a Messages végpont is streamel. Framejeik egy event: sort hordoznak az esemény nevével, és egy data: sort a JSON-jával. Mindegyik oldal sorrendben felsorolja az eseményeit.

A streamben Chat CompletionsResponsesMessages
Framek data: sorokevent: és data: sorokevent: és data: sorok
A gondolkodás ebben érkezik delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
A válaszszöveg ebben érkezik delta.contentresponse.output_text.deltatext_delta
A használati adatok ebben érkeznek Az utolsó chunkresponse.completedmessage_delta
A stream vége Az utolsó chunk, majd a data: [DONE]response.completedmessage_stop