abydahana/aksara-ai

Framework-agnostic AI provider wrapper for Aksara CMS editorial workflows.

Maintainers

Package info

github.com/abydahana/aksara-ai

pkg:composer/abydahana/aksara-ai

Transparency log

Statistics

Installs: 2

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-08-13 19:53 UTC

This package is auto-updated.

Last update: 2026-08-13 19:58:18 UTC


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.