გადასვლა შინაარსზე
სტრიმინგი

სტრიმინგი

დააყენეთ stream მნიშვნელობაზე true და პასუხი server-sent events-ის სახით მოვა, სანამ მოდელი წერს: ჯერ მსჯელობა, შემდეგ პასუხი, შემდეგ ბოლო ნაწილი დასრულების მიზეზითა და usage-ით. გამოიყენეთ ეს ყოველთვის, როცა ადამიანი ტექსტს ელოდება, და გრძელი პასუხებისთვის.

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

OpenAI SDK-ები ნაწილებს სათითაოდ გაძლევენ. თითოეული ნაწილი იმის მიხედვით წაიკითხეთ, რასაც ის შეიცავს: მსჯელობის ნაწილს, პასუხის ნაწილს, ინსტრუმენტის გამოძახებას ან პასუხის დასასრულს.

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. მოდის ერთხელ, სტრიმის დასაწყისში. Hosted open-weight მოდელები, shannon-2-lite, shannon-2-pro
reasoning_content string მოდელის მსჯელობის ნაწილი. ნაწილები თანმიმდევრობით გააერთიანეთ. Hosted open-weight მოდელები, Shannon 3-ის ოჯახი, shannon-1.6-pro, shannon-coder-1
content string პასუხის ნაწილი. ნაწილები თანმიმდევრობით გააერთიანეთ. ყველა მოდელი
tool_calls array ერთი სრული ინსტრუმენტის გამოძახება. იხილეთ Tool call deltas. ყველა მოდელი

ჯერ მსჯელობა მოდის, შემდეგ პასუხი, შემდეგ ინსტრუმენტების გამოძახებები. ნაწილი წაიკითხეთ მისი 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-ში აგზავნის პასუხამდე. ორი ტექსტი ერთმანეთისგან განაცალკევეთ: მსჯელობა აჩვენეთ ცალკე, ჩაკეცვად ნაწილად, ან გამოტოვეთ.

hosted open-weight მოდელები მსჯელობას არ აგზავნიან, როცა reasoning_effort არის off, და არც მაშინ, როცა მოთხოვნას აქვს response_format.

shannon-2-lite და shannon-2-pro პასუხამდე ასევე მსჯელობენ. ამ endpoint-ზე, სანამ ისინი მსჯელობენ, კომენტარის ხაზებს : thinking აგზავნიან, შემდეგ კი პასუხს.

reasoning_content OpenAI SDK-ების ტიპიზებულ მოდელებში არ არის. Python-ში წაიკითხეთ getattr(delta, "reasoning_content", None)-ით; JavaScript-ში delta.reasoning_content უცვლელად მუშაობს.

Tool call დელტები

ინსტრუმენტის გამოძახება მოდის მთლიანად: თითო გამოძახებაზე ერთი ნაწილი, რომლის 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
  }
}

hosted open-weight მოდელებზე 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-ის ველები endpoint-თან ერთად არის აღწერილი. Chat Completions

Keep-alive ხაზები

სანამ მოდელი მუშაობს და გასაგზავნი ჯერ არაფერი აქვს, სტრიმი კომენტარის ხაზებს შეიცავს. მონაცემები მათში არ არის. SSE პარსერი მათ თავად გამოტოვებს; კოდმა, რომელიც ნედლ ხაზებს კითხულობს, უნდა გამოტოვოს ყოველი ხაზი, რომელიც ორწერტილით იწყება.

ხაზი ვინ აგზავნის როდის
: ping Shannon 3-ის ოჯახი, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 ყოველ 15 წამში.
: keepalive Hosted open-weight მოდელები ყოველ 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 საუბარი მოდელის კონტექსტის ფანჯარაზე გრძელია. შეამოკლეთ, სანამ ხელახლა გააგზავნით. შეტყობინების ტექსტი იცვლება.

hosted open-weight მოდელები error წევრს მხოლოდ type-ითა და message-ით აგზავნიან.

OpenAI Python და 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 endpoint-ებიც სტრიმინგს უჭერენ მხარს. მათი ფრეიმები შეიცავს ხაზს event: მოვლენის სახელით და ხაზს data: მისი JSON-ით. ყოველი გვერდი თავის მოვლენებს თანმიმდევრობით ჩამოთვლის.

სტრიმში Chat CompletionsResponsesMessages
ფრეიმები data: ხაზებიevent: და data: ხაზებიevent: და data: ხაზები
მსჯელობა მოდის delta.reasoning_contentresponse.reasoning_summary_text.deltathinking_delta
პასუხის ტექსტი მოდის delta.contentresponse.output_text.deltatext_delta
Usage მოდის ბოლო ნაწილიresponse.completedmessage_delta
სტრიმი მთავრდება ბოლო ნაწილი, შემდეგ data: [DONE]response.completedmessage_stop