Fluigo
Agordu stream al true kaj la respondo alvenas kiel server-sent events dum la modelo skribas ĝin: unue rezonado, poste la respondo, poste lasta peco kun la kialo de fino kaj la usage. Uzu ĝin kiam ajn homo atendas la tekston, kaj por longaj respondoj.
POST https://api.shannon-ai.com/v1/chat/completions
La SDK de OpenAI donas al vi la pecojn unu post la alia. Legu ĉiun pecon laŭ tio, kion ĝi portas: peceton de rezonado, peceton de la respondo, vokon de ilo, aŭ la finon de la respondo.
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
}' Kiel fluo estas enkadrigita
- La respondo havas staton
200kaj la kapojncontent-type: text/event-streamkajcache-control: no-cache. - Ĉiu evento estas unu linio, kiu komenciĝas per
data:kaj estas sekvata de malplena linio. La teksto postdata:estas unu JSON-peco. Ne ekzistas liniojevent:aŭid:. - Linio, kiu komenciĝas per dupunkto, kiel
: ping, estas komento, kiu tenas la konekton malfermita. Preterpasu ĝin. - La fluo finiĝas per
data: [DONE]. Fluo, kiu haltas sen ĝi, estas nekompleta.
Pecoj laŭorde
Ĉiu peco estas objekto 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
}
]
} Ĉiuj pecoj de unu fluo portas la samajn id, created kaj model. choices ĉiam enhavas unu eron. Ĝia delta enhavas tion, kio estas nova, kaj finish_reason estas null ĝis la lasta peco.
| Ŝlosilo en delta | Tipo | Priskribo | Sendita de |
|---|---|---|---|
role | string | Ĉiam assistant. Ĝi alvenas unufoje, ĉe la komenco de la fluo. | Gastigitaj malfermpezaj modeloj, shannon-2-lite, shannon-2-pro |
reasoning_content | string | Peceto de la rezonado de la modelo. Kunigu la pecetojn laŭorde. | Gastigitaj malfermpezaj modeloj, familio Shannon 3, shannon-1.6-pro, shannon-coder-1 |
content | string | Peceto de la respondo. Kunigu la pecetojn laŭorde. | Ĉiuj modeloj |
tool_calls | array | Unu kompleta voko de ilo. Vidu Deltoj de vokoj de iloj. | Ĉiuj modeloj |
Rezonado venas unue, poste la respondo, poste eventualaj vokoj de iloj. Legu pecon laŭ la ŝlosiloj en ĝia delta, ne laŭ ĝia loko en la fluo: delta povas enhavi role kune kun la unua peceto de teksto, kaj la lasta estas malplena.
Tuta fluo tia, kia ĝi vojaĝas. Por teni la liniojn mallongaj, id, object, created kaj model estas lasitaj ekster ĉiu peco ĉi tie.
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] Deltoj de rezonado
Modelo, kiu rezonas, sendas sian rezonadon en delta.reasoning_content antaŭ la respondo. Tenu la du tekstojn apartaj: montru la rezonadon kiel apartan, faldeblan parton, aŭ lasu ĝin ekster.
La gastigitaj malfermpezaj modeloj sendas neniun rezonadon kiam reasoning_effort estas off, kaj neniun kiam la peto havas response_format.
shannon-2-lite kaj shannon-2-pro ankaŭ rezonas antaŭ ol respondi. Ĉe ĉi tiu endpoint ili sendas komentliniojn : thinking dum ili faras tion, kaj poste la respondon.
reasoning_content ne estas en la tipitaj modeloj de la SDK de OpenAI. En Python legu ĝin per getattr(delta, "reasoning_content", None); en JavaScript delta.reasoning_content funkcias tia, kia ĝi estas.
Deltoj de vokoj de iloj
Voko de ilo alvenas tuta: unu peco por voko, kies delta enhavas la vokon kun la kompleta ĉeno arguments. index numeras la vokojn de la respondo ekde 0. Kodo, kiu kunigas arguments peceton post peceto, funkcias senŝanĝe, ĉar ĝi kunigas unu peceton.
{
"tool_calls": [
{
"index": 0,
"id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
} Post la vokoj, la lasta peco havas finish_reason tool_calls.
Kiam la peto havas tools, shannon-1.6-lite, shannon-1.6-pro kaj shannon-coder-1 sendas la tekston de la respondo kiel unu delta ĉe la fino de la respondo.
La lasta peco
La lasta peco antaŭ data: [DONE] havas malplenan delta, la finish_reason kaj la usage de la peto, ĉio en la sama peco. Ĉiu fluo havas ĝin; vi ne bezonas 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
}
} Ĉe la gastigitaj malfermpezaj modeloj usage havas ankaŭ prompt_tokens_details.cached_tokens kaj completion_tokens_details.reasoning_tokens.
Post peto kun web_search: true, kies serĉo trovis ion, la lasta peco portas ankaŭ sources, kaj unu peco antaŭ ĝi portas la citmarkojn kiel delta.annotations. Retserĉo
| finish_reason | Priskribo | Sendita de |
|---|---|---|
stop | La modelo finis sian respondon, aŭ aperis ĉeno stop. | Ĉiuj modeloj |
tool_calls | La respondo enhavas unu aŭ plurajn vokojn de iloj. | Ĉiuj modeloj |
length | La respondo estis detranĉita ĉe la eliga limo. | Familio Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 |
La kampoj de usage estas priskribitaj ĉe la endpoint. Chat Completions
Linioj keep-alive
Dum la modelo laboras kaj ankoraŭ havas nenion por sendi, la fluo portas komentliniojn. Ili enhavas neniujn datumojn. Analizilo de SSE preterpasas ilin mem; kodo, kiu legas krudajn liniojn, devas preterpasi ĉiun linion, kiu komenciĝas per dupunkto.
| Linio | Sendita de | Kiam |
|---|---|---|
: ping | Familio Shannon 3, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 | Ĉiujn 15 sekundojn. |
: keepalive | Gastigitaj malfermpezaj modeloj | Ĉiujn 15 sekundojn dum la modelo laboras. |
: thinking | shannon-2-lite, shannon-2-pro | Dum la modelo rezonas. |
Eraroj en fluo
Post kiam fluo komenciĝis, ĝia stato estas 200, do malsukceso alvenas ene de la fluo: peco kun membro error tie, kie estus 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] Post la erara peco ankoraŭ sekvas la lasta peco, kun finish_reason kaj usage, kaj data: [DONE]. Kontrolu ĉiun pecon pri error. Sen tiu kontrolo respondo, kiu malsukcesis meze, aspektas kompleta.
| Tipo | Kodo | Mesaĝo | Kiam |
|---|---|---|---|
api_error | The model backend failed to answer. Please retry. | La modelo malsukcesis, aŭ skribis nenion. Resendu la peton. | |
rate_limit_error | Shannon routes are temporarily busy. Please retry. | La modelo estas okupata en ĉi tiu momento. Atendu kelkajn sekundojn kaj resendu la peton. | |
invalid_request_error | context_length_exceeded | La konversacio estas pli longa ol la kunteksta fenestro de la modelo. Mallongigu ĝin antaŭ ol resendi. La teksto de la mesaĝo varias. |
La gastigitaj malfermpezaj modeloj sendas la membron error nur kun type kaj message.
La SDK de OpenAI por Python kaj JavaScript ĵetas eraran pecon kiel API-eraron dum vi iteracias, do envolvu la ciklon en via kutima traktado de eraroj.
Statokodoj, la erarkorpo kaj kion reprovi havas propran paĝon. Erarotraktado
Fermo de la konekto
Fermi la konekton haltigas la liveron, ne la peton. La modelo skribas la respondon ĝis ĝia fino kaj la peto estas kalkulata kvazaŭ vi legus ĝin tutan. Por pagi malpli, petu malpli: agordu pli malaltan max_tokens ĉe modelo, kiu aplikas ĝin.
Fluo povas ankaŭ finiĝi frue survoje al vi, ekzemple pro rompo de reto aŭ dum servilo estas ĝisdatigata. Ĝi tiam haltas sen la lasta peco kaj sen data: [DONE]. Traktu tiun respondon kiel nekompletan kaj resendu la peton.
Legado de fluo sen SDK
Ĝusta legilo faras kvin aferojn: ĝi sendas erarojn, kiuj alvenas antaŭ la fluo, al sia traktado de eraroj, dividas la korpon en liniojn, preterpasas komentliniojn, haltas ĉe data: [DONE], kaj kontrolas ĉiun pecon pri 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); La ekzemplo de Python uzas la pakaĵon requests. La ekzemplo de JavaScript funkcias en Node.js 18 aŭ pli nova.
Fluoj de la aliaj formatoj
La endpoints Responses kaj Messages ankaŭ fluas. Iliaj kadroj portas linion event: kun la nomo de la evento kaj linion data: kun ĝia JSON. Ĉiu paĝo listigas siajn eventojn laŭorde.
| En la fluo | Chat Completions | Responses | Messages |
|---|---|---|---|
| Kadroj | Linioj data: | Linioj event: kaj data: | Linioj event: kaj data: |
| Rezonado alvenas en | delta.reasoning_content | response.reasoning_summary_text.delta | thinking_delta |
| Teksto de la respondo alvenas en | delta.content | response.output_text.delta | text_delta |
| Usage alvenas en | La lasta peco | response.completed | message_delta |
| La fluo finiĝas per | La lasta peco, poste data: [DONE] | response.completed | message_stop |