Transmetim
Vendosni stream në true dhe përgjigjja arrin si server-sent events ndërsa modeli e shkruan: fillimisht arsyetimi, pastaj përgjigjja, pastaj një pjesë e fundit me arsyen e mbarimit dhe përdorimin. Përdoreni sa herë që një person pret tekstin, dhe për përgjigje të gjata.
POST https://api.shannon-ai.com/v1/chat/completions
SDK-të OpenAI ju japin pjesët një nga një. Lexojeni çdo pjesë sipas asaj që mban: një copë arsyetimi, një copë përgjigjeje, një thirrje mjeti, ose fundin e përgjigjes.
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
}' Si kornizohet një stream
- Përgjigjja ka statusin
200dhe header-atcontent-type: text/event-streamdhecache-control: no-cache. - Çdo ngjarje është një rresht që fillon me
data:dhe ndiqet nga një rresht bosh. Teksti pasdata:është një pjesë JSON. Nuk ka rreshtaevent:oseid:. - Një rresht që fillon me dy pika, si
: ping, është koment që e mban lidhjen të hapur. Kapërcejeni. - Stream-i mbaron me
data: [DONE]. Një stream që ndalon pa të është i paplotë.
Pjesët në radhë
Çdo pjesë është një objekt 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
}
]
} Të gjitha pjesët e një stream-i mbajnë të njëjtat id, created dhe model. choices mban gjithmonë një hyrje. delta i saj mban atë që është e re, dhe finish_reason është null deri te pjesa e fundit.
| Çelësi në delta | Lloji | Përshkrimi | Dërguar nga |
|---|---|---|---|
role | string | Gjithmonë assistant. Arrin një herë, në fillim të stream-it. | Modele open-weight të hostuara, shannon-2-lite, shannon-2-pro |
reasoning_content | string | Një copë e arsyetimit të modelit. Bashkojini copat sipas radhës. | Modele open-weight të hostuara, familja Shannon 3, shannon-1.6-pro, shannon-coder-1 |
content | string | Një copë e përgjigjes. Bashkojini copat sipas radhës. | Të gjitha modelet |
tool_calls | array | Një thirrje e plotë mjeti. Shihni Delta-t e thirrjeve të mjeteve. | Të gjitha modelet |
Arsyetimi vjen i pari, pastaj përgjigjja, pastaj çdo thirrje mjeti. Lexojeni një pjesë sipas çelësave në delta të saj, jo sipas vendit në stream: një delta mund të mbajë role së bashku me copën e parë të tekstit, dhe e fundit është bosh.
Një stream i plotë siç udhëton. Për t'i mbajtur rreshtat të shkurtër, id, object, created dhe model lihen jashtë çdo pjese këtu.
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-t e arsyetimit
Një model që arsyeton e dërgon arsyetimin te delta.reasoning_content para përgjigjes. Mbajini dy tekstet të ndara: shfaqeni arsyetimin si pjesë të veçantë që mund të paloset, ose lëreni jashtë.
Modelet open-weight të hostuara nuk dërgojnë arsyetim kur reasoning_effort është off, dhe asnjë kur kërkesa ka një response_format.
shannon-2-lite dhe shannon-2-pro arsyetojnë gjithashtu para se të përgjigjen. Në këtë endpoint dërgojnë rreshta komenti : thinking ndërsa arsyetojnë, dhe pastaj përgjigjen.
reasoning_content nuk është te modelet e tipizuara të SDK-ve OpenAI. Në Python lexojeni me getattr(delta, "reasoning_content", None); në JavaScript delta.reasoning_content funksionon siç është.
Delta-t e thirrjeve të mjeteve
Një thirrje mjeti arrin e plotë: një pjesë për thirrje, delta i së cilës mban thirrjen me stringun e plotë arguments. index i numëron thirrjet e përgjigjes nga 0. Kodi që i bashkon arguments copë-copë funksionon pa ndryshime, sepse bashkon një copë.
{
"tool_calls": [
{
"index": 0,
"id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
} Pas thirrjeve, pjesa e fundit ka finish_reason tool_calls.
Kur kërkesa ka tools, shannon-1.6-lite, shannon-1.6-pro dhe shannon-coder-1 e dërgojnë tekstin e përgjigjes si një delta në fund të përgjigjes.
Pjesa e fundit
Pjesa e fundit para data: [DONE] ka një delta bosh, finish_reason dhe usage të kërkesës, të gjitha në të njëjtën pjesë. Çdo stream e ka; nuk keni nevojë për 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
}
} Te modelet open-weight të hostuara usage ka edhe prompt_tokens_details.cached_tokens dhe completion_tokens_details.reasoning_tokens.
Pas një kërkese me web_search: true kërkimi i së cilës gjeti diçka, pjesa e fundit mban edhe sources, dhe një pjesë para saj mban shenjat e citimit si delta.annotations. Kërkim i integruar në ueb
| finish_reason | Përshkrimi | Dërguar nga |
|---|---|---|
stop | Modeli e mbaroi përgjigjen, ose u shfaq një string stop. | Të gjitha modelet |
tool_calls | Përgjigjja mban një ose më shumë thirrje mjetesh. | Të gjitha modelet |
length | Përgjigjja u ndërpre te kufiri i daljes. | Familja Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 |
Fushat e usage përshkruhen me endpoint-in. Chat Completions
Rreshtat keep-alive
Ndërsa modeli punon dhe ende nuk ka çfarë të dërgojë, stream-i mban rreshta komenti. Ata nuk mbajnë të dhëna. Një parser SSE i kapërcen vetë; kodi që lexon rreshta të papërpunuar duhet të kapërcejë çdo rresht që fillon me dy pika.
| Rreshti | Dërguar nga | Kur |
|---|---|---|
: ping | Familja Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 | Çdo 15 sekonda. |
: keepalive | Modele open-weight të hostuara | Çdo 15 sekonda ndërsa modeli punon. |
: thinking | shannon-2-lite, shannon-2-pro | Ndërsa modeli arsyeton. |
Gabimet brenda një stream-i
Pasi një stream ka nisur statusi i tij është 200, prandaj një dështim arrin brenda stream-it: një pjesë me anëtar error aty ku do të ishte 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] Pas pjesës së gabimit vijnë prapë pjesa e fundit, me finish_reason dhe usage, dhe data: [DONE]. Kontrolloni çdo pjesë për error. Pa këtë kontroll një përgjigje që dështoi në mes duket e plotë.
| Lloji | Kodi | Mesazhi | Kur |
|---|---|---|---|
api_error | The model backend failed to answer. Please retry. | Modeli dështoi, ose nuk shkroi asgjë. Dërgoni kërkesën përsëri. | |
rate_limit_error | Shannon routes are temporarily busy. Please retry. | Modeli është i zënë në këtë çast. Prisni disa sekonda dhe dërgoni kërkesën përsëri. | |
invalid_request_error | context_length_exceeded | Biseda është më e gjatë se dritarja e kontekstit të modelit. Shkurtojeni para se ta dërgoni përsëri. Teksti i mesazhit ndryshon. |
Modelet open-weight të hostuara dërgojnë anëtarin error vetëm me type dhe message.
SDK-të OpenAI Python dhe JavaScript e ngrenë një pjesë gabimi si gabim API ndërsa iteroni, prandaj mbështjellni ciklin në trajtimin tuaj të zakonshëm të gabimeve.
Kodet e statusit, trupi i gabimit dhe çfarë të provohet përsëri kanë faqen e tyre. Trajtimi i gabimeve
Mbyllja e lidhjes
Mbyllja e lidhjes e ndalon dërgimin, jo kërkesën. Modeli e shkruan përgjigjen deri në fund dhe kërkesa faturohet sikur ta kishit lexuar të gjithën. Për të paguar më pak, kërkoni më pak: vendosni një max_tokens më të ulët te një model që e zbaton.
Një stream mund të mbarojë edhe herët gjatë rrugës drejt jush, për shembull në një ndërprerje rrjeti ose gjatë përditësimit të një serveri. Atëherë ndalon pa pjesën e fundit dhe pa data: [DONE]. Trajtojeni atë përgjigje si të paplotë dhe dërgoni kërkesën përsëri.
Leximi i një stream-i pa SDK
Një lexues i saktë bën pesë gjëra: i dërgon te trajtimi i gabimeve gabimet që arrijnë para stream-it, e ndan trupin në rreshta, kapërcen rreshtat e komenteve, ndalon te data: [DONE], dhe kontrollon çdo pjesë për 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); Shembulli Python përdor paketën requests. Shembulli JavaScript ekzekutohet në Node.js 18 ose më të ri.
Stream-et e formateve të tjera
Endpoint-et Responses dhe Messages bëjnë gjithashtu streaming. Frame-et e tyre mbajnë një rresht event: me emrin e ngjarjes dhe një rresht data: me JSON-in e saj. Çdo faqe i liston ngjarjet e veta në radhë.
| Në stream | Chat Completions | Responses | Messages |
|---|---|---|---|
| Frame-et | Rreshta data: | Rreshta event: dhe data: | Rreshta event: dhe data: |
| Arsyetimi arrin në | delta.reasoning_content | response.reasoning_summary_text.delta | thinking_delta |
| Teksti i përgjigjes arrin në | delta.content | response.output_text.delta | text_delta |
| Përdorimi arrin në | Pjesa e fundit | response.completed | message_delta |
| Stream-i mbaron me | Pjesa e fundit, pastaj data: [DONE] | response.completed | message_stop |