ব্লগে ফিরে যান

API ও SDKগাইড

OpenAI SDK দিয়ে Node.js-এ চ্যাট রেসপন্স স্ট্রিম করুন

Node.js-এ টোকেন ধরে ধরে চ্যাট কমপ্লিশন স্ট্রিম করুন: for await লুপ, চাহিদামতো থামানো, ত্রুটি সামলানো এবং OpenAI SDK দিয়ে টোকেন usage।

CheapTokens Team দ্বারাপ্রকাশিত হয়েছে আপডেট করা হয়েছে 7 মিনিট পড়ার সময়

একটি Node.js স্ক্রিপ্ট থেকে টোকেন ধরে ধরে স্ট্রিম হওয়া উত্তর প্রিন্ট করছে টার্মিনাল উইন্ডো
এই পৃষ্ঠায়
  1. যা যা লাগবে
  2. ধাপ ১. for await দিয়ে উত্তর স্ট্রিম করুন
  3. ধাপ ২. চাহিদামতো স্ট্রিম থামান
  4. ধাপ ৩. ত্রুটি সামলান
  5. ধাপ ৪. usage পড়ুন ও খরচ অনুমান করুন
  6. সম্পূর্ণ স্ক্রিপ্ট
  7. পরবর্তী ধাপ

স্ট্রিমিং আপনার অ্যাপকে সম্পূর্ণ উত্তরটির জন্য অপেক্ষা না করে উত্তরটি তৈরি হওয়ার সাথে সাথে দেখানোর সুযোগ দেয়। এই গাইডে আপনি একটি ছোট Node.js স্ক্রিপ্ট তৈরি করবেন যা CheapTokens API থেকে টোকেন ধরে ধরে একটি চ্যাট কমপ্লিশন স্ট্রিম করে, চাহিদামতো থামায় এবং ত্রুটি সামলায়। এই বিষয়ের Python সংস্করণ রয়েছে Streaming responses in Python-এ।

যা যা লাগবে#

  • Node.js 22 বা নতুন (openai প্যাকেজের README-তে উল্লেখিত সর্বনিম্ন সংস্করণ, ২২ অক্টোবর ২০২৬-এ যাচাই করা)।
  • একটি CheapTokens API key। এখনো না থাকলে Your first API request with CheapTokens অনুসরণ করুন।
  • অফিসিয়াল SDK: npm install openai।

CheapTokens https://api.cheaptokens.dev/v1-এ একটি OpenAI-সামঞ্জস্যপূর্ণ API দেয়, তাই অফিসিয়াল openai প্যাকেজটি শুধু base URL আর key বদলালেই কাজ করে। key-টি কোডে নয়, একটি environment variable-এ রাখুন:

bash
export CHEAPTOKENS_API_KEY="your-key"

ধাপ ১. for await দিয়ে উত্তর স্ট্রিম করুন#

stream.mjs তৈরি করুন (.mjs এক্সটেনশন আপনাকে import এবং টপ-লেভেল await ব্যবহার করতে দেয়):

javascript
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 API থেকে Server-Sent Events পড়ে এবং for await-এর প্রতিটি পুনরাবৃত্তিতে আপনাকে একটি chunk দেয়। তিনটি বিষয় গুরুত্বপূর্ণ:

  • টেক্সট chunk.choices[0].delta.content-এ আসে। কিছু chunk-এ কোনো টেক্সট থাকে না (প্রথমটিতে প্রায়ই শুধু role থাকে), তাই delta খালি কি না দেখে নিন।
  • process.stdout.write লাইন ব্রেক ছাড়া প্রিন্ট করে, তাই শব্দগুলো একটার পর একটা দেখা যায়।
  • stream_options: { include_usage: true } দিলে শেষ chunk-এ prompt ও completion টোকেন সংখ্যাসহ একটি usage অবজেক্ট থাকে, আর এর choices অ্যারে খালি থাকে। এজন্যই কোডে optional chaining-সহ chunk.choices[0]?. ব্যবহার করা হয়েছে।

মডেল id আসে CheapTokens ডকুমেন্টেশন থেকে: একই মডেলকে বিভিন্ন প্রোভাইডার ভিন্ন নামে ডাকে, তাই id সেখান থেকেই কপি করুন।

ধাপ ২. চাহিদামতো স্ট্রিম থামান#

ব্যবহারকারী Stop চাপেন, ট্যাব বন্ধ করেন বা চ্যাট বদলান। রিকোয়েস্ট বাতিল করুন যাতে কেউ পড়বে না এমন টোকেনের জন্য অপেক্ষা না করতে হয়। রিকোয়েস্ট অপশনে একটি AbortSignal দিন:

javascript
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.controller.abort()-ও আছে, আর break দিয়ে for await লুপ থেকে বেরোলে সংযোগও বন্ধ হয়। আপনার কোডের যেটি নিকটতর সেটিই ব্যবহার করুন: ক্যানসেল বাটন অন্য জায়গায় থাকলে signal, আর লুপের ভেতরেই সিদ্ধান্ত নিলে (যেমন কোনো stop word পেয়েছেন) break।

ধাপ ৩. ত্রুটি সামলান#

একটি স্ট্রিম প্রথম টোকেনের আগেই ব্যর্থ হতে পারে (ভুল key, খালি ব্যালেন্স) বা মাঝপথে (নেটওয়ার্ক ড্রপ)। রিকোয়েস্ট ও লুপ দুটোই একটি try-এ মুড়ে দিন:

javascript
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-এ HTTP status থাকে: 401 মানে ভুল বা অনুপস্থিত key, 429 মানে অতিরিক্ত রিকোয়েস্ট, আর টোকেন সংক্রান্ত বার্তা মানে ব্যালেন্স রিচার্জ করতে হবে। আরও কারণ ও সমাধান আছে Common connection errors and how to fix them এবং "Not enough tokens" error-এ।
  • ত্রুটির পরেও আগে প্রিন্ট হওয়া টেক্সট বৈধ থাকে। আংশিক উত্তর দেখাতে চাইলে তা রেখে দিন।

ধাপ ৪. usage পড়ুন ও খরচ অনুমান করুন#

usage অবজেক্টে prompt_tokens ও completion_tokens থাকে। প্রতি ১M টোকেনের মডেল মূল্যের সাথে মিলিয়ে এটি আপনাকে একটি রিকোয়েস্টের সঠিক খরচ দেয়। কিছু চালানোর আগে মোটামুটি অনুমানের জন্য নিচের লাইভ টেবিলটি ব্যবহার করুন: এটি প্রতিটিতে ২,০০০ ইনপুট ও ৮০০ আউটপুট টোকেনসহ ১,০০০ রিকোয়েস্টের খরচ দেখায়।

মডেল প্রতি অনুরোধে ১,০০০টি অনুরোধ OpenRouter, ১,০০০টি অনুরোধ
GLM-5.3-Flash $0.000072 $0.072 $0.143
DeepSeek-V4-Flash $0.00011 $0.11 $0.22
MiniMax-M2.7 $0.00022 $0.22 $1.56

প্রতি অনুরোধে ২,০০০ ইনপুট এবং ৮০০ আউটপুট টোকেন। প্রতি 1M টোকেনের দাম বর্তমান এবং আমাদের ট্যারিফের সাথে পরিবর্তিত হয়; OpenRouter কলামটি একই ওয়ার্কলোডের জন্য রেফারেন্স মূল্য দেখায়।

স্ট্রিম করা চ্যাট উত্তরের জন্য MiniMax-M2.7-এর মূল্য প্রতি ১M ইনপুটে $0.05 এবং প্রতি ১M আউটপুট টোকেনে $0.15, ১৮০K কনটেক্সট ও প্রতি উত্তরে সর্বোচ্চ ৮,১৯২ আউটপুট টোকেনসহ। মূল্য দিনের যেকোনো সময়ে প্রতি মডেলে নির্দিষ্ট, আর একটি ব্যালেন্সই সব মডেল কভার করে, তাই নিজের prompt-এ তুলনা করতে model স্ট্রিং বদলালেই যথেষ্ট।

সম্পূর্ণ স্ক্রিপ্ট#

javascript
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;
  }
}

পরবর্তী ধাপ#

  • নিজের endpoint থেকে Server-Sent Events দিয়ে chunk-গুলো ব্রাউজারে পাঠান, যাতে key কখনো সার্ভার ছেড়ে না যায়।
  • শুধু model স্ট্রিং বদলে অন্য মডেল চেষ্টা করুন: MiniMax-M2.7 একই ব্যালেন্স ও একই base URL-এ পাওয়া যায়।
  • key নিয়ে স্ক্রিপ্ট চালান: create a CheapTokens API key এবং ব্যালেন্স রিচার্জ করুন।

প্রতিটি মডেলের endpoint, মডেল id ও সীমা আছে CheapTokens docs-এ।