البثّ
أرسل stream: true فيتحوّل الرد إلى text/event-stream: أحداث متتابعة تصل كلّما وُلِّد جزء جديد، فيرى المستخدم النصّ ينمو بدل شاشة انتظار صامتة.
البثّ
curl
curl -N https://<host>/v1/chat/completions \
-H "Authorization: Bearer vx_live_..." \
-H "Content-Type: application/json" \
-d '{
"model": "vorix-flash",
"stream": true,
"messages": [{ "role": "user", "content": "Count to five." }]
}'شكل الحدث
كل حدث سطرٌ يبدأ بـ «data: » يليه كائن JSON، وتفصل بين الأحداث سطران فارغان. الجزء النصّي في choices[0].delta.content.
text
data: {"id":"req_9f4d…","object":"chat.completion.chunk","created":1769040000,"model":"vorix-flash","choices":[{"index":0,"delta":{"content":"One"},"finish_reason":null}]}
data: {"id":"req_9f4d…","object":"chat.completion.chunk","created":1769040000,"model":"vorix-flash","choices":[{"index":0,"delta":{"content":", two"},"finish_reason":null}]}
data: {"id":"req_9f4d…","object":"chat.completion.chunk","created":1769040000,"model":"vorix-flash","served_by":"vorix-flash","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":34,"completion_tokens":12,"total_tokens":46,"cost":"$0.000023","cost_micros":23,"estimated":true}}
data: [DONE]نهاية البثّ
الحدث قبل الأخير يحمل حقل usage كاملًا (العدّادات والكلفة) مع finish_reason: "stop" وdelta فارغ، ثم يصل السطر الأخير data: [DONE] معلنًا انتهاء المجرى. توقّف عنده ولا تحاول تفسيره كـ JSON.
أمثلة كاملة
javascript
const response = await fetch("https://<host>/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VORIX_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "vorix-flash",
stream: true,
messages: [{ role: "user", content: "Count to five." }],
}),
})
const reader = response.body.getReader()
const decoder = new TextDecoder()
let buffer = ""
let usage = null
for (;;) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
// Events are separated by a blank line, and a network chunk may cut one
// in half — keep the remainder buffered until it completes.
const events = buffer.split("\n\n")
buffer = events.pop() ?? ""
for (const event of events) {
const line = event.trim()
if (!line.startsWith("data:")) continue
const payload = line.slice(5).trim()
if (payload === "[DONE]") break
const chunk = JSON.parse(payload)
if (chunk.error) throw new Error(chunk.error.message)
if (chunk.usage) usage = chunk.usage
const delta = chunk.choices?.[0]?.delta?.content
if (delta) process.stdout.write(delta)
}
}
console.log("\n", usage)python
import json
import os
import requests
with requests.post(
"https://<host>/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['VORIX_API_KEY']}"},
json={
"model": "vorix-flash",
"stream": True,
"messages": [{"role": "user", "content": "Count to five."}],
},
stream=True,
timeout=300,
) as response:
response.raise_for_status()
usage = None
for line in response.iter_lines(decode_unicode=True):
if not line or not line.startswith("data:"):
continue
payload = line[5:].strip()
if payload == "[DONE]":
break
chunk = json.loads(payload)
if "error" in chunk:
raise RuntimeError(chunk["error"]["message"])
if "usage" in chunk:
usage = chunk["usage"]
delta = chunk["choices"][0]["delta"].get("content", "")
print(delta, end="", flush=True)
print("\n", usage)تنبيهات عملية
- قد تصل الحزمة مقطوعة في منتصف حدث — احتفظ بالبقية في مخزن مؤقت حتى يكتمل.
- مع البثّ تكون عدّادات التوكن تقديرية (estimated: true) لأنها لا تُعرف إلا بعد الانتهاء.
- إن انقطع الاتصال أثناء التوليد فما وُلِّد حتى تلك اللحظة يُحاسَب — تعامل مع الانقطاع لا تتجاهله.
خطأ أثناء البثّ
إن تعذّر إكمال التوليد بعد بدء البثّ يصلك حدث يحمل حقل error بنفس صيغة الأخطاء المعتادة، ثم ينتهي المجرى. افحص وجود error في كل حدث قبل قراءة delta.