# Vercel AI SDK

> @ai-sdk/openai-compatible-এর createOpenAICompatible দিয়ে Vercel AI SDK-তে Tokens-কে provider হিসেবে বসান, তারপর generateText ও streamText চালান, Next.js route-সহ।

Vercel AI SDK Tokens-এর সাথে কথা বলে `@ai-sdk/openai-compatible` দিয়ে। এটা যেকোনো OpenAI-compatible API-র জন্য provider package। `createOpenAICompatible` দিয়ে provider একবার বানিয়ে নিন। এরপর `generateText`, `streamText` আর tool calling ঠিক সেভাবেই চলবে, যেভাবে first-party provider-গুলোতে চলে।

OpenCode ভেতরে এই একই package ব্যবহার করে। তাই ওখানে আপনি Tokens set up করে থাকলে ([Tokens CLI](/docs/tokens-cli) এটা আপনার হয়ে করে দেয়), সবকিছু চেনা চেনা লাগবে।

## Package install করুন

```bash
npm install ai @ai-sdk/openai-compatible zod
export TOKENS_API_KEY="tok_live_your_key"
```

উদাহরণগুলো AI SDK 7 (`ai@7`, `@ai-sdk/openai-compatible@3`) ধরে লেখা, যার জন্য Node.js 22 বা তার পরের version লাগে। AI SDK 5 বা 6-এ code একই থাকবে, শুধু system prompt দিতে হবে `instructions`-এর বদলে `system` দিয়ে।

## createOpenAICompatible দিয়ে Tokens provider বানান

```ts title="lib/tokens.ts"
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";

export const tokens = createOpenAICompatible({
  name: "tokens",
  baseURL: "https://tokens.bd/v1",
  apiKey: process.env.TOKENS_API_KEY,
  includeUsage: true, // ask for token counts on streamed responses
});
```

`apiKey` যায় `Authorization: Bearer <key>` হিসেবে। `includeUsage: true` দিলে streaming call-এ provider `stream_options.include_usage` set করে দেয়। এটা না থাকলে Tokens-এর stream-এ usage-এর কোনো তথ্য থাকে না, আর `streamText` call-এর পর `usage` ফাঁকা থেকে যায়।

Model-কে ডাকতে হয় তার Tokens ID দিয়ে: `tokens("deepseek/deepseek-v4.1-flash")`। ID নিজে টাইপ না করে [/models](/models) বা `GET /v1/models` থেকে copy করুন।

## generateText

```ts title="summarize.ts"
import { generateText } from "ai";
import { tokens } from "./lib/tokens";

const { text, usage, finishReason } = await generateText({
  model: tokens("deepseek/deepseek-v4.1-flash"),
  instructions: "You summarize pull requests for busy reviewers.",
  prompt:
    "Summarize: refactored auth middleware to use a single session lookup; removed two duplicate DB calls.",
  maxOutputTokens: 300,
});

console.log(text);
console.log(finishReason, usage.inputTokens, usage.outputTokens);
```

## streamText

`streamText`-এ `await` দিতে হয় না। এটা সাথে সাথে ফেরত আসে, আর আপনি `textStream` পড়তে থাকেন।

```ts title="stream.ts"
import { streamText } from "ai";
import { tokens } from "./lib/tokens";

const result = streamText({
  model: tokens("deepseek/deepseek-v4.1-flash"),
  prompt: "Explain the difference between TCP and UDP for a junior developer.",
  maxOutputTokens: 600,
  onError: ({ error }) => console.error(error),
});

for await (const delta of result.textStream) {
  process.stdout.write(delta);
}

console.log("\n", await result.usage);
```

Stream চলার সময় যে error হয়, তা throw হয় না, আসে `onError`-এর মধ্য দিয়ে। তাই এটা অবশ্যই বসিয়ে নিন। নইলে `402 insufficient_credits` শুধু একটা ফাঁকা response-এর মতো দেখাবে।

## Next.js route handler থেকে stream করুন

Tokens browser থেকে call করতে দেয় না (CORS header নেই), আর client code-এ আপনি key রাখতেও চান না। তাই `streamText` চালান একটা route handler-এ:

```ts title="app/api/chat/route.ts"
import { streamText } from "ai";
import { tokens } from "@/lib/tokens";

export async function POST(req: Request) {
  const { prompt } = (await req.json()) as { prompt?: string };
  if (!prompt) return Response.json({ error: "prompt required" }, { status: 400 });

  const result = streamText({
    model: tokens("deepseek/deepseek-v4.1-flash"),
    prompt,
    maxOutputTokens: 1024,
    abortSignal: req.signal,
  });

  return result.toTextStreamResponse();
}
```

`toTextStreamResponse()` সাধারণ text chunk পাঠায়, যা আপনি `fetch` আর একটা stream reader দিয়ে পড়তে পারবেন। আপনার frontend যদি `@ai-sdk/react`-এর `useChat` ব্যবহার করে, তাহলে `prompt`-এর বদলে request body থেকে `messages` নিন, সেগুলোকে `convertToModelMessages` দিয়ে বদলে নিন, আর ফেরত দিন `result.toUIMessageStreamResponse()`। Tokens-এর অংশ, মানে provider, বদলায় না।

Model আর output limit server-এ ঠিক করুন, যাতে visitor আপনার হয়ে দামি model বেছে নিতে না পারে। Route-এর সামনে authentication-ও বসান।

## Tool calling

যে model tool সাপোর্ট করে, তাতেই tool চলে। আগে [/models](/models)-এ model-এর পেজটা দেখে নিন।

```ts
import { generateText, tool, stepCountIs } from "ai";
import { z } from "zod";
import { tokens } from "./lib/tokens";

const { text, steps } = await generateText({
  model: tokens("deepseek/deepseek-v4.1-flash"),
  tools: {
    getWeather: tool({
      description: "Current weather for a city",
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ city, condition: "light rain", tempC: 29 }),
    }),
  },
  stopWhen: stepCountIs(3),
  prompt: "Do I need an umbrella in Dhaka right now?",
});

console.log(text, `(${steps.length} steps)`);
```

Model tool সাপোর্ট না করলে upstream provider request ফিরিয়ে দেয়, অথবা model tool-গুলো এড়িয়ে text-এ উত্তর দেয়। তখন এমন একটা model নিন যার তালিকায় tool সাপোর্ট লেখা আছে। Wire-level খুঁটিনাটি আছে [Tool calling](/docs/tool-calling) পেজে।

## Error

HTTP error আসে `APICallError` হিসেবে (`ai` থেকে export করা), যাতে থাকে `statusCode`, `responseHeaders` আর `responseBody`। Tokens-এর error code থাকে `responseBody`-র ভেতরে JSON হিসেবে, `error.code`-এর নিচে।

```ts
import { APICallError, generateText } from "ai";
import { tokens } from "./lib/tokens";

try {
  await generateText({ model: tokens("deepseek/deepseek-v4.1-flash"), prompt: "hi" });
} catch (err) {
  if (APICallError.isInstance(err)) {
    console.error(err.statusCode, err.responseHeaders?.["x-tokens-request-id"], err.responseBody);
  }
}
```

AI SDK ব্যর্থ call default-এ দুইবার retry করে (`maxRetries`)। কিন্তু `429 window_exhausted` retry করে ঠিক হয় না, কারণ plan window reset হলে তবেই সেটা কাটে। Code আর সমাধান আছে [Errors](/docs/errors) ও [Troubleshooting](/docs/troubleshooting) পেজে।

---
Page: https://tokens.bd/bn/docs/vercel-ai-sdk
