abydahana / aksara-ai
Framework-agnostic AI provider wrapper for Aksara CMS editorial workflows.
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.5 || ^8.0
- psr/http-client: ^1.0
- psr/http-message: ^1.0 || ^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.14
- phpstan/phpdoc-parser: ^1.16
README
Aksara AI is a framework-agnostic PHP library that provides a unified interface for working with multiple AI providers. It is designed for Aksara CMS editorial workflows, but it can also be used in any PHP application, CLI tool, queue worker, API service, or framework.
Features
- Unified interface for text completion and chat.
- Built-in providers: OpenAI, OpenAI-compatible/custom endpoints, Anthropic Claude, Google Gemini, DeepSeek, and OpenRouter.
- Normalized text responses through
AksaraAI\ValueObjects\AIResponse. - Global configuration with per-provider overrides.
- Custom PSR/Guzzle HTTP client support for testing or custom transport.
- OpenAI image generation support.
Requirements
- PHP
^8.2 - Composer
guzzlehttp/guzzle^7.5 || ^8.0- An API key for the AI provider you want to use
Installation
Install the package with Composer:
composer require abydahana/aksara-ai
For local development from this repository:
composer install
The package uses PSR-4 autoloading:
use AksaraAI\AIManager;
Version Identification
The package version is defined in composer.json:
"version": "1.0.0"
The version is also available at runtime:
echo AIManager::VERSION; // 1.0.0
Supported Providers
| Provider | Configuration name | Notes |
|---|---|---|
| OpenAI | openai |
Uses the Responses API for text and the Images API for images. |
| OpenAI-compatible | openai_compatible, custom |
For endpoints compatible with the Chat Completions API. |
| Anthropic Claude | anthropic, claude |
Uses the Messages API. |
| Google Gemini | gemini, google |
Uses generateContent. |
| DeepSeek | deepseek |
OpenAI-compatible preset for DeepSeek. |
| OpenRouter | openrouter |
OpenAI-compatible preset with OpenRouter attribution headers. |
Basic Configuration
use AksaraAI\AIManager; $ai = new AIManager([ 'provider' => 'openai', 'api_key' => getenv('OPENAI_API_KEY'), 'model' => 'gpt-5.6', 'temperature' => 0.7, 'max_tokens' => 2048, 'timeout' => 70, 'connect_timeout' => 10, ]);
Common configuration keys:
| Key | Type | Description |
|---|---|---|
provider |
string |
Default provider. Falls back to openai when omitted. |
api_key |
string |
Provider API key. Required for provider requests. |
model |
string |
Model name. Each provider has its own default when omitted. |
base_url |
string |
Overrides the provider API endpoint. Useful for custom/OpenAI-compatible providers. |
temperature |
float |
Sampling temperature. Default: 0.7. |
max_tokens |
int |
Maximum output tokens. Default: 2048. |
timeout |
int |
HTTP request timeout in seconds. Default: 70. |
connect_timeout |
int |
HTTP connection timeout in seconds. Default: 10. |
Text Completion
use AksaraAI\AIManager; $ai = new AIManager([ 'provider' => 'openai', 'api_key' => getenv('OPENAI_API_KEY'), ]); $response = $ai->text()->complete('Write a short summary about Aksara CMS.'); if ($response->ok) { echo $response->content; } else { echo $response->message; }
With a system prompt and request options:
$response = $ai->text()->complete('Create an article title about data security.', [ 'system' => 'You are a senior English-language editor.', 'temperature' => 0.4, 'max_tokens' => 300, ]);
Note: request options are stored in AIRequest, but options such as temperature, max_tokens, model, and base_url are currently read from the provider configuration. Pass overrides when resolving the provider:
$response = $ai->text('openai', [ 'model' => 'gpt-5.6', 'temperature' => 0.4, 'max_tokens' => 300, ])->complete('Write a meta description for a product page.');
Chat
$response = $ai->text('anthropic', [ 'api_key' => getenv('ANTHROPIC_API_KEY'), 'model' => 'claude-sonnet-5', ])->chat([ [ 'role' => 'system', 'content' => 'Answer briefly and practically.', ], [ 'role' => 'user', 'content' => 'What are the benefits of an AI-powered editorial workflow?', ], ]); echo $response->content;
Generic message format:
[
['role' => 'system', 'content' => 'System instruction'],
['role' => 'user', 'content' => 'User message'],
['role' => 'assistant', 'content' => 'Previous assistant response'],
]
Each provider translates this generic message format into its native API format.
Provider Examples
OpenAI
$ai = new AIManager([ 'provider' => 'openai', 'api_key' => getenv('OPENAI_API_KEY'), 'model' => 'gpt-5.6', ]); $response = $ai->text()->complete('Create an SEO article outline.');
Default endpoint: https://api.openai.com/v1/responses
OpenAI-Compatible or Custom Endpoint
$ai = new AIManager([ 'provider' => 'custom', 'api_key' => getenv('CUSTOM_AI_API_KEY'), 'base_url' => 'https://example.com/v1', 'model' => 'custom-chat-model', ]); $response = $ai->text()->complete('Write a short promotional caption.');
Called endpoint: {base_url}/chat/completions
Anthropic Claude
$ai = new AIManager([ 'provider' => 'anthropic', 'api_key' => getenv('ANTHROPIC_API_KEY'), 'model' => 'claude-sonnet-5', ]); $response = $ai->text()->complete('Improve the following paragraph...');
Optional: use the version key to set the anthropic-version header. Default: 2023-06-01.
Gemini
$ai = new AIManager([ 'provider' => 'gemini', 'api_key' => getenv('GEMINI_API_KEY'), 'model' => 'gemini-3.6-flash', ]); $response = $ai->text()->complete('Generate content ideas for this week.');
DeepSeek
$ai = new AIManager([ 'provider' => 'deepseek', 'api_key' => getenv('DEEPSEEK_API_KEY'), ]); $response = $ai->text()->complete('Help refactor this product description.');
Default model: deepseek-chat
OpenRouter
$ai = new AIManager([ 'provider' => 'openrouter', 'api_key' => getenv('OPENROUTER_API_KEY'), 'model' => 'openai/gpt-4o-mini', 'referer' => 'https://aksaracms.com', 'title' => 'Aksara CMS', ]); $response = $ai->text()->complete('Summarize this news article.');
Image Generation
Image generation is currently available through OpenAIProvider.
$ai = new AIManager([ 'provider' => 'openai', 'api_key' => getenv('OPENAI_API_KEY'), 'image_model' => 'gpt-image-2', ]); $provider = $ai->image('openai'); if (method_exists($provider, 'generateImage')) { $image = $provider->generateImage('A modern editorial dashboard illustration.', [ 'size' => '1024x768', ]); if (200 === $image['status']) { echo '<img src="' . htmlspecialchars($image['image'], ENT_QUOTES, 'UTF-8') . '">'; } }
Image responses use a plain array:
[
'status' => 200,
'message' => 'OK',
'image' => 'data:image/png;base64,...',
'raw' => [],
]
Text Response Structure
All text requests return an AksaraAI\ValueObjects\AIResponse instance:
$response->ok; // bool $response->status; // int $response->message; // string $response->content; // string $response->usage; // ?array $response->raw; // ?array
Convert a response to an array:
$array = $response->toArray();
Array structure:
[
'status' => 200,
'message' => 'OK',
'content' => '...',
'usage' => [],
'raw' => [],
]
Error Handling
Providers do not throw exceptions for provider HTTP errors. They return an AIResponse with:
$response->ok === false; $response->status; // HTTP-like status $response->message; // provider error message or fallback message
Exceptions may still be thrown when required configuration is missing, such as an empty api_key:
try { $response = $ai->text()->complete('Hello'); } catch (InvalidArgumentException $e) { echo $e->getMessage(); }
Custom HTTP Client
AIManager accepts a GuzzleHttp\ClientInterface instance so the HTTP transport can be customized or mocked in tests.
use AksaraAI\AIManager; use GuzzleHttp\Client; $http = new Client([ 'timeout' => 30, 'http_errors' => false, ]); $ai = new AIManager([ 'provider' => 'openai', 'api_key' => getenv('OPENAI_API_KEY'), ], $http);
Creating a New Provider
Text providers must implement AksaraAI\Contracts\ProviderInterface:
use AksaraAI\Contracts\ProviderInterface; use AksaraAI\ValueObjects\AIRequest; use AksaraAI\ValueObjects\AIResponse; final class MyProvider implements ProviderInterface { public function complete(AIRequest $request): AIResponse { return new AIResponse(true, 200, 'OK', 'Generated content'); } }
For HTTP providers, extend AksaraAI\Providers\AbstractProvider to reuse helpers such as postJson(), response(), required(), baseUrl(), model(), maxTokens(), and temperature().
Development
Install dependencies:
composer install
Run PHP CS Fixer:
composer cs-fix
Refresh Composer autoload files:
composer dump-autoload
License
Aksara AI is released under the MIT license. See LICENSE for details.