knivey / amphp-openai
v1.0.1
2026-06-06 00:56 UTC
Requires
- php: ^8.3
- amphp/byte-stream: ^2.0
- amphp/http-client: ^5.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-06-06 01:07:06 UTC
README
An async PHP wrapper around the OpenAI Chat Completions API, built on amphp v3. Designed as a reusable library for any PHP project that needs OpenAI integration with full API coverage.
Requirements
- PHP 8.3+
- amphp/http-client ^5.0
Installation
composer require knivey/amphp-openai
Quick Start
use Knivey\OpenAi\OpenAiClient; use Knivey\OpenAi\Request\ChatRequest; use Knivey\OpenAi\Request\Message; $client = new OpenAiClient('sk-...'); $response = $client->chatCompletion(new ChatRequest( model: 'gpt-4o', messages: [ Message::system('You are a helpful assistant.'), Message::user('Hello!'), ], )); echo $response->choices[0]->message->content; echo $response->usage->totalTokens . " tokens used\n";
Streaming
$stream = $client->chatCompletionStream(new ChatRequest( model: 'gpt-4o', messages: [Message::user('Tell me a story')], streamOptions: new StreamingOptions(includeUsage: true), )); foreach ($stream as $chunk) { echo $chunk->choices[0]->delta['content'] ?? ''; }
Multimodal (Images, Audio, Files)
use Knivey\OpenAi\Request\Content\TextPart; use Knivey\OpenAi\Request\Content\ImagePart; use Knivey\OpenAi\Request\Content\AudioPart; use Knivey\OpenAi\Request\Content\FilePart; $response = $client->chatCompletion(new ChatRequest( model: 'gpt-4o', messages: [ Message::user([ new TextPart('What is in this image?'), // Pass a URL — OpenAI fetches it server-side, no download on your end ImagePart::url('https://example.com/photo.jpg', 'high'), // Or pass raw base64 bytes directly — wrapped into a data URI for you ImagePart::base64($base64Data, 'image/png'), ]), ], ));
Tool Calling
use Knivey\OpenAi\Request\Tool\FunctionTool; use Knivey\OpenAi\Request\Tool\CustomTool; $tools = [ new FunctionTool( 'get_weather', description: 'Get the current weather', parameters: [ 'type' => 'object', 'properties' => [ 'location' => ['type' => 'string'], ], 'required' => ['location'], ], strict: true, ), new CustomTool('my_custom_tool', description: 'A custom tool'), ]; $response = $client->chatCompletion(new ChatRequest( model: 'gpt-4o', messages: [Message::user('What is the weather in SF?')], tools: $tools, toolChoice: 'auto', )); $toolCall = $response->choices[0]->message->toolCalls[0]; echo $toolCall->function['name']; // get_weather echo $toolCall->function['arguments']; // {"location":"San Francisco"}
Audio Output
use Knivey\OpenAi\Request\Audio\AudioOutputOptions; $response = $client->chatCompletion(new ChatRequest( model: 'gpt-4o-audio-preview', messages: [Message::user('Say hello in a friendly voice')], modalities: ['text', 'audio'], audio: new AudioOutputOptions('alloy', 'mp3'), )); $audio = $response->choices[0]->message->audio; echo $audio['transcript'];
Custom Base URL
Works with any OpenAI-compatible API (Ollama, Together, Groq, etc.):
$client = new OpenAiClient( apiKey: 'your-key', baseUrl: 'https://api.groq.com/openai/v1', );
Error Handling
use Knivey\OpenAi\Exception\RateLimitException; use Knivey\OpenAi\Exception\AuthenticationException; use Knivey\OpenAi\Exception\ApiException; try { $response = $client->chatCompletion($request); } catch (RateLimitException $e) { // 429 — automatically retried up to 3 times with Retry-After header } catch (AuthenticationException $e) { // 401/403 } catch (ApiException $e) { // All other HTTP errors echo $e->getStatusCode(); echo $e->getResponseBody(); }
Cancellation
use Amp\Cancellation; $response = $client->chatCompletion($request, cancellation: $cancellation);
All Request Parameters
ChatRequest supports the full Chat Completions API surface:
| Parameter | Type | Description |
|---|---|---|
model |
string |
Model ID (required) |
messages |
list<Message> |
Conversation messages (required) |
temperature |
?float |
Sampling temperature (0-2) |
topP |
?float |
Nucleus sampling threshold |
maxTokens |
?int |
Max tokens to generate |
maxCompletionTokens |
?int |
Upper bound including reasoning |
n |
?int |
Number of choices |
stop |
string|list<string>|null |
Stop sequences |
stream |
?bool |
Enable streaming (set automatically) |
streamOptions |
?StreamingOptions |
Stream options (include_usage, etc.) |
tools |
list<ToolDefinition>|null |
Available tools |
toolChoice |
string|array|null |
Tool selection mode |
parallelToolCalls |
?bool |
Allow parallel tool calls |
responseFormat |
?array |
Structured output format |
logprobs |
?bool |
Return log probabilities |
topLogprobs |
?int |
Number of top logprobs (0-20) |
logitBias |
array<string, int>|null |
Token likelihood modification |
seed |
?int |
Deterministic sampling seed |
frequencyPenalty |
?float |
Frequency penalty (-2.0 to 2.0) |
presencePenalty |
?float |
Presence penalty (-2.0 to 2.0) |
modalities |
list<string>|null |
Output modalities (text, audio) |
audio |
?AudioOutputOptions |
Audio output configuration |
store |
?bool |
Store completion for retrieval |
metadata |
?array |
Developer metadata |
serviceTier |
?string |
Processing tier |
user |
?string |
End-user identifier |
prediction |
?array |
Predicted output for latency |
reasoningEffort |
?string |
Reasoning effort (low/medium/high) |
webSearch |
?array |
Web search tool config |
moderation |
?array |
Moderation settings |
Development
composer test # Run tests composer stan # PHPStan level 9 composer cs:check # PSR-12 check composer cs:fix # PSR-12 fix composer check # Run all checks
License
MIT