# Laravel AI SDK

> Official Laravel AI SDK (laravel/ai) থেকে openai-compatible provider দিয়ে Tokens ব্যবহার করুন: config/ai.php, .env, প্রথম call, streaming, tool, timeout আর Laravel 12 ও 13-এর error handling।

Laravel AI SDK (`laravel/ai`) হলো agent, text generation আর অন্যান্য AI feature-এর জন্য Laravel-এর official package। এতে একটা `openai-compatible` driver আছে, যা OpenAI-র মতো `/chat/completions` endpoint আছে এমন যেকোনো server-এর সাথে কথা বলে, আর Tokens ঠিক তেমনই। `config/ai.php`-এ Tokens-এর URL আর key দিয়ে একটা provider declare করুন, তারপর agent call করার সময় নাম ধরে সেটা বেছে নিন।

Laravel-এর agent layer ছাড়া শুধু PHP-র সাধারণ OpenAI client চাইলে [PHP](/docs/php) পেজ দেখুন।

:::note[কী কী যাচাই করা হয়েছে]
এই পেজ লেখা হয়েছে Laravel 13.x ও 12.x-এর জন্য Laravel AI SDK-র documentation (laravel.com/docs, 2026 সালের অক্টোবরে দেখা) আর `laravel/ai` 1.2.0-এর source দেখে (release হয়েছে 7 অক্টোবর 2026-এ)। 12.x-এর পেজে `openai-compatible` driver-এর কথা লেখা নেই, তাই ওই অংশ এসেছে 13.x-এর docs আর package-এর source থেকে। Code-টা documentation আর source দেখে মেলানো হয়েছে, Tokens-এর বিরুদ্ধে শুরু থেকে শেষ পর্যন্ত চালিয়ে দেখা হয়নি।
:::

## যা যা লাগবে

- PHP 8.3 বা নতুন, আর Laravel 12 বা 13। `laravel/ai` 1.2.0-এর শর্ত `php ^8.3` আর `illuminate/* ^12.0|^13.0`।
- [API keys](/docs/api-keys) থেকে নেওয়া একটা Tokens key।
- [/models](/models) থেকে একটা model ID। আপনার agent tool ব্যবহার করলে এমন model নিন যা tool calling সাপোর্ট করে ([Choosing a model](/docs/choosing-a-model))।

## Install করুন

```bash
composer require laravel/ai
php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
php artisan migrate
```

Publish করলে `config/ai.php` আর একটা migration তৈরি হয়। Migration বানায় `agent_conversations` আর `agent_conversation_messages` table, যেখানে SDK conversation জমা রাখে। Conversation memory এখনই ব্যবহার না করলেও এটা চালিয়ে নিন।

আগের কোনো release থেকে আপনার `config/ai.php` আগে থেকেই থাকলে `composer update laravel/ai` চালান, তারপর আপনার file-টা এখনকার published file-এর সাথে মিলিয়ে দেখুন। পুরোনো copy-তে `openai-compatible` entry না-ও থাকতে পারে, সেক্ষেত্রে নিচে দেখানো মতো হাতে যোগ করে নিন।

## Tokens provider সাজান

`config/ai.php`-এর `providers` array-তে একটা provider যোগ করুন। নামটা আপনার ইচ্ছা, এই পেজে `tokens` ব্যবহার করা হয়েছে।

```php title="config/ai.php"
return [
    'default' => 'tokens',

    // ...

    'providers' => [
        // ... the providers that ship in the file stay as they are

        'tokens' => [
            'driver' => 'openai-compatible',
            'url' => env('TOKENS_BASE_URL', 'https://tokens.bd/v1'),
            'key' => env('TOKENS_API_KEY'),
            'models' => [
                'text' => [
                    'default' => env('TOKENS_MODEL', 'deepseek/deepseek-v4.1-flash'),
                ],
            ],
        ],
    ],
];
```

```ini title=".env"
TOKENS_API_KEY=tok_live_your_key
```

প্রতিটা option কীভাবে কাজ করে:

- **`url`** বাধ্যতামূলক। `https://tokens.bd/v1` দিন, `/v1`-সহ। Driver শেষের slash কেটে তার নিচে `chat/completions`-এ post করে, তাই request যায় `https://tokens.bd/v1/chat/completions`-এ।
- **`key`** SDK-তে ঐচ্ছিক, থাকলে bearer token হিসেবে যায়। Tokens-এর ক্ষেত্রে এটা অবশ্যই লাগবে।
- **`models.text.default`** হলো সেই model, যা call-এ আলাদা করে কোনোটার নাম না দিলে ব্যবহার হয়। এটা না থাকলে `model:` না দেওয়া call-এ `InvalidArgumentException` throw হয়, যেখানে লেখা থাকে provider-এর একটা default text model লাগবে।
- **`'default' => 'tokens'`** দিলে text-এর জন্য এই provider-ই default হয়ে যায়। Default না ছুঁতে চাইলে এটা বাদ দিন, আর নিচের উদাহরণগুলোর মতো প্রতিটা call-এ `provider: 'tokens'` দিন।

:::warning[Key-কে config file থেকে দূরে রাখুন]
ওপরের মতো `env()` দিয়ে key পড়ুন, আর `.env` version control-এর বাইরে রাখুন। যে server config cache করে রাখে, সেখানে `.env` বা `config/ai.php` বদলানোর পর `php artisan config:clear` চালান (অথবা আবার `config:cache`)।
:::

### Laravel 12 ও 13: variable-এর নাম আলাদা, config একই

Laravel-এর docs-এ variable-এর নাম version ভেদে আলাদা, আর সেটা শুধু built-in `openai` provider-এর বেলায় কাজে আসে:

| Docs         | Custom OpenAI URL-এর variable | `openai-compatible` provider                                                                                           |
| ------------ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Laravel 13.x | `OPENAI_URL`                  | Documented। Shipped `config/ai.php`-এ env-এর নাম: `OPENAI_COMPATIBLE_URL`, `OPENAI_COMPATIBLE_API_KEY`।                  |
| Laravel 12.x | `OPENAI_BASE_URL`             | পেজে বর্ণনা নেই। Driver আছে `laravel/ai` package-এ, যা Laravel 12 ও 13 দুটোই সাপোর্ট করে।                               |

Variable-এর নাম বলতে বোঝায় আপনার `config/ai.php` যা `env()`-কে দেয়, তার বেশি কিছু নয়। ওপরের `tokens` entry নিজের নাম ব্যবহার করে, তাই দুই version-এই চলে। File-এ আগে থেকে যে entry আছে সেটাই চাইলে `.env`-এ `OPENAI_COMPATIBLE_URL=https://tokens.bd/v1` আর `OPENAI_COMPATIBLE_API_KEY` দিন, আর ওই entry-তে `models.text.default` যোগ করুন।

### Built-in openai provider কেন নয়

Built-in `openai` provider-ও custom `url` নেয়। Package-এর source অনুযায়ী সেটা request পাঠায় `{url}/responses`-এ, অর্থাৎ OpenAI Responses API-তে, আর `openai-compatible` driver পাঠায় `{url}/chat/completions`-এ। Tokens দুটোই চালায় ([Chat Completions](/docs/chat-completions), [Responses](/docs/responses))। এই পেজে `openai-compatible` নেওয়া হয়েছে কারণ docs তৃতীয় পক্ষের gateway-র জন্য এই driver-টার কথাই বলে, আর এটা Chat Completions format পাঠায়, যা সবচেয়ে বেশি সাপোর্ট পাওয়া format।

## প্রথম call করুন

Anonymous agent-এর জন্য আলাদা class লাগে না। এটা route-এ রাখুন, নয়তো `php artisan tinker`-এ চালান:

```php
use function Laravel\Ai\agent;

$response = agent(
    instructions: 'Answer in one short paragraph.',
)->prompt(
    'When should I use a queue instead of running code in the request?',
    provider: 'tokens',
    model: 'deepseek/deepseek-v4.1-flash',
);

echo $response->text;
```

`$response->text` হলো উত্তর। `(string) $response` একই text দেয়। `config/ai.php`-এ আপনার দেওয়া নামের provider-এর জন্য `provider`-এ সাধারণ string দিলেই চলে। `models.text.default` set থাকলে `model:` বাদ দিতে পারেন।

## ঠিকমতো চলছে কি না দেখুন

আগে Laravel-এর বাইরে থেকে দেখুন key দিয়ে কোন কোন model চলে:

```bash
curl -s https://tokens.bd/v1/models -H "Authorization: Bearer $TOKENS_API_KEY"
```

তারপর app-এর ভেতরে:

```bash
php artisan tinker
```

```php
\Laravel\Ai\agent(instructions: 'Reply with one word.')->prompt('Say ready.', provider: 'tokens')->text
```

ছোট একটা উত্তর এলে বুঝবেন URL, key আর model ঠিক আছে। `404 model_not_found` মানে ID ভুল; `401` মানে key পৌঁছায়নি। নিচের Troubleshooting দেখুন।

## Agent class

যা বারবার কাজে লাগবে তার জন্য একটা agent class বানান, আর attribute দিয়ে তার provider ও model বেঁধে দিন:

```bash
php artisan make:agent ReviewCoach
```

```php title="app/Ai/Agents/ReviewCoach.php"
namespace App\Ai\Agents;

use Laravel\Ai\Attributes\Model;
use Laravel\Ai\Attributes\Provider;
use Laravel\Ai\Attributes\Timeout;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Promptable;
use Stringable;

#[Provider('tokens')]
#[Model('deepseek/deepseek-v4.1-flash')]
#[Timeout(120)]
class ReviewCoach implements Agent
{
    use Promptable;

    public function instructions(): Stringable|string
    {
        return 'You review pull request descriptions and suggest one improvement.';
    }
}
```

```php
$response = (new \App\Ai\Agents\ReviewCoach)->prompt('Fix login redirect');
echo $response->text;
```

`Provider`, `Model` আর `Timeout` attribute-গুলো আছে Laravel-এর docs-এর agent configuration section-এ। `Provider` একটা `Lab` enum-এর মান, একটা string বা একটা array নেয়।

## Response stream করুন

`stream()` একটা `StreamableAgentResponse` ফেরত দেয়। সেটা route থেকে return করলে browser-এ server-sent event হিসেবে চলে যায়:

```php title="routes/web.php"
use function Laravel\Ai\agent;

Route::get('/ask', function () {
    return agent(instructions: 'Answer briefly.')
        ->stream('Explain job batching in Laravel.', provider: 'tokens');
});
```

Event নিজে সামলাতে চাইলে, যেমন Artisan command-এ, stream-এর ওপর loop চালান:

```php
use Laravel\Ai\Streaming\Events\Error;
use Laravel\Ai\Streaming\Events\TextDelta;

$stream = agent(instructions: 'Answer briefly.')
    ->stream('Explain job batching in Laravel.', provider: 'tokens');

foreach ($stream as $event) {
    if ($event instanceof TextDelta) {
        echo $event->delta;
    } elseif ($event instanceof Error) {
        // $event->type holds the error code, $event->message the text
        fwrite(STDERR, "{$event->type}: {$event->message}\n");
    }
}
```

Laravel-এর docs-এ একটা `then()` callback-এর কথাও আছে, যেটা পুরো response stream হয়ে গেলে চলে; তার `StreamedAgentResponse`-এ থাকে `text`, `events` আর `usage`। Driver Tokens-এর কাছে `stream_options.include_usage` চায়, তাই token count আসে stream-এর শেষে। Wire format-এর জন্য [Streaming](/docs/streaming) দেখুন।

## Tool

Tool হলো এমন class যার একটা description, একটা schema আর একটা `handle` method আছে। একটা বানিয়ে নিন:

```bash
php artisan make:tool GetWeather
```

```php title="app/Ai/Tools/GetWeather.php"
namespace App\Ai\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

class GetWeather implements Tool
{
    public function description(): Stringable|string
    {
        return 'Current weather for a city.';
    }

    public function handle(Request $request): Stringable|string
    {
        return $request['city'].': light rain, 29C'; // call your real weather source here
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'city' => $schema->string()->required(),
        ];
    }
}
```

এটা anonymous agent-কে দিন, অথবা agent class-এর `tools()` method থেকে return করুন:

```php
use App\Ai\Tools\GetWeather;
use function Laravel\Ai\agent;

$response = agent(
    instructions: 'Use the weather tool when asked about weather.',
    tools: [new GetWeather],
)->prompt('Is it raining in Dhaka?', provider: 'tokens');

echo $response->text;
```

SDK tool-এর definition পাঠায় Chat Completions format-এ, আর loop-টা আপনার হয়ে নিজেই চালায়। Tool calling শুধু সেই model-এ চলে যা এটা সাপোর্ট করে, তাই [/models](/models)-এ model-এর পেজ দেখুন আর পড়ুন [Tool calling](/docs/tool-calling)।

## Timeout

Agent-এর timeout-এর default 60 সেকেন্ড। এটা call পিছু, class পিছু, বা দুই জায়গাতেই বাড়াতে পারেন:

```php
$response = $agent->prompt('...', provider: 'tokens', timeout: 180);
```

Class-এর জন্য `#[Timeout(180)]` দিন। মানটা ওই request-এর HTTP client timeout, সেকেন্ডে। Laravel-এর HTTP client-এ এটা Guzzle-এর `timeout` option, যাকে Guzzle বলে request-এর মোট সময়। তাই লম্বা stream করা উত্তর ওই সীমায় কেটে যেতে পারে। আপনার সবচেয়ে লম্বা generation-এর চেয়ে বেশি মান দিন।

আরও কয়েকটা সীমা জেনে রাখুন:

- Upstream থেকে response header আসার জন্য gateway 600 সেকেন্ড পর্যন্ত অপেক্ষা করে, তাই ওর দিক থেকে লম্বা generation-এ সমস্যা নেই।
- তার আগেই PHP নিজে web request থামিয়ে দিতে পারে। `php.ini`-তে `max_execution_time` দেখুন (web request-এর জন্য PHP-র default 30 সেকেন্ড), অথবা লম্বা কাজ queue-তে চালান।

## Error সামলান

SDK কিছু HTTP status নিজের exception-এ রূপান্তর করে। Tokens-এর ক্ষেত্রে:

| Status                | Exception                                           | Tokens-এর code                                                                                         |
| --------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| 402                   | `Laravel\Ai\Exceptions\InsufficientCreditsException` | `insufficient_credits`, `no_funding`, `outstanding_debt`, `member_cap_reached`                         |
| 429                   | `Laravel\Ai\Exceptions\RateLimitedException`        | `rate_limited`, `concurrency_limit`, `window_exhausted`, `model_limit_reached`, `rate_limit_exceeded` |
| 502, 503, 504         | `Laravel\Ai\Exceptions\ProviderOverloadedException` | `upstream_unreachable`, `no_upstream_available`, `upstream_timeout`                                    |
| connection ব্যর্থ হলে | `Laravel\Ai\Exceptions\ProviderConnectionException` | নেই; request Tokens পর্যন্ত পৌঁছায়ইনি                                                                  |
| বাকি সব 4xx ও 5xx     | `Illuminate\Http\Client\RequestException`           | `invalid_api_key`, `model_not_found`, `model_not_allowed_on_key` আর বাকিগুলো                           |

Tokens-এর `code` থাকে response body-তে। SDK-র exception আসল error-টাকে previous exception হিসেবে রেখে দেয়:

```php
use Illuminate\Http\Client\RequestException;
use Laravel\Ai\Exceptions\AiException;

try {
    $text = agent(instructions: 'Say hi.')->prompt('hi', provider: 'tokens')->text;
} catch (AiException|RequestException $e) {
    $request = $e instanceof RequestException ? $e : $e->getPrevious();
    $response = $request instanceof RequestException ? $request->response : null;

    logger()->warning('Tokens call failed', [
        'status' => $response?->status(),
        'code' => $response?->json('error.code'),
        'request_id' => $response?->header('x-tokens-request-id'),
    ]);

    throw $e;
}
```

প্রতিটা ব্যর্থতার সাথে `x-tokens-request-id` log করে রাখুন। ওটা ধরে Support আপনার request খুঁজে বের করতে পারে ([Errors](/docs/errors))। `429 window_exhausted` হলে window reset না হওয়া পর্যন্ত retry করে লাভ নেই; `Retry-After` বলে দেয় কখন।

## openai-compatible driver কী কী করে

Package-এর source অনুযায়ী এই driver text generation (streaming আর tool-সহ), embeddings আর transcription করে। Image generation আর text-to-speech চলে SDK-র অন্য provider দিয়ে, এটা দিয়ে নয়। Embeddings-এর জন্য provider config-এ `models.embeddings.default` লাগে; Tokens embeddings চালায় ([Embeddings](/docs/embeddings)), তবে এই পেজে সেই setup ধাপে ধাপে দেখানো হয়নি।

## সমস্যা হলে

| লক্ষণ                                                                      | কারণ ও সমাধান                                                                                                                  |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `The [tokens] openai-compatible provider requires a 'url' to be configured` | `url` ফাঁকা। `tokens` entry দেখুন আর config cache clear করুন।                                                                  |
| `... requires a default text model`                                        | `models.text.default` দিন, নয়তো call-এ `model:` দিন।                                                                           |
| `404 model_not_found`                                                      | Model ID ভুল। [/models](/models) থেকে copy করুন।                                                                               |
| প্রতিটা call-এ `404`, error message-এ একটা path-এর উল্লেখ                  | URL-এ `/v1` নেই। `https://tokens.bd/v1` দিন।                                                                                   |
| `401 missing_api_key` বা `invalid_api_key`                                 | চলমান process-এ `TOKENS_API_KEY` ফাঁকা, প্রায়ই কারণ config cache করা। `php artisan config:clear` চালান।                      |
| `RateLimitedException`                                                     | ওপরে দেখানো মতো `error.code` পড়ুন। `rate_limited` আর `concurrency_limit` `Retry-After`-এর পর ঠিক হয়; `window_exhausted` ঠিক হয় plan reset হলে। |
| `InsufficientCreditsException`                                             | [Billing](/dashboard/billing)-এ টাকা যোগ করুন।                                                                                  |
| `cURL error 28: Operation timed out`                                       | `timeout`-এর মান response আসার সময়ের চেয়ে কম। বাড়ান।                                                                        |
| Stream করা text মাঝপথে থেমে যায়                                           | মোট timeout শেষ হয়ে গেছে, অথবা PHP-র `max_execution_time` request থামিয়ে দিয়েছে। দুটোই বাড়ান।                              |

Tokens-এর সব error code আছে [Errors](/docs/errors) পেজে, আর সাধারণ সমাধান পাবেন [Troubleshooting](/docs/troubleshooting)-এ।

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