Hire Laravel developers with AI expertise at $20/hr. Get started in 48 hours.

Claude Php Sdk Laravel

Claude Php Sdk Laravel stats

Downloads
4
Stars
4
Open Issues
0
Forks
0

View on GitHub →

Laravel integration for the Claude PHP SDK - Anthropic Claude API

Claude PHP SDK for Laravel

The official Laravel integration for the Claude PHP SDK, providing seamless access to Anthropic's Claude API in your Laravel applications.

Requirements

  • PHP 8.2+
  • Laravel 11.x or 12.x

Installation

Install the package via Composer:

composer require claude-php/claude-php-sdk-laravel

The package will automatically register the service provider and facade.

Configuration

Publish the configuration file:

php artisan vendor:publish --tag=claude-config

Add your Anthropic API key to your .env file:

ANTHROPIC_API_KEY=your-api-key-here

Available Configuration Options

Option Environment Variable Default Description
api_key ANTHROPIC_API_KEY null Your Anthropic API key
base_url ANTHROPIC_BASE_URL https://api.anthropic.com/v1 API base URL
timeout ANTHROPIC_TIMEOUT 30.0 Request timeout in seconds
max_retries ANTHROPIC_MAX_RETRIES 2 Maximum retry attempts
headers - [] Custom headers for requests

📚 More Examples & Tutorials

This Laravel package wraps the Claude PHP SDK. The main SDK contains 80+ examples and 15 comprehensive tutorials covering advanced topics like:

  • 🤖 Agentic Patterns: ReAct, Chain-of-Thought, Tree-of-Thoughts, Plan-and-Execute
  • 🔧 Tool Use: Function calling, Computer Use, Bash tools, MCP integration
  • 🧠 Extended Thinking: Deep reasoning with thinking budgets
  • 📄 Document Processing: PDFs, images, citations
  • Streaming: Real-time responses with event handling
  • 📦 Batch Processing: High-volume request handling

Using SDK Examples with Laravel

All examples in the main SDK can be adapted for Laravel by replacing the client initialization with the Facade:

SDK Example (standalone):

use ClaudePhp\ClaudePhp;
 
$client = new ClaudePhp(apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([...]);

Laravel (using Facade):

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([...]);

Laravel (using Dependency Injection):

use ClaudePhp\ClaudePhp;
 
public function __construct(private ClaudePhp $claude) {}
 
// Then use: $this->claude->messages()->create([...]);

👉 Browse Examples | Read Tutorials


Usage Examples

Basic Message

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Hello, Claude!']
]
]);
 
echo $response['content'][0]['text'];

Using in a Controller

<?php
 
namespace App\Http\Controllers;
 
use ClaudePhp\ClaudePhp;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
 
class ChatController extends Controller
{
public function __construct(
private ClaudePhp $claude
) {}
 
public function chat(Request $request): JsonResponse
{
$validated = $request->validate([
'message' => 'required|string|max:10000',
]);
 
$response = $this->claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => $validated['message']]
]
]);
 
return response()->json([
'response' => $response['content'][0]['text'],
'usage' => $response['usage']
]);
}
}

System Prompts

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'system' => 'You are a helpful assistant that speaks like a pirate. Always end responses with "Arrr!"',
'messages' => [
['role' => 'user', 'content' => 'What is Laravel?']
]
]);

Multi-Turn Conversations

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'My name is Alice.'],
['role' => 'assistant', 'content' => 'Hello Alice! Nice to meet you. How can I help you today?'],
['role' => 'user', 'content' => 'What is my name?']
]
]);
 
// Response: "Your name is Alice..."

Streaming Responses

use ClaudePhp\Laravel\Facades\Claude;
 
$stream = Claude::messages()->stream([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Tell me a story about a brave knight']
]
]);
 
foreach ($stream as $event) {
$type = $event['type'] ?? '';
 
if ($type === 'content_block_delta') {
echo $event['delta']['text'] ?? '';
flush();
}
}

Streaming with Laravel Response

use ClaudePhp\Laravel\Facades\Claude;
use Symfony\Component\HttpFoundation\StreamedResponse;
 
public function streamChat(Request $request): StreamedResponse
{
return response()->stream(function () use ($request) {
$stream = Claude::messages()->stream([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 2048,
'messages' => [
['role' => 'user', 'content' => $request->input('message')]
]
]);
 
foreach ($stream as $event) {
if (($event['type'] ?? '') === 'content_block_delta') {
echo "data: " . json_encode(['text' => $event['delta']['text'] ?? '']) . "\n\n";
ob_flush();
flush();
}
}
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'X-Accel-Buffering' => 'no',
]);
}

Vision - Analyzing Images

use ClaudePhp\Laravel\Facades\Claude;
 
// From URL
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
[
'role' => 'user',
'content' => [
[
'type' => 'image',
'source' => [
'type' => 'url',
'url' => 'https://example.com/image.jpg'
]
],
[
'type' => 'text',
'text' => 'What do you see in this image?'
]
]
]
]
]);
 
// From base64
$imageData = base64_encode(file_get_contents(storage_path('app/photo.jpg')));
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
[
'role' => 'user',
'content' => [
[
'type' => 'image',
'source' => [
'type' => 'base64',
'media_type' => 'image/jpeg',
'data' => $imageData
]
],
[
'type' => 'text',
'text' => 'Describe this image in detail.'
]
]
]
]
]);

PDF Document Analysis

use ClaudePhp\Laravel\Facades\Claude;
 
$pdfData = base64_encode(file_get_contents(storage_path('app/document.pdf')));
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 4096,
'messages' => [
[
'role' => 'user',
'content' => [
[
'type' => 'document',
'source' => [
'type' => 'base64',
'media_type' => 'application/pdf',
'data' => $pdfData
]
],
[
'type' => 'text',
'text' => 'Summarize the key points of this document.'
]
]
]
]
]);

Tool Use (Function Calling)

use ClaudePhp\Laravel\Facades\Claude;
 
$tools = [
[
'name' => 'get_weather',
'description' => 'Get the current weather in a given location',
'input_schema' => [
'type' => 'object',
'properties' => [
'location' => [
'type' => 'string',
'description' => 'City and country, e.g. London, UK'
],
'unit' => [
'type' => 'string',
'enum' => ['celsius', 'fahrenheit'],
'description' => 'Temperature unit'
]
],
'required' => ['location']
]
],
[
'name' => 'search_products',
'description' => 'Search for products in the catalog',
'input_schema' => [
'type' => 'object',
'properties' => [
'query' => ['type' => 'string', 'description' => 'Search query'],
'category' => ['type' => 'string', 'description' => 'Product category'],
'max_price' => ['type' => 'number', 'description' => 'Maximum price']
],
'required' => ['query']
]
]
];
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'tools' => $tools,
'messages' => [
['role' => 'user', 'content' => "What's the weather in Tokyo?"]
]
]);
 
// Check if Claude wants to use a tool
foreach ($response['content'] as $block) {
if ($block['type'] === 'tool_use') {
$toolName = $block['name'];
$toolInput = $block['input'];
$toolUseId = $block['id'];
 
// Execute your tool and get result
$result = match($toolName) {
'get_weather' => getWeather($toolInput['location'], $toolInput['unit'] ?? 'celsius'),
'search_products' => searchProducts($toolInput),
default => ['error' => 'Unknown tool']
};
 
// Continue conversation with tool result
$followUp = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'tools' => $tools,
'messages' => [
['role' => 'user', 'content' => "What's the weather in Tokyo?"],
['role' => 'assistant', 'content' => $response['content']],
[
'role' => 'user',
'content' => [
[
'type' => 'tool_result',
'tool_use_id' => $toolUseId,
'content' => json_encode($result)
]
]
]
]
]);
}
}

Extended Thinking (Deep Reasoning)

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 16000,
'thinking' => [
'type' => 'enabled',
'budget_tokens' => 10000 // Tokens allocated for reasoning
],
'messages' => [
['role' => 'user', 'content' => 'Prove that there are infinitely many prime numbers.']
]
]);
 
foreach ($response['content'] as $block) {
if ($block['type'] === 'thinking') {
// Claude's internal reasoning process
echo "Thinking: " . substr($block['thinking'], 0, 500) . "...\n\n";
} elseif ($block['type'] === 'text') {
// Final answer
echo "Answer: " . $block['text'];
}
}

Structured Output (JSON Mode)

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
[
'role' => 'user',
'content' => 'Extract the following info as JSON:
Name: John Smith
Email: john@example.com
Age: 30
Skills: PHP, Laravel, JavaScript
 
Return a JSON object with keys: name, email, age, skills (as array)'
]
]
]);
 
$data = json_decode($response['content'][0]['text'], true);

Prompt Caching

use ClaudePhp\Laravel\Facades\Claude;
 
// Cache large system prompts or context for efficiency
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'system' => [
[
'type' => 'text',
'text' => $largeSystemPrompt, // Your extensive instructions
'cache_control' => ['type' => 'ephemeral']
]
],
'messages' => [
['role' => 'user', 'content' => 'Process this request...']
]
]);
 
// Subsequent requests with same cached content are faster and cheaper

Listing Available Models

use ClaudePhp\Laravel\Facades\Claude;
 
$models = Claude::models()->list();
 
foreach ($models['data'] as $model) {
echo "Model: {$model['id']}\n";
echo " Display Name: {$model['display_name']}\n";
echo " Created: {$model['created_at']}\n\n";
}

Async Operations

use ClaudePhp\Laravel\Facades\Claude;
 
// Get the async proxy for concurrent operations
$async = Claude::async();
 
// Create multiple requests concurrently
$promise1 = $async->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [['role' => 'user', 'content' => 'Summarize quantum physics']]
]);
 
$promise2 = $async->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [['role' => 'user', 'content' => 'Explain machine learning']]
]);
 
// Wait for both to complete
$response1 = $promise1->await();
$response2 = $promise2->await();

Beta Features - Message Batches

use ClaudePhp\Laravel\Facades\Claude;
 
// Create a batch of requests for high-volume processing
$batch = Claude::beta()->messages()->batches()->create([
'requests' => [
[
'custom_id' => 'request-1',
'params' => [
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Translate to French: Hello world']
]
]
],
[
'custom_id' => 'request-2',
'params' => [
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Translate to Spanish: Hello world']
]
]
]
]
]);
 
// Check batch status
$status = Claude::beta()->messages()->batches()->retrieve($batch['id']);
 
// List all batches
$batches = Claude::beta()->messages()->batches()->list();

Web Search Integration

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 4096,
'tools' => [
[
'type' => 'web_search_20250305',
'name' => 'web_search',
'max_uses' => 5
]
],
'messages' => [
['role' => 'user', 'content' => 'What are the latest developments in PHP 8.4?']
]
]);

Token Counting

use ClaudePhp\Laravel\Facades\Claude;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Hello, Claude!']
]
]);
 
$usage = $response['usage'];
echo "Input tokens: {$usage['input_tokens']}\n";
echo "Output tokens: {$usage['output_tokens']}\n";
echo "Total tokens: " . ($usage['input_tokens'] + $usage['output_tokens']) . "\n";
 
// With caching
if (isset($usage['cache_creation_input_tokens'])) {
echo "Cache creation tokens: {$usage['cache_creation_input_tokens']}\n";
}
if (isset($usage['cache_read_input_tokens'])) {
echo "Cache read tokens: {$usage['cache_read_input_tokens']}\n";
}

🤖 Agentic Laravel

Build intelligent AI agents that can reason, use tools, and solve complex problems autonomously.

📚 For comprehensive tutorials on agentic patterns, see the main SDK tutorials.

ReAct Agent Loop

The ReAct (Reason-Act-Observe) pattern enables iterative problem solving:

<?php
 
namespace App\Services;
 
use ClaudePhp\ClaudePhp;
 
class ReActAgent
{
private array $tools;
private array $messages = [];
 
public function __construct(
private ClaudePhp $claude,
private int $maxIterations = 10
) {
$this->tools = $this->defineTools();
}
 
public function run(string $task): string
{
$this->messages = [['role' => 'user', 'content' => $task]];
$iteration = 0;
 
while ($iteration < $this->maxIterations) {
$iteration++;
 
// REASON: Ask Claude to analyze the situation
$response = $this->claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 4096,
'system' => 'You are a helpful assistant. Use tools when needed to solve problems.',
'messages' => $this->messages,
'tools' => $this->tools
]);
 
// Add assistant response to history
$this->messages[] = ['role' => 'assistant', 'content' => $response['content']];
 
// COMPLETE: Check if task is finished
if ($response['stop_reason'] === 'end_turn') {
return $this->extractText($response);
}
 
// ACT: Execute any requested tools
if ($response['stop_reason'] === 'tool_use') {
$toolResults = $this->executeTools($response['content']);
 
// OBSERVE: Add tool results to conversation
$this->messages[] = ['role' => 'user', 'content' => $toolResults];
 
// Continue loop to get Claude's response to tool results
continue;
}
 
// Unexpected stop reason - break to avoid infinite loop
break;
}
 
return 'Max iterations reached without completion.';
}
 
private function executeTools(array $content): array
{
$results = [];
 
foreach ($content as $block) {
if ($block['type'] === 'tool_use') {
$result = $this->executeTool($block['name'], $block['input']);
$results[] = [
'type' => 'tool_result',
'tool_use_id' => $block['id'],
'content' => is_string($result) ? $result : json_encode($result)
];
}
}
 
return $results;
}
 
private function executeTool(string $name, array $input): mixed
{
return match ($name) {
'calculate' => $this->calculate($input['expression']),
'search_database' => $this->searchDatabase($input['query']),
'get_current_time' => now()->toDateTimeString(),
default => "Unknown tool: {$name}"
};
}
 
private function calculate(string $expression): string
{
// Use a safe math parser in production!
try {
$result = eval("return {$expression};");
return (string) $result;
} catch (\Throwable $e) {
return "Error: {$e->getMessage()}";
}
}
 
private function searchDatabase(string $query): array
{
// Your database search logic
return \App\Models\Product::search($query)->take(5)->get()->toArray();
}
 
private function defineTools(): array
{
return [
[
'name' => 'calculate',
'description' => 'Perform mathematical calculations',
'input_schema' => [
'type' => 'object',
'properties' => [
'expression' => [
'type' => 'string',
'description' => 'Math expression (e.g., "25 * 4 + 10")'
]
],
'required' => ['expression']
]
],
[
'name' => 'search_database',
'description' => 'Search the product database',
'input_schema' => [
'type' => 'object',
'properties' => [
'query' => [
'type' => 'string',
'description' => 'Search query'
]
],
'required' => ['query']
]
],
[
'name' => 'get_current_time',
'description' => 'Get the current date and time',
'input_schema' => [
'type' => 'object',
'properties' => []
]
]
];
}
 
private function extractText(array $response): string
{
foreach ($response['content'] as $block) {
if ($block['type'] === 'text') {
return $block['text'];
}
}
return '';
}
}

Usage:

use App\Services\ReActAgent;
 
class AgentController extends Controller
{
public function solve(Request $request, ReActAgent $agent)
{
$result = $agent->run($request->input('task'));
 
return response()->json(['result' => $result]);
}
}

Multi-Tool Agent with Laravel Services

Integrate Laravel services as tools for your agent:

<?php
 
namespace App\Services;
 
use App\Models\Order;
use App\Models\Customer;
use ClaudePhp\ClaudePhp;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
 
class CustomerServiceAgent
{
private array $tools;
 
public function __construct(
private ClaudePhp $claude
) {
$this->tools = [
[
'name' => 'lookup_customer',
'description' => 'Look up customer information by email or ID',
'input_schema' => [
'type' => 'object',
'properties' => [
'identifier' => ['type' => 'string', 'description' => 'Customer email or ID']
],
'required' => ['identifier']
]
],
[
'name' => 'get_order_history',
'description' => 'Get recent orders for a customer',
'input_schema' => [
'type' => 'object',
'properties' => [
'customer_id' => ['type' => 'integer', 'description' => 'Customer ID'],
'limit' => ['type' => 'integer', 'description' => 'Number of orders (default 5)']
],
'required' => ['customer_id']
]
],
[
'name' => 'check_inventory',
'description' => 'Check product inventory levels',
'input_schema' => [
'type' => 'object',
'properties' => [
'product_sku' => ['type' => 'string', 'description' => 'Product SKU']
],
'required' => ['product_sku']
]
],
[
'name' => 'create_support_ticket',
'description' => 'Create a support ticket for the customer',
'input_schema' => [
'type' => 'object',
'properties' => [
'customer_id' => ['type' => 'integer'],
'subject' => ['type' => 'string'],
'description' => ['type' => 'string'],
'priority' => ['type' => 'string', 'enum' => ['low', 'medium', 'high']]
],
'required' => ['customer_id', 'subject', 'description']
]
]
];
}
 
public function handleInquiry(string $inquiry, ?int $customerId = null): array
{
$context = $customerId
? "You are helping customer ID: {$customerId}. "
: "You are a customer service agent. ";
 
$messages = [['role' => 'user', 'content' => $inquiry]];
$iterations = 0;
 
while ($iterations < 8) {
$iterations++;
 
$response = $this->claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 4096,
'system' => $context . 'Use available tools to help resolve customer inquiries. Be helpful and thorough.',
'messages' => $messages,
'tools' => $this->tools
]);
 
$messages[] = ['role' => 'assistant', 'content' => $response['content']];
 
// COMPLETE: Task finished
if ($response['stop_reason'] === 'end_turn') {
return [
'response' => $this->extractText($response),
'iterations' => $iterations,
'tools_used' => $this->countToolUses($messages)
];
}
 
// ACT & OBSERVE: Execute tools and continue
if ($response['stop_reason'] === 'tool_use') {
$results = [];
foreach ($response['content'] as $block) {
if ($block['type'] === 'tool_use') {
$results[] = [
'type' => 'tool_result',
'tool_use_id' => $block['id'],
'content' => json_encode($this->executeTool($block['name'], $block['input']))
];
}
}
$messages[] = ['role' => 'user', 'content' => $results];
continue; // Get Claude's response to tool results
}
 
// Unexpected stop reason
break;
}
 
return ['response' => 'Unable to complete request.', 'iterations' => $iterations];
}
 
private function executeTool(string $name, array $input): mixed
{
return match ($name) {
'lookup_customer' => $this->lookupCustomer($input['identifier']),
'get_order_history' => $this->getOrderHistory($input['customer_id'], $input['limit'] ?? 5),
'check_inventory' => $this->checkInventory($input['product_sku']),
'create_support_ticket' => $this->createTicket($input),
default => ['error' => 'Unknown tool']
};
}
 
private function lookupCustomer(string $identifier): array
{
$customer = Customer::where('email', $identifier)
->orWhere('id', $identifier)
->first();
 
return $customer ? $customer->toArray() : ['error' => 'Customer not found'];
}
 
private function getOrderHistory(int $customerId, int $limit): array
{
return Order::where('customer_id', $customerId)
->with('items')
->latest()
->take($limit)
->get()
->toArray();
}
 
private function checkInventory(string $sku): array
{
return Cache::remember("inventory_{$sku}", 300, function () use ($sku) {
// Your inventory check logic
return ['sku' => $sku, 'quantity' => rand(0, 100), 'status' => 'in_stock'];
});
}
 
private function createTicket(array $input): array
{
$ticket = \App\Models\SupportTicket::create([
'customer_id' => $input['customer_id'],
'subject' => $input['subject'],
'description' => $input['description'],
'priority' => $input['priority'] ?? 'medium',
'status' => 'open'
]);
 
return ['ticket_id' => $ticket->id, 'status' => 'created'];
}
 
private function extractText(array $response): string
{
foreach ($response['content'] as $block) {
if ($block['type'] === 'text') {
return $block['text'];
}
}
return '';
}
 
private function countToolUses(array $messages): int
{
$count = 0;
foreach ($messages as $msg) {
if (is_array($msg['content'] ?? null)) {
foreach ($msg['content'] as $block) {
if (($block['type'] ?? '') === 'tool_use') {
$count++;
}
}
}
}
return $count;
}
}

Agentic Framework with State Management

Build a complete agent framework with task decomposition:

<?php
 
namespace App\Services\Agents;
 
use ClaudePhp\ClaudePhp;
use Illuminate\Support\Facades\Log;
 
class AgentFramework
{
private array $state = [];
private array $agents = [];
 
public function __construct(
private ClaudePhp $claude
) {}
 
public function registerAgent(string $name, array $tools, string $systemPrompt): self
{
$this->agents[$name] = [
'tools' => $tools,
'system' => $systemPrompt
];
return $this;
}
 
public function execute(string $goal): array
{
Log::info("Agent Framework: Starting goal", ['goal' => $goal]);
 
// Step 1: Decompose the goal into subtasks
$subtasks = $this->decompose($goal);
 
// Step 2: Execute each subtask
$results = [];
foreach ($subtasks as $i => $subtask) {
Log::info("Executing subtask", ['index' => $i, 'task' => $subtask['task']]);
 
$agentName = $subtask['agent'] ?? 'default';
$result = $this->runAgent($agentName, $subtask['task']);
 
$results[] = $result;
$this->state["subtask_{$i}"] = $result;
}
 
// Step 3: Synthesize final result
$finalResult = $this->synthesize($goal, $results);
 
return [
'goal' => $goal,
'subtasks' => $subtasks,
'results' => $results,
'final_result' => $finalResult,
'state' => $this->state
];
}
 
private function decompose(string $goal): array
{
$response = $this->claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 2048,
'thinking' => ['type' => 'enabled', 'budget_tokens' => 3000],
'messages' => [[
'role' => 'user',
'content' => "Break this goal into 2-4 concrete subtasks. Format as numbered list.\n\nGoal: {$goal}"
]]
]);
 
return $this->parseSubtasks($this->extractText($response));
}
 
private function runAgent(string $agentName, string $task): string
{
$agent = $this->agents[$agentName] ?? $this->agents['default'] ?? null;
 
if (!$agent) {
return "No agent available for: {$agentName}";
}
 
$messages = [['role' => 'user', 'content' => $task]];
$iteration = 0;
 
while ($iteration < 10) {
$iteration++;
 
$params = [
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 4096,
'system' => $agent['system'],
'messages' => $messages,
];
 
if (!empty($agent['tools'])) {
$params['tools'] = $agent['tools'];
}
 
$response = $this->claude->messages()->create($params);
$messages[] = ['role' => 'assistant', 'content' => $response['content']];
 
// COMPLETE: Task finished
if ($response['stop_reason'] === 'end_turn') {
return $this->extractText($response);
}
 
// ACT & OBSERVE: Execute tools and continue loop
if ($response['stop_reason'] === 'tool_use') {
$results = $this->executeTools($response['content']);
$messages[] = ['role' => 'user', 'content' => $results];
continue;
}
 
// Unexpected stop reason
break;
}
 
return 'Agent reached iteration limit.';
}
 
private function synthesize(string $goal, array $results): string
{
$resultsText = collect($results)
->map(fn($r, $i) => ($i + 1) . ". {$r}")
->implode("\n\n");
 
$response = $this->claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 2048,
'messages' => [[
'role' => 'user',
'content' => "Original goal: {$goal}\n\nSubtask results:\n{$resultsText}\n\nProvide a synthesized final answer."
]]
]);
 
return $this->extractText($response);
}
 
private function executeTools(array $content): array
{
$results = [];
foreach ($content as $block) {
if ($block['type'] === 'tool_use') {
// Implement your tool execution logic
$results[] = [
'type' => 'tool_result',
'tool_use_id' => $block['id'],
'content' => json_encode(['status' => 'executed'])
];
}
}
return $results;
}
 
private function parseSubtasks(string $text): array
{
$subtasks = [];
foreach (explode("\n", $text) as $line) {
if (preg_match('/^\d+\.\s+(.+)$/', trim($line), $matches)) {
$subtasks[] = ['task' => $matches[1], 'agent' => 'default'];
}
}
return $subtasks ?: [['task' => $text, 'agent' => 'default']];
}
 
private function extractText(array $response): string
{
foreach ($response['content'] as $block) {
if ($block['type'] === 'text') {
return $block['text'];
}
}
return '';
}
 
public function getState(): array
{
return $this->state;
}
}

Usage in a Controller:

use App\Services\Agents\AgentFramework;
 
class ResearchController extends Controller
{
public function analyze(Request $request, AgentFramework $framework)
{
// Register specialized agents
$framework
->registerAgent('default', [], 'You are a helpful research assistant.')
->registerAgent('calculator', [
[
'name' => 'calculate',
'description' => 'Perform calculations',
'input_schema' => [
'type' => 'object',
'properties' => [
'expression' => ['type' => 'string']
],
'required' => ['expression']
]
]
], 'You are a math specialist.');
 
$result = $framework->execute($request->input('goal'));
 
return response()->json($result);
}
}

Streaming Agent Responses

For real-time agent feedback:

use ClaudePhp\Laravel\Facades\Claude;
use Symfony\Component\HttpFoundation\StreamedResponse;
 
public function streamAgent(Request $request): StreamedResponse
{
return response()->stream(function () use ($request) {
$task = $request->input('task');
$messages = [['role' => 'user', 'content' => $task]];
$tools = $this->getTools();
 
for ($i = 0; $i < 10; $i++) {
// Stream: Send iteration marker
echo "data: " . json_encode(['type' => 'iteration', 'number' => $i + 1]) . "\n\n";
ob_flush(); flush();
 
$stream = Claude::messages()->stream([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 4096,
'messages' => $messages,
'tools' => $tools
]);
 
$fullContent = [];
$stopReason = null;
 
foreach ($stream as $event) {
$type = $event['type'] ?? '';
 
if ($type === 'content_block_delta' && isset($event['delta']['text'])) {
// Stream text to client
echo "data: " . json_encode([
'type' => 'text',
'content' => $event['delta']['text']
]) . "\n\n";
ob_flush(); flush();
}
 
if ($type === 'message_delta') {
$stopReason = $event['delta']['stop_reason'] ?? null;
}
 
if ($type === 'content_block_start' || $type === 'content_block_delta') {
// Accumulate for history
// ... accumulation logic
}
}
 
if ($stopReason === 'end_turn') {
echo "data: " . json_encode(['type' => 'complete']) . "\n\n";
break;
}
 
if ($stopReason === 'tool_use') {
echo "data: " . json_encode(['type' => 'tool_execution']) . "\n\n";
ob_flush(); flush();
 
// Execute tools and continue loop
// ... tool execution logic
}
}
 
echo "data: [DONE]\n\n";
ob_flush(); flush();
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'X-Accel-Buffering' => 'no',
]);
}

Agent with Extended Thinking

For complex reasoning tasks:

use ClaudePhp\Laravel\Facades\Claude;
 
class ReasoningAgent
{
public function solve(string $problem): array
{
$messages = [['role' => 'user', 'content' => $problem]];
$thinkingHistory = [];
$iteration = 0;
 
while ($iteration < 5) {
$iteration++;
 
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 16000,
'thinking' => [
'type' => 'enabled',
'budget_tokens' => 8000
],
'messages' => $messages,
'tools' => $this->getTools()
]);
 
// Capture thinking for analysis
foreach ($response['content'] as $block) {
if ($block['type'] === 'thinking') {
$thinkingHistory[] = [
'iteration' => $iteration,
'thinking' => $block['thinking']
];
}
}
 
$messages[] = ['role' => 'assistant', 'content' => $response['content']];
 
// COMPLETE: Problem solved
if ($response['stop_reason'] === 'end_turn') {
return [
'answer' => $this->extractText($response),
'thinking_steps' => count($thinkingHistory),
'thinking_history' => $thinkingHistory,
'iterations' => $iteration
];
}
 
// ACT & OBSERVE: Execute tools and continue reasoning
if ($response['stop_reason'] === 'tool_use') {
$results = $this->executeTools($response['content']);
$messages[] = ['role' => 'user', 'content' => $results];
continue;
}
 
// Unexpected stop reason
break;
}
 
return ['answer' => 'Could not solve within iteration limit.'];
}
 
// ... helper methods
}

Laravel-Specific Patterns

Using with Jobs

<?php
 
namespace App\Jobs;
 
use ClaudePhp\ClaudePhp;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
 
class ProcessWithClaude implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
public function __construct(
private string $content,
private int $userId
) {}
 
public function handle(ClaudePhp $claude): void
{
$response = $claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 2048,
'messages' => [
['role' => 'user', 'content' => $this->content]
]
]);
 
// Store result...
\App\Models\AiResponse::create([
'user_id' => $this->userId,
'response' => $response['content'][0]['text']
]);
}
}

Using with Artisan Commands

<?php
 
namespace App\Console\Commands;
 
use ClaudePhp\ClaudePhp;
use Illuminate\Console\Command;
 
class AskClaude extends Command
{
protected $signature = 'claude:ask {question}';
protected $description = 'Ask Claude a question';
 
public function handle(ClaudePhp $claude): int
{
$question = $this->argument('question');
 
$this->info("Asking Claude: {$question}");
$this->newLine();
 
$response = $claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 2048,
'messages' => [
['role' => 'user', 'content' => $question]
]
]);
 
$this->line($response['content'][0]['text']);
 
return Command::SUCCESS;
}
}

Using with Service Classes

<?php
 
namespace App\Services;
 
use ClaudePhp\ClaudePhp;
use Illuminate\Support\Facades\Cache;
 
class AiService
{
public function __construct(
private ClaudePhp $claude
) {}
 
public function summarize(string $text, int $maxLength = 200): string
{
$cacheKey = 'summary_' . md5($text);
 
return Cache::remember($cacheKey, 3600, function () use ($text, $maxLength) {
$response = $this->claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => $maxLength,
'system' => 'You are a summarization expert. Provide concise summaries.',
'messages' => [
['role' => 'user', 'content' => "Summarize this: {$text}"]
]
]);
 
return $response['content'][0]['text'];
});
}
 
public function translate(string $text, string $targetLanguage): string
{
$response = $this->claude->messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 2048,
'messages' => [
['role' => 'user', 'content' => "Translate to {$targetLanguage}: {$text}"]
]
]);
 
return $response['content'][0]['text'];
}
}

Testing

Mocking the Facade

use ClaudePhp\Laravel\Facades\Claude;
 
public function test_chat_endpoint(): void
{
Claude::shouldReceive('messages->create')
->once()
->andReturn([
'id' => 'msg_123',
'type' => 'message',
'role' => 'assistant',
'content' => [
['type' => 'text', 'text' => 'Mocked response']
],
'model' => 'claude-sonnet-4-5-20250929',
'stop_reason' => 'end_turn',
'usage' => [
'input_tokens' => 10,
'output_tokens' => 5
]
]);
 
$response = $this->postJson('/api/chat', [
'message' => 'Hello'
]);
 
$response->assertOk()
->assertJsonPath('response', 'Mocked response');
}

Using Fake API Responses

use ClaudePhp\ClaudePhp;
use Mockery;
 
public function test_with_dependency_injection(): void
{
$mockClaude = Mockery::mock(ClaudePhp::class);
$mockMessages = Mockery::mock();
 
$mockClaude->shouldReceive('messages')->andReturn($mockMessages);
$mockMessages->shouldReceive('create')->andReturn([
'content' => [['type' => 'text', 'text' => 'Test response']]
]);
 
$this->app->instance(ClaudePhp::class, $mockClaude);
 
// Your test...
}

Error Handling

use ClaudePhp\Laravel\Facades\Claude;
use ClaudePhp\Exceptions\AuthenticationException;
use ClaudePhp\Exceptions\RateLimitException;
use ClaudePhp\Exceptions\ApiException;
use ClaudePhp\Exceptions\InvalidRequestException;
use Illuminate\Support\Facades\Log;
 
try {
$response = Claude::messages()->create([
'model' => 'claude-sonnet-4-5-20250929',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Hello!']
]
]);
} catch (AuthenticationException $e) {
// Invalid API key
Log::error('Claude authentication failed', ['error' => $e->getMessage()]);
abort(500, 'AI service configuration error');
 
} catch (RateLimitException $e) {
// Rate limited - implement retry logic
$retryAfter = $e->retryAfter ?? 60;
Log::warning('Claude rate limited', ['retry_after' => $retryAfter]);
 
// Optionally dispatch to queue for later
ProcessWithClaude::dispatch($content)->delay(now()->addSeconds($retryAfter));
 
} catch (InvalidRequestException $e) {
// Bad request parameters
Log::error('Invalid Claude request', ['error' => $e->getMessage()]);
abort(400, 'Invalid request');
 
} catch (ApiException $e) {
// General API error
Log::error('Claude API error', [
'message' => $e->getMessage(),
'code' => $e->getCode()
]);
abort(503, 'AI service temporarily unavailable');
}

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

Security

If you discover any security-related issues, please use the issue tracker.

License

The MIT License (MIT). Please see License File for more information.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.


Claude Php Claude Php Sdk Laravel Related Articles

PHP SDK for Anthropic Claude image

PHP SDK for Anthropic Claude

Read article
Typesense Search logo

Typesense Search

Typesense is an open source, blazing-fast search engine, optimized for helping you build delightful search experiences for your sites and apps. Natively integrated with Laravel Scout.

Typesense Search
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
Acquaint Softtech logo

Acquaint Softtech

Acquaint Softtech offers AI-ready Laravel developers who onboard in 48 hours at $3000/Month with no lengthy sales process and a 100 percent money-back guarantee.

Acquaint Softtech
Blastup logo

Blastup

Blastup provides social media enhancement services including buying Instagram likes, followers, and views, with features like instant delivery and a variety of packages to suit different needs.

Blastup
LoadForge logo

LoadForge

Scalable load testing for web apps & APIs. Simulate real-world traffic and identify breaking points and performance limits with powerful, scalable load tests designed for Laravel.

LoadForge
Celebian logo

Celebian

Celebian is a social media marketing agency specializing in helping their clients go viral on TikTok. Whether you're looking to reach a bigger audience or gain more Tiktok followers, likes, and views, they've got you covered.

Celebian