API та SDKПосібник
Стрімте відповіді чату в Node.js за допомогою OpenAI SDK
Стрімте завершення чату токен за токеном у Node.js: цикл for await, зупинка на вимогу, обробка помилок і використання токенів за допомогою OpenAI SDK.

На цій сторінці
Стрімінг дозволяє вашому застосунку показувати відповідь у міру її генерації, а не чекати на всю відповідь. У цьому посібнику ви створите невеликий скрипт Node.js, який стрімить завершення чату з API CheapTokens токен за токеном, зупиняє його на вимогу та обробляє помилки. Версія цієї теми для Python — у Стрімінгові відповіді в Python.
Що вам потрібно#
- Node.js 22 або новіший (мінімальна версія, зазначена в README пакета
openai, перевірено 22 жовтня 2026 року). - Ключ API CheapTokens. Якщо у вас його ще немає, дотримуйтеся Вашого першого запиту до API з CheapTokens.
- Офіційний SDK:
npm install openai.
CheapTokens надає API, сумісний з OpenAI, за адресою https:/, тому офіційний пакет openai працює, якщо змінити лише базову URL-адресу та ключ. Тримайте ключ у змінній середовища, а не в коді:
export CHEAPTOKENS_API_KEY="your-key"Крок 1. Стрімте відповідь за допомогою for await#
Створіть stream.mjs (розширення .mjs дозволяє використовувати import та await верхнього рівня):
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);Запустіть його за допомогою node stream.mjs. SDK читає Server-Sent Events з API і передає вам один фрагмент за кожну ітерацію for await. Важливі три деталі:
- Текст надходить у
chunk.. Деякі фрагменти не містять тексту (перший часто містить лише роль), тому перевіряйте, щоchoices[0]. delta. content deltaне порожній. process.друкує без перенесення рядка, тому слова з'являються одне за одним.stdout. write - З
stream_останній фрагмент містить об'єктoptions: { include_ usage: true } usageз кількістю токенів запиту та завершення, а його масивchoicesпорожній. Саме тому код використовуєchunk.з опціональним ланцюжком.choices[0]?.
Ідентифікатор моделі береться з документації CheapTokens: провайдери називають ту саму модель по-різному, тому копіюйте ідентифікатор звідти.
Крок 2. Зупиніть стрім на вимогу#
Користувачі натискають Stop, закривають вкладку або перемикають чати. Скасуйте запит, щоб не чекати на токени, які ніхто не прочитає. Передайте AbortSignal в опціях запиту:
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 },
);Об'єкт стріму також має власний stream., а вихід із циклу for await за допомогою break також закриває з'єднання. Використовуйте те, що ближче до вашого коду: сигнал, коли кнопка скасування розташована в іншому місці, break, коли рішення ухвалюється всередині циклу (наприклад, ви знайшли стоп-слово).
Крок 3. Обробляйте помилки#
Стрім може зазнати невдачі до першого токена (неправильний ключ, порожній баланс) або посередині (обрив мережі). Обгорніть і запит, і цикл в один 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;
}
}APIUserAbortError— це те, що ви отримуєте після власногоabort(). Це не збій, тому не показуйте його як збій.APIErrorмає HTTPstatus: 401 означає неправильний або відсутній ключ, 429 означає забагато запитів, а повідомлення про токени означає, що баланс потрібно поповнити. Більше причин і виправлень — у Поширені помилки з'єднання та як їх виправити та Помилка "Not enough tokens".- Текст, який уже надруковано, залишається дійсним після помилки. Збережіть його, якщо хочете показати часткову відповідь.
Крок 4. Читайте використання та оцінюйте вартість#
Об'єкт usage містить prompt_tokens та completion_. Разом із ціною моделі за 1M токенів він дає вам точну вартість запиту. Для приблизної оцінки перед запуском чогось використовуйте живу таблицю нижче: вона показує вартість 1 000 запитів із 2 000 вхідних і 800 вихідних токенів кожен.
| Модель | Вхід $/1М | Вихід $/1М | За запит | 1 000 запитів | OpenRouter, 1 000 запитів |
|---|---|---|---|---|---|
| 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 вхідних і 800 вихідних токенів на запит. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.
Для стрімових відповідей чату MiniMax-M2.7 коштує $0.05 за 1M вхідних і $0.15 за 1M вихідних токенів, із контекстом 180K і до 8 192 вихідних токенів на відповідь. Ціна фіксована для моделі в будь-який час доби, і один баланс покриває всі моделі, тому достатньо змінити рядок model, щоб порівняти їх на власних запитах.
Повний скрипт#
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;
}
}Наступні кроки#
- Надсилайте фрагменти до браузера за допомогою Server-Sent Events зі своєї кінцевої точки, щоб ключ ніколи не залишав сервер.
- Спробуйте інші моделі, змінивши лише рядок
model: MiniMax-M2.7 доступна на тому самому балансі та тій самій базовій URL-адресі. - Отримайте ключ і запустіть скрипт: створіть ключ API CheapTokens і поповніть баланс.
Кінцеві точки, ідентифікатори моделей і ліміти для кожної моделі — у документації CheapTokens.



