Ստրիմինգ
Սահմանեք stream-ը true, և պատասխանը կգա որպես server-sent events՝ մոդելի գրելու ընթացքում. սկզբում reasoning-ը, ապա պատասխանը, ապա վերջին chunk-ը՝ ավարտի պատճառով և usage-ով։ Օգտագործեք այն, երբ մարդը սպասում է տեքստին, և երկար պատասխանների համար։
POST https://api.shannon-ai.com/v1/chat/completions
OpenAI SDK-ները 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}") 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-cacheheader-ները։ - Յուրաքանչյուր իրադարձություն մեկ տող է, որը սկսվում է
data:-ով, և հետևում է դատարկ տողը։data:-ից հետո տեքստը մեկ JSON chunk է։event:կամid:տողեր չկան։ - Երկու կետով սկսվող տողը, օրինակ՝
: ping, մեկնաբանություն է, որը բաց է պահում կապը։ Բաց թողեք այն։ - Հոսքն ավարտվում է
data: [DONE]-ով։ Առանց դրա կանգ առած հոսքը թերի է։
Chunk-երը՝ ըստ հերթականության
Յուրաքանչյուր chunk 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
}
]
} Մեկ հոսքի բոլոր chunk-երը կրում են նույն id-ն, created-ը և model-ը։ choices-ը միշտ պարունակում է մեկ գրառում։ Դրա delta-ն պարունակում է նորը, իսկ finish_reason-ը null է մինչև վերջին chunk-ը։
| Բանալի delta-ում | Տեսակ | Նկարագրություն | Ուղարկում է |
|---|---|---|---|
role | string | Միշտ assistant։ Գալիս է մեկ անգամ՝ հոսքի սկզբում։ | Hosted open-weight մոդելներ, shannon-2-lite, shannon-2-pro |
reasoning_content | string | Մոդելի reasoning-ի մի կտոր։ Կտորները միացրեք ըստ հերթականության։ | Hosted open-weight մոդելներ, Shannon 3 ընտանիք, shannon-1.6-pro, shannon-coder-1 |
content | string | Պատասխանի մի կտոր։ Կտորները միացրեք ըստ հերթականության։ | Բոլոր մոդելները |
tool_calls | array | Մեկ ամբողջական գործիքի կանչ։ Տե՛ս Գործիքի կանչի delta-ներ։ | Բոլոր մոդելները |
Սկզբում գալիս է reasoning-ը, ապա պատասխանը, ապա գործիքի կանչերը, եթե կան։ Chunk-ը կարդացեք ըստ իր delta-ի բանալիների, ոչ թե հոսքում ունեցած տեղով. delta-ն կարող է պարունակել role տեքստի առաջին կտորի հետ, իսկ վերջինը դատարկ է։
Ամբողջ հոսքն այնպես, ինչպես այն ճանապարհորդում է։ Տողերը կարճ պահելու համար id, object, created և model այստեղ բաց են թողնված յուրաքանչյուր chunk-ից։
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 delta-ներ
Տրամաբանող մոդելն իր reasoning-ը ուղարկում է delta.reasoning_content-ում՝ պատասխանից առաջ։ Երկու տեքստերը պահեք առանձին. reasoning-ը ցույց տվեք որպես առանձին, ծալվող մաս, կամ թողեք այն։
Hosted open-weight մոդելները reasoning չեն ուղարկում, երբ reasoning_effort-ը off է, և երբ հարցումն ունի response_format։
shannon-2-lite-ը և shannon-2-pro-ն նույնպես տրամաբանում են՝ պատասխանելուց առաջ։ Այս endpoint-ում դրանք տրամաբանելու ընթացքում ուղարկում են : thinking մեկնաբանության տողեր, ապա՝ պատասխանը։
reasoning_content-ը OpenAI SDK-ների տիպավորված մոդելներում չկա։ Python-ում կարդացեք այն getattr(delta, "reasoning_content", None)-ով. JavaScript-ում delta.reasoning_content-ն աշխատում է այնպես, ինչպես կա։
Գործիքի կանչի delta-ներ
Գործիքի կանչը գալիս է ամբողջությամբ. մեկ chunk յուրաքանչյուր կանչի համար, որի delta-ն պարունակում է կանչը՝ arguments ամբողջական տողով։ index-ը համարակալում է պատասխանի կանչերը 0-ից։ arguments-ը կտոր առ կտոր միացնող կոդն աշխատում է առանց փոփոխության, քանի որ միացնում է մեկ կտոր։
{
"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-ը
data: [DONE]-ից առաջ վերջին chunk-ն ունի դատարկ delta, finish_reason և հարցման usage՝ բոլորը նույն chunk-ում։ Յուրաքանչյուր հոսք ունի այն. 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 ունեցող և որոնումով ինչ-որ բան գտած հարցումից հետո վերջին 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-ի դաշտերը նկարագրված են endpoint-ի հետ։ Chat Completions
Keep-alive տողեր
Մինչ մոդելը աշխատում է և դեռ ուղարկելու բան չունի, հոսքը կրում է մեկնաբանության տողեր։ Դրանք տվյալ չեն պարունակում։ SSE parser-ն ինքը բաց է թողնում դրանք. հում տողեր կարդացող կոդը պետք է բաց թողնի երկու կետով սկսվող յուրաքանչյուր տող։
| Տող | Ուղարկում է | Երբ |
|---|---|---|
: 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 է, ուստի ձախողումը գալիս է հոսքի ներսում. chunk՝ error անդամով այնտեղ, որտեղ կլիներ choices-ը։
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 | Զրույցը մոդելի համատեքստի պատուհանից երկար է։ Կրճատեք այն՝ նախքան կրկին ուղարկելը։ Հաղորդագրության տեքստը տարբեր է լինում։ |
Hosted open-weight մոդելները ուղարկում են error անդամը միայն type-ով և message-ով։
OpenAI Python և JavaScript SDK-ները սխալի chunk-ը բարձրացնում են որպես API սխալ, մինչ դուք անցնում եք հոսքով, ուստի ցիկլը պատեք ձեր սովորական սխալների մշակմամբ։
Կարգավիճակի կոդերը, սխալի մարմինը և ինչը կրկնել՝ ունեն իրենց էջը։ Սխալների մշակում
Կապի փակում
Կապը փակելը դադարեցնում է առաքումը, ոչ թե հարցումը։ Մոդելը գրում է պատասխանն ամբողջությամբ, և հարցումը գանձվում է այնպես, կարծես կարդացել եք ամբողջը։ Քիչ վճարելու համար քիչ խնդրեք. սահմանեք ավելի ցածր max_tokens այն մոդելի վրա, որը կիրառում է այն։
Հոսքը կարող է նաև շուտ ավարտվել ձեզ հասնելու ճանապարհին, օրինակ՝ ցանցի խզման ժամանակ կամ երբ սերվերը թարմացվում է։ Այդ դեպքում այն կանգ է առնում առանց վերջին chunk-ի և առանց data: [DONE]-ի։ Այդ պատասխանը համարեք թերի և ուղարկեք հարցումը կրկին։
Հոսքի ընթերցում առանց SDK-ի
Ճիշտ ընթերցողը հինգ բան է անում. հոսքից առաջ եկած սխալները ուղարկում է իր սխալների մշակմանը, մարմինը բաժանում է տողերի, բաց է թողնում մեկնաբանության տողերը, կանգ է առնում 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)) 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-ները նույնպես աշխատում են streaming-ով։ Դրանց շրջանակները կրում են event: տող՝ իրադարձության անունով, և data: տող՝ դրա JSON-ով։ Յուրաքանչյուր էջ թվարկում է իր իրադարձությունները ըստ հերթականության։
| Հոսքում | 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-ը գալիս է | Վերջին chunk-ը | response.completed | message_delta |
| Հոսքն ավարտվում է | Վերջին chunk-ը, ապա data: [DONE] | response.completed | message_stop |