Стриминг
Stream мәнін true етіп орнатыңыз, сонда жауап модель жазып жатқанда server-sent events ретінде келеді: алдымен пайымдау, содан кейін жауап, одан кейін аяқталу себебі мен usage бар соңғы чанк. Мәтінді адам күтіп тұрғанда және ұзақ жауаптар үшін қолданыңыз.
POST https://api.shannon-ai.com/v1/chat/completions
OpenAI SDK-лары чанктарды бір-бірлеп береді. Әр чанкті оның алып жүргені бойынша оқыңыз: пайымдау бөлігі, жауап бөлігі, құрал шақыруы немесе жауаптың соңы.
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
}' Стрим қалай кадрланады
- Жауаптың мәртебесі
200, тақырыптарыcontent-type: text/event-streamжәнеcache-control: no-cache. - Әр оқиға —
data:деп басталатын бір жол, одан кейін бос жол.data:кейінгі мәтін — бір JSON чанкі.event:немесеid:жолдары жоқ. : pingсияқты қос нүктеден басталатын жол — қосылымды ашық ұстайтын түсініктеме. Оны өткізіп жіберіңіз.- Стрим
data: [DONE]жолымен аяқталады. Онсыз тоқтаған стрим толық емес.
Чанктар реті бойынша
Әр чанк — chat.completion.chunk нысаны:
{
"id": "chatcmpl-5f0c1e7a9b3d4c62a8e1f07d2b46c9a3",
"object": "chat.completion.chunk",
"created": 1791625200,
"model": "shannon-3",
"choices": [
{
"index": 0,
"delta": {
"content": "Server-sent"
},
"finish_reason": null
}
]
} Бір стримнің барлық чанктары бірдей id, created және model алып жүреді. choices әрқашан бір жазбаны қамтиды. Оның delta мәні жаңаны қамтиды, ал finish_reason соңғы чанкке дейін null болады.
| Delta ішіндегі кілт | Түрі | Сипаттама | Жіберетін |
|---|---|---|---|
role | string | Әрқашан assistant. Ол стримнің басында бір рет келеді. | Hosted open-weight модельдер, shannon-2-lite, shannon-2-pro |
reasoning_content | string | Модель пайымдауының бір бөлігі. Бөліктерді ретімен біріктіріңіз. | Hosted open-weight модельдер, Shannon 3 тобы, shannon-1.6-pro, shannon-coder-1 |
content | string | Жауаптың бір бөлігі. Бөліктерді ретімен біріктіріңіз. | Барлық модельдер |
tool_calls | array | Бір толық құрал шақыруы. «Құрал шақыру дельталары» бөлімін қараңыз. | Барлық модельдер |
Алдымен пайымдау, содан кейін жауап, одан кейін құрал шақырулары келеді. Чанкті стримдегі орнымен емес, delta ішіндегі кілттерімен оқыңыз: delta ішінде role бірінші мәтін бөлігімен бірге болуы мүмкін, ал соңғысы бос.
Бүкіл стрим жолда жүргендей. Жолдар қысқа болуы үшін мұнда әр чанктан id, object, created және model алынып тасталған.
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_content ішінде жібереді. Екі мәтінді бөлек ұстаңыз: пайымдауды жеке, жиылатын бөлік ретінде көрсетіңіз немесе қалдырып кетіңіз.
reasoning_effort мәні off болғанда, сондай-ақ сұрауда response_format болғанда hosted open-weight модельдер пайымдауды жібермейді.
shannon-2-lite және shannon-2-pro да жауап бермес бұрын пайымдайды. Бұл endpoint-те олар пайымдап жатқанда : thinking түсініктеме жолдарын, содан кейін жауапты жібереді.
reasoning_content OpenAI SDK-ларының типтелген модельдерінде жоқ. Python-да оны getattr(delta, "reasoning_content", None) арқылы оқыңыз; JavaScript-те delta.reasoning_content өзгеріссіз жұмыс істейді.
Құрал шақыру дельталары
Құрал шақыруы тұтас келеді: әр шақыруға бір чанк, оның delta мәнінде шақыру толық arguments жолымен тұрады. index жауаптың шақыруларын 0-ден нөмірлейді. arguments мәнін бөлік-бөлігімен біріктіретін код өзгеріссіз жұмыс істейді, өйткені ол бір бөлікті біріктіреді.
{
"tool_calls": [
{
"index": 0,
"id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
} Шақырулардан кейін соңғы чанкте finish_reason мәні tool_calls болады.
Сұрауда tools болғанда, shannon-1.6-lite, shannon-1.6-pro және shannon-coder-1 жауап мәтінін жауаптың соңында бір дельта ретінде жібереді.
Соңғы чанк
data: [DONE] алдындағы соңғы чанкте бос delta, finish_reason және сұраудың usage мәні бар, бәрі бір чанкте. Оны әр стрим алып жүреді; stream_options қажет емес.
{
"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
}
} Hosted open-weight модельдерде usage ішінде prompt_tokens_details.cached_tokens және completion_tokens_details.reasoning_tokens де бар.
Іздеуі бірдеңе тапқан web_search: true бар сұраудан кейін соңғы чанкте sources де болады, ал одан бұрынғы бір чанкте дәйексөз белгілері delta.annotations ретінде келеді. Кіріктірілген веб іздеу
| finish_reason | Сипаттама | Жіберетін |
|---|---|---|
stop | Модель жауабын аяқтады, немесе stop жолы пайда болды. | Барлық модельдер |
tool_calls | Жауапта бір немесе бірнеше құрал шақыруы бар. | Барлық модельдер |
length | Жауап шығыс лимитінде үзілді. | Shannon 3 тобы, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 |
usage өрістері endpoint-пен бірге сипатталған. Chat Completions
Keep-alive жолдары
Модель жұмыс істеп жатып, жіберетін ештеңесі жоқ кезде, стрим түсініктеме жолдарын алып жүреді. Оларда дерек жоқ. SSE парсері оларды өзі өткізіп жібереді; шикі жолдарды оқитын код қос нүктеден басталатын әр жолды өткізіп жіберуі керек.
| Жол | Жіберетін | Қашан |
|---|---|---|
: ping | Shannon 3 тобы, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 | Әр 15 секунд сайын. |
: keepalive | Hosted open-weight модельдер | Модель жұмыс істеп жатқанда әр 15 секунд сайын. |
: thinking | shannon-2-lite, shannon-2-pro | Модель пайымдап жатқанда. |
Стрим ішіндегі қателер
Стрим басталғаннан кейін оның мәртебесі 200 болады, сондықтан ақау стрим ішінде келеді: choices тұратын жерде error мүшесі бар чанк.
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] Қате чанкінен кейін finish_reason және usage бар соңғы чанк және data: [DONE] бәрібір келеді. Әр чанкті 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 | Сөйлесу модельдің контекст терезесінен ұзын. Қайта жібермес бұрын оны қысқартыңыз. Хабарлама мәтіні өзгеріп тұрады. |
Hosted open-weight модельдер error мүшесін тек type және message арқылы жібереді.
OpenAI Python және JavaScript SDK-лары қате чанкін итерация кезінде API қатесі ретінде көтереді, сондықтан циклді әдеттегі қателерді өңдеумен орап қойыңыз.
Мәртебе кодтарының, қате денесінің және нені қайталау керегінің өз беті бар. Қателерді өңдеу
Қосылымды жабу
Қосылымды жабу жеткізуді тоқтатады, сұрауды емес. Модель жауапты соңына дейін жазады, ал сұрауға бәрін оқығандай төленеді. Аз төлеу үшін аз сұраңыз: оны қолданатын модельде max_tokens мәнін төмен орнатыңыз.
Стрим сізге жеткенше ерте үзілуі де мүмкін, мысалы желі үзілгенде немесе сервер жаңартылып жатқанда. Сонда ол соңғы чанксіз және data: [DONE] жолынсыз тоқтайды. Мұндай жауапты толық емес деп санаңыз және сұрауды қайта жіберіңіз.
Стримді SDK-сыз оқу
Дұрыс оқушы бес нәрсе жасайды: стримнен бұрын келген қателерді өз қателерді өңдеуіне жібереді, денені жолдарға бөледі, түсініктеме жолдарын өткізіп жібереді, data: [DONE] жолында тоқтайды және әр чанкті 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); Python мысалы requests пакетін қолданады. JavaScript мысалы Node.js 18 немесе одан жаңасында жұмыс істейді.
Басқа форматтардың стримдері
Responses және Messages endpoint-тері де стримдейді. Олардың кадрларында оқиға атауы бар event: жолы және оның JSON-ы бар data: жолы болады. Әр бет өз оқиғаларын ретімен тізеді.
| Стримде | Chat Completions | Responses | Messages |
|---|---|---|---|
| Кадрлар | data: жолдары | event: және data: жолдары | event: және data: жолдары |
| Пайымдау мында келеді | delta.reasoning_content | response.reasoning_summary_text.delta | thinking_delta |
| Жауап мәтіні мында келеді | delta.content | response.output_text.delta | text_delta |
| Usage мында келеді | Соңғы чанк | response.completed | message_delta |
| Стрим мынамен аяқталады | Соңғы чанк, содан кейін data: [DONE] | response.completed | message_stop |