تخطَّ إلى المحتوى
البث

البث

اضبط stream على true فيصل الرد كأحداث يرسلها الخادم (server-sent events) أثناء كتابة النموذج له: الاستدلال أولاً، ثم الإجابة، ثم جزء أخير فيه سبب الانتهاء والاستخدام. استخدمه كلما كان شخص ينتظر النص، وفي الردود الطويلة.

POST https://api.shannon-ai.com/v1/chat/completions

تعطيك OpenAI SDKs الأجزاء واحداً واحداً. اقرأ كل جزء بحسب ما يحمله: قطعة استدلال، أو قطعة من الإجابة، أو استدعاء أداة، أو نهاية الرد.

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}")

كيف يُؤطَّر البث

  • للرد الحالة 200 والترويستان content-type: text/event-stream وcache-control: no-cache.
  • كل حدث سطر واحد يبدأ بـ data: ويتبعه سطر فارغ. والنص بعد data: هو جزء JSON واحد. ولا توجد أسطر event: أو id:.
  • السطر الذي يبدأ بنقطتين رأسيتين، مثل : ping، تعليق يُبقي الاتصال مفتوحاً. تخطَّه.
  • ينتهي البث بـ data: [DONE]. والبث الذي يتوقف بدونه غير مكتمل.

الأجزاء بالترتيب

كل جزء كائن chat.completion.chunk:

JSON
{
  "id": "chatcmpl-5f0c1e7a9b3d4c62a8e1f07d2b46c9a3",
  "object": "chat.completion.chunk",
  "created": 1791625200,
  "model": "shannon-3",
  "choices": [
    {
      "index": 0,
      "delta": {
        "content": "Server-sent"
      },
      "finish_reason": null
    }
  ]
}

تحمل كل أجزاء البث الواحد قيم id وcreated وmodel نفسها. وchoices يحتوي دائماً على إدخال واحد. ويحمل delta فيه ما هو جديد، وتبقى finish_reason بقيمة null حتى الجزء الأخير.

المفتاح في delta النوع الوصف يرسله
role string دائماً assistant. ويصل مرة واحدة، في بداية البث. النماذج المفتوحة الأوزان المستضافة، وshannon-2-lite، وshannon-2-pro
reasoning_content string قطعة من استدلال النموذج. اربط القطع بالترتيب. النماذج المفتوحة الأوزان المستضافة، وعائلة Shannon 3، وshannon-1.6-pro، وshannon-coder-1
content string قطعة من الإجابة. اربط القطع بالترتيب. كل النماذج
tool_calls array استدعاء أداة كامل واحد. انظر دلتا استدعاء الأداة. كل النماذج

يأتي الاستدلال أولاً، ثم الإجابة، ثم أي استدعاءات أدوات. اقرأ الجزء حسب المفاتيح في delta الخاص به، لا حسب موضعه في البث: فقد يحمل delta الحقل role مع أول قطعة نص، وآخر جزء يكون فارغاً.

بث كامل كما ينتقل. ولإبقاء الأسطر قصيرة، حُذفت id وobject وcreated وmodel من كل جزء هنا.

200 text/event-stream
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.reasoning_content قبل الإجابة. أبقِ النصين منفصلين: اعرض الاستدلال كجزء مستقل قابل للطي، أو احذفه.

لا ترسل النماذج المفتوحة الأوزان المستضافة استدلالاً عندما تكون reasoning_effort هي off، ولا ترسله عندما يحتوي الطلب على response_format.

يستدل shannon-2-lite وshannon-2-pro قبل أن يجيبا أيضاً. وعلى نقطة النهاية هذه يرسلان أسطر تعليق : thinking أثناء ذلك، ثم الإجابة.

reasoning_content ليس في النماذج المصنَّفة (typed models) في OpenAI SDKs. في Python اقرأه بـ getattr(delta, "reasoning_content", None)؛ وفي JavaScript يعمل delta.reasoning_content كما هو.

دلتا استدعاء الأداة

يصل استدعاء الأداة كاملاً: جزء واحد لكل استدعاء، يحمل delta فيه الاستدعاء مع السلسلة arguments كاملة. ويرقّم index استدعاءات الرد بدءاً من 0. والكود الذي يجمع arguments قطعة قطعة يعمل دون تغيير، لأنه يجمع قطعة واحدة.

delta
{
  "tool_calls": [
    {
      "index": 0,
      "id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"city\":\"Paris\"}"
      }
    }
  ]
}

بعد الاستدعاءات، يحمل الجزء الأخير finish_reason بقيمة tool_calls.

عندما يحتوي الطلب على tools، يرسل shannon-1.6-lite وshannon-1.6-pro وshannon-coder-1 نص الإجابة كدلتا واحدة في نهاية الرد.

الجزء الأخير

آخر جزء قبل data: [DONE] فيه delta فارغ وfinish_reason وusage الطلب، كلها في الجزء نفسه. ويوجد في كل بث؛ ولا تحتاج إلى stream_options.

JSON
{
  "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 موصوفة مع نقطة النهاية. 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 أثناء استدلال النموذج.

الأخطاء داخل البث

بعد أن يبدأ البث تكون حالته 200، ولذلك يصل الفشل داخل البث: جزء فيه عضو error في مكان choices.

200 text/event-stream
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]

بعد جزء الخطأ يتبع مع ذلك الجزء الأخير، مع finish_reason وusage، ثم data: [DONE]. افحص كل جزء بحثاً عن 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 SDK وJavaScript SDK جزء الخطأ كخطأ API أثناء التكرار، فضع الحلقة داخل معالجة الأخطاء المعتادة لديك.

لرموز الحالة ونص الخطأ وما ينبغي إعادة محاولته صفحتها الخاصة. معالجة الأخطاء

إغلاق الاتصال

إغلاق الاتصال يوقف التسليم، لا الطلب. يكتب النموذج الرد حتى نهايته ويُحاسَب الطلب كأنك قرأته كله. وللدفع أقل، اطلب أقل: اضبط max_tokens أقل في نموذج يطبّقه.

قد ينتهي البث مبكراً أيضاً في طريقه إليك، مثلاً عند انقطاع الشبكة أو أثناء تحديث خادم. عندئذٍ يتوقف دون الجزء الأخير ودون data: [DONE]. اعتبر ذلك الرد غير مكتمل وأعد إرسال الطلب.

قراءة البث بدون SDK

القارئ الصحيح يفعل خمسة أشياء: يرسل الأخطاء التي تصل قبل البث إلى معالجة الأخطاء لديه، ويقسّم النص إلى أسطر، ويتخطى أسطر التعليق، ويتوقف عند data: [DONE]، ويفحص كل جزء بحثاً عن 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))

يستخدم مثال Python الحزمة requests. ويعمل مثال JavaScript على Node.js 18 أو أحدث.

البث في الصيغ الأخرى

تبث نقطتا نهاية Responses وMessages أيضاً. وتحمل إطاراتهما سطر event: فيه اسم الحدث وسطر data: فيه JSON الخاص به. وتسرد كل صفحة أحداثها بالترتيب.

في البث Chat CompletionsResponsesMessages
الإطارات أسطر data:أسطر event: وdata:أسطر event: وdata:
يصل الاستدلال في delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
يصل نص الإجابة في delta.contentresponse.output_text.deltatext_delta
يصل الاستخدام في الجزء الأخيرresponse.completedmessage_delta
ينتهي البث بـ الجزء الأخير، ثم data: [DONE]response.completedmessage_stop