Streaming
Đặt stream: true là gateway chuyển tiếp server-sent events theo nhịp model sinh chữ. Trang này nói về những phần SDK không tự lo cho bạn: chunk usage cuối, delta reasoning, và lỗi xảy ra sau khi HTTP 200 đã cam kết.
Bật streaming
curl -N https://api.clfaigateway.dev/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "kimi-k2.6",
"messages": [{"role": "user", "content": "Explain SSE in one sentence."}],
"stream": true,
"stream_options": {"include_usage": true}
}'Chunk đến dưới dạng sự kiện data: và stream kết thúc bằng data: [DONE]. Chunk cuối luôn mang số token chính thức trong usage — mọi stream đều có, bất kể bạn có xin hay không. stream_options.include_usage chỉ đổi hình dạng các chunk ở giữa: bật thì chúng mang "usage": null (đúng schema OpenAI); tắt thì vắng hẳn trường này.
JSON mode không stream được: response_format kiểu JSON đi cùng stream: true trả 400 json_mode_no_stream.
Model reasoning
Model reasoning (cả bảy model đang mở) stream phần suy nghĩ qua delta.reasoning_content trước khi câu trả lời bắt đầu trong delta.content. Gateway chuyển tiếp nguyên văn để bạn hiển thị trực tiếp. Mỗi delta reasoning tăng bộ đếm reasoning_tokens, trả trong completion_tokens_details của chunk usage cuối.
reasoning_tokens chỉ để nhìn
Token reasoning là tập con của completion_tokens và không bao giờ bị tính tiền như một chiều giá riêng — con số này tồn tại để bạn thấy vì sao câu trả lời dài hơn phần chữ nhìn thấy. Request không stream không tách được phần này nên reasoning_tokens ở đó bằng 0.
Lỗi sau khi stream đã bắt đầu
Phát byte đầu tiên xong là HTTP status đã chốt 200, không đổi được nữa. Nếu upstream hỏng giữa chừng, gateway phát đúng một sự kiện cuối mang finish_reason: "error" và object error, rồi [DONE]:
data: {"id":"req_01j9zxg0aabbccddeeff00112233","object":"chat.completion.chunk","created":1774694600,"model":"kimi-k2.6","choices":[{"index":0,"delta":{},"finish_reason":"error"}],"error":{"message":"Upstream provider error while streaming. You were charged only for tokens already delivered.","type":"api_error","code":"upstream_error","param":null}}
data: [DONE]SDK sẽ không tự báo lỗi này
SDK chỉ nhìn HTTP status sẽ thấy một stream thành công nhưng kết thúc sớm. Hãy kiểm finish_reason trên từng chunk — mẫu Python và JavaScript phía trên đã làm — và coi "error" là request hỏng. Bạn chỉ bị tính phần token đã nhận trước lúc hỏng.
Nếu bạn ngắt kết nối trước
Ngắt kết nối không hủy phần đã sinh: bạn bị tính số token đã phát tới lúc ngắt, đếm chính xác từ usage per-chunk — không bao giờ ước lượng. Chặn trần xấu nhất bằng max_tokens. Toàn bộ chính sách phía tiền, kể cả bảo hiểm zero-completion, nằm ở Billing.
Stream chốt sổ ra sao: trường status
Mỗi request xuất hiện trong GET /v1/generation?id={x-request-id} sau khi chốt sổ, kèm status:
| status | Nghĩa là | Bạn trả |
|---|---|---|
success | Stream đã xong — kể cả trường hợp bạn chủ động ngắt sớm. | Toàn bộ token đã phát. |
partial | Upstream hỏng giữa stream; bạn đã nhận finish_reason: "error". | Chỉ phần token đã phát trước lúc hỏng. |
error | Request hỏng trước khi có bất kỳ output nào. | $0 khi bảo hiểm zero-completion áp dụng — xem Billing. |
Chi phí stream đọc sau khi xong
Stream không mang header x-gw-cost-nano — header đã gửi đi trước khi biết chi phí. Đọc chi phí đã chốt từ GET /v1/generation bằng header x-request-id (cũng chính là trường id của mọi chunk).
Một quy tắc riêng nữa của stream: Idempotency-Key không hỗ trợ cho request stream — Idempotency giải thích vì sao.
Nếu stream của bạn dồn về một cục
Gateway flush từng chunk ngay lập tức và không bao giờ nén text/event-stream. Nếu chunk vẫn dồn về một cục ở cuối, có thứ gì đó GIỮA chúng tôi và code của bạn đang buffer — gần như luôn là proxy công ty, hoặc reverse proxy của chính bạn (nginx mặc định buffer response: đặt proxy_buffering off; cho route SSE, hoặc tôn trọng quy ước X-Accel-Buffering: no). Nền tảng serverless buffer response cũng gây hệt vậy.
Cách phân định nhanh: chạy đúng request đó bằng curl -N từ chính mạng bị nghi. curl chảy mượt thì chỗ buffer nằm trong stack của bạn, không phải trên đường truyền.
Sau proxy công ty, cấu hình SDK tường minh (httpx ≥ 0.28 đổi tên tùy chọn thành số ít proxy): Python OpenAI(http_client=httpx.Client(proxy="http://proxy:8080")), Node qua fetchOptions. Gặp APIConnectionError nghĩa là request chưa tới được chúng tôi — soi proxy/DNS/TLS phía bạn; APIStatusError mới là đã tới.
Timeout phía client và model reasoning
Mặc định của SDK (timeout tổng 10 phút, Python áp per-read khi stream) an toàn cho model reasoning. Sai lầm phổ biến là hạ nó toàn cục — timeout=30 — trong khi model reasoning có thể nghĩ 30–70 giây trước token nhìn-thấy-được đầu tiên với prompt dài (chúng tôi stream reasoning_content sớm chính là để kết nối không bao giờ im lặng lâu). Timeout quá thấp ném APITimeoutError giữa chừng, rồi SDK tự gửi lại cả request — với call không stream mà thiếu Idempotency-Key, thế là tính tiền hai lần.
# Python — connect ngắn, read rộng (thay vì một timeout nhỏ toàn cục)
import httpx
client = OpenAI(
base_url="https://api.clfaigateway.dev/v1",
api_key=os.environ["CLF_API_KEY"],
timeout=httpx.Timeout(600.0, connect=5.0),
)