DEV Community

Cover image for Stop juggling AI SDKs in PHP — meet Prisma
Aimeos
Aimeos

Posted on

Stop juggling AI SDKs in PHP — meet Prisma

If you've ever integrated an AI provider into a PHP application, you know the drill. You pull in the OpenAI SDK. Then the client wants image generation, so you add StabilityAI — different SDK, different request format, different error handling. Then they want audio transcription — enter Deepgram, yet another integration. Before you know it, you're maintaining three separate API integrations, each with their own quirks, response formats, and retry logic.

Prisma fixes this. One package. One API. 25+ providers. Text, image, audio, and video.

composer require aimeos/prisma
Enter fullscreen mode Exit fullscreen mode

What is Prisma?

Prisma is a light-weight, MIT-licensed PHP package that provides a unified interface for working with AI providers across four media domains. No framework coupling. Just Guzzle under the hood, PHP 8.2+, and a clean fluent API.

Here's the core idea — every provider works through the same interface:

use Aimeos\Prisma\Prisma;

// Generate text with OpenAI
$text = Prisma::text()
    ->using('openai', ['api_key' => '...'])
    ->write('Summarize the benefits of renewable energy')
    ->text();

// Generate an image with StabilityAI
$image = Prisma::image()
    ->using('stabilityai', ['api_key' => '...'])
    ->imagine('a mountain landscape at sunset')
    ->binary();

// Transcribe audio with Deepgram
$transcript = Prisma::audio()
    ->using('deepgram', ['api_key' => '...'])
    ->transcribe($audioFile)
    ->text();

// Describe a video with Gemini
$description = Prisma::video()
    ->using('gemini', ['api_key' => '...'])
    ->describe($videoFile)
    ->text();
Enter fullscreen mode Exit fullscreen mode

Same pattern everywhere: Prisma::{domain}()->using(provider, config)->{method}(). Switch providers by changing a single string. Your application code stays the same.

Why this matters in practice

Imagine you're building a product listing tool. You need to:

  1. Generate product descriptions from images
  2. Extract structured data (name, price, category) from the descriptions
  3. Generate marketing images for missing

Without Prisma, that's three different SDK integrations. With Prisma:

use Aimeos\Prisma\Prisma;
use Aimeos\Prisma\Schema\Schema;

// 1. Describe a product image
$description = Prisma::image()
    ->using('openai', ['api_key' => '...'])
    ->describe($productPhoto)
    ->text();

// 2. Extract structured data
$schema = Schema::for('product', [
    'name' => Schema::string()->required(),
    'price' => Schema::number(),
    'category' => Schema::string()->enum(['electronics', 'clothing', 'food', 'other']),
]);

$product = Prisma::text()
    ->using('gemini', ['api_key' => '...'])
    ->structure("Extract product info from: {$description}", $schema)
    ->structured();
// ['name' => 'Wireless Headphones', 'price' => 79.99, 'category' => 'electronics']

// 3. Generate a product image
$image = Prisma::image()
    ->using('stabilityai', ['api_key' => '...'])
    ->imagine("Professional illustration of {$product['name']}")
    ->binary();
Enter fullscreen mode Exit fullscreen mode

Three providers, three media types, one consistent API. And if tomorrow you want to swap Gemini for Anthropic, you change one line.

You can also check provider capabilities at runtime:

$provider = Prisma::image()->using('clipdrop', ['api_key' => '...']);

if ($provider->has('upscale')) {
    $image = $provider->upscale($lowResImage)->binary();
}

// Or throw if the capability is missing
$provider->ensure('upscale'); // throws NotImplementedException if not supported
Enter fullscreen mode Exit fullscreen mode

What's new in 0.4

Version 0.4 is a major release that adds text generation, structured output, tool calling, and a lot more. Here's what landed.

Text generation across 14 providers

The write() method brings text generation to Prisma with support for OpenAI, Anthropic, Gemini, Bedrock, Mistral, Groq, Cohere, Deepseek, Alibaba, xAI, Perplexity, OpenRouter, Ollama, and Deepseek. All multimodal — pass images, audio, or documents alongside your prompt:

use Aimeos\Prisma\Base\File;

$response = Prisma::text()
    ->using('anthropic', ['api_key' => '...'])
    ->withSystemPrompt('You are a helpful assistant')
    ->write('Describe this image', [File::from('photo.jpg')]);

echo $response->text();
Enter fullscreen mode Exit fullscreen mode

Structured output

Get typed JSON responses from LLMs using a fluent Schema builder. Prisma uses each provider's native structured output API — no prompt engineering hacks:

use Aimeos\Prisma\Schema\Schema;

$schema = Schema::for('event', [
    'title' => Schema::string()->required(),
    'date' => Schema::string()->format('date')->required(),
    'attendees' => Schema::array()->items(Schema::string()),
    'location' => Schema::object([
        'city' => Schema::string()->required(),
        'country' => Schema::string(),
    ]),
]);

$response = Prisma::text()
    ->using('openai', ['api_key' => '...'])
    ->structure('Parse: Team meetup on June 15 in Berlin with Alice and Bob', $schema);

$event = $response->structured();
// [
//     'title' => 'Team meetup',
//     'date' => '2026-06-15',
//     'attendees' => ['Alice', 'Bob'],
//     'location' => ['city' => 'Berlin', 'country' => 'Germany'],
// ]
Enter fullscreen mode Exit fullscreen mode

The Schema builder supports strings, integers, numbers, booleans, arrays, objects, enums (including PHP BackedEnum), nested objects, required(), nullable(), min()/max(), regex pattern(), and more. You can also pass raw JSON Schema arrays via Schema::fromArray().

Tool calling with auto-execution

This is where things get interesting. Prisma supports full agentic tool calling with an automatic execution loop. You define tools, the model calls them, Prisma executes the handlers and feeds results back — all automatically:

use Aimeos\Prisma\Tools;
use Aimeos\Prisma\Schema\Schema;

$searchTool = Tools::make(
    'search_products',
    'Search the product database',
    Schema::for('search', [
        'query' => Schema::string()->description('Search query')->required(),
        'limit' => Schema::integer()->description('Max results')->min(1)->max(50),
    ]),
    function(array $args): string {
        $results = ProductSearch::query($args['query'], $args['limit'] ?? 10);
        return json_encode($results);
    }
);

$response = Prisma::text()
    ->using('openai', ['api_key' => '...'])
    ->withTools([$searchTool])
    ->withMaxSteps(5)
    ->write('Find me the top 3 wireless headphones under $100');

echo $response->text();
// The model searched, got results, and wrote a summary
Enter fullscreen mode Exit fullscreen mode

You can inspect what happened during the tool loop:

foreach ($response->steps() as $step) {
    echo $step->name();      // 'search_products'
    echo $step->arguments(); // ['query' => 'wireless headphones', 'limit' => 3]
    echo $step->result();    // '[ ... search results ... ]'
}
Enter fullscreen mode Exit fullscreen mode

Tools also support:

  • Per-tool call limits: Tools::make(...)->max(3) — the tool can only be called 3 times
  • Custom error handlers: ->failed(fn($e, $args) => 'Fallback message') — catch exceptions gracefully
  • Concurrent execution: ->concurrent() — tools run in parallel via pcntl_fork
  • Decorators: wrap tools with logging, caching, or any custom behavior
  • Tool choice: ->withToolChoice(Provider::REQ) to force tool use

Provider tools

Some providers offer built-in server-side tools like web search and code execution. Prisma normalizes these too:

use Aimeos\Prisma\Tools;

$response = Prisma::text()
    ->using('anthropic', ['api_key' => '...'])
    ->withTools([
        Tools::provider('web_search'),
        Tools::provider('code_execution'),
    ])
    ->write('Search for the latest PHP version and verify it');
Enter fullscreen mode Exit fullscreen mode

Available provider tools include web_search, code_execution, file_search, web_fetch, image_generation, and document_library — each mapped to the providers that support them. You can mix custom and provider tools in the same request.

Framework tool adapters

Already have tools built for Laravel AI or Symfony? Reuse them directly:

// Laravel AI / Prism tools
$tool = Tools::laravel(new MyLaravelTool());

// Symfony #[AsTool] classes
$tool = Tools::symfony(MySymfonyTool::class);
Enter fullscreen mode Exit fullscreen mode

Thinking budgets

Enable extended reasoning for models that support it. Prisma automatically maps token counts to each provider's native format:

$response = Prisma::text()
    ->using('anthropic', ['api_key' => '...'])
    ->withThinkingBudget(5000)
    ->withMaxTokens(4096)
    ->write('Solve this step by step: ...');

// Access the model's reasoning
$thinking = $response->meta()['thinking'] ?? null;
Enter fullscreen mode Exit fullscreen mode

Citations

Normalized citation objects across providers that support them (Anthropic, OpenAI, Gemini, Perplexity, xAI):

foreach ($response->citations() as $citation) {
    $citation->title();  // source title
    $citation->url();    // source URL
    $citation->text();   // output text that cites this source
    $citation->source(); // verbatim quote from the source
}
Enter fullscreen mode Exit fullscreen mode

Rate limits and retry handling

// Automatic retry with exponential backoff
$response = Prisma::text()
    ->using('openai', ['api_key' => '...'])
    ->withClientRetry(3, fn($attempt, $resp) => 100 * pow(2, $attempt))
    ->write('...');

// Inspect rate limit headers
$limit = $response->rateLimit();
$limit->remaining();  // requests left
$limit->retryAfter(); // seconds until reset
Enter fullscreen mode Exit fullscreen mode

Provider coverage

Here's what's supported for text providers in 0.4:

Provider structure translate write citations custom tools provider tools thinking budget
Alibaba yes yes - yes yes -
Anthropic yes yes yes yes yes yes
Bedrock yes yes - yes yes
Cohere yes yes - yes -
Deepseek yes yes - yes -
DeepL yes
Gemini yes yes yes yes yes yes
Google yes
Groq yes yes - yes -
Mistral yes yes - yes yes -
Ollama beta beta - yes -
OpenAI yes yes yes yes yes yes
Openrouter yes yes - yes yes -
Perplexity beta beta yes yes -
xAI beta beta yes yes yes yes

And that's just text. Prisma also covers 9 audio providers (speak, transcribe, demix, denoise, revoice, describe), 14 image providers (imagine, inpaint, upscale, vectorize, background, erase, and more), and video description via Gemini.

Getting started

composer require aimeos/prisma
Enter fullscreen mode Exit fullscreen mode

Minimal example:

<?php

use Aimeos\Prisma\Prisma;

// Text
echo Prisma::text()
    ->using('openai', ['api_key' => getenv('OPENAI_API_KEY')])
    ->write('What makes PHP great?')
    ->text();

// Image
$binary = Prisma::image()
    ->using('openai', ['api_key' => getenv('OPENAI_API_KEY')])
    ->imagine('a PHP elephant wearing sunglasses')
    ->binary();

file_put_contents('elephant.png', $binary);
Enter fullscreen mode Exit fullscreen mode

No service providers, no config files, no framework dependencies. Just require and use.


Prisma is MIT-licensed and open source. Check it out:

If you've been looking for a unified way to work with AI providers in PHP — especially beyond just text — give it a try. Feedback, issues, and stars are all welcome.

In case you like Prisma, leave a star in the Github repo :-)

Top comments (0)