ສະຕຣີມມິ້ງ
ຕັ້ງ stream ເປັນ true ແລ້ວຄຳຕອບຈະມາເປັນ server-sent events ໃນຂະນະທີ່ model ຂຽນມັນ: reasoning ກ່ອນ, ແລ້ວຄຳຕອບ, ແລ້ວ chunk ສຸດທ້າຍທີ່ມີເຫດຜົນທີ່ຈົບ ແລະ usage. ໃຊ້ມັນທຸກຄັ້ງທີ່ມີຄົນລໍຖ້າຂໍ້ຄວາມ, ແລະ ສຳລັບຄຳຕອບຍາວ.
POST https://api.shannon-ai.com/v1/chat/completions
SDK ຂອງ OpenAI ໃຫ້ chunk ແກ່ທ່ານເທື່ອລະອັນ. ອ່ານແຕ່ລະ chunk ຕາມສິ່ງທີ່ມັນມີ: ສ່ວນຂອງ reasoning, ສ່ວນຂອງຄຳຕອບ, ການເອີ້ນ tool, ຫຼື ຈຸດຈົບຂອງຄຳຕອບ.
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
}' stream ຖືກຈັດ frame ແນວໃດ
- ຄຳຕອບມີ status
200ແລະ headercontent-type: text/event-streamແລະcache-control: no-cache. - ແຕ່ລະ event ແມ່ນໜຶ່ງແຖວທີ່ເລີ່ມດ້ວຍ
data:ແລະ ຕາມດ້ວຍແຖວວ່າງ. ຂໍ້ຄວາມຫຼັງdata:ແມ່ນ chunk JSON ໜຶ່ງອັນ. ບໍ່ມີແຖວevent:ຫຼືid:. - ແຖວທີ່ເລີ່ມດ້ວຍຈ້ຳສອງຈຸດ, ເຊັ່ນ
: ping, ແມ່ນ comment ທີ່ຮັກສາການເຊື່ອມຕໍ່ໃຫ້ເປີດຢູ່. ຂ້າມມັນ. - stream ຈົບດ້ວຍ
data: [DONE]. stream ທີ່ຢຸດໂດຍບໍ່ມີມັນແມ່ນບໍ່ສົມບູນ.
Chunk ຕາມລຳດັບ
ທຸກ chunk ແມ່ນ object 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 ຂອງ stream ດຽວມີ id, created ແລະ model ດຽວກັນ. choices ມີໜຶ່ງລາຍການສະເໝີ. delta ຂອງມັນເກັບສິ່ງໃໝ່, ແລະ finish_reason ເປັນ null ຈົນຮອດ chunk ສຸດທ້າຍ.
| Key ໃນ delta | ປະເພດ | ຄຳອະທິບາຍ | ສົ່ງໂດຍ |
|---|---|---|---|
role | string | ເປັນ assistant ສະເໝີ. ມັນມາເທື່ອດຽວ, ຕອນເລີ່ມ stream. | Hosted open-weight models, shannon-2-lite, shannon-2-pro |
reasoning_content | string | ສ່ວນໜຶ່ງຂອງ reasoning ຂອງ model. ຕໍ່ສ່ວນຕ່າງໆຕາມລຳດັບ. | model open-weight ທີ່ host ໄວ້, ຕະກູນ Shannon 3, shannon-1.6-pro ແລະ shannon-coder-1 |
content | string | ສ່ວນໜຶ່ງຂອງຄຳຕອບ. ຕໍ່ສ່ວນຕ່າງໆຕາມລຳດັບ. | ທຸກ model |
tool_calls | array | ການເອີ້ນ tool ທີ່ຄົບຖ້ວນໜຶ່ງອັນ. ເບິ່ງ Tool call deltas. | ທຸກ model |
Reasoning ມາກ່ອນ, ແລ້ວຄຳຕອບ, ແລ້ວການເອີ້ນ tool ຖ້າມີ. ອ່ານ chunk ຕາມ key ໃນ delta ຂອງມັນ, ບໍ່ແມ່ນຕາມຕຳແໜ່ງໃນ stream: delta ອາດມີ role ພ້ອມກັບຂໍ້ຄວາມສ່ວນທຳອິດ, ແລະ ອັນສຸດທ້າຍວ່າງ.
stream ທັງໝົດຕາມທີ່ມັນເດີນທາງ. ເພື່ອໃຫ້ແຖວສັ້ນ, 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] Delta ຂອງ reasoning
model ທີ່ຄິດຫາເຫດຜົນສົ່ງ reasoning ຂອງມັນໃນ delta.reasoning_content ກ່ອນຄຳຕອບ. ແຍກສອງຂໍ້ຄວາມອອກຈາກກັນ: ສະແດງ reasoning ເປັນສ່ວນແຍກທີ່ພັບໄດ້, ຫຼື ຕັດມັນອອກ.
Hosted open-weight models ບໍ່ສົ່ງ reasoning ເມື່ອ reasoning_effort ເປັນ off, ແລະ ບໍ່ສົ່ງເມື່ອຄຳຮ້ອງຂໍມີ response_format.
shannon-2-lite ແລະ shannon-2-pro ກໍຄິດຫາເຫດຜົນກ່ອນຕອບເຊັ່ນກັນ. ໃນ endpoint ນີ້ພວກມັນສົ່ງແຖວ comment : thinking ໃນຂະນະທີ່ເຮັດ, ແລ້ວຈຶ່ງສົ່ງຄຳຕອບ.
reasoning_content ບໍ່ຢູ່ໃນ typed model ຂອງ SDK ຂອງ OpenAI. ໃນ Python ໃຫ້ອ່ານດ້ວຍ getattr(delta, "reasoning_content", None); ໃນ JavaScript delta.reasoning_content ໃຊ້ໄດ້ຕາມທີ່ມັນເປັນ.
Delta ຂອງການເອີ້ນ tool
ການເອີ້ນ tool ມາທັງກ້ອນ: ໜຶ່ງ chunk ຕໍ່ການເອີ້ນ, ທີ່ delta ເກັບການເອີ້ນພ້ອມ string 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 ສຸດທ້າຍ
chunk ສຸດທ້າຍກ່ອນ data: [DONE] ມີ delta ວ່າງ, finish_reason ແລະ usage ຂອງຄຳຮ້ອງຂໍ, ທັງໝົດໃນ chunk ດຽວກັນ. ທຸກ stream ມີມັນ; ທ່ານບໍ່ຕ້ອງການ 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 models usage ຍັງມີ prompt_tokens_details.cached_tokens ແລະ completion_tokens_details.reasoning_tokens ນຳ.
ຫຼັງຄຳຮ້ອງຂໍທີ່ມີ web_search: true ເຊິ່ງການຄົ້ນຫາພົບບາງຢ່າງ, chunk ສຸດທ້າຍຍັງມີ sources ນຳ, ແລະ chunk ໜຶ່ງກ່ອນໜ້ານັ້ນມີເຄື່ອງໝາຍການອ້າງອີງເປັນ delta.annotations. ຄົ້ນຫາເວັບ
| finish_reason | ຄຳອະທິບາຍ | ສົ່ງໂດຍ |
|---|---|---|
stop | model ຕອບຈົບແລ້ວ, ຫຼື string stop ປາກົດຂຶ້ນ. | ທຸກ model |
tool_calls | ຄຳຕອບມີການເອີ້ນ tool ໜຶ່ງອັນ ຫຼື ຫຼາຍກວ່ານັ້ນ. | ທຸກ model |
length | ຄຳຕອບຖືກຕັດທີ່ຂີດຈຳກັດ output. | ຕະກູນ Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 |
ຟີລດ໌ຂອງ usage ຖືກອະທິບາຍພ້ອມກັບ endpoint. Chat Completions
ແຖວ Keep-alive
ໃນຂະນະທີ່ model ເຮັດວຽກແລະ ຍັງບໍ່ມີຫຍັງຈະສົ່ງ, stream ມີແຖວ comment. ພວກມັນບໍ່ມີຂໍ້ມູນ. ຕົວແຍກ SSE ຂ້າມພວກມັນເອງ; ໂຄ້ດທີ່ອ່ານແຖວດິບຕ້ອງຂ້າມທຸກແຖວທີ່ເລີ່ມດ້ວຍຈ້ຳສອງຈຸດ.
| ແຖວ | ສົ່ງໂດຍ | ເມື່ອໃດ |
|---|---|---|
: ping | ຕະກູນ Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 | ທຸກ 15 ວິນາທີ. |
: keepalive | Hosted open-weight models | ທຸກ 15 ວິນາທີໃນຂະນະທີ່ model ເຮັດວຽກ. |
: thinking | shannon-2-lite, shannon-2-pro | ໃນຂະນະທີ່ model ຄິດຫາເຫດຜົນ. |
ຂໍ້ຜິດພາດພາຍໃນ stream
ເມື່ອ stream ເລີ່ມແລ້ວ status ຂອງມັນເປັນ 200, ດັ່ງນັ້ນຄວາມລົ້ມເຫຼວມາພາຍໃນ stream: 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 ຫຼືບໍ່. ຖ້າບໍ່ກວດ ຄຳຕອບທີ່ລົ້ມເຫຼວກາງທາງຈະເບິ່ງຄືສົມບູນ.
| ປະເພດ | Code | ຂໍ້ຄວາມ | ເມື່ອໃດ |
|---|---|---|---|
api_error | The model backend failed to answer. Please retry. | model ລົ້ມເຫຼວ, ຫຼື ບໍ່ໄດ້ຂຽນຫຍັງ. ສົ່ງຄຳຮ້ອງຂໍອີກ. | |
rate_limit_error | Shannon routes are temporarily busy. Please retry. | model ກຳລັງຍຸ່ງໃນເວລານີ້. ລໍຖ້າສອງສາມວິນາທີ ແລ້ວສົ່ງຄຳຮ້ອງຂໍອີກ. | |
invalid_request_error | context_length_exceeded | ການສົນທະນາຍາວກວ່າ context window ຂອງ model. ເຮັດໃຫ້ມັນສັ້ນລົງກ່ອນສົ່ງອີກ. ຂໍ້ຄວາມປ່ຽນແປງໄດ້. |
Hosted open-weight models ສົ່ງສະມາຊິກ error ທີ່ມີສະເພາະ type ແລະ message.
SDK ຂອງ OpenAI ສຳລັບ Python ແລະ JavaScript ຍົກ chunk ຂໍ້ຜິດພາດເປັນ API error ໃນຂະນະທີ່ທ່ານວົນ, ດັ່ງນັ້ນໃຫ້ຫໍ່ loop ດ້ວຍການຈັດການຂໍ້ຜິດພາດປົກກະຕິຂອງທ່ານ.
Status code, body ຂອງຂໍ້ຜິດພາດ ແລະ ສິ່ງທີ່ຄວນລອງໃໝ່ມີໜ້າຂອງຕົນເອງ. ການຈັດການຂໍ້ຜິດພາດ
ການປິດການເຊື່ອມຕໍ່
ການປິດການເຊື່ອມຕໍ່ຢຸດການສົ່ງ, ບໍ່ແມ່ນຄຳຮ້ອງຂໍ. model ຂຽນຄຳຕອບຈົນຈົບ ແລະ ຄຳຮ້ອງຂໍຖືກຄິດເງິນຄືກັບທີ່ທ່ານອ່ານທັງໝົດ. ເພື່ອຈ່າຍໜ້ອຍລົງ, ໃຫ້ຂໍໜ້ອຍລົງ: ຕັ້ງ max_tokens ທີ່ຕ່ຳກວ່າໃນ model ທີ່ນຳໃຊ້ມັນ.
stream ຍັງຈົບກ່ອນເວລາໃນທາງມາຫາທ່ານໄດ້, ຕົວຢ່າງເມື່ອເຄືອຂ່າຍຂາດ ຫຼື ໃນຂະນະທີ່ server ກຳລັງອັບເດດ. ຈາກນັ້ນມັນຢຸດໂດຍບໍ່ມີ chunk ສຸດທ້າຍ ແລະ ບໍ່ມີ data: [DONE]. ຖືວ່າຄຳຕອບນັ້ນບໍ່ສົມບູນ ແລະ ສົ່ງຄຳຮ້ອງຂໍອີກ.
ການອ່ານ stream ໂດຍບໍ່ໃຊ້ SDK
ຕົວອ່ານທີ່ຖືກຕ້ອງເຮັດຫ້າຢ່າງ: ສົ່ງຂໍ້ຜິດພາດທີ່ມາກ່ອນ stream ໄປຫາຕົວຈັດການຂໍ້ຜິດພາດຂອງມັນ, ແຍກ body ເປັນແຖວ, ຂ້າມແຖວ comment, ຢຸດທີ່ 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 ໃຊ້ package requests. ຕົວຢ່າງ JavaScript ຮັນເທິງ Node.js 18 ຫຼື ໃໝ່ກວ່າ.
Stream ຂອງຮູບແບບອື່ນ
endpoint Responses ແລະ Messages ກໍ stream ໄດ້ຄືກັນ. frame ຂອງພວກມັນມີແຖວ event: ທີ່ມີຊື່ຂອງ event ແລະ ແຖວ data: ທີ່ມີ JSON ຂອງມັນ. ແຕ່ລະໜ້າລາຍຊື່ event ຂອງຕົນຕາມລຳດັບ.
| ໃນ stream | Chat Completions | Responses | Messages |
|---|---|---|---|
| Frame | ແຖວ 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 |
| stream ຈົບດ້ວຍ | chunk ສຸດທ້າຍ, ແລ້ວ data: [DONE] | response.completed | message_stop |