symfony/ai-open-responses-platform
Symfony AI Platform integration for Open Responses. Use the Open Responses specification and OpenAI Responses API contract to build and run responses consistently within Symfony, with links to docs, spec, source, and contribution resources.
Install the Package
composer require symfony/ai-open-responses-platform
Ensure symfony/ai-platform (^0.9) is also installed.
Configure the Client
Add the Open Responses client to your Symfony AI configuration (e.g., config/packages/ai.yaml):
framework:
ai:
clients:
open_responses:
client: Symfony\Component\AI\OpenResponses\OpenResponsesClient
options:
api_key: '%env(OPEN_RESPONSES_API_KEY)%'
endpoint: 'https://api.openresponses.org/v1'
First Use Case: Basic Chat Completion Inject the client into a service and use it for a simple chat request:
use Symfony\Component\AI\OpenResponses\OpenResponsesClient;
class ChatService {
public function __construct(private OpenResponsesClient $client) {}
public function askQuestion(string $question): string {
$response = $this->client->chat([
'model' => 'openresponses/gpt-4',
'messages' => [
['role' => 'user', 'content' => $question],
],
]);
return $response->getContent();
}
}
Key Classes to Explore
OpenResponsesClient: Main client for API interactions.OpenResponsesModel: Model abstraction for routing.DeltaInterface: For streaming responses (e.g., reasoning content).Use the Provider abstraction to route requests to different models dynamically:
// Define a custom provider (e.g., for multi-model support)
$provider = new class implements OpenResponsesModelProviderInterface {
public function getModel(string $modelName): OpenResponsesModel {
return new OpenResponsesModel($modelName, 'openresponses');
}
};
// Register the provider in Symfony AI config:
framework:
ai:
clients:
open_responses:
provider: your.namespace.OpenResponsesModelProvider
Leverage DeltaInterface for real-time streaming (e.g., for chatbots or reasoning):
use Symfony\Component\AI\OpenResponses\StreamingResponse;
public function streamResponse(string $prompt): StreamingResponse {
return $this->client->chatStream([
'model' => 'openresponses/gpt-4',
'messages' => [['role' => 'user', 'content' => $prompt]],
'stream' => true,
]);
}
// In a controller:
$stream = $this->chatService->streamResponse('Explain Laravel');
foreach ($stream as $delta) {
echo $delta->getContent(); // Process chunks incrementally
}
Use tool calls for multi-step workflows (e.g., function calling):
$response = $this->client->chat([
'model' => 'openresponses/gpt-4-tools',
'messages' => [
['role' => 'user', 'content' => 'Book a flight to Paris'],
],
'tools' => [
[
'type' => 'function',
'function' => [
'name' => 'book_flight',
'description' => 'Book a flight',
'parameters' => ['type' => 'object', 'properties' => [...]],
],
],
],
]);
Combine with other Symfony AI components (e.g., Symfony\Component\AI\Client\ClientInterface):
use Symfony\Component\AI\Client\ClientInterface;
class HybridAIService {
public function __construct(
private ClientInterface $aiClient,
private OpenResponsesClient $openResponsesClient
) {}
public function hybridResponse(string $query): string {
// Use Open Responses for complex queries
if (str_contains($query, 'explain')) {
return $this->openResponsesClient->chat([...])->getContent();
}
// Fallback to other providers
return $this->aiClient->chat([...])->getContent();
}
}
Environment Variables
Store API keys and endpoints in .env:
OPEN_RESPONSES_API_KEY=your_api_key_here
OPEN_RESPONSES_ENDPOINT=https://api.openresponses.org/v1
Dependency Injection Tag services for AI-related tasks to auto-wire dependencies:
#[Tag('ai.client')]
class OpenResponsesClientService {
// ...
}
Error Handling Wrap client calls in try-catch blocks to handle API errors gracefully:
try {
$response = $this->client->chat([...]);
} catch (Symfony\Component\AI\Exception\AiException $e) {
// Log or retry logic
throw new \RuntimeException('AI service unavailable', 0, $e);
}
Testing Use mock clients for unit tests:
use Symfony\Component\AI\OpenResponses\MockOpenResponsesClient;
$mockClient = new MockOpenResponsesClient();
$mockClient->expects('chat')->andReturn(new OpenResponsesResponse('Mocked reply'));
Payload Validation
InvalidArgumentException if a string payload is passed to ModelClient (fixed in v0.6.0). Always ensure payloads are arrays:
// ❌ Wrong
$this->client->chat('string_payload');
// ✅ Correct
$this->client->chat(['model' => '...', 'messages' => [...]]);
Tool Call Serialization
$tools = [
'type' => 'function',
'function' => [
'name' => 'valid_name',
'parameters' => ['type' => 'object', 'properties' => [...]],
],
];
Streaming Quirks
stream: true in the request and proper handling of DeltaInterface chunks. Avoid mixing typed/untyped chunks (deprecated in v0.7.0):
// ❌ Avoid (untyped chunks)
foreach ($stream as $chunk) {
echo $chunk; // May break
}
// ✅ Correct
foreach ($stream as $delta) {
echo $delta->getContent();
}
Model Routing Conflicts
$this->client->getModelProvider()->getModel('custom-model');
Enable API Logging
Configure HTTP client logging in config/packages/http_client.yaml:
framework:
http_client:
logging: true
Validate API Responses
Use var_dump() or dd() to inspect raw responses:
$response = $this->client->chat([...]);
dd($response->toArray()); // Debug full response
Check for Deprecations
DeltaInterface in v0.7.0. Update code if using older patterns.Custom Model Providers
Extend OpenResponsesModelProviderInterface to support:
Middleware for Requests Add middleware to modify requests/responses globally:
use Symfony\Component\AI\OpenResponses\OpenResponsesClientInterface;
class OpenResponsesMiddleware implements OpenResponsesClientInterface {
public function __construct(private OpenResponsesClientInterface $client) {}
public function chat(array $payload) {
$payload['custom_header'] = 'value'; // Modify payload
$response = $this->client->chat($payload);
$response->setCustomData('processed'); // Modify response
return $response;
}
}
Event Listeners
Listen to AI events (e.g., AiEvent::CHAT) to log or transform responses:
use Symfony\Component\AI\Event\AiEvent;
$dispatcher->addListener(AiEvent::CHAT, function (AiEvent $event) {
if ($event->getClient() instanceof OpenResponsesClient) {
// Custom logic for Open Responses
}
});
Fallback Mechanisms Implement fallback logic for failed requests:
public function safeChat(array $payload): string {
try {
return $this->client->chat($payload)->getContent();
} catch (AiException
How can I help you explore Laravel packages today?