العودة إلى المدونة

API وSDKدليل

بثّ ردود المحادثة في Node.js باستخدام OpenAI SDK

بثّ إكمال محادثة رمزًا برمز في Node.js: حلقة for await، والإيقاف عند الطلب، والتعامل مع الأخطاء، واستخدام الرموز مع OpenAI SDK.

بواسطة CheapTokens Teamنُشر في تم التحديث في 4 دقيقة قراءة

نافذة طرفية تطبع إجابة مبثوثة رمزًا برمز من سكربت Node.js
في هذه الصفحة
  1. ما تحتاج إليه
  2. الخطوة 1. بثّ الإجابة باستخدام for await
  3. الخطوة 2. إيقاف البثّ عند الطلب
  4. الخطوة 3. التعامل مع الأخطاء
  5. الخطوة 4. قراءة الاستخدام وتقدير التكلفة
  6. السكربت الكامل
  7. الخطوات التالية

يتيح البثّ لتطبيقك عرض الإجابة أثناء توليدها بدلًا من انتظار الرد كاملًا. في هذا الدليل ستبني سكربت Node.js صغيرًا يبثّ إكمال محادثة من واجهة CheapTokens API رمزًا برمز، ويوقفه عند الطلب ويتعامل مع الأخطاء. النسخة بلغة Python من هذا الموضوع موجودة في بثّ الردود في Python.

ما تحتاج إليه#

  • Node.js 22 أو أحدث (الحد الأدنى المذكور في README حزمة openai، تم التحقق منه في 22 أكتوبر 2026).
  • مفتاح CheapTokens API. إن لم يكن لديك واحد بعد، اتبع أول طلب API لك مع CheapTokens.
  • حزمة SDK الرسمية: npm install openai.

توفّر CheapTokens واجهة API متوافقة مع OpenAI على https://api.cheaptokens.dev/v1، لذا تعمل حزمة openai الرسمية بتغيير عنوان URL الأساسي والمفتاح فقط. احتفظ بالمفتاح في متغيّر بيئة، لا في الكود:

bash
export CHEAPTOKENS_API_KEY="your-key"

الخطوة 1. بثّ الإجابة باستخدام 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 أحداث 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. إيقاف البثّ عند الطلب#

يضغط المستخدمون على إيقاف، أو يغلقون تبويبًا، أو يبدّلون المحادثة. ألغِ الطلب حتى لا تنتظر رموزًا لن يقرأها أحد. مرّر 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() خاصًا به، كما أن الخروج من حلقة for await باستخدام break يغلق الاتصال أيضًا. استخدم أيًّا منهما أقرب إلى كودك: إشارة عندما يكون زر الإلغاء في مكان آخر، وbreak عندما يُتخذ القرار داخل الحلقة (مثلًا عند العثور على كلمة إيقاف).

الخطوة 3. التعامل مع الأخطاء#

قد يفشل البثّ قبل الرمز الأول (مفتاح خاطئ، رصيد فارغ) أو في المنتصف (انقطاع الشبكة). غلّف الطلب والحلقة معًا في 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 مفتاحًا خاطئًا أو مفقودًا، ويعني 429 طلبات كثيرة جدًا، وتشير رسالة عن الرموز إلى أن الرصيد يحتاج إلى تعبئة. مزيد من الأسباب والحلول في أخطاء الاتصال الشائعة وكيفية إصلاحها وخطأ "Not enough tokens".
  • يبقى النص الذي طُبع بالفعل صالحًا بعد الخطأ. احتفظ به إن أردت عرض الإجابة الجزئية.

الخطوة 4. قراءة الاستخدام وتقدير التكلفة#

يحمل كائن usage كلا من prompt_tokens وcompletion_tokens. مع سعر النموذج لكل مليون رمز، يمنحك التكلفة الدقيقة للطلب. لتقدير تقريبي قبل تشغيل أي شيء، استخدم الجدول المباشر أدناه: يعرض تكلفة 1,000 طلب بواقع 2,000 رمز إدخال و800 رمز إخراج لكل طلب.

النموذج لكل طلب 1,000 طلبات OpenRouter، 1,000 طلبات
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

2,000 رمز إدخال و800 رمز إخراج لكل طلب. الأسعار لكل مليون رمز حالية وتتغير مع تعريفاتنا؛ يعرض عمود OpenRouter أسعارًا مرجعية لنفس حجم العمل.

بالنسبة لردود المحادثة المبثوثة، يُسعَّر MiniMax-M2.7 عند $0.05 لكل مليون رمز إدخال و$0.15 لكل مليون رمز إخراج، مع سياق 180K وحتى 8,192 رمز إخراج لكل إجابة. السعر ثابت لكل نموذج في أي وقت من اليوم، ورصيد واحد يغطي جميع النماذج، لذا يكفي تبديل سلسلة 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;
  }
}

الخطوات التالية#

  • أرسل المقاطع إلى المتصفح باستخدام Server-Sent Events من نقطة النهاية الخاصة بك، حتى لا يغادر المفتاح الخادم أبدًا.
  • جرّب نماذج أخرى بتغيير سلسلة model فقط: يتوفر MiniMax-M2.7 على الرصيد نفسه وعنوان URL الأساسي نفسه.
  • احصل على مفتاح وشغّل السكربت: أنشئ مفتاح CheapTokens API وعبّئ الرصيد.

نقاط النهاية ومعرّفات النماذج والحدود لكل نموذج موجودة في وثائق CheapTokens.