besfortmehmeti/tmbp-php-ai

AI abstraction layer for legacy PHP systems

Maintainers

Package info

github.com/besfortmehmeti/tmbp-php-ai

pkg:composer/besfortmehmeti/tmbp-php-ai

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-04-30 14:47 UTC

This package is auto-updated.

Last update: 2026-04-30 14:49:52 UTC


README

AI abstraction layer for legacy PHP systems.

This library provides a small, provider-agnostic API for:

  • text generation
  • multi-turn chat
  • streaming responses
  • switching between providers without changing calling code

Supported providers:

  • OpenAI
  • Anthropic
  • Gemini
  • Z.ai

It targets PHP >= 7.4 and uses PSR HTTP interfaces internally. The default runtime transport is cURL-based, so the PHP cURL extension should be available if you use the built-in transport.

Installation

composer require besfortmehmeti/tmbp-php-ai

Quick Start

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Tmbp\Ai\TmbpAi;

$ai = TmbpAi::createManager([
    'default_provider' => 'openai',
    'providers' => [
        'openai' => [
            'api_key' => getenv('OPENAI_API_KEY'),
            'default_model' => 'gpt-4o-mini',
        ],
    ],
]);

$text = $ai->text()
    ->prompt('Explain quantum computing in one sentence.')
    ->asText();

echo $text . PHP_EOL;

If you use the included bootstrap file:

require_once __DIR__ . '/bootstrap.php';

$ai = TmbpAi::createManager();

bootstrap.php also defines the global alias TmbpAi for legacy codebases that prefer it.

Configuration

The default config file is config/ai.php.

Example:

<?php

return [
    'default_provider' => 'openai',
    'providers' => [
        'openai' => [
            'api_key' => $_ENV['OPENAI_API_KEY'] ?? getenv('OPENAI_API_KEY'),
            'default_model' => 'gpt-4o-mini',
        ],
        'anthropic' => [
            'api_key' => $_ENV['ANTHROPIC_API_KEY'] ?? getenv('ANTHROPIC_API_KEY'),
            'default_model' => 'claude-sonnet-4-5',
        ],
        'gemini' => [
            'api_key' => $_ENV['GEMINI_API_KEY'] ?? getenv('GEMINI_API_KEY'),
            'default_model' => 'gemini-2.0-flash',
        ],
        'zai' => [
            'api_key' => $_ENV['ZAI_API_KEY'] ?? getenv('ZAI_API_KEY'),
            'default_model' => 'glm-4-flash',
        ],
    ],
];

Important behavior:

  • A provider is registered only if its api_key is present.
  • default_provider is used only when you do not call ->provider(...).
  • If you call ->provider('zai'), OpenAI is not involved.
  • If you call ->provider('zai') and zai has no api_key, the request throws a configuration error.

Usage

Plain text

$text = $ai->text()
    ->provider('openai', 'gpt-4o-mini')
    ->systemPrompt('You are a helpful assistant. Be concise.')
    ->prompt('What is the capital of France?')
    ->asText();

Full response metadata

$response = $ai->text()
    ->provider('openai', 'gpt-4o-mini')
    ->prompt('What is the capital of France?')
    ->asResponse();

echo $response->text();
echo $response->model();
echo $response->finishReason();
echo $response->usage()->totalTokens();

Multi-turn chat

$answer = $ai->chat()
    ->provider('anthropic', 'claude-haiku-4-5-20251001')
    ->systemPrompt('You are a helpful assistant. Be concise.')
    ->messages([
        ['role' => 'user', 'content' => 'My name is Alex.'],
        ['role' => 'assistant', 'content' => 'Hello Alex, nice to meet you!'],
    ])
    ->prompt('What is my name?')
    ->asText();

Streaming

foreach (
    $ai->text()
        ->provider('openai', 'gpt-4o-mini')
        ->prompt('Count from 1 to 5, one number per line.')
        ->asStream() as $chunk
) {
    if ($chunk->isDone()) {
        $final = $chunk->finalResponse();
        echo PHP_EOL . 'Total tokens: ' . $final->usage()->totalTokens() . PHP_EOL;
    } else {
        echo $chunk->text();
        flush();
    }
}

Builder API

Shared builder methods:

  • provider(string $name, ?string $model = null)
  • systemPrompt(string $prompt)
  • temperature(float $value)
  • maxTokens(int $tokens)
  • options(array $options)
  • asText()
  • asResponse()
  • asStream()

Text builder:

  • prompt(string $text)

Chat builder:

  • messages(array $messages)
  • prompt(string $text)

Provider Selection

There are two ways to choose a provider:

Explicit:

$ai->text()->provider('gemini', 'gemini-2.0-flash')->prompt('Hello')->asText();

Implicit via default_provider:

$ai->text()->prompt('Hello')->asText();

Use explicit selection when:

  • different flows use different providers
  • migrations are in progress
  • some environments only expose a subset of providers

Extension Points

TmbpAi::createManager() accepts optional dependencies:

public static function createManager(
    ?array $config = null,
    ?HttpTransport $transport = null,
    ?ProviderRegistryFactory $registryFactory = null
): Manager

This allows you to:

  • swap the default cURL transport for your own PSR-18 / PSR-17 stack
  • register custom providers
  • change provider construction rules in one place

Custom transport

use GuzzleHttp\Psr7\HttpFactory;
use Tmbp\Ai\Http\CurlSseStreamer;
use Tmbp\Ai\Http\HttpTransport;
use Tmbp\Ai\TmbpAi;

$factory = new HttpFactory();
$transport = new HttpTransport(
    $myPsr18Client,
    $factory,
    $factory,
    new CurlSseStreamer()
);

$ai = TmbpAi::createManager($config, $transport);

Custom provider registration

The default registry factory registers the built-in providers through provider factories. You can supply your own ProviderRegistryFactory to add or replace providers cleanly at the composition root.

Errors

Common configuration errors:

  • Default provider 'openai' is not registered... This happens when no explicit provider was selected and the configured default provider has no usable api_key.
  • Provider 'zai' is not registered... This happens when you explicitly select a provider that was not registered because its config is incomplete.

Provider API and network failures throw:

  • Tmbp\Ai\Exceptions\ProviderException
  • Tmbp\Ai\Exceptions\StreamException
  • Tmbp\Ai\Exceptions\ConfigurationException

Examples

See the runnable examples in:

Development

Run tests:

vendor/bin/pest

Run code style checks:

vendor/bin/php-cs-fixer fix --dry-run --diff