Стриминг
stream'ди true кылыңыз, жооп модель аны жазып жатканда server-sent events катары келет: адегенде reasoning, андан кийин жооп, андан кийин аяктоо себеби жана usage бар акыркы чанк. Аны адам текстти күтүп жатканда жана узун жооптор үчүн колдонуңуз.
POST https://api.shannon-ai.com/v1/chat/completions
OpenAI SDK'лары чанктарды бирден берет. Ар бир чанкты эмнени алып келгенине жараша окуңуз: 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}") 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. Ал бир жолу, стримдин башында келет. | Хостингдеги ачык салмактуу моделдер, shannon-2-lite, shannon-2-pro |
reasoning_content | string | Модельдин reasoning'инин бир бөлүгү. Бөлүктөрдү тартиби менен бириктириңиз. | Хостингдеги ачык салмактуу моделдер, Shannon 3 үй-бүлөсү, shannon-1.6-pro, shannon-coder-1 |
content | string | Жооптун бир бөлүгү. Бөлүктөрдү тартиби менен бириктириңиз. | Бардык моделдер |
tool_calls | array | Бир толук курал чалуусу. Курал чалуу дельталарын караңыз. | Бардык моделдер |
Адегенде reasoning, андан кийин жооп, андан кийин курал чалуулары келет. Чанкты стримдеги орду боюнча эмес, анын 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] Reasoning дельталары
Reasoning жүргүзгөн модель өзүнүн reasoning'ин жооптун алдында delta.reasoning_content ичинде жөнөтөт. Эки текстти бөлүп кармаңыз: reasoning'ди өзүнчө, жыйыла турган бөлүк катары көрсөтүңүз, же койбой коюңуз.
Хостингдеги ачык салмактуу моделдер reasoning_effort off болгондо reasoning жөнөтпөйт, ошондой эле сурамда response_format болгондо да жөнөтпөйт.
shannon-2-lite жана shannon-2-pro да жооп бергенге чейин reasoning жүргүзөт. Бул 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
}
} Хостингдеги ачык салмактуу моделдерде 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 | Хостингдеги ачык салмактуу моделдер | Модель иштеп жатканда ар 15 секунд сайын. |
: thinking | shannon-2-lite, shannon-2-pro | Модель reasoning жүргүзүп жатканда. |
Стримдин ичиндеги каталар
Стрим башталгандан кийин анын статусу 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 | Сүйлөшүү модельдин контекст терезесинен узун. Кайра жөнөтүүдөн мурун аны кыскартыңыз. Билдирүү тексти ар башка. |
Хостингдеги ачык салмактуу моделдер 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: саптары |
| Reasoning келет | 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 |