API & SDKHướng dẫn
Stream phản hồi chat trong Node.js với OpenAI SDK
Stream một chat completion theo từng token trong Node.js: vòng lặp for await, dừng theo yêu cầu, xử lý lỗi và sử dụng token với OpenAI SDK.

Trên trang này
Streaming cho phép ứng dụng của bạn hiển thị câu trả lời ngay khi nó được tạo ra thay vì chờ toàn bộ phản hồi. Trong hướng dẫn này, bạn sẽ xây dựng một script Node.js nhỏ để stream một chat completion từ API CheapTokens theo từng token, dừng nó theo yêu cầu và xử lý lỗi. Phiên bản Python của chủ đề này nằm trong Streaming responses in Python.
Những gì bạn cần#
- Node.js 22 trở lên (phiên bản tối thiểu được nêu trong README của gói
openai, đã kiểm tra ngày 22 tháng 10 năm 2026). - Một khóa API CheapTokens. Nếu bạn chưa có, hãy làm theo Your first API request with CheapTokens.
- SDK chính thức:
npm install openai.
CheapTokens cung cấp API tương thích OpenAI tại https:/, vì vậy gói openai chính thức hoạt động chỉ bằng cách thay đổi base URL và khóa. Giữ khóa trong một biến môi trường, không phải trong mã:
export CHEAPTOKENS_API_KEY="your-key"Bước 1. Stream câu trả lời với for await#
Tạo stream.mjs (phần mở rộng .mjs cho phép bạn sử dụng import và await cấp cao nhất):
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CHEAPTOKENS_API_KEY,
baseURL: 'https://api.cheaptokens.dev/v1',
});
const stream = await client.chat.completions.create({
model: 'deepseek-ai/DeepSeek-V4-Flash-0731', // take the id from the CheapTokens docs
messages: [{ role: 'user', content: 'Explain streaming in two sentences.' }],
stream: true,
stream_options: { include_usage: true },
});
let usage = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
if (chunk.usage) usage = chunk.usage;
}
console.log('\n\nusage:', usage);Chạy nó với node stream.mjs. SDK đọc Server-Sent Events từ API và đưa cho bạn một chunk mỗi lần lặp của for await. Ba chi tiết quan trọng:
- Văn bản đến trong
chunk.. Một số chunk không mang văn bản (chunk đầu tiên thường chỉ chứa role), vì vậy hãy kiểm tra rằngchoices[0]. delta. content deltakhông rỗng. process.in mà không có ngắt dòng, vì vậy các từ xuất hiện lần lượt.stdout. write - Với
stream_, chunk cuối cùng mang một đối tượngoptions: { include_ usage: true } usagevới số lượng token prompt và completion, và mảngchoicescủa nó rỗng. Đó là lý do mã sử dụngchunk.với optional chaining.choices[0]?.
Id model đến từ tài liệu CheapTokens: các nhà cung cấp đặt tên cùng một model khác nhau, vì vậy hãy sao chép id từ đó.
Bước 2. Dừng stream theo yêu cầu#
Người dùng nhấn Stop, đóng tab hoặc chuyển cuộc trò chuyện. Hủy yêu cầu để bạn không phải chờ các token mà không ai đọc. Truyền một AbortSignal trong tùy chọn yêu cầu:
const controller = new AbortController();
process.on('SIGINT', () => controller.abort()); // Ctrl+C stops the stream
const stream = await client.chat.completions.create(
{
model: 'deepseek-ai/DeepSeek-V4-Flash-0731',
messages: [{ role: 'user', content: 'Write a long story about a lighthouse.' }],
stream: true,
},
{ signal: controller.signal },
);Đối tượng stream cũng có stream. riêng, và thoát khỏi vòng lặp for await với break cũng đóng kết nối. Sử dụng cái nào gần mã của bạn hơn: một signal khi nút hủy nằm ở nơi khác, break khi quyết định được đưa ra bên trong vòng lặp (ví dụ: bạn tìm thấy một từ dừng).
Bước 3. Xử lý lỗi#
Một stream có thể thất bại trước token đầu tiên (sai khóa, số dư trống) hoặc ở giữa (mất mạng). Bao bọc cả yêu cầu và vòng lặp trong một try:
try {
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
} catch (err) {
if (err instanceof OpenAI.APIUserAbortError) {
console.log('\n[stopped by user]');
} else if (err instanceof OpenAI.APIError) {
console.error(`\nAPI error ${err.status}: ${err.message}`);
} else {
throw err;
}
}APIUserAbortErrorlà những gì bạn nhận được sauabort()của riêng bạn. Đó không phải là lỗi, vì vậy đừng hiển thị nó như một lỗi.APIErrorcóstatusHTTP: 401 nghĩa là sai hoặc thiếu khóa, 429 nghĩa là quá nhiều yêu cầu, và thông báo về token nghĩa là số dư cần được nạp thêm. Nguyên nhân và cách khắc phục khác có trong Common connection errors and how to fix them và "Not enough tokens" error.- Văn bản đã được in vẫn hợp lệ sau một lỗi. Giữ nó nếu bạn muốn hiển thị câu trả lời một phần.
Bước 4. Đọc usage và ước tính chi phí#
Đối tượng usage chứa prompt_tokens và completion_. Cùng với giá model trên 1 triệu token, nó cho bạn chi phí chính xác của một yêu cầu. Để có ước tính sơ bộ trước khi chạy bất cứ điều gì, hãy sử dụng bảng trực tiếp bên dưới: nó hiển thị chi phí của 1.000 yêu cầu với 2.000 token đầu vào và 800 token đầu ra mỗi yêu cầu.
| Mô hình | Đầu vào $/1M | Đầu ra $/1M | Mỗi yêu cầu | 1.000 yêu cầu | OpenRouter, 1.000 yêu cầu |
|---|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.000072 | $0.072 | $0.143 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.00011 | $0.11 | $0.22 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.00022 | $0.22 | $1.56 |
2.000 token đầu vào và 800 token đầu ra mỗi yêu cầu. Giá trên 1 triệu token là giá hiện hành và thay đổi cùng với biểu phí của chúng tôi; cột OpenRouter hiển thị giá tham chiếu cho cùng khối lượng công việc.
Đối với các câu trả lời chat được stream, MiniMax-M2.7 có giá $0.05 trên 1 triệu token đầu vào và $0.15 trên 1 triệu token đầu ra, với ngữ cảnh 180K và tối đa 8.192 token đầu ra mỗi câu trả lời. Giá được cố định cho mỗi model vào bất kỳ thời điểm nào trong ngày, và một số dư bao gồm tất cả các model, vì vậy chỉ cần thay đổi chuỗi model là đủ để so sánh chúng trên các prompt của riêng bạn.
Script đầy đủ#
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CHEAPTOKENS_API_KEY,
baseURL: 'https://api.cheaptokens.dev/v1',
});
const controller = new AbortController();
process.on('SIGINT', () => controller.abort());
try {
const stream = await client.chat.completions.create(
{
model: 'deepseek-ai/DeepSeek-V4-Flash-0731',
messages: [{ role: 'user', content: 'Explain streaming in two sentences.' }],
stream: true,
stream_options: { include_usage: true },
},
{ signal: controller.signal },
);
let usage = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
if (chunk.usage) usage = chunk.usage;
}
console.log('\n\nusage:', usage);
} catch (err) {
if (err instanceof OpenAI.APIUserAbortError) {
console.log('\n[stopped by user]');
} else if (err instanceof OpenAI.APIError) {
console.error(`\nAPI error ${err.status}: ${err.message}`);
} else {
throw err;
}
}Các bước tiếp theo#
- Gửi các chunk đến trình duyệt với Server-Sent Events từ endpoint của riêng bạn, để khóa không bao giờ rời khỏi máy chủ.
- Thử các model khác chỉ bằng cách thay đổi chuỗi
model: MiniMax-M2.7 có sẵn trên cùng số dư và cùng base URL. - Lấy khóa và chạy script: create a CheapTokens API key và nạp thêm số dư.
Các endpoint, id model và giới hạn cho mọi model có trong CheapTokens docs.



