اسٹریمنگ
stream کو true پر سیٹ کریں اور جواب ماڈل کے لکھنے کے ساتھ server-sent events کے طور پر آتا ہے: پہلے reasoning، پھر جواب، پھر اختتام کی وجہ اور usage کے ساتھ آخری chunk۔ جب کوئی شخص متن کا انتظار کر رہا ہو، اور لمبے جوابات کے لیے، اسے استعمال کریں۔
POST https://api.shannon-ai.com/v1/chat/completions
OpenAI SDKs آپ کو chunks ایک ایک کر کے دیتے ہیں۔ ہر 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-cacheہیں۔ - ہر ایونٹ ایک لائن ہے جو
data:سے شروع ہوتی ہے اور اس کے بعد ایک خالی لائن آتی ہے۔data:کے بعد کا متن ایک JSON chunk ہے۔event:یاid:لائنیں نہیں ہوتیں۔ - جو لائن کولن سے شروع ہو، جیسے
: ping، وہ تبصرہ ہے جو کنکشن کھلا رکھتا ہے۔ اسے چھوڑ دیں۔ - سٹریم
data: [DONE]پر ختم ہوتی ہے۔ جو سٹریم اس کے بغیر رک جائے وہ نامکمل ہے۔
chunks ترتیب سے
ہر 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
}
]
} ایک سٹریم کے تمام chunks میں وہی id، created اور model ہوتے ہیں۔ choices میں ہمیشہ ایک اندراج ہوتی ہے۔ اس کے delta میں جو نیا ہے وہ ہوتا ہے، اور finish_reason آخری chunk تک 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 آتی ہے، پھر جواب، پھر ٹول کالز۔ chunk کو اس کے delta کی کیز سے پڑھیں، سٹریم میں اس کی جگہ سے نہیں: delta میں role متن کے پہلے ٹکڑے کے ساتھ ہو سکتا ہے، اور آخری خالی ہوتا ہے۔
پوری سٹریم جیسی وہ سفر کرتی ہے۔ لائنیں مختصر رکھنے کے لیے یہاں ہر chunk سے 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 SDKs کے ٹائپڈ ماڈلز میں نہیں ہے۔ Python میں اسے getattr(delta, "reasoning_content", None) سے پڑھیں؛ JavaScript میں delta.reasoning_content جوں کا توں کام کرتا ہے۔
ٹول کال ڈیلٹاز
ٹول کال پوری آتی ہے: ہر کال کے لیے ایک 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 جواب کا متن جواب کے آخر میں ایک ڈیلٹا کے طور پر بھیجتے ہیں۔
آخری 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
}
} ہوسٹڈ اوپن ویٹ ماڈلز پر 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 ہوتا ہے، اس لیے ناکامی سٹریم کے اندر آتی ہے: ایسا chunk جس میں 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] ایرر 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 | گفتگو ماڈل کی کانٹیکسٹ ونڈو سے لمبی ہے۔ دوبارہ بھیجنے سے پہلے اسے مختصر کریں۔ پیغام کا متن مختلف ہو سکتا ہے۔ |
ہوسٹڈ اوپن ویٹ ماڈلز error رکن صرف type اور message کے ساتھ بھیجتے ہیں۔
OpenAI Python اور JavaScript SDKs آپ کے iterate کرتے وقت ایرر 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 endpoints بھی سٹریم کرتے ہیں۔ ان کے فریمز میں ایونٹ کے نام والی 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 یہاں آتا ہے | آخری chunk | response.completed | message_delta |
| سٹریم اس پر ختم ہوتی ہے | آخری chunk، پھر data: [DONE] | response.completed | message_stop |