Installation
composer require agence-gw/openai-php-symfony
Add the bundle to config/bundles.php:
return [
// ...
AgenceGw\OpenAI\OpenAIBundle::class => ['all' => true],
];
Configuration Publish the default config:
php bin/console config:dump-reference AgenceGw\OpenAI
Update config/packages/agence_gw_openai.yaml with your OpenAI API key:
agence_gw_openai:
api_key: '%env(OPENAI_API_KEY)%'
base_uri: 'https://api.openai.com/v1'
First Use Case: Chat Completion Inject the client in a Symfony service/controller:
use AgenceGw\OpenAI\Client\OpenAIClientInterface;
class ChatController
{
public function __construct(private OpenAIClientInterface $openAI)
{
}
public function askQuestion(Request $request)
{
$response = $this->openAI->chat()->create([
'model' => 'gpt-3.5-turbo',
'messages' => [
['role' => 'user', 'content' => $request->get('question')],
],
]);
return new JsonResponse($response->getChoices()[0]->getMessage()->getContent());
}
}
Chat Interactions
$chat = $this->openAI->chat();
$response = $chat->create([
'model' => 'gpt-4',
'messages' => [
['role' => 'system', 'content' => 'You are a helpful assistant.'],
['role' => 'user', 'content' => 'Explain Laravel middleware.'],
],
'temperature' => 0.3,
]);
Embeddings for Semantic Search
$embeddings = $this->openAI->embeddings()->create([
'model' => 'text-embedding-ada-002',
'input' => ['"Laravel is a PHP framework"', '"Symfony is a PHP framework"'],
]);
Image Generation
$image = $this->openAI->images()->create([
'prompt' => 'A futuristic cityscape at sunset',
'n' => 1,
'size' => '1024x1024',
]);
OpenAIClientInterface over concrete implementations for testability.RateLimiter to handle OpenAI’s API limits gracefully.$this->messageBus->dispatch(new GenerateEmbeddingsMessage($texts));
$cache = $this->cache->get('faq_response_' . $question);
if (!$cache) {
$cache = $this->openAI->chat()->create([...]);
$this->cache->set('faq_response_' . $question, $cache, 3600);
}
API Key Exposure
OPENAI_API_KEY in config. Use Symfony’s %env() or a secrets manager.config/packages/agence_gw_openai.yaml:
api_key: '%env(OPENAI_API_KEY)%'
validate_key: true # Defaults to false; enable for early error detection.
Model Versioning
gpt-3.5-turbo → gpt-3.5-turbo-1106). Pin versions in your code:
$response = $this->openAI->chat()->create(['model' => 'gpt-3.5-turbo-1106', ...]);
Token Limits
gpt-3.5-turbo). Use tiktoken (PHP port) to validate:
if ($tokenCount > 4000) {
throw new \RuntimeException('Message too long. Truncate or split.');
}
Error Handling
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getThrowable();
if ($exception instanceof \GuzzleHttp\Exception\RequestException &&
$exception->getCode() === 429) {
$event->setResponse(new Response('Rate limit exceeded. Retry later.', 429));
}
}
agence_gw_openai:
http_client:
middleware: ['debug'] # Adds Guzzle's debug middleware
json_last_error() to debug malformed JSON from OpenAI:
$response = $this->openAI->chat()->create([...]);
$data = json_decode($response->getBody(), true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException('Invalid OpenAI response: ' . json_last_error_msg());
}
Custom Models Extend the base client to support non-OpenAI models (e.g., Anthropic):
class CustomOpenAIClient extends AbstractOpenAIClient
{
public function anthropic(): AnthropicClientInterface
{
return new AnthropicClient($this->httpClient, $this->config);
}
}
Prompts Library Create a service to manage reusable prompts (e.g., for documentation generation):
class PromptManager
{
public function getDocumentationPrompt(string $topic): array
{
return [
['role' => 'system', 'content' => 'You are a technical writer.'],
['role' => 'user', 'content' => "Write a 2-paragraph doc on {$topic}."],
];
}
}
Webhook Integration
Use Symfony’s HttpClient to listen for OpenAI webhooks (e.g., file creation events):
$client = $this->httpClient->stream(
'https://api.openai.com/v1/events',
['headers' => ['Authorization' => 'Bearer ...']]
);
How can I help you explore Laravel packages today?