تولید و استریم متن با AI SDK


مدل‌های زبانی بزرگ (LLMها) قادرند در پاسخ به یک prompt، متن تولید کنند؛ این پرامپت می‌تواند شامل دستورالعمل‌ها و اطلاعاتی برای پردازش باشد. به عنوان مثال، می‌توان از یک مدل خواست که یک دستور پخت ارائه دهد، پیش‌نویس یک ایمیل را تهیه کند، یا یک داکیومنت را خلاصه نماید.

هسته‌ی AI SDK دو تابع برای تولید متن و استریم آن از LLMها فراهم می‌کند:

  • generateText: متنی را با توجه به پرامپت و مدل، تولید می‌کند
  • streamText: متن را با یک پرامپت و مدل مشخص، به‌صورت stream ارائه می‌دهد

قابلیت‌های پیشرفته‌ی LLMها، مانند فراخوانی toolها و تولید داده‌های ساختارمند، بر پایه‌ی همین قابلیت تولید متن ساخته شده‌اند.


تابع generateText

شما می‌توانید با استفاده از تابع generateText متن تولید کنید. این تابع برای موارد غیرتعاملی ایده‌آل است؛ مانند زمانی که نیاز به تولید متن دارید (برای مثال: تهیه‌ی پیش‌نویس یک ایمیل یا خلاصه‌سازی صفحات وب) و همچنین برای agentهایی که از toolها استفاده می‌کنند.

کپی
import { generateText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { config } from 'dotenv';

config();
const my_model = createOpenAI({
  baseURL: process.env.BASE_URL!,
  apiKey: process.env.LIARA_API_KEY!,
});

const { text } = await generateText({
  model: my_model('openai/gpt-4o-mini'),
  prompt: 'Write a vegetarian lasagna recipe for 4 people.',
});

console.log(text)

می‌توانید از پرامپت‌های پیشرفته‌تر نیز، برای تولید متنی با دستورالعمل‌ها و محتوای پیچیده‌تر، استفاده کنید.

کپی
import { generateText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { config } from 'dotenv';

config();
const my_model = createOpenAI({
  baseURL: process.env.BASE_URL!,
  apiKey: process.env.LIARA_API_KEY!,
});

const article = 
`Octopus Brains Are Even Weirder Than We Thought
A new study has revealed that octopuses may process information using their arms
independently from their central brain, adding to their reputation as some of
the most intelligent and alien-like creatures on Earth.

Researchers from the University of Naples used electrodes to monitor neural activity
in Octopus vulgaris. They found that not only do octopus arms have their
own neural circuits, but they can also make decisions without input from
the central brain. This means each arm has a semi-autonomous "mini-brain,"
allowing the animal to multitask in complex ways—like opening a shell with
one arm while exploring its surroundings with another.

What’s more fascinating is the discovery of neurotransmitters in octopus arms
that are more commonly associated with learning and memory in mammals. 
This hints at convergent evolution—where different species independently
evolve similar traits.

Understanding how octopuses process information could offer clues for building
more adaptive robots or even new models of decentralized AI. As neuroscientist
Dr. Francesca Ferrante puts it: "If intelligence evolved this way in
an invertebrate, we have a lot to learn about the nature of cognition."
`

const { text } = await generateText({
  model: my_model('openai/gpt-4o-mini'),
  system:
    'You are a professional writer. ' +
    'You write simple, clear, and concise content.',
  prompt: `Summarize the following article in 3-5 sentences: ${article}`,
});

console.log(text)

آبجکت result که از تابع generateText بازگردانده می‌شود، شامل چندین promise است و زمانی که تمامی داده‌های مورد نیاز در دسترس قرار گیرند، مقداردهی می‌شوند:

  • result.text: متن تولید شده
  • result.reasoning: استدلال کاملی که مدل در مرحله آخر، تولید کرده است
  • result.sources: منابعی که به عنوان مرجع در مرحله آخر، استفاده شده‌اند (موجود فقط برای برخی مدل‌ها)
  • result.finishReason: دلیلی که مدل تولید متن را متوقف کرده است
  • result.usage: میزان مصرف مدل طی مرحله آخر تولید متن

دسترسی به هدرهای response و body

در برخی موارد، نیاز است به پاسخ کامل ارائه‌شده از سوی ارائه‌دهنده مدل دسترسی داشته باشید؛ برای مثال، جهت مشاهده‌ی برخی هدرها یا محتوای بدنه که به‌صورت خاص توسط ارائه‌دهنده ارسال می‌شوند. می‌توانید با استفاده از ویژگی response به هدرها و بدنه‌ی خام پاسخ دسترسی پیدا کنید.

کپی
import { generateText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { config } from 'dotenv';

config();
const my_model = createOpenAI({
  baseURL: process.env.BASE_URL!,
  apiKey: process.env.LIARA_API_KEY!,
});

const result = await generateText({
  model: my_model('openai/gpt-4o-mini'),
  prompt: 'Who is Arthur Schopenhauer?',
});

console.log(JSON.stringify(result.response.headers, null, 2));
console.log(JSON.stringify(result.response.body, null, 2));

تابع streamText

بسته به مدل مورد استفاده و نوع prompt، ممکن است تولید پاسخ توسط یک LLM تا یک دقیقه طول بکشد. این تأخیر در موارد استفاده‌ی تعاملی، مانند چت‌بات‌ها یا برنامه‌های بلادرنگ (real-time)، جایی که کاربران انتظار پاسخ‌های فوری دارند، غیرقابل‌قبول است.

هسته‌ی AI SDK تابعی به نام streamText دارد که stream متن از LLM را ساده می‌سازد.

کپی
import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { config } from 'dotenv';

config();
const my_model = createOpenAI({
  baseURL: process.env.BASE_URL!,
  apiKey: process.env.LIARA_API_KEY!,
});

const result = streamText({
  model: my_model('openai/gpt-4.1'),
  prompt: 'Invent a new holiday and describe its traditions.',
});

// example: use textStream as an async iterable
for await (const textPart of result.textStream) {
  console.log(textPart);
}

result.textStream هم ReadableStream و هم AsyncIterable است.

تابع streamText بلافاصله استریم متن را آغاز می‌کند و برای جلوگیری از کرش کردن سرور، خطاها را نادیده می‌گیرد. برای لاگ‌گرفتن از خطاها، از یک callback به نام onError استفاده کنید.

می‌توانید از streamText به‌صورت مستقل یا در ترکیب با AI SDK UI و AI SDK RSC استفاده کنید. آبجکت result شامل چندین تابع کمکی است که ادغام با AI SDK UI را ساده‌تر می‌سازند:

  • ()result.toDataStreamResponse: این تابع یک HTTP respone مبتنی بر UI Message stream (با فراخوانی tool و ...)، تولید می‌کند که می‌تواند در API Route برنامه‌های NextJS App Router مورد استفاده قرار بگیرد
  • ()result.pipeDataStreamToResponse: این تابع delta output مربوط به UI Message Stream را به یک آبجکت مشابه response در NodeJS قرار می‌دهد
  • ()result.toTextStreamResponse: یک پاسخ HTTP ساده برای استریم متن ایجاد می‌کند
  • ()result.pipeTextStreamToResponse: این تابع delta output متنی را به یک آبجکت مشابه respone در NodeJS می‌نویسد

تابع streamText از backpressure استفاده می‌کند و تنها زمانی توکن‌ها را تولید می‌کند که مورد درخواست قرار گیرند. برای پایان یافتن فرآیند، لازم است که stream را مصرف (consume) کنید

تابع streamText شامل چندین promise است که زمانی که تمامی داده‌های مورد نیاز در دسترس قرار گیرند، مقداردهی می‌شوند:

  • ()result.toDataStreamResponse: این تابع یک HTTP respone مبتنی بر UI Message stream (با فراخوانی tool و ...)، تولید می‌کند که می‌تواند در API Route برنامه‌های NextJS App Router مورد استفاده قرار بگیرد
  • ()result.pipeDataStreamToResponse: این تابع delta output مربوط به UI Message Stream را به یک آبجکت مشابه response در NodeJS قرار می‌دهد
  • ()result.toTextStreamResponse: یک پاسخ HTTP ساده برای استریم متن ایجاد می‌کند
  • ()result.pipeTextStreamToResponse: این تابع delta output متنی را به یک آبجکت مشابه respone در NodeJS می‌نویسد

onError callback

تابع streamText بلافاصله فرآیند streaming را آغاز می‌کند تا امکان ارسال داده بدون انتظار فراهم شود. در این روش، خطاها به‌صورت بخشی از استریم در نظر گرفته می‌شوند و throw نمی‌شوند، تا از بروز مشکلاتی مانند کرش کردن سرور جلوگیری شود.

برای log کردن خطاها، می‌توانید یک callback به نام onError تعریف کنید که در زمان وقوع خطا فعال (trigger) می‌شود.

کپی
import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { config } from 'dotenv';

config();
const my_model = createOpenAI({
  baseURL: process.env.BASE_URL!,
  apiKey: process.env.LIARA_API_KEY!,
});

const result = streamText({
  model: my_model('openai/gpt-4.1'),
  prompt: 'Invent a new holiday and describe its traditions.',
  onError({ error }) {
    console.error(error); // your error logging logic here
  },
});

for await (const textPart of result.textStream) {
  console.log(textPart);
}

onChunk callback

هنگام استفاده از تابع streamText، می‌توانید یک callback به نام onChunk تعریف کنید که برای هر بخش (chunk) از استریم فراخوانی می‌شود. این callback، انواع chunkهای زیر را دریافت می‌کند:

  • text-delta
  • reasoning
  • source
  • tool-call
  • tool-result
  • tool-call-streaming-start (وقتی که toolCallStreaming فعال است)
  • tool-call-delta (وقتی که toolCallStreaming فعال است)
کپی
import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { config } from 'dotenv';

config();
const my_model = createOpenAI({
  baseURL: process.env.BASE_URL!,
  apiKey: process.env.LIARA_API_KEY!,
});

const result = streamText({
  model: my_model('openai/gpt-4.1'),
  prompt: 'Invent a new holiday and describe its traditions.',
  onChunk({ chunk }) {

    // implement your own logic here, e.g.:
    if (chunk.type === 'text-delta') {
      console.log(chunk.textDelta);
    }
  },
});

for await (const textPart of result.textStream) {}

onFinish callback

هنگام استفاده از تابع streamText، می‌توانید یک callback به نام onFinish تعریف کنید که زمانی که استریم به پایان می‌رسد، فراخوانی می‌شود. این callback شامل اطلاعات جامعی از فرآیند تولید متن است، از جمله متن، اطلاعات مصرف توکن، علت توقف، پیام‌ها، مراحل، مصرف کلی توکن‌ها و ...

کپی
import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { config } from 'dotenv';

config();
const my_model = createOpenAI({
  baseURL: process.env.BASE_URL!,
  apiKey: process.env.LIARA_API_KEY!,
});

const result = streamText({
  model: my_model('openai/gpt-4.1'),
  prompt: 'hey',
  onFinish({ text, finishReason, usage, response, steps }) {

    // your own logic, e.g. for saving the chat history or recording usage
    console.log("finish reason:", finishReason)
  },
});

for await (const textPart of result.textStream) {
}