Прескокни до содржината
Стриминг

Стриминг

Поставете stream на true и одговорот пристигнува како server-sent events додека моделот го пишува: прво reasoning, потоа одговорот, па последен chunk со причината за завршување и usage. Користете го секогаш кога човек чека на текстот, и за долги одговори.

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

SDK-ата на OpenAI ви ги даваат chunk-овите еден по еден. Читајте го секој chunk според тоа што носи: парче reasoning, парче од одговорот, повик на алатка или крај на одговорот.

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

Како е обликуван streaming-от

  • Одговорот има статус 200 и header-и content-type: text/event-stream и cache-control: no-cache.
  • Секој настан е еден ред што започнува со data: и по него следува празен ред. Текстот по data: е еден JSON chunk. Нема редови event: или id:.
  • Ред што започнува со две точки, како : ping, е коментар што ја држи врската отворена. Прескокнете го.
  • Streaming-от завршува со data: [DONE]. Streaming што запира без него е нецелосен.

Chunk-ови по редослед

Секој chunk е објект 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
    }
  ]
}

Сите chunk-ови на еден streaming го носат истиот id, created и model. choices секогаш содржи еден запис. Неговиот delta го содржи она што е ново, а finish_reason е null до последниот chunk.

Клуч во delta Тип Опис Го испраќа
role string Секогаш assistant. Пристигнува еднаш, на почетокот на streaming-от. Хостирани open-weight модели, shannon-2-lite, shannon-2-pro
reasoning_content string Парче од reasoning-от на моделот. Спојте ги парчињата по редослед. Хостирани open-weight модели, фамилијата Shannon 3, shannon-1.6-pro, shannon-coder-1
content string Парче од одговорот. Спојте ги парчињата по редослед. Сите модели
tool_calls array Еден комплетен повик на алатка. Видете Delta-и на повик на алатка. Сите модели

Прво доаѓа reasoning-от, потоа одговорот, потоа евентуални повици на алатки. Читајте го chunk-от според клучевите во неговиот delta, а не според неговото место во streaming-от: delta може да содржи role заедно со првото парче текст, а последниот е празен.

Цел streaming онака како што патува. За редовите да бидат кратки, id, object, created и model се изоставени од секој chunk овде.

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-и на reasoning

Модел што прави reasoning го испраќа својот reasoning во delta.reasoning_content пред одговорот. Чувајте ги двата текста одвоено: прикажете го reasoning-от како посебен дел што се собира, или изоставете го.

Хостираните open-weight модели не испраќаат reasoning кога reasoning_effort е off, ниту кога барањето има response_format.

shannon-2-lite и shannon-2-pro исто така прават reasoning пред да одговорат. На овој ендпоинт, додека тоа го прават, испраќаат редови-коментари : thinking, а потоа одговорот.

reasoning_content не е во типизираните модели на SDK-ата на OpenAI. Во Python прочитајте го со getattr(delta, "reasoning_content", None); во JavaScript delta.reasoning_content работи каков што е.

Delta-и на повик на алатка

Повикот на алатка пристигнува цел: еден chunk по повик, чиј delta го содржи повикот со комплетниот стринг arguments. index ги нумерира повиците на одговорот од 0. Кодот што ги спојува arguments парче по парче работи непроменет, бидејќи спојува едно парче.

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

По повиците, последниот chunk има finish_reason tool_calls.

Кога барањето има tools, shannon-1.6-lite, shannon-1.6-pro и shannon-coder-1 го испраќаат текстот на одговорот како една delta на крајот од одговорот.

Последниот chunk

Последниот chunk пред data: [DONE] има празен delta, finish_reason и usage на барањето, сите во истиот chunk. Го има секој streaming; не ви треба 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
  }
}

На хостираните open-weight модели usage ги има и prompt_tokens_details.cached_tokens и completion_tokens_details.reasoning_tokens.

По барање со web_search: true чие пребарување нашло нешто, последниот chunk носи и sources, а еден chunk пред него ги носи ознаките за цитирање како delta.annotations. Вградено веб пребарување

finish_reason Опис Го испраќа
stop Моделот го завршил својот одговор, или се појавил стринг stop. Сите модели
tool_calls Одговорот содржи еден или повеќе повици на алатки. Сите модели
length Одговорот е прекинат на излезното ограничување. Фамилијата Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

Полињата на usage се опишани заедно со ендпоинтот. Chat Completions

Keep-alive редови

Додека моделот работи и сè уште нема што да испрати, streaming-от носи редови-коментари. Не содржат податоци. SSE парсерот сам ги прескокнува; кодот што чита сурови редови мора да го прескокне секој ред што започнува со две точки.

Ред Го испраќа Кога
: ping Фамилијата Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 На секои 15 секунди.
: keepalive Хостирани open-weight модели На секои 15 секунди додека моделот работи.
: thinking shannon-2-lite, shannon-2-pro Додека моделот прави reasoning.

Грешки во streaming

Откако streaming-от ќе започне, неговиот статус е 200, па неуспехот пристигнува во streaming-от: chunk со член error таму каде што би биле 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]

По chunk-от со грешка сепак следуваат последниот chunk, со finish_reason и usage, и data: [DONE]. Проверувајте го секој chunk за error. Без таа проверка одговор што пропаднал на половина изгледа комплетен.

Тип Код Порака Кога
api_error The model backend failed to answer. Please retry. Моделот пропадна, или не напиша ништо. Испратете го барањето повторно.
rate_limit_error Shannon routes are temporarily busy. Please retry. Моделот е зафатен во овој момент. Почекајте неколку секунди и испратете го барањето повторно.
invalid_request_error context_length_exceeded Разговорот е подолг од контекстниот прозорец на моделот. Скратете го пред да го испратите повторно. Текстот на пораката варира.

Хостираните open-weight модели го испраќаат членот error само со type и message.

SDK-ата на OpenAI за Python и JavaScript го креваат chunk-от со грешка како API грешка додека итерирате, затоа завиткајте ја јамката во вашето вообичаено ракување со грешки.

Статус кодовите, телото на грешката и што да се повтори имаат своја страница. Ракување со грешки

Затворање на врската

Затворањето на врската ја запира испораката, а не барањето. Моделот го пишува одговорот до крај и барањето се наплатува како да сте го прочитале целиот. За да платите помалку, побарајте помалку: поставете понизок max_tokens на модел што го применува.

Streaming може да заврши предвреме и на патот до вас, на пример при прекин на мрежата или додека се ажурира сервер. Тогаш запира без последниот chunk и без data: [DONE]. Третирајте го тој одговор како нецелосен и испратете го барањето повторно.

Читање streaming без SDK

Исправен читач прави пет работи: ги праќа грешките што пристигнуваат пред streaming-от на своето ракување со грешки, го дели телото на редови, ги прескокнува редовите-коментари, запира на data: [DONE] и го проверува секој chunk за 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 го користи пакетот requests. Примерот за JavaScript работи на Node.js 18 или понов.

Streaming на другите формати

Ендпоинтите Responses и Messages исто така стримуваат. Нивните фрејмови носат ред event: со името на настанот и ред data: со неговиот JSON. Секоја страница ги наведува своите настани по редослед.

Во streaming-от Chat CompletionsResponsesMessages
Фрејмови Редови data:Редови event: и data:Редови event: и data:
Reasoning-от пристигнува во delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
Текстот на одговорот пристигнува во delta.contentresponse.output_text.deltatext_delta
Usage пристигнува во Последниот chunkresponse.completedmessage_delta
Streaming-от завршува со Последниот chunk, потоа data: [DONE]response.completedmessage_stop