symfony/ai-anthropic-platform
Symfony AI integration for Anthropic’s Claude via the Anthropic Platform. Provides a PHP client and abstractions to send prompts, handle responses, and plug Claude into Symfony apps with a consistent AI interface for chat and text generation.
composer require symfony/ai-anthropic-platform
.env:
ANTHROPIC_API_KEY=your_api_key_here
config/services.yaml:
Symfony\AI\Client\AnthropicClient:
arguments:
$apiKey: '%env(ANTHROPIC_API_KEY)%'
Use it in a controller or service:
use Symfony\AI\Client\AnthropicClient;
class ChatController {
public function __construct(private AnthropicClient $client) {}
public function ask(string $question): string {
$response = $this->client->chat([
'model' => 'claude-3-haiku',
'messages' => [
['role' => 'user', 'content' => $question],
],
]);
return $response['content'][0]['text'];
}
}
Build a Chatbot Endpoint:
// src/Controller/ChatController.php
use Symfony\AI\Client\AnthropicClient;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ChatController {
public function __construct(private AnthropicClient $client) {}
#[Route('/chat', name: 'app_chat')]
public function __invoke(string $question): Response {
$response = $this->client->chat([
'model' => 'claude-3-haiku',
'messages' => [['role' => 'user', 'content' => $question]],
]);
return new Response($response['content'][0]['text']);
}
}
Test with:
curl -X POST "http://localhost:8000/chat?question=What%20is%20Symfony%3F"
Synchronous Chat:
$response = $client->chat([
'model' => 'claude-3-sonnet',
'messages' => [
['role' => 'system', 'content' => 'You are a helpful assistant.'],
['role' => 'user', 'content' => 'Explain Laravel Eloquent.'],
],
]);
// Access: $response['content'][0]['text']
Streaming Responses:
Use MultiPartResult for real-time output (e.g., chat UIs):
$response = $client->chatStream([
'model' => 'claude-3-haiku',
'messages' => [['role' => 'user', 'content' => 'Tell me a joke.']],
]);
foreach ($response as $chunk) {
echo $chunk['content'][0]['text'];
}
Tool Use: Define tools and invoke them:
$response = $client->chat([
'model' => 'claude-3-sonnet',
'tools' => [
['type' => 'function', 'name' => 'get_weather', 'description' => 'Fetch weather data.'],
],
'messages' => [
['role' => 'user', 'content' => 'What is the weather in Paris?'],
],
]);
// Check $response['tool_calls'] for execution details.
Prompt Caching: Cache prompts to reduce API calls:
$cache = new \Symfony\Component\Cache\Adapter\FilesystemAdapter();
$cachedPrompt = $cache->get('prompt_key', function() use ($client) {
return $client->chat([/* prompt */]);
});
Symfony Messenger: Offload async tasks (e.g., batch content generation):
use Symfony\Component\Messenger\MessageBusInterface;
class ContentGenerator {
public function __construct(
private AnthropicClient $client,
private MessageBusInterface $bus
) {}
public function generate(string $prompt) {
$this->bus->dispatch(new GenerateContentMessage($prompt));
}
}
API Platform: Expose AI endpoints as REST resources:
# config/api_platform/resources.yaml
App\Entity\ChatResponse:
collectionOperations:
ask:
method: 'POST'
path: '/chat'
controller: App\Controller\ChatController::ask
Mercure: Stream responses to clients:
use Symfony\Component\Mercure\Update;
use Symfony\Component\Mercure\HubInterface;
class ChatController {
public function __construct(private AnthropicClient $client, private HubInterface $hub) {}
public function stream(string $question) {
$response = $this->client->chatStream([/* ... */]);
foreach ($response as $chunk) {
$this->hub->publish(new Update('chat', $chunk['content'][0]['text']));
}
}
}
Dependency Injection: Route models dynamically:
services:
Symfony\AI\Client\AnthropicClient:
arguments:
$modelRouter: '@app.model_router' # Custom service to route models
Tool Calls in Streaming:
Null Content in Normalization:
AssistantMessageNormalizer may return null content (bug #1670).$content = $response['content'] ?? ['text' => ''];
String Payloads:
ModelClient throws InvalidArgumentException (bug #1711).$client->chat(['model' => 'claude-3-haiku', 'messages' => [...]]);
Multi-Part Responses:
MultiPartResult correctly.$response = $client->chatStream([...]);
$multiPart = new \Symfony\AI\Result\MultiPartResult($response);
Enable HTTP Logging:
# config/packages/http_client.yaml
http_client:
logging: true
Check logs for API errors (e.g., InvalidRequestError).
Validate API Keys: Test with a minimal payload first:
$client->chat(['model' => 'claude-3-haiku', 'messages' => []]);
Rate Limits: Anthropic enforces rate limits (e.g., 3000 tokens/minute). Implement exponential backoff:
use Symfony\Contracts\HttpClient\Exception\RateLimited;
try {
$response = $client->chat([...]);
} catch (RateLimited $e) {
sleep($e->getRetryAfter());
retry();
}
Custom Clients:
Extend AnthropicClient for provider-specific logic:
class CustomAnthropicClient extends AnthropicClient {
protected function getDefaultModel(): string {
return 'claude-3-opus'; // Override default
}
}
Model Routing:
Implement ModelRouterInterface to dynamically select models:
class ModelRouter implements ModelRouterInterface {
public function route(array $payload): string {
return $payload['complexity'] > 0.8 ? 'claude-3-opus' : 'claude-3-haiku';
}
}
Prompt Templates:
Use Symfony’s Twig for dynamic prompts:
$template = $this->twig->createTemplate('Hello {{ name }}!');
$prompt = $template->render(['name' => 'User']);
Caching Strategies: Cache by prompt hash:
$cacheKey = md5(serialize($payload));
$cache->get($cacheKey, fn() => $client->chat($payload));
Base URI: Override the default Anthropic endpoint:
services:
Symfony\AI\Client\AnthropicClient:
arguments:
$baseUri: 'https://custom.anthropic.com/v1'
Timeouts: Adjust HTTP client timeouts:
http_client:
timeout_control:
dividers:
connect: 30
dns: 10
request: 60
Environment Variables:
Use env() helper for dynamic config:
$apiKey = env
How can I help you explore Laravel packages today?