API i SDKPrzewodnik
Strumieniuj odpowiedzi czatu w Node.js z OpenAI SDK
Strumieniuj uzupełnienie czatu token po tokenie w Node.js: pętla for await, zatrzymywanie na żądanie, obsługa błędów i użycie tokenów z OpenAI SDK.

Na tej stronie
Strumieniowanie pozwala Twojej aplikacji pokazywać odpowiedź w miarę jej generowania, zamiast czekać na całą odpowiedź. W tym przewodniku zbudujesz mały skrypt Node.js, który strumieniuje uzupełnienie czatu z API CheapTokens token po tokenie, zatrzymuje je na żądanie i obsługuje błędy. Wersja w Pythonie tego tematu znajduje się w Strumieniowanie odpowiedzi w Pythonie.
Czego potrzebujesz#
- Node.js 22 lub nowszy (minimalna wersja podana w README pakietu
openai, sprawdzona 22 października 2026). - Klucz API CheapTokens. Jeśli jeszcze go nie masz, postępuj zgodnie z Twoje pierwsze żądanie API z CheapTokens.
- Oficjalne SDK:
npm install openai.
CheapTokens udostępnia API kompatybilne z OpenAI pod https:/, więc oficjalny pakiet openai działa po zmianie tylko bazowego URL i klucza. Trzymaj klucz w zmiennej środowiskowej, a nie w kodzie:
export CHEAPTOKENS_API_KEY="your-key"Krok 1. Strumieniuj odpowiedź za pomocą for await#
Utwórz stream.mjs (rozszerzenie .mjs pozwala używać import i najwyższego poziomu 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);Uruchom go za pomocą node stream.mjs. SDK odczytuje Server-Sent Events z API i przekazuje Ci jeden fragment na każdą iterację for await. Trzy szczegóły mają znaczenie:
- Tekst przychodzi w
chunk.. Niektóre fragmenty nie zawierają tekstu (pierwszy często zawiera tylko rolę), więc sprawdź, czychoices[0]. delta. content deltanie jest puste. process.drukuje bez znaku nowej linii, więc słowa pojawiają się jedno po drugim.stdout. write - W
stream_ostatni fragment zawiera obiektoptions: { include_ usage: true } usagez liczbą tokenów promptu i uzupełnienia, a jego tablicachoicesjest pusta. Dlatego kod używachunk.z opcjonalnym łańcuchowaniem.choices[0]?.
Identyfikator modelu pochodzi z dokumentacji CheapTokens: dostawcy nazywają ten sam model różnie, więc skopiuj identyfikator stamtąd.
Krok 2. Zatrzymaj strumień na żądanie#
Użytkownicy naciskają Stop, zamykają kartę lub przełączają czaty. Anuluj żądanie, aby nie czekać na tokeny, których nikt nie przeczyta. Przekaż AbortSignal w opcjach żądania:
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 },
);Obiekt strumienia ma również własne stream., a wyjście z pętli for await za pomocą break również zamyka połączenie. Użyj tego, co jest bliżej Twojego kodu: sygnału, gdy przycisk anulowania znajduje się gdzie indziej, break, gdy decyzja jest podejmowana wewnątrz pętli (na przykład znalazłeś słowo stop).
Krok 3. Obsługa błędów#
Strumień może zawieść przed pierwszym tokenem (zły klucz, puste saldo) lub w środku (utrata sieci). Otocz zarówno żądanie, jak i pętlę jednym 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;
}
}APIUserAbortErrorto, co otrzymujesz po własnymabort(). To nie jest błąd, więc nie pokazuj go jako błędu.APIErrorma HTTPstatus: 401 oznacza zły lub brakujący klucz, 429 oznacza zbyt wiele żądań, a komunikat o tokenach oznacza, że saldo wymaga doładowania. Więcej przyczyn i rozwiązań znajduje się w Typowe błędy połączenia i jak je naprawić i Błąd "Not enough tokens".- Tekst, który został już wydrukowany, pozostaje ważny po błędzie. Zachowaj go, jeśli chcesz pokazać częściową odpowiedź.
Krok 4. Odczytaj użycie i oszacuj koszt#
Obiekt usage zawiera prompt_tokens i completion_. Razem z ceną modelu za 1M tokenów daje to dokładny koszt żądania. Aby uzyskać przybliżony szacunek przed uruchomieniem czegokolwiek, użyj poniższej tabeli na żywo: pokazuje koszt 1 000 żądań z 2 000 tokenów wejściowych i 800 tokenów wyjściowych każde.
| Model | Wejście $/1M | Wyjście $/1M | Za żądanie | 1000 żądań | OpenRouter, 1000 żądań |
|---|---|---|---|---|---|
| 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 |
2000 tokenów wejściowych i 800 tokenów wyjściowych na żądanie. Ceny za 1 mln tokenów są aktualne i zmieniają się wraz z naszymi taryfami; kolumna OpenRouter pokazuje ceny referencyjne dla tego samego obciążenia.
Dla strumieniowanych odpowiedzi czatu MiniMax-M2.7 jest wyceniany na $0.05 za 1M tokenów wejściowych i $0.15 za 1M tokenów wyjściowych, z kontekstem 180K i do 8 192 tokenów wyjściowych na odpowiedź. Cena jest stała dla modelu o każdej porze dnia, a jedno saldo obejmuje wszystkie modele, więc zmiana ciągu model wystarczy, aby porównać je na własnych promptach.
Pełny skrypt#
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;
}
}Następne kroki#
- Wysyłaj fragmenty do przeglądarki za pomocą Server-Sent Events z własnego punktu końcowego, aby klucz nigdy nie opuszczał serwera.
- Wypróbuj inne modele, zmieniając tylko ciąg
model: MiniMax-M2.7 jest dostępny na tym samym saldzie i pod tym samym bazowym URL. - Zdobądź klucz i uruchom skrypt: utwórz klucz API CheapTokens i doładuj saldo.
Punkty końcowe, identyfikatory modeli i limity dla każdego modelu znajdują się w dokumentacji CheapTokens.



