स्ट्रीमिंग
stream ला true सेट करा, आणि उत्तर मॉडेल लिहीत असताना server-sent events म्हणून येते: आधी रीझनिंग, मग उत्तर, मग finish reason आणि 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. तो स्ट्रीमच्या सुरुवातीला एकदाच येतो. | होस्ट केलेली open-weight मॉडेल्स, shannon-2-lite, shannon-2-pro |
reasoning_content | string | मॉडेलच्या रीझनिंगचा एक तुकडा. तुकडे क्रमाने जोडा. | होस्ट केलेली open-weight मॉडेल्स, Shannon 3 कुटुंब, shannon-1.6-pro, shannon-coder-1 |
content | string | उत्तराचा एक तुकडा. तुकडे क्रमाने जोडा. | सर्व मॉडेल्स |
tool_calls | array | एक पूर्ण टूल कॉल. Tool call deltas पाहा. | सर्व मॉडेल्स |
आधी रीझनिंग येते, मग उत्तर, मग कोणतेही टूल कॉल्स. चंक त्याच्या 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 असताना होस्ट केलेली open-weight मॉडेल्स रीझनिंग पाठवत नाहीत, आणि रिक्वेस्टमध्ये response_format असतानाही नाही.
shannon-2-lite आणि shannon-2-pro देखील उत्तरापूर्वी रीझनिंग करतात. या एंडपॉइंटवर ते रीझनिंग करत असताना : 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
}
} होस्ट केलेल्या open-weight मॉडेल्सवर usage मध्ये prompt_tokens_details.cached_tokens आणि completion_tokens_details.reasoning_tokens देखील असतात.
ज्याच्या शोधात काही सापडले अशा web_search: true रिक्वेस्टनंतर शेवटच्या चंकमध्ये sources देखील असते, आणि त्याच्या आधीचा एक चंक उल्लेखाच्या खुणा delta.annotations म्हणून वाहतो. Built‑in Web Search
| finish_reason | वर्णन | कोण पाठवते |
|---|---|---|
stop | मॉडेलने उत्तर पूर्ण केले, किंवा stop स्ट्रिंग आली. | सर्व मॉडेल्स |
tool_calls | उत्तरात एक किंवा अधिक टूल कॉल्स आहेत. | सर्व मॉडेल्स |
length | उत्तर आउटपुट मर्यादेवर तोडले गेले. | Shannon 3 कुटुंब, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 |
usage ची फील्ड्स एंडपॉइंटसोबत वर्णन केली आहेत. Chat Completions
Keep-alive ओळी
मॉडेल काम करत असताना आणि अजून पाठवण्यासारखे काही नसताना स्ट्रीम कमेंट ओळी देतो. त्यांत डेटा नसतो. SSE पार्सर त्या आपोआप वगळतो; रॉ ओळी वाचणाऱ्या कोडने कोलनने सुरू होणारी प्रत्येक ओळ वगळली पाहिजे.
| ओळ | कोण पाठवते | केव्हा |
|---|---|---|
: ping | Shannon 3 कुटुंब, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 | दर 15 सेकंदांनी. |
: keepalive | होस्ट केलेली 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 | संभाषण मॉडेलच्या कॉन्टेक्स्ट विंडोपेक्षा लांब आहे. पुन्हा पाठवण्यापूर्वी ते लहान करा. संदेशाचा मजकूर बदलतो. |
होस्ट केलेली 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 एंडपॉइंट्सही स्ट्रीम करतात. त्यांच्या फ्रेम्समध्ये इव्हेंटचे नाव असलेली 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 |