Stroming
Stel stream op true en die antwoord kom as bediener-gestuurde gebeure aan terwyl die model dit skryf: eers redenering, dan die antwoord, dan 'n laaste chunk met die voltooiingsrede en die gebruik. Gebruik dit wanneer 'n mens op die teks wag, en vir lang antwoorde.
POST https://api.shannon-ai.com/v1/chat/completions
Die OpenAI-SDK's gee jou die chunks een vir een. Lees elke chunk volgens wat dit dra: 'n stuk redenering, 'n stuk van die antwoord, 'n gereedskapoproep, of die einde van die antwoord.
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}") import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.shannon-ai.com/v1",
});
const stream = await client.chat.completions.create({
model: "shannon-3",
messages: [{ role: "user", content: "Explain server-sent events in three sentences." }],
stream: true,
});
let answer = "";
let reasoning = "";
const toolCalls = [];
let finishReason = null;
let usage = null;
try {
for await (const chunk of stream) {
if (chunk.usage) usage = chunk.usage;
const choice = chunk.choices?.[0];
if (!choice) continue;
const delta = choice.delta;
if (delta.reasoning_content) reasoning += delta.reasoning_content;
if (delta.content) {
answer += delta.content;
process.stdout.write(delta.content);
}
for (const call of delta.tool_calls ?? []) {
toolCalls.push({ id: call.id, name: call.function.name, arguments: call.function.arguments });
}
if (choice.finish_reason) finishReason = choice.finish_reason;
}
} catch (error) {
// A failure after the stream started arrives as an error chunk.
console.error("\nstream failed:", error.message);
}
console.log("\n", finishReason, usage); curl -N https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "shannon-3",
"messages": [{"role": "user", "content": "Explain server-sent events in three sentences."}],
"stream": true
}' Hoe 'n stroom geraam word
- Die antwoord het status
200en die headerscontent-type: text/event-streamencache-control: no-cache. - Elke gebeurtenis is een reël wat met
data:begin en deur 'n leë reël gevolg word. Die teks nadata:is een JSON-chunk. Daar is geenevent:- ofid:-reëls nie. - 'n Reël wat met 'n dubbelpunt begin, soos
: ping, is 'n kommentaar wat die verbinding oop hou. Slaan dit oor. - Die stroom eindig met
data: [DONE]. 'n Stroom wat daarsonder stop, is onvolledig.
Chunks in volgorde
Elke chunk is 'n chat.completion.chunk-objek:
{
"id": "chatcmpl-5f0c1e7a9b3d4c62a8e1f07d2b46c9a3",
"object": "chat.completion.chunk",
"created": 1791625200,
"model": "shannon-3",
"choices": [
{
"index": 0,
"delta": {
"content": "Server-sent"
},
"finish_reason": null
}
]
} Alle chunks van een stroom dra dieselfde id, created en model. choices hou altyd een inskrywing. Sy delta hou wat nuut is, en finish_reason is null tot die laaste chunk.
| Sleutel in delta | Tipe | Beskrywing | Gestuur deur |
|---|---|---|---|
role | string | Altyd assistant. Dit kom een keer aan, aan die begin van die stroom. | Gehuisveste oopgewig-modelle, shannon-2-lite, shannon-2-pro |
reasoning_content | string | 'n Stuk van die model se redenering. Voeg die stukke in volgorde saam. | Gehuisveste oopgewig-modelle, Shannon 3-familie, shannon-1.6-pro, shannon-coder-1 |
content | string | 'n Stuk van die antwoord. Voeg die stukke in volgorde saam. | Alle modelle |
tool_calls | array | Een volledige gereedskapoproep. Sien Gereedskapoproep-deltas. | Alle modelle |
Redenering kom eerste, dan die antwoord, dan enige gereedskapoproepe. Lees 'n chunk volgens die sleutels in sy delta, nie volgens sy plek in die stroom nie: 'n delta kan role saam met die eerste stuk teks hou, en die laaste een is leeg.
'n Hele stroom soos dit reis. Om die reëls kort te hou, is id, object, created en model hier uit elke chunk gelaat.
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] Redeneer-deltas
'n Model wat redeneer, stuur sy redenering in delta.reasoning_content voor die antwoord. Hou die twee tekste uitmekaar: wys die redenering as 'n afsonderlike, invoubare deel, of laat dit uit.
Die gehuisveste oopgewig-modelle stuur geen redenering wanneer reasoning_effort off is nie, en ook nie wanneer die versoek 'n response_format het nie.
shannon-2-lite en shannon-2-pro redeneer ook voordat hulle antwoord. Op hierdie eindpunt stuur hulle : thinking-kommentaarreëls terwyl hulle dit doen, en dan die antwoord.
reasoning_content is nie in die getipeerde modelle van die OpenAI-SDK's nie. In Python lees dit met getattr(delta, "reasoning_content", None); in JavaScript werk delta.reasoning_content soos dit is.
Gereedskapoproep-deltas
'n Gereedskapoproep kom heel aan: een chunk per oproep, waarvan die delta die oproep met die volledige arguments-string hou. index nommer die oproepe van die antwoord vanaf 0. Kode wat arguments stuk vir stuk saamvoeg, werk ongewysig, omdat dit een stuk saamvoeg.
{
"tool_calls": [
{
"index": 0,
"id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
} Ná die oproepe het die laaste chunk finish_reason tool_calls.
Wanneer die versoek tools het, stuur shannon-1.6-lite, shannon-1.6-pro en shannon-coder-1 die antwoordteks as een delta aan die einde van die antwoord.
Die laaste chunk
Die laaste chunk voor data: [DONE] het 'n leë delta, die finish_reason en die usage van die versoek, almal in dieselfde chunk. Elke stroom het dit; jy het nie stream_options nodig nie.
{
"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
}
} Op die gehuisveste oopgewig-modelle het usage ook prompt_tokens_details.cached_tokens en completion_tokens_details.reasoning_tokens.
Ná 'n versoek met web_search: true waarvan die soektog iets gevind het, dra die laaste chunk ook sources, en een chunk daarvoor dra die aanhalingsmerke as delta.annotations. Websoektog
| finish_reason | Beskrywing | Gestuur deur |
|---|---|---|
stop | Die model het sy antwoord voltooi, of 'n stop-string het verskyn. | Alle modelle |
tool_calls | Die antwoord hou een of meer gereedskapoproepe. | Alle modelle |
length | Die antwoord is by die uitvoerperk afgesny. | Shannon 3-familie, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 |
Die velde van usage word saam met die eindpunt beskryf. Chat Completions
Keep-alive-reëls
Terwyl die model werk en nog niks het om te stuur nie, dra die stroom kommentaarreëls. Hulle hou geen data nie. 'n SSE-ontleder slaan hulle self oor; kode wat rou reëls lees, moet elke reël wat met 'n dubbelpunt begin, oorslaan.
| Reël | Gestuur deur | Wanneer |
|---|---|---|
: ping | Shannon 3-familie, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 | Elke 15 sekondes. |
: keepalive | Gehuisveste oopgewig-modelle | Elke 15 sekondes terwyl die model werk. |
: thinking | shannon-2-lite, shannon-2-pro | Terwyl die model redeneer. |
Foute binne 'n stroom
Sodra 'n stroom begin het, is sy status 200, so 'n mislukking kom binne die stroom aan: 'n chunk met 'n error-lid waar choices sou wees.
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] Ná die fout-chunk volg die laaste chunk, met finish_reason en usage, en data: [DONE] steeds. Toets elke chunk vir error. Sonder daardie toets lyk 'n antwoord wat halfpad misluk het volledig.
| Tipe | Kode | Boodskap | Wanneer |
|---|---|---|---|
api_error | The model backend failed to answer. Please retry. | Die model het misluk, of niks geskryf nie. Stuur die versoek weer. | |
rate_limit_error | Shannon routes are temporarily busy. Please retry. | Die model is op hierdie oomblik besig. Wag 'n paar sekondes en stuur die versoek weer. | |
invalid_request_error | context_length_exceeded | Die gesprek is langer as die model se konteksvenster. Verkort dit voordat jy dit weer stuur. Die boodskapteks wissel. |
Die gehuisveste oopgewig-modelle stuur die error-lid met net type en message.
Die OpenAI Python- en JavaScript-SDK's gooi 'n fout-chunk as 'n API-fout terwyl jy herhaal, so draai die lus in jou gewone foutafhandeling toe.
Statuskodes, die foutliggaam en wat om weer te probeer, het hul eie bladsy. Fouthantering
Die verbinding toemaak
Om die verbinding toe te maak, stop die aflewering, nie die versoek nie. Die model skryf die antwoord tot by sy einde en die versoek word gehef asof jy dit alles gelees het. Om minder te betaal, vra vir minder: stel 'n laer max_tokens op 'n model wat dit toepas.
'n Stroom kan ook vroeg op pad na jou eindig, byvoorbeeld by 'n netwerkbreuk of terwyl 'n bediener opgedateer word. Dit stop dan sonder die laaste chunk en sonder data: [DONE]. Behandel daardie antwoord as onvolledig en stuur die versoek weer.
'n Stroom sonder 'n SDK lees
'n Korrekte leser doen vyf dinge: dit stuur foute wat voor die stroom aankom na sy foutafhandeling, verdeel die liggaam in reëls, slaan kommentaarreëls oor, stop by data: [DONE], en toets elke chunk vir 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)) const response = await fetch("https://api.shannon-ai.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "shannon-3",
messages: [{ role: "user", content: "Explain server-sent events in three sentences." }],
stream: true,
}),
});
// 1. Errors before the stream are plain JSON with their own status.
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const decoder = new TextDecoder();
let buffer = "";
let answer = "";
let done = false;
read: for await (const bytes of response.body) {
buffer += decoder.decode(bytes, { stream: true });
const lines = buffer.split("\n"); // 2. one line at a time
buffer = lines.pop(); // keep the unfinished line
for (const line of lines) {
if (!line.startsWith("data: ")) continue; // 3. empty lines and ": ping" comments
const data = line.slice(6);
if (data === "[DONE]") { // 4. the end of the stream
done = true;
break read;
}
const chunk = JSON.parse(data);
if (chunk.error) throw new Error(chunk.error.message); // 5. a failure inside the stream
const choice = chunk.choices[0];
if (choice.delta.content) answer += choice.delta.content;
if (chunk.usage) console.log(choice.finish_reason, chunk.usage);
}
}
if (!done) throw new Error("the stream ended before [DONE]");
console.log(answer); Die Python-voorbeeld gebruik die requests-pakket. Die JavaScript-voorbeeld loop op Node.js 18 of nuuwer.
Strome van die ander formate
Die Responses- en Messages-eindpunte stroom ook. Hul rame dra 'n event:-reël met die naam van die gebeurtenis en 'n data:-reël met sy JSON. Elke bladsy lys sy gebeure in volgorde.
| In die stroom | Chat Completions | Responses | Messages |
|---|---|---|---|
| Rame | data:-reëls | event:- en data:-reëls | event:- en data:-reëls |
| Redenering kom aan in | delta.reasoning_content | response.reasoning_summary_text.delta | thinking_delta |
| Antwoordteks kom aan in | delta.content | response.output_text.delta | text_delta |
| Gebruik kom aan in | Die laaste chunk | response.completed | message_delta |
| Die stroom eindig met | Die laaste chunk, dan data: [DONE] | response.completed | message_stop |