A PHP library for handling the Vercel AI SDK Stream Protocol. This package provides an easy-to-use interface for creating streaming AI responses with support for tool calls, attachments, and various AI providers.
- 🚀 Easy Integration: Simple, fluent API for streaming responses
- 🔧 Tool Support: Built-in tool calling and execution
- 📎 Attachments: Handle file and image attachments
- 🔄 Multi-Provider: Support for OpenAI and other providers
- 📊 Protocol Compliant: Follows Vercel AI SDK Stream Protocol specifications
- ⚡ Symfony Integration: Built with Symfony components
composer require premieroctet/php-stream-protocol
use PremierOctet\PhpStreamProtocol\StreamProtocol;
use OpenAI;
// Create a new StreamProtocol instance
$protocol = StreamProtocol::create()
->withSystemPrompt('You are a helpful assistant.');
// Register tools
$protocol->registerTool('get_weather', [WeatherService::class, 'getCurrentWeather']);
// In your controller
public function chat(Request $request): Response
{
// Parse incoming messages
$messages = $protocol->parseMessages($request->getContent());
// Convert to OpenAI format and create request
$openaiRequest = $protocol->buildOpenAIRequest($messages, 'gpt-4');
// Create OpenAI stream
$client = OpenAI::client($apiKey);
$stream = $client->chat()->createStreamed($openaiRequest);
// Return streaming response
return $protocol->stream($stream);
}
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use PremierOctet\PhpStreamProtocol\StreamProtocol;
use App\Tools\WeatherTool;
use OpenAI;
class ChatController extends AbstractController
{
private StreamProtocol $streamProtocol;
public function __construct()
{
$this->streamProtocol = StreamProtocol::create()
->withSystemPrompt('You are a demo assistant showcasing the integration of Vercel AI SDK with a Symfony controller.')
->registerTool(new WeatherTool());
}
#[Route('/api/chat', name: 'api_chat', methods: ['POST'])]
public function chat(Request $request): Response
{
return $this->streamProtocol->handleRequest(
$request->getContent(),
function(array $openaiMessages) {
$client = OpenAI::client($_ENV['OPENAI_API_KEY']);
return $client->chat()->createStreamed([
'model' => 'gpt-4',
'messages' => $openaiMessages,
'stream' => true,
'tools' => [WeatherTool::getToolDefinition()],
]);
},
);
}
#[Route('/chat', name: 'chat')]
public function chatPage(): Response
{
return $this->render('chat.html.twig');
}
}
use PremierOctet\PhpStreamProtocol\StreamProtocol;
class ChatController
{
public function chat(Request $request): Response
{
$protocol = StreamProtocol::create()
->withSystemPrompt('You are a helpful assistant with access to various tools.')
->registerTool('search_web', [$this, 'searchWeb'])
->registerTool('get_weather', [$this, 'getWeather'])
->registerTool('send_email', [$this, 'sendEmail']);
return $protocol->handleRequest(
$request->getContent(),
function($messages) use ($request) {
$client = OpenAI::client(env('OPENAI_API_KEY'));
return $client->chat()->createStreamed([
'model' => 'gpt-4',
'messages' => $messages,
'stream' => true,
'tools' => $protocol->getToolDefinitions(),
]);
}
);
}
private function searchWeb(string $query): array
{
// Your web search implementation
return ['results' => "Search results for: {$query}"];
}
private function getWeather(string $location): array
{
// Your weather service implementation
return ['weather' => "Weather in {$location}: Sunny, 25°C"];
}
}
// Convert messages to different AI provider formats
$messages = $protocol->parseMessages($jsonData);
// For OpenAI
$openaiMessages = $protocol->convertToOpenAI($messages);
public function demo(): Response
{
$protocol = StreamProtocol::create();
return $protocol->streamText(
'This is a demo of streaming text word by word.',
50 // delay in milliseconds
);
}
The library handles messages in the Vercel AI SDK format, supporting:
- Text messages: Simple text content
- Tool calls: Function calling with arguments and results
- Attachments: File and image attachments
- Message parts: Complex message structures
Example message structure:
{
"messages": [
{
"role": "user",
"content": "What's the weather like?",
"experimental_attachments": [
{
"contentType": "image/jpeg",
"url": "data:image/jpeg;base64,..."
}
]
},
{
"role": "assistant",
"content": "",
"toolInvocations": [
{
"toolCallId": "call_123",
"toolName": "get_weather",
"args": { "location": "New York" },
"result": { "temperature": "25°C", "condition": "sunny" }
}
]
}
]
}
The library implements the Vercel AI SDK Stream Protocol with the following message types:
0:
- Text content9:
- Tool calla:
- Tool resultb:
- Tool call streaming startc:
- Tool call deltad:
- Finish messagee:
- Finish stepf:
- Message start
Tools must follow this interface:
class WeatherTool implements ToolInterface
{
public function getName(): string
{
return 'get_current_weather';
}
public function getDescription(): string
{
return 'Get current weather for a location';
}
public function execute(array $parameters): mixed
{
return [
'location' => $parameters['location'],
'temperature' => '25°C',
'condition' => 'sunny'
];
}
public function getParameters(): array
{
return [
'type' => 'object',
'properties' => [
'location' => [
'type' => 'string',
'description' => 'The city and state, e.g. San Francisco, CA'
]
],
'required' => ['location']
];
}
public function isStrict(): bool
{
return true;
}
}
- PHP 8.1 or higher
- Symfony HttpFoundation component
MIT License - see LICENSE file for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request