Hopp til innholdet
Strømming

Strømming

Sett stream til true, så kommer svaret som server-sent events mens modellen skriver det: først resonnering, så svaret, så en siste chunk med finish reason og usage. Bruk det når en person venter på teksten, og for lange svar.

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

OpenAI-SDK-ene gir deg chunkene én for én. Les hver chunk etter det den inneholder: et stykke resonnering, et stykke av svaret, et verktøykall eller slutten på svaret.

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

Hvordan en strøm er bygget opp

  • Svaret har status 200 og headerne content-type: text/event-stream og cache-control: no-cache.
  • Hver hendelse er én linje som starter med data: og etterfølges av en tom linje. Teksten etter data: er én JSON-chunk. Det finnes ingen event:- eller id:-linjer.
  • En linje som starter med kolon, som : ping, er en kommentar som holder forbindelsen åpen. Hopp over den.
  • Strømmen ender med data: [DONE]. En strøm som stopper uten den, er ufullstendig.

Chunker i rekkefølge

Hver chunk er et chat.completion.chunk-objekt:

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

Alle chunker i én strøm har samme id, created og model. choices har alltid én oppføring. Dens delta inneholder det som er nytt, og finish_reason er null til den siste chunken.

Nøkkel i delta Type Beskrivelse Sendes av
role string Alltid assistant. Den kommer én gang, i starten av strømmen. Hostede åpne vektmodeller, shannon-2-lite, shannon-2-pro
reasoning_content string Et stykke av modellens resonnering. Slå sammen stykkene i rekkefølge. Hostede åpne vektmodeller, Shannon 3-familien, shannon-1.6-pro, shannon-coder-1
content string Et stykke av svaret. Slå sammen stykkene i rekkefølge. Alle modeller
tool_calls array Ett komplett verktøykall. Se Verktøykall-deltaer. Alle modeller

Resonnering kommer først, deretter svaret, deretter eventuelle verktøykall. Les en chunk etter nøklene i delta, ikke etter plassen i strømmen: en delta kan inneholde role sammen med det første tekststykket, og den siste er tom.

En hel strøm slik den sendes. For å holde linjene korte er id, object, created og model utelatt fra hver chunk her.

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]

Resonnerings-deltaer

En modell som resonnerer, sender resonneringen sin i delta.reasoning_content før svaret. Hold de to tekstene atskilt: vis resonneringen som en egen, sammenleggbar del, eller utelat den.

De hostede åpne vektmodellene sender ingen resonnering når reasoning_effort er off, og ingen når forespørselen har en response_format.

shannon-2-lite og shannon-2-pro resonnerer også før de svarer. På dette endepunktet sender de kommentarlinjer : thinking mens de gjør det, og deretter svaret.

reasoning_content finnes ikke i de typede modellene i OpenAI-SDK-ene. I Python leser du den med getattr(delta, "reasoning_content", None); i JavaScript fungerer delta.reasoning_content som den er.

Verktøykall-deltaer

Et verktøykall kommer helt: én chunk per kall, der delta inneholder kallet med hele arguments-strengen. index nummererer kallene i svaret fra 0. Kode som slår sammen arguments stykke for stykke, fungerer uendret, fordi den slår sammen ett stykke.

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

Etter kallene har den siste chunken finish_reason tool_calls.

Når forespørselen har tools, sender shannon-1.6-lite, shannon-1.6-pro og shannon-coder-1 svarteksten som én delta på slutten av svaret.

Den siste chunken

Den siste chunken før data: [DONE] har en tom delta, finish_reason og usage for forespørselen, alt i samme chunk. Hver strøm har den; du trenger ikke 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
  }
}

På de hostede åpne vektmodellene har usage også prompt_tokens_details.cached_tokens og completion_tokens_details.reasoning_tokens.

Etter en forespørsel med web_search: true der søket fant noe, har den siste chunken også sources, og en chunk før den har siteringsmarkeringene som delta.annotations. Innebygd websøk

finish_reason Beskrivelse Sendes av
stop Modellen fullførte svaret sitt, eller en stop-streng dukket opp. Alle modeller
tool_calls Svaret inneholder ett eller flere verktøykall. Alle modeller
length Svaret ble avkortet ved utdatagrensen. Shannon 3-familien, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1

Feltene i usage er beskrevet sammen med endepunktet. Chat Completions

Keep-alive-linjer

Mens modellen arbeider og ennå ikke har noe å sende, har strømmen kommentarlinjer. De inneholder ingen data. En SSE-parser hopper over dem av seg selv; kode som leser rå linjer, må hoppe over hver linje som starter med kolon.

Linje Sendes av Når
: ping Shannon 3-familien, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 Hvert 15. sekund.
: keepalive Hostede åpne vektmodeller Hvert 15. sekund mens modellen arbeider.
: thinking shannon-2-lite, shannon-2-pro Mens modellen resonnerer.

Feil inne i en strøm

Når en strøm har startet, er statusen 200, så en feil kommer inne i strømmen: en chunk med et error-medlem der choices ellers ville stått.

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]

Etter feil-chunken følger likevel den siste chunken, med finish_reason og usage, og data: [DONE]. Sjekk hver chunk for error. Uten den sjekken ser et svar som feilet halvveis, komplett ut.

Type Kode Melding Når
api_error The model backend failed to answer. Please retry. Modellen feilet, eller skrev ingenting. Send forespørselen på nytt.
rate_limit_error Shannon routes are temporarily busy. Please retry. Modellen er opptatt akkurat nå. Vent noen sekunder og send forespørselen på nytt.
invalid_request_error context_length_exceeded Samtalen er lengre enn modellens kontekstvindu. Kort den ned før du sender den på nytt. Meldingsteksten varierer.

De hostede åpne vektmodellene sender error-medlemmet med bare type og message.

OpenAI-SDK-ene for Python og JavaScript kaster en feil-chunk som en API-feil mens du itererer, så pakk løkken inn i din vanlige feilhåndtering.

Statuskoder, feilkroppen og hva som kan prøves på nytt, har sin egen side. Feilhåndtering

Lukke forbindelsen

Å lukke forbindelsen stopper leveringen, ikke forespørselen. Modellen skriver svaret til slutten, og forespørselen belastes som om du hadde lest alt. For å betale mindre må du be om mindre: sett en lavere max_tokens på en modell som tar hensyn til det.

En strøm kan også ta slutt tidlig på veien til deg, for eksempel ved et nettverksbrudd eller mens en server oppdateres. Den stopper da uten den siste chunken og uten data: [DONE]. Behandle det svaret som ufullstendig og send forespørselen på nytt.

Lese en strøm uten SDK

En korrekt leser gjør fem ting: den sender feil som kommer før strømmen, til feilhåndteringen sin, deler kroppen i linjer, hopper over kommentarlinjer, stopper ved data: [DONE] og sjekker hver chunk for 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-eksemplet bruker pakken requests. JavaScript-eksemplet kjører på Node.js 18 eller nyere.

Strømmer i de andre formatene

Endepunktene Responses og Messages støtter også streaming. Rammene deres har en event:-linje med navnet på hendelsen og en data:-linje med JSON-en. Hver side lister hendelsene sine i rekkefølge.

I strømmen Chat CompletionsResponsesMessages
Rammer data:-linjerevent:- og data:-linjerevent:- og data:-linjer
Resonnering kommer i delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
Svartekst kommer i delta.contentresponse.output_text.deltatext_delta
Usage kommer i Den siste chunkenresponse.completedmessage_delta
Strømmen ender med Den siste chunken, deretter data: [DONE]response.completedmessage_stop