Ροή
Ορίστε το stream σε true και η απάντηση φτάνει ως server-sent events καθώς τη γράφει το μοντέλο: πρώτα ο συλλογισμός, μετά η απάντηση, και τέλος ένα chunk με τον λόγο ολοκλήρωσης και το usage. Χρησιμοποιήστε το όποτε ένας άνθρωπος περιμένει το κείμενο, και για μεγάλες απαντήσεις.
POST https://api.shannon-ai.com/v1/chat/completions
Τα SDK της OpenAI σας δίνουν τα chunks ένα ένα. Διαβάστε κάθε chunk με βάση ό,τι φέρει: ένα κομμάτι συλλογισμού, ένα κομμάτι της απάντησης, μια κλήση εργαλείου ή το τέλος της απάντησης.
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
- Η απάντηση έχει κατάσταση
200και τις κεφαλίδεςcontent-type: text/event-streamκαιcache-control: no-cache. - Κάθε συμβάν είναι μία γραμμή που αρχίζει με
data:και ακολουθείται από κενή γραμμή. Το κείμενο μετά τοdata:είναι ένα chunk JSON. Δεν υπάρχουν γραμμέςevent:ήid:. - Μια γραμμή που αρχίζει με άνω και κάτω τελεία, όπως το
: ping, είναι σχόλιο που κρατά τη σύνδεση ανοιχτή. Προσπεράστε την. - Το stream τελειώνει με
data: [DONE]. Ένα stream που σταματά χωρίς αυτό είναι ατελές.
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 ενός stream φέρουν τα ίδια id, created και model. Το choices περιέχει πάντα μία εγγραφή. Το delta της περιέχει ό,τι είναι καινούργιο, και το finish_reason είναι null μέχρι το τελευταίο chunk.
| Κλειδί στο delta | Τύπος | Περιγραφή | Αποστέλλεται από |
|---|---|---|---|
role | string | Πάντα assistant. Φτάνει μία φορά, στην αρχή του stream. | Φιλοξενούμενα μοντέλα ανοιχτών βαρών, shannon-2-lite, shannon-2-pro |
reasoning_content | string | Ένα κομμάτι του συλλογισμού του μοντέλου. Ενώστε τα κομμάτια με τη σειρά. | Φιλοξενούμενα μοντέλα ανοιχτών βαρών, οικογένεια Shannon 3, shannon-1.6-pro, shannon-coder-1 |
content | string | Ένα κομμάτι της απάντησης. Ενώστε τα κομμάτια με τη σειρά. | Όλα τα μοντέλα |
tool_calls | array | Μία πλήρης κλήση εργαλείου. Δείτε Deltas κλήσεων εργαλείων. | Όλα τα μοντέλα |
Πρώτα έρχεται ο συλλογισμός, μετά η απάντηση, μετά τυχόν κλήσεις εργαλείων. Διαβάστε ένα chunk με βάση τα κλειδιά στο 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] Deltas συλλογισμού
Ένα μοντέλο που συλλογίζεται στέλνει τον συλλογισμό του στο delta.reasoning_content πριν από την απάντηση. Κρατήστε τα δύο κείμενα χωριστά: δείξτε τον συλλογισμό ως ξεχωριστό, αναδιπλούμενο τμήμα, ή παραλείψτε τον.
Τα φιλοξενούμενα μοντέλα ανοιχτών βαρών δεν στέλνουν συλλογισμό όταν το reasoning_effort είναι off, ούτε όταν το αίτημα έχει response_format.
Τα shannon-2-lite και shannon-2-pro συλλογίζονται επίσης πριν απαντήσουν. Σε αυτό το endpoint στέλνουν γραμμές σχολίων : thinking όσο το κάνουν, και μετά την απάντηση.
Το reasoning_content δεν υπάρχει στα τυποποιημένα μοντέλα των SDK της OpenAI. Στην Python διαβάστε το με getattr(delta, "reasoning_content", None)· στη JavaScript το delta.reasoning_content δουλεύει ως έχει.
Deltas κλήσεων εργαλείων
Μια κλήση εργαλείου φτάνει ολόκληρη: ένα 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
}
} Στα φιλοξενούμενα μοντέλα ανοιχτών βαρών το usage έχει επίσης prompt_tokens_details.cached_tokens και completion_tokens_details.reasoning_tokens.
Μετά από ένα αίτημα με web_search: true του οποίου η αναζήτηση βρήκε κάτι, το τελευταίο chunk φέρει επίσης sources και ένα chunk πριν από αυτό φέρει τους δείκτες παραπομπής ως delta.annotations. Ενσωματωμένη αναζήτηση ιστού
| finish_reason | Περιγραφή | Αποστέλλεται από |
|---|---|---|
stop | Το μοντέλο ολοκλήρωσε την απάντησή του, ή εμφανίστηκε ένα string stop. | Όλα τα μοντέλα |
tool_calls | Η απάντηση περιέχει μία ή περισσότερες κλήσεις εργαλείων. | Όλα τα μοντέλα |
length | Η απάντηση κόπηκε στο όριο εξόδου. | Οικογένεια Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 |
Τα πεδία του usage περιγράφονται μαζί με το endpoint. Chat Completions
Γραμμές keep-alive
Όσο το μοντέλο δουλεύει και δεν έχει ακόμη τίποτα να στείλει, το stream φέρει γραμμές σχολίων. Δεν περιέχουν δεδομένα. Ένας parser 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 | Όσο το μοντέλο συλλογίζεται. |
Σφάλματα μέσα σε stream
Αφού ξεκινήσει ένα stream η κατάστασή του είναι 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. Χωρίς αυτόν τον έλεγχο μια απάντηση που απέτυχε στη μέση φαίνεται ολοκληρωμένη.
| Τύπος | Κωδικός | Μήνυμα | Πότε |
|---|---|---|---|
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.
Τα SDK Python και JavaScript της OpenAI εγείρουν ένα chunk σφάλματος ως σφάλμα API καθώς κάνετε iterate, οπότε τυλίξτε τον βρόχο στον συνηθισμένο χειρισμό σφαλμάτων σας.
Οι κωδικοί κατάστασης, το σώμα σφάλματος και τι να ξαναδοκιμάσετε έχουν τη δική τους σελίδα. Χειρισμός σφαλμάτων
Κλείσιμο της σύνδεσης
Το κλείσιμο της σύνδεσης σταματά την παράδοση, όχι το αίτημα. Το μοντέλο γράφει την απάντηση μέχρι το τέλος και το αίτημα χρεώνεται σαν να τη διαβάσατε ολόκληρη. Για να πληρώσετε λιγότερο, ζητήστε λιγότερα: ορίστε χαμηλότερο max_tokens σε μοντέλο που το εφαρμόζει.
Ένα stream μπορεί επίσης να τελειώσει νωρίς στη διαδρομή προς εσάς, για παράδειγμα σε διακοπή δικτύου ή ενώ ενημερώνεται ένας διακομιστής. Σταματά τότε χωρίς το τελευταίο chunk και χωρίς data: [DONE]. Αντιμετωπίστε αυτή την απάντηση ως ατελή και στείλτε ξανά το αίτημα.
Ανάγνωση ενός stream χωρίς SDK
Ένας σωστός αναγνώστης κάνει πέντε πράγματα: στέλνει τα σφάλματα που φτάνουν πριν από το stream στον χειρισμό σφαλμάτων του, χωρίζει το σώμα σε γραμμές, προσπερνά τις γραμμές σχολίων, σταματά στο 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 ή νεότερο.
Streams των άλλων μορφών
Τα endpoints Responses και Messages κάνουν επίσης streaming. Τα frames τους φέρουν μια γραμμή event: με το όνομα του συμβάντος και μια γραμμή data: με το JSON του. Κάθε σελίδα απαριθμεί τα συμβάντα της με τη σειρά.
| Στο stream | Chat Completions | Responses | Messages |
|---|---|---|---|
| Frames | Γραμμές data: | Γραμμές event: και data: | Γραμμές event: και data: |
| Ο συλλογισμός φτάνει στο | delta.reasoning_content | response.reasoning_summary_text.delta | thinking_delta |
| Το κείμενο της απάντησης φτάνει στο | delta.content | response.output_text.delta | text_delta |
| Η χρήση φτάνει στο | Το τελευταίο chunk | response.completed | message_delta |
| Το stream τελειώνει με | Το τελευταίο chunk, και μετά data: [DONE] | response.completed | message_stop |