symfony/ai-chat
Symfony AI Chat is a lightweight package for building chat-style AI features in Symfony apps. It provides simple abstractions to connect to LLM providers, manage messages and context, and integrate conversational workflows with clean, framework-friendly APIs.
Install the Package
composer require symfony/ai-chat
Register the Service Provider
Add to config/app.php under providers:
Symfony\Component\AI\Chat\ChatServiceProvider::class,
Configure AI Provider Publish the config and set your LLM provider (e.g., OpenAI):
php artisan vendor:publish --provider="Symfony\Component\AI\Chat\ChatServiceProvider" --tag="config"
Update .env:
AI_CHAT_PROVIDER=openai
OPENAI_API_KEY=your_key_here
First Chat Endpoint Create a controller:
use Symfony\Component\AI\Chat\Chat;
use Symfony\Component\AI\Chat\Message;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ChatController extends Controller
{
public function handle(Request $request): Response
{
$chat = new Chat();
$chat->addMessage(new Message($request->get('user_message'), 'user'));
$response = $chat->getCompletion(); // Triggers AI call
$chat->addMessage(new Message($response->getContent(), 'assistant'));
return response()->json(['message' => $response->getContent()]);
}
}
Route the Endpoint
Route::post('/chat', [ChatController::class, 'handle']);
Test with cURL
curl -X POST http://your-app.test/chat \
-H "Content-Type: application/json" \
-d '{"user_message": "What is Laravel?"}'
Define agents with roles and tools (e.g., a "SupportAgent" with a knowledge base tool):
use Symfony\Component\AI\Chat\Agent\Agent;
use Symfony\Component\AI\Chat\Agent\Tool\ToolInterface;
class SupportAgent extends Agent
{
public function __construct(private ToolInterface $knowledgeBase)
{
$this->tools = [$this->knowledgeBase];
}
public function getRole(): string
{
return 'support';
}
}
Register the agent in Laravel’s container:
$this->app->bind(SupportAgent::class, function ($app) {
return new SupportAgent($app->make(KnowledgeBaseTool::class));
});
Create custom tools (e.g., database queries or API calls):
use Symfony\Component\AI\Chat\Agent\Tool\ToolInterface;
class DatabaseTool implements ToolInterface
{
public function execute(string $query): string
{
return DB::select($query)->first()->answer;
}
}
Bind the tool to Laravel’s container:
$this->app->bind(ToolInterface::class, function ($app) {
return new DatabaseTool();
});
Extend chat processing with middleware (e.g., logging, auth):
use Symfony\Component\AI\Chat\Middleware\MiddlewareInterface;
class LogMiddleware implements MiddlewareInterface
{
public function handle(Chat $chat, callable $next): Chat
{
Log::info('Chat started', ['messages' => $chat->getMessages()]);
return $next($chat);
}
}
Add to the Chat instance:
$chat->addMiddleware(new LogMiddleware());
Persist chat history using Laravel’s cache or database:
use Symfony\Component\AI\Chat\Memory\MemoryInterface;
class RedisMemory implements MemoryInterface
{
public function save(string $sessionId, array $messages): void
{
Cache::put("chat_{$sessionId}", $messages);
}
public function load(string $sessionId): array
{
return Cache::get("chat_{$sessionId}", []);
}
}
Bind to the Chat instance:
$chat->setMemory(new RedisMemory());
Listen to chat events (e.g., ChatStarted, MessageSent):
use Symfony\Component\AI\Chat\Event\ChatEvent;
use Symfony\Component\AI\Chat\Event\ChatEvents;
Event::listen(ChatEvents::CHAT_STARTED, function (ChatEvent $event) {
// Example: Log chat initiation
Log::channel('chat')->info('Chat started', ['user_id' => auth()->id()]);
});
Offload AI calls to Laravel queues:
use Symfony\Component\AI\Chat\Chat;
use Illuminate\Support\Facades\Bus;
Bus::dispatch(new ProcessChat($chat));
Job implementation:
class ProcessChat implements ShouldQueue
{
public function handle(Chat $chat)
{
$response = $chat->getCompletion();
$chat->addMessage(new Message($response->getContent(), 'assistant'));
}
}
Render chat UIs with Laravel Blade:
// resources/views/chat.blade.php
@foreach($messages as $message)
<div class="message {{ $message->getRole() }}">
{{ $message->getContent() }}
</div>
@endforeach
Controller:
public function show()
{
$chat = new Chat();
$chat->setMemory(new RedisMemory());
$messages = $chat->getMessages();
return view('chat', compact('messages'));
}
Mock AI responses for unit tests:
use Symfony\Component\AI\Chat\Chat;
use Symfony\Component\AI\Chat\Message;
public function testChatResponse()
{
$chat = new Chat();
$chat->addMessage(new Message('Test', 'user'));
// Mock the completion
$mockResponse = $this->partialMock(ChatCompletion::class, ['getContent']);
$mockResponse->method('getContent')->willReturn('Mocked response');
$chat->setCompletion($mockResponse);
$this->assertEquals('Mocked response', $chat->getLastMessage()->getContent());
}
DependencyInjection may clash with Laravel’s container.SymfonyBridge or manually bind services:
$this->app->bind(
Symfony\Component\AI\Chat\Chat::class,
function ($app) {
return new Chat();
}
);
'queue' => [
'default' => 'sync',
'connections' => [
'redis' => [
'driver' => 'redis',
'queue' => 'default',
'retry_after' => 90, // Retry after 90 seconds
],
],
],
Cache::put("chat_{$sessionId}", $messages, now()->addHours(1));
timeout helper or Symfony’s HttpClient options:
$httpClient = HttpClient::create(['timeout' => 30]);
class SanitizeInputMiddleware implements MiddlewareInterface
{
public function handle(Chat $chat, callable $next): Chat
{
$sanitizedMessages = array_map(function ($message) {
return new Message(htmlspecialchars($message->getContent()), $message->getRole());
}, $chat->getMessages());
$chat->setMessages($sanitizedMessages);
return $next($chat);
}
}
Add debug logging to middleware:
class DebugMiddleware implements MiddlewareInterface
{
public function handle(Chat $chat, callable $next):
How can I help you explore Laravel packages today?