A PHP server integration package for AG-UI - standardized AI agent frontend communication via Server-Sent Events and other transport methods.
AG-UI provides a real-time, event-driven protocol for streaming AI agent responses, tool calls, and state updates to frontends. This package makes it easy to integrate AG-UI into your PHP AI projects.
- Complete AG-UI Event Support - All event types: messages, tool calls, lifecycle, state management
- Flexible Message API - Simple one-shot messages or streaming with closures/iterables
- Pluggable Transporters - SSE included, easily extend with WebSocket, polling, etc.
- Adaptive Delta Buffering - Optimized streaming performance
- PSR-14 Compatible - Interoperable with existing event systems
- Type Safe - PHPStan types for better developer experience
- Framework Agnostic - Works with Laravel, Symfony, or any PHP application
composer require swis/ag-ui-server<?php
use Swis\AgUiServer\AgUiState;
use Swis\AgUiServer\Transporter\SseTransporter;
// Initialize state
$state = new AgUiState();
// Start a conversation run
$threadId = 'thread_' . uniqid();
$runId = 'run_' . uniqid();
$state->startRun($threadId, $runId);
// Simple message
$state->addMessage('Hello! How can I help you?');
// Streaming message from LLM
$state->addMessage(function() {
return $this->llm->streamCompletion($prompt); // Returns iterable
});
// Tool call
$state->addToolCall('web_search', '{"query": "weather today"}');
// Finish the run
$state->finishRun();The package supports all AG-UI event types:
- Lifecycle:
RunStarted,RunFinished,RunError,StepStarted,StepFinished - Messages:
TextMessageStart,TextMessageContent,TextMessageEnd - Reasoning:
ReasoningStart,TextReasoningMessageStart,TextReasoningMessageContent,TextReasoningMessageEnd,TextReasoningEnd - Tool Calls:
ToolCallStart,ToolCallArgs,ToolCallEnd - State:
StateSnapshot,StateDelta,MessagesSnapshot - Special:
Raw,Custom
// String content - sent as single message
$state->addMessage('Complete response here');
// Closure returning string
$state->addMessage(function() {
return $this->llm->complete($prompt);
});
// Closure returning iterable - streamed as deltas
$state->addMessage(function() {
return $this->llm->streamCompletion($prompt);
};
// Direct iterable
$state->addMessage(['Hello ', 'world', '!']);
// Manual control for complex scenarios
$messageId = $state->startMessage();
foreach ($complexStream as $chunk) {
$state->addMessageContent($messageId, $chunk);
}
$state->finishMessage($messageId);Easily swap transport methods:
// Server-Sent Events (default)
$transporter = new SseTransporter();
$transporter->initialize(); // Sends headers
// Custom headers
$transporter = new SseTransporter([
'Access-Control-Allow-Origin' => 'https://yourapp.com',
'Cache-Control' => 'no-cache',
]);
$transporter->initialize(); // Sends headers
// Future: WebSocket support
// $transporter = new WebSocketTransporter($connection);public function handleChatRequest(Request $request)
{
$userMessage = $request->input('message');
$threadId = $request->input('threadId') ?? 'thread_' . uniqid();
$runId = 'run_' . uniqid();
$state = new AgUiState();
$state->startRun($threadId, $runId);
try {
// Step 1: Analyze query
$state->startStep('analyzing_query');
// ... analysis logic ...
$state->finishStep();
// Step 2: Retrieve context
$state->startStep('retrieving_context');
$state->addToolCall(null, 'vector_search', json_encode([
'query' => $userMessage,
'top_k' => 5
]));
$documents = $this->vectorSearch($userMessage);
$state->finishStep();
// Step 3: Generate response
$state->startStep('generating_response');
$state->addMessage(null, function() use ($userMessage, $documents) {
return $this->llm->streamWithContext($userMessage, $documents);
}, 'assistant');
$state->finishStep();
$state->finishRun();
} catch (\Exception $e) {
$state->errorRun($e->getMessage());
}
}While AgUiState provides a convenient high-level API for typical AI workflows, you can also trigger events manually for complete control over your application's behavior:
use Swis\AgUiServer\Events\TextMessageStartEvent;
use Swis\AgUiServer\Events\TextMessageContentEvent;
use Swis\AgUiServer\Events\TextMessageEndEvent;
use Swis\AgUiServer\Transporter\SseTransporter;
use Psr\EventDispatcher\EventDispatcherInterface;
// Set up event dispatcher and transporter
$dispatcher = $container->get(EventDispatcherInterface::class);
$transporter = new SseTransporter();
$transporter->initialize(); // Sends headers
$transporter->setEventDispatcher($dispatcher);
// Now trigger events from your application
$messageId = 'msg_' . uniqid();
// The transporter will automatically listen for these events and send them
$dispatcher->dispatch(new TextMessageStartEvent(
messageId: $messageId,
role: 'assistant'
));
$dispatcher->dispatch(new TextMessageContentEvent(
messageId: $messageId,
content: 'Hello from my custom application!'
));
$dispatcher->dispatch(new TextMessageEndEvent(
messageId: $messageId
));Direct Event Dispatching (Without PSR-14)
If you prefer not to use PSR-14, you can send events directly:
$transporter = new SseTransporter();
$transporter->initialize();
// Send events directly
$event = new TextMessageStartEvent(
messageId: 'msg_123',
role: 'assistant'
);
$transporter->send($event);
$transporter->close();When to Use Manual Events vs AgUiState
- Use
AgUiStatefor typical AI agent workflows with automatic state management, message streaming, and tool calls - Use manual events when you need:
- Complete control over event timing and data
- Integration with existing event-driven architectures
- Custom event flows that don't fit the standard AI agent pattern
- Building your own higher-level abstractions
Delta buffering is optional and can be enabled to optimize streaming performance:
// Without delta buffering (default)
$state = new AgUiState();
// With delta buffering for optimized streaming
$state = (new AgUiState())->withDeltaBuffering(
deltaBufferThreshold: 150, // chars
deltaFlushInterval: 0.3 // seconds
);All AG-UI events implement PSR-14 interfaces:
use Psr\EventDispatcher\EventDispatcherInterface;
// Your existing PSR-14 dispatcher
$dispatcher = $container->get(EventDispatcherInterface::class);
// Listen to AG-UI events
$dispatcher->addListener(TextMessageStartEvent::class, function($event) {
// Log message start
$this->logger->info('Message started', ['messageId' => $event->messageId]);
});Implement your own transport:
class WebSocketTransporter implements TransporterInterface
{
public function __construct(private $connection) {}
public function initialize(): void
{
// Setup WebSocket connection
}
public function send(AgUiEvent $event): void
{
$this->connection->send($event->toJson());
}
public function sendComment(string $comment): void
{
// WebSocket doesn't need comments
}
public function close(): void
{
$this->connection->close();
}
}TextMessageStart- Begin streaming a messageTextMessageContent- Content chunk (delta)TextMessageEnd- Message complete
ReasoningStart- Begin reasoning state (optional)ReasoningMessageStart- Begin streaming a reasoning messageReasoningMessageContent- Content chunk (delta)ReasoningMessageEnd- Reasoning message completeReasoningStart- End reasoning state (optional)
ToolCallStart- Begin tool executionToolCallArgs- Tool arguments (can be streamed)ToolCallEnd- Tool execution completeToolCallResult- Results of a Tool execution
RunStarted- Agent run beginsRunFinished- Agent run completes successfullyRunError- Agent run failedStepStarted- Processing step beginsStepFinished- Processing step completes
StateSnapshot- Complete stateStateDelta- State changes (JSON Patch)MessagesSnapshot- All conversation messages
Please see CHANGELOG for more information on what has changed recently.
Please review our security policy on how to report security vulnerabilities.
This package is open-sourced software licensed under the MIT license.
This package is Treeware. If you use it in production, then we ask that you buy the world a tree to thank us for our work. By contributing to the Treeware forest you’ll be creating employment for local families and restoring wildlife habitats.
SWIS is a web agency from Leiden, the Netherlands. We love working with open source software.