Tutorial11 menit baca
Tutorial Integrasi AI API ke Laravel (PHP) — OpenAI Compatible
Panduan step-by-step integrasi GPT-5/Claude/Gemini ke aplikasi Laravel dengan client OpenAI PHP, queue async, error handling, rate limiting. Production-ready code lengkap.
Stack yang Dipakai
- Laravel 11+
openai-php/clientv0.10+ (kompatibel dengan OpenAI-compatible gateway)- Laravel Queue (Redis driver)
- ExtPeak sebagai gateway (1 endpoint untuk GPT-5/Claude/Gemini)
Step 1: Install Package
composer require openai-php/clientStep 2: Config
Tambah ke .env:
EXTPEAK_API_KEY=snfx-xxxx
EXTPEAK_BASE_URL=https://extpeak.com/v1
EXTPEAK_DEFAULT_MODEL=anthropic/claude-sonnet-4.6config/services.php:
'extpeak' => [
'api_key' => env('EXTPEAK_API_KEY'),
'base_url' => env('EXTPEAK_BASE_URL', 'https://extpeak.com/v1'),
'model' => env('EXTPEAK_DEFAULT_MODEL', 'anthropic/claude-sonnet-4.6'),
],Step 3: Service Class
app/Services/AIService.php:
<?php
namespace App\Services;
use OpenAI;
use OpenAI\Client;
use Illuminate\Support\Facades\Log;
class AIService
{
protected Client $client;
protected string $model;
public function __construct()
{
$this->client = OpenAI::factory()
->withApiKey(config('services.extpeak.api_key'))
->withBaseUri(config('services.extpeak.base_url'))
->make();
$this->model = config('services.extpeak.model');
}
public function chat(array $messages, ?string $model = null, array $opts = []): string
{
try {
$response = $this->client->chat()->create([
'model' => $model ?? $this->model,
'messages' => $messages,
'temperature' => $opts['temperature'] ?? 0.7,
'max_tokens' => $opts['max_tokens'] ?? 2048,
]);
return $response->choices[0]->message->content;
} catch (\Exception $e) {
Log::error('AI chat failed', ['error' => $e->getMessage()]);
throw $e;
}
}
public function stream(array $messages, callable $onChunk, ?string $model = null): void
{
$stream = $this->client->chat()->createStreamed([
'model' => $model ?? $this->model,
'messages' => $messages,
]);
foreach ($stream as $response) {
$delta = $response->choices[0]->delta->content ?? '';
if ($delta !== '') {
$onChunk($delta);
}
}
}
}Step 4: Queue Job (untuk Long-Running)
php artisan make:job ProcessAIRequestapp/Jobs/ProcessAIRequest.php:
<?php
namespace App\Jobs;
use App\Services\AIService;
use App\Models\AIConversation;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessAIRequest implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 5;
public function __construct(public int $conversationId, public string $userMessage) {}
public function handle(AIService $ai): void
{
$conv = AIConversation::findOrFail($this->conversationId);
$messages = [
['role' => 'system', 'content' => 'You are a helpful assistant for an Indonesian fintech app.'],
...$conv->messages()->get()->map(fn($m) => ['role' => $m->role, 'content' => $m->content])->toArray(),
['role' => 'user', 'content' => $this->userMessage],
];
$reply = $ai->chat($messages);
$conv->messages()->create(['role' => 'user', 'content' => $this->userMessage]);
$conv->messages()->create(['role' => 'assistant', 'content' => $reply]);
}
}Step 5: Controller
public function ask(Request $request)
{
$request->validate([
'message' => 'required|string|max:4000',
'conversation_id' => 'required|exists:ai_conversations,id',
]);
ProcessAIRequest::dispatch($request->conversation_id, $request->message);
return response()->json(['status' => 'queued']);
}Step 6: Streaming via SSE (Real-Time Chat UI)
public function stream(Request $request)
{
$messages = $request->input('messages');
return response()->stream(function () use ($messages) {
app(AIService::class)->stream($messages, function ($chunk) {
echo "data: " . json_encode(['delta' => $chunk]) . "\n\n";
ob_flush();
flush();
});
echo "data: [DONE]\n\n";
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'X-Accel-Buffering' => 'no',
]);
}Step 7: Rate Limiting
routes/web.php:
Route::middleware(['auth', 'throttle:60,1'])->group(function () {
Route::post('/api/chat', [ChatController::class, 'ask']);
Route::post('/api/chat/stream', [ChatController::class, 'stream']);
});Cost Estimation
Dengan Claude Sonnet 4.6 dan rata-rata 500 input + 300 output token per request:
- Cost per request: $0.00255 (~Rp 40)
- 1000 chat/hari: Rp 40,000
- 30 hari: Rp 1,200,000
Switch ke GPT-5 Mini untuk turunkan ke Rp 6/request → Rp 180,000/bulan.
Deploy Checklist
- ✅
EXTPEAK_API_KEYdi production env (jangan commit ke git) - ✅ Queue worker running:
php artisan queue:work --queue=default - ✅ Redis driver untuk queue dan cache
- ✅ Error logging ke Sentry/Bugsnag
- ✅ Rate limit di nginx layer juga (defense-in-depth)
- ✅ Monitoring usage via ExtPeak dashboard
Mulai pakai ExtPeak gratis
Akses 21+ model AI premium dengan harga termurah. OpenAI-compatible. Bayar pakai QRIS.
Daftar gratis