PHPackages                             aimeos/prisma - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. aimeos/prisma

ActiveLibrary[API Development](/categories/api)

aimeos/prisma
=============

A powerful PHP package for integrating media related Large Language Models (LLMs) into your applications

0.5.0(4d ago)1943.1k↑1672.2%[1 issues](https://github.com/aimeos/prisma/issues)2MITPHPPHP ^8.2CI failing

Since Nov 13Pushed 4d ago1 watchersCompare

[ Source](https://github.com/aimeos/prisma)[ Packagist](https://packagist.org/packages/aimeos/prisma)[ RSS](/packages/aimeos-prisma/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (9)Dependencies (30)Versions (86)Used By (2)

PHP Prisma
==========

[](#php-prisma)

Light-weight PHP package for integrating multi-media and text related Large Language Models (LLMs) into your applications using a unified interface.

[Supported providers](#supported-providers)

- [Audio](#audio)
- [Image](#image)
- [Text](#text)
- [Video](#video)

[API usage](#api-usage)

- [ensure](#ensure): Ensures that the provider has implemented the method
- [has](#has): Tests if the provider has implemented the method
- [model](#model): Use the model passed by its name
- [withClientOptions](#withclientoptions): Add options for the Guzzle HTTP client
- [withClientRetry](#withclientretry): Configure automatic retry for failed HTTP requests
- [withMaxResponseSize](#withmaxresponsesize): Set the maximum bytes read for a single provider response
- [withSystemPrompt](#withsystemprompt): Add a system prompt for the LLM
- [withMessages](#withmessages): Add prior conversation turns for multi-turn chat
- [withMaxTokens](#withmaxtokens): Set the maximum number of output tokens
- [withThinkingBudget](#withthinkingbudget): Set the thinking/reasoning budget in tokens
- [Response objects](#response-objects): How data is returned by the API
- [Citations](#citations): Source references from provider responses
- [Finish reason](#finish-reason): Why generation stopped
- [Tool steps](#tool-steps): Inspect tool call history
- [Rate limit](#rate-limit): Rate limit information from providers

[Schemas](#schemas)

- [Building schemas](#building-schemas): Define tool parameters using the fluent Schema builder
- [From arrays](#from-arrays): Create schemas from JSON Schema arrays
- [Type reference](#type-reference): Available types and their methods

[Tools](#tools)

- [Creating tools](#creating-tools): Create tools using the Tools facade
- [Provider tools](#provider-tools): Built-in tools executed server-side
- [Tool state](#tool-state): Check a tool's remaining call budget
- [Error handling](#error-handling): Customize how tool errors are returned
- [Concurrent tools](#concurrent-tools): Run tools in parallel
- [Decorating tools](#decorating-tools): Wrap tools with additional behavior

[Audio API](#audio-api)

- [demix](#demix): Separate an audio file into its individual tracks
- [denoise](#denoise): Remove noise from an audio file
- [describe](#describe): Describe the content of an audio file
- [revoice](#revoice): Exchange the voice in an audio file
- [speak](#speak): Convert text to speech in an audio file
- [transcribe](#transcribe): Converts speech of an audio file to text

[Image API](#image-api)

- [background](#background): Replace background according to the prompt
- [describe](#describe): Describe the content of an image
- [detext](#detext): Remove all text from the image
- [erase](#erase): Erase parts of the image
- [imagine](#imagine): Generate an image from the prompt
- [inpaint](#inpaint): Edit an image area according to a prompt
- [isolate](#isolate): Remove the image background
- [recognize](#recognize): Recognize the text in an image (OCR)
- [relocate](#relocate): Place the foreground object on a new background
- [repaint](#repaint): Repaint an image according to the prompt
- [uncrop](#uncrop): Extend/outpaint the image
- [upscale](#upscale): Scale up the image
- [vectorize](#vectorize): Creates embedding vectors from images

[Text API](#text-api)

- [stream](#stream): Stream a response token by token by iterating the response
- [structure](#structure): Generate structured output from a prompt and schema
- [translate](#translate): Translate texts from one language to another
- [vectorize](#vectorize-1): Creates embedding vectors from texts
- [write](#write): Generate text from the given prompt

[Video API](#video-api)

- [describe](#describe): Describe the content of a video

[Custom providers](CUSTOM-PROVIDERS)

Supported providers
-------------------

[](#supported-providers)

- [Alibaba](https://www.alibabacloud.com/help/en/model-studio/model-api-reference/)
- [Anthropic](https://docs.anthropic.com/en/api)
- [AudioPod AI](https://audiopod.ai/)
- [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/)
- [Bedrock Titan (AWS)](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-models.html)
- [Black Forest Labs](https://docs.bfl.ai/quick_start/introduction)
- [Clipdrop](https://clipdrop.co/apis)
- [Cohere](https://docs.cohere.com/docs/the-cohere-platform)
- [DeepL](https://developers.deepl.com/docs)
- [Deepgram](https://deepgram.com/)
- [Deepseek](https://api-docs.deepseek.com/)
- [ElevenLabs](https://elevenlabs.io/docs/overview/intro)
- [Gemini (Google)](https://aistudio.google.com/models/gemini-2-5-flash-image)
- [Google Translate](https://cloud.google.com/translate/docs/reference/rest/v2/translate)
- [Groq](https://groq.com/)
- [Ideogram](https://ideogram.ai/api)
- [Mistral](https://docs.mistral.ai/api)
- [ModelsLab](https://docs.modelslab.com/)
- [Murf](https://murf.ai/api)
- [Ollama](https://ollama.com/)
- [OpenAI](https://openai.com/api/)
- [Openrouter](https://openrouter.ai/docs/quickstart)
- [Perplexity](https://docs.perplexity.ai/)
- [RemoveBG](https://www.remove.bg/api)
- [Replicate](https://replicate.com/docs)
- [StabilityAI](https://platform.stability.ai/)
- [VertexAI (Google)](https://cloud.google.com/vertex-ai/generative-ai/docs)
- [VoyageAI](https://docs.voyageai.com/)
- [xAI](https://docs.x.ai/)

### Audio

[](#audio)

demixdenoisedescriberevoicespeaktranscribe**Alibaba**----yes-**AudioPod**yesyes-yesyesyes**Deepgram**----yesyes**ElevenLabs**---yesyesyes**Gemini**--yes---**Groq**--yes-yesyes**Mistral**--yes--yes**Murf**---yesyes-**OpenAI**--yes-yesyes### Image

[](#image)

backgrounddescribedetexteraseimagineinpaintisolaterecognizerelocaterepaintuncropupscalevectorize**Alibaba**----yes-------yes**Bedrock Titan**----yesyesyes-----yes**Black Forest Labs**----betabeta----beta--**Clipdrop**yes-yesyesyes-yes---yesyes-**Cohere**------------yes**Gemini**-yes--yes----yes---**Groq**-yes-----------**Ideogram**betabeta--betabeta---beta-beta-**Mistral**-------yes-----**ModelsLab**----beta--------**OpenAI**-yes--yesyes-------**RemoveBG**------yes-yes----**Replicate**----beta--------**StabilityAI**---yesyesyesyes---yesyes-**VertexAI**----yesyes-----yesyes**VoyageAI**------------yes**xAI**----beta--------### Text

[](#text)

streamstructuretranslatevectorizewritecitationscustom toolsprovider toolssystem promptthinking budget**Alibaba**yesyesyesyes-yesyesyes-**Anthropic**yesyes-yesyesyesyesyesyes**Azure**betabetabetabeta-yesyes-**Bedrock**-yesyesyes-yesyesyes**Cohere**-yesyesyes-yesyes-**Deepseek**yesyes-yes-yesyes-**DeepL**yes**Gemini**yesyesyesyesyesyesyesyesyes**Google**yes**Groq**yesyes-yes-yesyes-**Mistral**yesyesyesyes-yesyesyes-**Ollama**betabetabetabeta-yesyes-**OpenAI**yesyesyesyesyesyesyesyesyes**Openrouter**yesyes-yes-yesyesyes-**Perplexity**betabeta-betayesyesyes-**Vertexai**betabetabetabetayesyesyesyesyes**xAI**betabeta-betayesyesyesyesyes### Video

[](#video)

describe**Gemini**yesInstallation
------------

[](#installation)

```
composer req aimeos/prisma

```

API usage
---------

[](#api-usage)

Basic usage:

```
use Aimeos\Prisma\Prisma;

$image = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->model( '' ) // if model can be selected
    ->ensure( 'imagine' ) // make sure interface is implemented
    ->imagine( 'a grumpy cat' )
    ->binary();

$texts = Prisma::text()
    ->using( 'deepl', ['api_key' => 'xxx'])
    ->ensure( 'translate' )
    ->translate( ['Hello'], 'de' )
    ->texts();
```

#### OpenAI-compatible gateways

[](#openai-compatible-gateways)

Any OpenAI-compatible endpoint (local servers, proxies or gateways like LiteLLM, vLLM or OpenRouter-style services) works with the `openai` provider by overriding the base `url`:

```
$text = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx', 'url' => 'https://my-gateway.example.com'] )
    ->model( 'my-model' )
    ->write( 'Hello' )
    ->text();
```

### ensure

[](#ensure)

Ensures that the provider has implemented the method.

```
public function ensure( string $method ) : self
```

- @param **string** `$method` Method name
- @return **Provider**
- @throws \\Aimeos\\Prisma\\Exceptions\\NotImplementedException

**Example:**

```
\Aimeos\Prisma\Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->ensure( 'imagine' );
```

### has

[](#has)

Tests if the provider has implemented the method.

```
public function has( string $method ) : bool
```

- @param **string** `$method` Method name
- @return **bool** TRUE if implemented, FALSE if absent

**Example:**

```
\Aimeos\Prisma\Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->has( 'imagine' );
```

### model

[](#model)

Use the model passed by its name.

Used if the provider supports more than one model and allows to select between the different models. Otherwise, it's ignored.

```
public function model( ?string $model ) : self
```

- @param **string|null** `$model` Model name
- @return **self** Provider interface

**Example:**

```
\Aimeos\Prisma\Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->model( 'dall-e-3' );
```

### withClientOptions

[](#withclientoptions)

Add options for the Guzzle HTTP client.

```
public function withClientOptions( array `$options` ) : self
```

- @param **array&lt;string, mixed&gt;** `$options` Associative list of name/value pairs
- @return **self** Provider interface

**Example:**

```
\Aimeos\Prisma\Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->withClientOptions( ['timeout' => 120] );
```

### withClientRetry

[](#withclientretry)

Configure automatic retry for failed HTTP requests.

```
public function withClientRetry( int `$maxAttempts` = 3, \Closure|int `$delayMs` = 100, ?\Closure `$when` = null ) : self
```

- @param **int** `$maxAttempts` Total number of attempts including the initial request
- @param **\\Closure|int** `$delayMs` Fixed delay in ms or closure: fn(int $attempt, ?ResponseInterface $response): int
- @param **\\Closure|null** `$when` Retry condition: fn(ResponseInterface $response, int $attempt): bool
- @return **self** Provider interface

By default, retries on status codes 429, 500, 502, 503, 504 and connection exceptions.

**Examples:**

```
// Fixed delay of 200ms between retries
\Aimeos\Prisma\Prisma::text()
    ->using( '', ['api_key' => 'xxx'])
    ->withClientRetry( 3, 200 );

// Exponential backoff
\Aimeos\Prisma\Prisma::text()
    ->using( '', ['api_key' => 'xxx'])
    ->withClientRetry( 3, fn( $attempt, $response ) => 100 * pow( 2, $attempt ) );

// Custom retry condition
\Aimeos\Prisma\Prisma::text()
    ->using( '', ['api_key' => 'xxx'])
    ->withClientRetry( 3, 100, fn( $response, $attempt ) => $response->getStatusCode() === 429 );
```

### withMaxResponseSize

[](#withmaxresponsesize)

Set the maximum number of bytes read for a single provider response.

Bounds the bytes consumed from one response - streamed or not - so a runaway or hostile endpoint cannot grow the read buffer or the assembled result (text, reasoning, tool-call arguments) without limit. Defaults to 64 MB and applies per request, so each tool-loop turn is bounded independently rather than spanning a whole multi-turn conversation.

```
public function withMaxResponseSize( int `$bytes` ) : self
```

- @param **int** `$bytes` Maximum bytes per response (minimum 1)
- @return **self** Provider interface

**Example:**

```
\Aimeos\Prisma\Prisma::text()
    ->using( '', ['api_key' => 'xxx'])
    ->withMaxResponseSize( 16 * 1024 * 1024 ); // cap responses at 16 MB
```

### withSystemPrompt

[](#withsystemprompt)

Add a system prompt for the LLM.

It may be used by providers supporting system prompts. Otherwise, it's ignored.

```
public function withSystemPrompt( ?string $prompt ) : self
```

- @param **string|null** `$prompt` System prompt
- @return **self** Provider interface

**Example:**

```
\Aimeos\Prisma\Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->withSystemPrompt( 'You are a professional illustrator' );
```

### withMessages

[](#withmessages)

Add prior conversation turns sent before the current prompt, so the model has context from earlier exchanges in a multi-turn chat.

Each entry is an array with a `role` of `user` or `assistant` and a string `content`. User turns may add a `files` key with an array of `File` objects for multimodal input, subject to the provider's file support (images for all text providers; PDFs additionally on Anthropic; images, audio, video and PDFs on Gemini). System context is set via [withSystemPrompt](#withsystemprompt), not as a message. The current prompt passed to `stream()`/`write()`/`structure()` is appended as the final user turn.

```
public function withMessages( array $messages ) : self
```

- @param **array** `$messages` Conversation turns (`['role' => 'user'|'assistant', 'content' => '…', 'files' => []]`)
- @return **self** Provider interface

**Example:**

```
\Aimeos\Prisma\Prisma::text()
    ->using( '', ['api_key' => 'xxx'] )
    ->withMessages( [
        ['role' => 'user', 'content' => 'Recommend a laptop'],
        ['role' => 'assistant', 'content' => 'Sure - what is your budget?'],
    ] )
    ->write( 'Around $1500' );
```

### withMaxTokens

[](#withmaxtokens)

Set the maximum number of output tokens for the response.

```
public function withMaxTokens( ?int $tokens ) : self
```

- @param **int|null** `$tokens` Maximum output tokens
- @return **self** Provider interface

**Example:**

```
\Aimeos\Prisma\Prisma::text()
    ->using( '', ['api_key' => 'xxx'] )
    ->withMaxTokens( 4096 )
    ->write( 'Tell me a story' );
```

### withThinkingBudget

[](#withthinkingbudget)

Set the thinking/reasoning budget in tokens for models that support extended thinking. The budget is mapped to each provider's native format automatically: token counts for Anthropic, OpenAI, Gemini and Bedrock; effort levels for other OpenAI-API providers (≤ 1024 → low, ≤ 8192 → medium, &gt; 8192 → high).

```
public function withThinkingBudget( ?int $budget ) : self
```

- @param **int|null** `$budget` Thinking budget in tokens
- @return **self** Provider interface

**Example:**

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

// Access the model's reasoning (if returned by the provider)
$thinking = $response->meta()->thinking();
```

### Response objects

[](#response-objects)

The methods return a *FileResponse*, *TextResponse* or *VectorResponse* object that contains the returned data with optional meta/usage/description information.

**FileResponse** objects:

```
$base64 = $response->base64(); // first base64 data, from binary, base64 and URL, waits for async requests
$file = $response->binary(); // first binary data, from binary, base64 and URL, waits for async requests
$url = $response->url(); // first URL, only if URLs are returned, otherwise NULL
$mime = $response->mimeType(); // image mime type, waits for async requests
$text = $response->description(); // image description if returned by provider
$bool = $response->ready(); // FALSE for async APIs until file is available
$file = $response->first(); // first available file object
$array = $response->files(); // all available file objects

// loop over all available files
foreach( $response as $name => $file ) {
    $file->binary()
}
```

URLs are automatically converted to binary and base64 data if requested and conversion between binary and base64 data is done on request too.

**TextResponse** objects:

```
$text = $response->text(); // first text content (non-streaming)
$text = $response->first(); // first available text
$texts = $response->texts(); // all texts (non-streaming)

// loop over all available texts
foreach( $response as $text ) {
    echo $text;
}
```

**VectorResponse** objects:

```
$vector = $response->first(); // first embedding vector if only one input has been passed
$vectors = $response->vectors(); // embedding vectors for the passed inputs in the same order

// loop over all available vectors
foreach( $response as $vector ) {
    print_r( $vector );
}
```

Included **meta data** (optional):

```
$meta = $response->meta();

$meta->id();               // provider response ID or NULL
$meta->model();            // model that produced the response or NULL
$meta->thinking();         // extended thinking/reasoning output or NULL
$meta->reasoningDetails(); // encrypted reasoning blocks for multi-turn continuity or NULL
```

`meta()` returns a `Values\Meta` object with typed accessors for the fields shared across providers. It also behaves like the array it replaced, so provider-specific keys stay reachable by subscript, iteration and `json_encode()`:

```
$created = $response->meta()['created'] ?? null; // raw provider key
$raw = $response->meta()->all();                 // complete provider map as array
```

Included **usage data** (optional):

```
$usage = $response->usage();

$usage->promptTokens();     // input tokens or NULL
$usage->completionTokens(); // generated output tokens or NULL
$usage->totalTokens();      // total tokens (falls back to prompt + completion) or NULL
$usage->cacheReadTokens();  // cached input tokens or NULL
$usage->cacheWriteTokens(); // tokens written to the provider cache or NULL
$usage->thoughtTokens();    // reasoning/thinking tokens or NULL
$usage->used();             // used units as float (tokens for text, credits/cost for media)
```

`usage()` returns a `Values\Usage` object whose typed accessors normalize the differing token keys each provider reports (e.g. `input_tokens`, `prompt_tokens`, `promptTokenCount`, `inputTokens` all map to `promptTokens()`). Accessors return `NULL` when a provider does not report that figure. Like `meta()`, it stays array-compatible for raw keys:

```
$used = $response->usage()['used'];  // raw key, still works
$raw = $response->usage()->all();    // complete provider map as array
```

### Citations

[](#citations)

TextResponse objects include citations when returned by providers that support them (Anthropic, Gemini, OpenAI, Perplexity, xAI). Each citation is a normalized array with four fields:

```
$response = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->write( 'What is the capital of France?' );

$citations = $response->citations(); // array of Citation objects

foreach( $citations as $citation ) {
    $citation->title();  // string|null — source title
    $citation->url();    // string|null — source URL
    $citation->text();   // string|null — output text that references the source
    $citation->source(); // string|null — verbatim quote from the source document
}
```

The `text` field contains the snippet from the model's **output** that cites the source (populated by OpenAI, xAI, Gemini). The `source` field contains a verbatim quote from the **input/source document** (populated by Anthropic). For Perplexity, only `url` is available.

Anthropic requires opting in via options:

```
$response = Prisma::text()
    ->using( 'anthropic', ['api_key' => 'xxx'] )
    ->write( 'Summarize this document', $files, ['citations' => true] );
```

### Finish reason

[](#finish-reason)

TextResponse objects include a finish reason indicating why the model stopped generating:

```
$response = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->withTools( [$tool] )
    ->withMaxSteps( 5 )
    ->write( 'What is the weather in Berlin?' );

$reason = $response->reason(); // 'stop', 'tool', 'length', 'content', 'error', or 'unknown'
```

ReasonMeaning`stop`The model finished normally (reached a natural end or stop sequence)`tool`The model stopped to request tool calls; returned when `withMaxSteps()` is exhausted mid-loop`length`Output was truncated because it hit the max token limit`content`Output was blocked or truncated by a safety/content filter`error`The provider returned an error during generation`unknown`The provider returned an unrecognized finish reason### Tool steps

[](#tool-steps)

After a tool-using request completes, inspect the full history of tool calls and their results via `steps()`:

```
$response = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->withTools( [$tool] )
    ->withMaxSteps( 5 )
    ->write( 'What is the weather in Berlin?' );

foreach( $response->steps() as $step ) {
    $step->id();        // tool call ID from the provider
    $step->name();      // tool name (e.g. 'weather')
    $step->arguments(); // arguments the model passed (e.g. ['city' => 'Berlin'])
    $step->result();    // result string returned to the model
}
```

### Rate limit

[](#rate-limit)

TextResponse and FileResponse objects can include rate limit information from the provider:

```
$rateLimit = $response->rateLimit(); // RateLimit object or null

$rateLimit->limit();      // int|null — request limit
$rateLimit->remaining();  // int|null — remaining requests
$rateLimit->reset();      // string|null — reset timestamp
$rateLimit->retryAfter(); // int|null — retry after seconds
```

Returns `null` if the provider does not return rate limit headers.

Schemas
-------

[](#schemas)

Schemas define the parameters that tools accept. They are used by `Tools::make()` to tell the LLM what arguments a tool expects.

### Building schemas

[](#building-schemas)

Use the fluent `Schema` builder to define tool parameters:

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

$schema = Schema::for( 'search', [
    'query' => Schema::string()->description( 'Search query' )->required(),
    'limit' => Schema::integer()->description( 'Max results' )->min( 1 )->max( 100 ),
] );
```

`Schema::for()` creates a named schema with an object type. The first argument is the schema name, the second is an associative array of property names to types.

**Nested objects:**

```
$schema = Schema::for( 'create_event', [
    'title' => Schema::string()->required(),
    'location' => Schema::object( [
        'city' => Schema::string()->required(),
        'country' => Schema::string(),
    ] )->required(),
] );
```

**Arrays:**

```
$schema = Schema::for( 'tag', [
    'tags' => Schema::array()->items( Schema::string() )->min( 1 )->max( 10 )->required(),
    'scores' => Schema::array()->items( Schema::number() ),
] );
```

**Enums:**

```
$schema = Schema::for( 'sort', [
    'order' => Schema::string()->enum( ['asc', 'desc'] )->required(),
] );

// Or from a BackedEnum:
$schema = Schema::for( 'sort', [
    'order' => Schema::string()->enum( SortOrder::class )->required(),
] );
```

**Strict mode and no additional properties** (for providers that support it, e.g. OpenAI):

```
$schema = Schema::for( 'search', [
    'query' => Schema::string()->required(),
] )->strict()->withoutAdditionalProperties();
```

**Union types** allow a value to match any of several types (JSON Schema `anyOf`):

```
$schema = Schema::for( 'result', [
    'value' => Schema::anyOf( [
        Schema::string(),
        Schema::object( [
            'code' => Schema::integer()->required(),
            'message' => Schema::string()->required(),
        ] ),
    ] )->description( 'Either a plain string or an error object' )->required(),
] );
```

`anyOf` is supported by OpenAI, Anthropic and Gemini (it is not supported at the root of an OpenAI schema). `oneOf` is not supported by any provider. Each branch is adapted to the target provider automatically (object branches are closed for OpenAI/Anthropic/Cohere and reduced to the OpenAPI subset for Gemini).

**Reusable definitions** let you declare a sub-schema once and reference it from multiple places (JSON Schema `$defs` and `$ref`). Register a definition with `def()` and point to it with `Schema::ref()`:

```
$schema = Schema::for( 'order', [
    'billing'  => Schema::ref( 'Address' )->required(),
    'shipping' => Schema::ref( 'Address' )->required(),
] )->def( 'Address', Schema::object( [
    'street' => Schema::string()->required(),
    'city'   => Schema::string()->required(),
] ) );
```

`Schema::ref( 'Address' )` resolves to the pointer `#/$defs/Address`; a value already starting with `#` is used verbatim. Definitions are adapted to the target provider just like inline schemas (closed for OpenAI/Anthropic/Cohere, reduced to the OpenAPI subset for Gemini). `$ref`/`$defs` are supported by OpenAI, Anthropic, Gemini and Cohere; for providers without native schema support (e.g. Bedrock) they are passed through in the prompt as-is.

### From arrays

[](#from-arrays)

If you already have a JSON Schema array, use `Schema::fromArray()`:

```
$schema = Schema::fromArray( 'search', [
    'type' => 'object',
    'properties' => [
        'query' => ['type' => 'string', 'description' => 'Search query'],
        'limit' => ['type' => 'integer'],
    ],
    'required' => ['query'],
] );
```

### Type reference

[](#type-reference)

All types support these common methods: `description()`, `required()`, `nullable()`, `title()`, `enum()`.

Factory methodTypeAdditional methods`Schema::string()`String`min()`, `max()`, `pattern()`, `format()`, `default()``Schema::integer()`Integer`min()`, `max()`, `multipleOf()`, `default()``Schema::number()`Number (float)`min()`, `max()`, `multipleOf()`, `default()``Schema::boolean()`Boolean`default()``Schema::array()`Array`items()`, `min()`, `max()`, `unique()`, `default()``Schema::object()`Object`withoutAdditionalProperties()`, `default()`, `def()``Schema::anyOf()`Union (`anyOf`)`add()`, `default()``Schema::ref()`Reference (`$ref`)—Tools
-----

[](#tools)

Tools enable LLMs to call functions during text generation. Prisma supports both custom tools (executed locally) and provider tools (executed server-side by the LLM provider).

### Creating tools

[](#creating-tools)

Create tools using the `Tools` facade:

**From scratch:**

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

$tool = Tools::make( 'search', 'Search the web', Schema::for( 'search', [
    'query' => Schema::string()->description( 'Search query' )->required(),
] ), fn( $args ) => file_get_contents( 'https://api.example.com/search?q=' . $args['query'] ) );
```

**From a Laravel AI / MCP tool:**

```
$tool = Tools::laravel( new MyLaravelTool() );
// or pass the fully qualified class name (resolved via the Laravel container):
$tool = Tools::laravel( MyLaravelTool::class );
```

The tool must extend `\Laravel\Mcp\Server\Tool` (MCP) or implement `\Laravel\Ai\Contracts\Tool` (AI). When a class name is given, the instance is resolved through the Laravel container (`app()`), so constructor dependencies are injected. MCP tools are executed via `handle()`; AI tools via `__invoke()` or `handle()`.

**From a Symfony #\[AsTool\] class:**

```
$tool = Tools::symfony( MySymfonyTool::class );
// or with a specific tool name when the class has multiple #[AsTool] attributes:
$tool = Tools::symfony( MySymfonyTool::class, 'tool-name' );
```

**Using tools with a provider:**

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

$tool = Tools::make( 'weather', 'Get current weather', Schema::for( 'weather', [
    'city' => Schema::string()->description( 'City name' )->required(),
] ), fn( $args ) => json_encode( ['temp' => '22°C', 'city' => $args['city']] ) );

$response = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->withTools( [$tool] )
    ->withMaxSteps( 5 )
    ->write( 'What is the weather in Berlin?' );
```

`withMaxSteps()` controls the maximum number of tool-loop steps performed (default is 25). Raise it for workflows that need more tool calls, or lower it to cap cost.

> **Note:** Tool handlers can return any value. Strings are passed through as-is; all other return types (arrays, objects, numbers) are automatically JSON-encoded.

**Tool choice:**

`withToolChoice()` controls whether the model must use tools:

ConstantDescription`Provider::AUTO`Model decides (default)`Provider::REQ`Must use a tool`Provider::NONE`No tools```
use Aimeos\Prisma\Providers\Base as Provider;

->withToolChoice( Provider::REQ )
```

**Limiting tool calls:**

```
$tool = Tools::make( ... )->max( 3 ); // This specific tool can only be called 3 times per request
```

### Provider tools

[](#provider-tools)

Provider tools are built-in tools executed server-side by the LLM provider (e.g., web search, code execution). They don't require local function handlers. Create them using `Tools::provider()`:

```
use Aimeos\Prisma\Prisma;
use Aimeos\Prisma\Tools;

$response = Prisma::text()
    ->using( 'anthropic', ['api_key' => 'xxx'] )
    ->withTools( [
        Tools::provider( 'web_search' ),
        Tools::provider( 'code_execution' ),
    ] )
    ->write( 'Search for the latest PHP version and write code to check it' );
```

**Available provider tools:**

Tool nameProviders`web_search`Anthropic, OpenAI, Gemini, Mistral, xAI, OpenRouter, Alibaba`web_search_premium`Mistral`code_execution`Anthropic, OpenAI, Gemini, Mistral, xAI`web_fetch`Anthropic`file_search`OpenAI`image_generation`Mistral`document_library`MistralProvider tool names not supported by the chosen provider are silently ignored. Providers without any provider tool support (e.g. Bedrock, Cohere, Deepseek, Perplexity) ignore all provider tools.

Custom and provider tools can be mixed in a single `withTools()` call:

```
$response = Prisma::text()
    ->using( 'anthropic', ['api_key' => 'xxx'] )
    ->withTools( [
        $customTool,
        Tools::provider( 'web_search' ),
        Tools::provider( 'code_execution' ),
    ] )
    ->withMaxSteps( 5 )
    ->write( 'Search and analyze' );
```

Pass provider-specific options using `with()`:

```
Tools::provider( 'web_search' )->with( [
    'allowed_domains' => ['example.com', 'docs.example.com'],
    'blocked_domains' => ['spam.com'],
] )
```

Unknown or unsupported options are silently ignored by each provider.

**Normalized options** (translated automatically per provider):

OptionDescriptionSupported by`allowed_domains`Only include results from these domainsAnthropic, OpenAI, OpenRouter`blocked_domains`Exclude results from these domainsAnthropic, xAI, OpenRouter`search_context_size`Search depth: `"low"`, `"medium"`, `"high"`OpenAI, xAI`user_location`User location object for localized resultsOpenAI, Anthropic**Provider-specific options:**

OptionProviderToolDescription`max_uses`Anthropicweb\_search, web\_fetchMax server-side uses (also set via `->max()`)`search_engine`OpenRouterweb\_search`"auto"`, `"native"`, `"exa"``container`OpenAIcode\_executionContainer config (`['type' => 'auto']`)`vector_store_ids`OpenAIfile\_searchVector store IDs to search`max_num_results`OpenAIfile\_searchMax results returned`library_ids`Mistraldocument\_libraryDocument library IDs### Tool state

[](#tool-state)

The configured call limit is available via `limit()`:

```
$tool = Tools::make( ... )->max( 3 );

$tool->limit(); // 3 — configured maximum calls
```

The remaining budget is tracked per request, not on the tool itself: every `write()` / `structure()` call starts fresh, so a tool capped at 3 can be called up to 3 times in each request. Every executed call counts against the budget, including calls whose handler throws. Once the budget is exhausted within a request, further calls to that tool return an error to the model.

### Error handling

[](#error-handling)

By default, when a tool handler throws an exception, the error message is returned to the model as `"Error: {message}"` instead of propagating the exception. You can override this with a custom error handler using `failed()`:

```
$tool = Tools::make( 'search', 'Search the web', $schema, fn( $args ) => doSearch( $args ) )
    ->failed( function( \Throwable $e, array $arguments ) : string {
        Log::error( 'Tool failed', ['error' => $e->getMessage(), 'args' => $arguments] );
        return 'Search is currently unavailable, please try a different approach.';
    } );
```

The handler receives the thrown exception and the original arguments, and must return a string that is sent back to the model.

### Concurrent tools

[](#concurrent-tools)

Tools can be marked as concurrent so they are eligible to run in parallel when the configured concurrency strategy supports it:

```
$schema = Schema::for( 'tool' );

$search = Tools::make( 'search', 'Search the web', $schema, fn( $args ) => '...' )->concurrent();
$weather = Tools::make( 'weather', 'Get weather', $schema, fn( $args ) => '...' )->concurrent();
$save = Tools::make( 'save', 'Save to database', $schema, fn( $args ) => '...' ); // sequential (default)
```

When the LLM calls multiple tools in a single step, the concurrent tools are handed to the configured concurrency strategy while sequential tools always run one after another. You can also disable concurrency again:

```
$tool->concurrent( false );
```

**Concurrency strategy:**

Prisma uses the `Sequential` strategy by default, which runs every step one after another. To run concurrent tools in parallel, provide your own strategy (see below). You can also set the strategy explicitly:

```
use Aimeos\Prisma\Tools\Concurrency\Sequential;

$response = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->withConcurrency( new Sequential() )
    ->withTools( [$search, $weather] )
    ->write( 'Search and get weather for Berlin' );
```

**Custom concurrency strategy:**

Implement the `Concurrency` interface to use your own execution strategy (e.g., async I/O, thread pools, or framework-specific solutions):

```
use Aimeos\Prisma\Tools\Concurrency\Concurrency;
use Aimeos\Prisma\Tools\Step;

class ReactConcurrency implements Concurrency
{
    public function run( array $steps ) : array
    {
        foreach( $steps as $step )
        {
            if( $tool = $step->tool() )
            {
                $step->complete( $tool( $step->arguments() ) );
            }
        }

        return $steps;
    }
}
```

Each `$steps` entry is a `Step` object with `tool()`, `arguments()`, `id()`, `name()`, and `result()`. Call `$step->complete()` with the result string.

> **Note:** Read-only tools that don't modify state should be marked as concurrent.

### Decorating tools

[](#decorating-tools)

Use the `Decorator` abstract class to wrap tools with additional behavior:

```
use Aimeos\\Prisma\\Tools\Adapter\Decorator;
use Aimeos\\Prisma\\Tools\Adapter\Adapter;

class LoggingTool extends Decorator
{
    private $logger;

    public function __construct( Adapter $adapter, $logger )
    {
        parent::__construct( $adapter );
        $this->logger = $logger;
    }

    public function __invoke( array $arguments ) : string
    {
        $this->logger->info( 'Tool called: ' . $this->name(), $arguments );
        return parent::__invoke( $arguments );
    }
}

$tool = new LoggingTool( Tools::make( 'search', 'Search', $schema, fn( $args ) => '...' ), $logger );
```

Decorators delegate all `Adapter` interface methods to the wrapped tool. Override any [provider method](https://github.com/aimeos/prisma/blob/master/src/Tools/Adapter/Adapter.php) to add custom behavior.

Audio API
---------

[](#audio-api)

### demix

[](#demix)

Separate an audio file into its individual tracks.

```
public function demix( Audio $audio, int $stems, array $options = [] ) : FileResponse
```

- @param **Audio** `$audio` Input audio object
- @param **int** `$stems` Number of stems to separate into (e.g. 2 for vocals and accompaniment)
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Audio file response

**Supported options:**

- AudioPod

### denoise

[](#denoise)

Remove noise from an audio file.

```
public function denoise( Audio $audio, array $options = [] ) : FileResponse
```

- @param **Audio** `$audio` Input audio object
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Audio file response

**Supported options:**

- [AudioPod](https://docs.audiopod.ai/api-reference/noise-reduction)

### describe

[](#describe)

Describe the content of an audio file.

```
public function describe( Audio $audio, ?string $lang = null, array $options = [] ) : TextResponse
```

- @param **Audio** `$audio` Input audio object
- @param **string|null** `$lang` ISO language code the description should be generated in
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Response text

**Supported options:**

- Gemini
- Groq
- [OpenAI](https://platform.openai.com/docs/api-reference/audio/createTranscription)

### revoice

[](#revoice)

Exchange the voice in an audio file.

```
public function revoice( Audio $audio, string $voice, array $options = [] ) : FileResponse;
```

- @param **Audio** `$audio` Input audio object
- @param **string** `$voice` Voice name or identifier
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Audio file response

**Supported options:**

- AudioPod
- [ElevenLabs](https://elevenlabs.io/docs/api-reference/speech-to-speech/convert)
- [Murf](https://murf.ai/api/docs/api-reference/voice-changer/convert)

### speak

[](#speak)

Converts text to speech.

```
public function speak( string $text, ?string $voice = null, array $options = [] ) : FileResponse;
```

- @param **string** `$text` Text to be converted to speech
- @param **string|null** `$voice` Voice identifier for speech synthesis
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Audio file response

**Supported options:**

- [Alibaba](https://www.alibabacloud.com/help/en/model-studio/qwen-tts-api)
- [AudioPod](https://docs.audiopod.ai/api-reference/text-to-speech#generate-speech)
- [Deepgram](https://developers.deepgram.com/reference/text-to-speech/speak-request)
- [ElevenLabs](https://elevenlabs.io/docs/api-reference/text-to-speech/convert)
- Groq
- [Murf](https://murf.ai/api/docs/api-reference/text-to-speech/generate)
- [OpenAI](https://platform.openai.com/docs/api-reference/audio/createSpeech)

### transcribe

[](#transcribe)

Converts speech to text.

```
public function transcribe( Audio $audio, ?string $lang = null, array $options = [] ) : TextResponse
```

- @param **Audio** `$audio` Input audio object
- @param **string|null** `$lang` ISO language code of the audio content
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Transcription text response

**Supported options:**

- [AudioPod](https://docs.audiopod.ai/api-reference/speech-to-text)
- [Deepgram](https://developers.deepgram.com/reference/text-to-speech/speak-request)
- [ElevenLabs](https://elevenlabs.io/docs/api-reference/speech-to-text/convert)
- Groq
- [Mistral](https://docs.mistral.ai/api/endpoint/audio/transcriptions)
- [OpenAI](https://platform.openai.com/docs/api-reference/audio/createTranscription)

Image API
---------

[](#image-api)

Most methods require an image object as input which contains a reference to the image that should be processed. This object can be created by:

```
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.php', 'image/png' );
$image = Image::fromLocalPath( 'path/to/image.png', 'image/png' );
$image = Image::fromBinary( 'PNG...', 'image/png' );
$image = Image::fromBase64( 'UE5H...', 'image/png' );

// Laravel only:
$image = Image::fromStoragePath( 'path/to/image.png', 'public', 'image/png' );
```

The last parameter of all methods (mime type) is optional. If it's not passed, the file content will be retrieved to determine the mime type if reqested.

**Note:** It's best to use **fromUrl()** if possible because all other formats (binary and base64) can be derived from the URL content but URLs can't be created from binary/base64 data.

### background

[](#background)

Replace image background with a background described by the prompt.

```
public function background( Image $image, string $prompt, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **string** `$prompt` Prompt describing the new background
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- Clipdrop
- [Ideogram](https://developer.ideogram.ai/api-reference/api-reference/replace-background-v3#request)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->background( $image, 'Golden sunset on a caribbean beach' );

$image = $fileResponse->binary();
```

### describe

[](#describe-1)

Describe the content of an image.

```
public function describe( Image $image, ?string $lang = null, array $options = [] ) : TextResponse
```

- @param **Image** `$image` Input image object
- @param **string|null** `$lang` ISO language code the description should be generated in
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Response text

**Supported options:**

- Gemini
- Groq
- [Ideogram](https://developer.ideogram.ai/api-reference/api-reference/describe#request)
- OpenAI

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$textResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->describe( $image, 'de' );

$text = $textResponse->text();
```

### detext

[](#detext)

Remove all text from the image.

```
public function detext( Image $image, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- Clipdrop

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->detext( `$image` );

$image = $fileResponse->binary();
```

### erase

[](#erase)

Erase parts of the image.

```
public function erase( Image $image, Image $mask, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **Image** `$mask` Mask image object
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

The mask must be an image with black parts (#000000) to keep and white parts (#FFFFFF) to remove.

**Supported options:**

- [Clipdrop](https://clipdrop.co/apis/docs/cleanup)
- [StabilityAI](https://platform.stability.ai/docs/api-reference#tag/Edit/paths/~1v2beta~1stable-image~1edit~1erase/post)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );
$mask = Image::fromBinary( 'PNG...' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->erase( $image, $mask );

$image = $fileResponse->binary();
```

### imagine

[](#imagine)

Generate an image from the prompt.

```
public function imagine( string $prompt, array $images = [], array $options = [] ) : FileResponse
```

- @param **string** `$prompt` Prompt describing the image
- @param **array&lt;int, \\Aimeos\\Prisma\\Files\\Image&gt;** `$images` Associative list of file name/Image instances
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- [Alibaba Qwen/Wan/Z-Image](https://www.alibabacloud.com/help/en/model-studio/qwen-image-api)
- [Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-image.html)
- [Black Forest Labs](https://docs.bfl.ai/api-reference/models/generate-or-edit-an-image-with-flux2-%5Bpro%5D)
- Clipdrop
- [Gemini](https://ai.google.dev/gemini-api/docs/image-generation#optional_configurations)
- [Ideogram](https://developer.ideogram.ai/api-reference/api-reference/generate-v3#request)
- [ModelsLab](https://docs.modelslab.com/image-generation/community-models/text2img)
- [Replicate](https://replicate.com/docs/topics/predictions/create-a-prediction)
- [VertexAI](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/imagen-api#generate_images)
- [OpenAI GPT image 1](https://platform.openai.com/docs/guides/image-generation?image-generation-model=gpt-image-1#customize-image-output)
- [OpenAI Dall-e-3](https://platform.openai.com/docs/guides/image-generation?image-generation-model=dall-e-3#customize-image-output)
- [OpenAI Dall-e-2](https://platform.openai.com/docs/guides/image-generation?image-generation-model=dall-e-2#customize-image-output)
- [StabilityAI Core](https://platform.stability.ai/docs/api-reference#tag/Generate/paths/~1v2beta~1stable-image~1generate~1core/post)
- [StabilityAI Ultra](https://platform.stability.ai/docs/api-reference#tag/Generate/paths/~1v2beta~1stable-image~1generate~1ultra/post)
- [StabilityAI Stable Diffusion 3.5](https://platform.stability.ai/docs/api-reference#tag/Generate/paths/~1v2beta~1stable-image~1generate~1sd3/post)
- [xAI Grok Image](https://docs.x.ai/docs/guides/image-generations)

**Example:**

```
use Aimeos\Prisma\Prisma;

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->imagine( 'Futuristic robot looking at a dashboard' );

$image = $fileResponse->binary();
```

### inpaint

[](#inpaint)

Edit an image by inpainting an area defined by a mask according to a prompt.

```
public function inpaint( Image $image, Image $mask, string $prompt, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **Image** `$mask` Input mask image object
- @param **string** `$prompt` Prompt describing the changes
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

The mask must be an image with black parts (#000000) to keep and white parts (#FFFFFF) to edit.

**Supported options:**

- [Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-image.html)
- [Black Forest Labs](https://docs.bfl.ai/api-reference/models/generate-an-image-with-flux1-fill-%5Bpro%5D-using-an-input-image-and-mask)
- [Ideogram](https://developer.ideogram.ai/api-reference/api-reference/edit-v3#request)
- [VertexAI](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/imagen-api-edit#parameters)
- [OpenAI GPT image 1](https://platform.openai.com/docs/guides/image-generation?image-generation-model=gpt-image-1#customize-image-output)
- [OpenAI Dall-e-3](https://platform.openai.com/docs/guides/image-generation?image-generation-model=dall-e-3#customize-image-output)
- [OpenAI Dall-e-2](https://platform.openai.com/docs/guides/image-generation?image-generation-model=dall-e-2#customize-image-output)
- [StabilityAI](https://platform.stability.ai/docs/api-reference#tag/Edit/paths/~1v2beta~1stable-image~1edit~1inpaint/post)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );
$mask = Image::fromBinary( 'PNG...' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->inpaint( $image, $mask, 'add a pink flamingo' );

$image = $fileResponse->binary();
```

### isolate

[](#isolate)

Remove the image background.

```
public function isolate( Image $image, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- [Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-image.html)
- [Clipdrop](https://clipdrop.co/apis/docs/remove-background)
- [RemoveBG](https://www.remove.bg/api#api-reference)
- [StabilityAI](https://platform.stability.ai/docs/api-reference#tag/Edit/paths/~1v2beta~1stable-image~1edit~1remove-background/post)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->isolate( `$image` );

$image = $fileResponse->binary();
```

### recognize

[](#recognize)

Recognizes the text in the given image (OCR).

```
public function recognize( Image $image, array $options = [] ) : TextResponse;
```

- @param **Image** `$image` Input image object
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Response text object

**Supported options:**

- [Mistral](https://docs.mistral.ai/api/endpoint/ocr#operation-ocr_v1_ocr_post)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$textTesponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->recognize( `$image` );

$text = $textResponse->text();
```

### relocate

[](#relocate)

Place the foreground object on a new background.

```
public function relocate( Image $image, Image $bgimage, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image with foreground object
- @param **Image** `$bgimage` Background image
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- [RemoveBG](https://www.remove.bg/api#api-reference)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );
$bgimage = Image::fromUrl( 'https://example.com/background.png' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->relocate( $image, $bgimage );

$image = $fileResponse->binary();
```

### repaint

[](#repaint)

Repaint an image according to the prompt.

```
public function repaint( Image $image, string $prompt, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **string** `$prompt` Prompt describing the changes
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- [Gemini](https://ai.google.dev/gemini-api/docs/image-generation#optional_configurations)
- [Ideogram](https://developer.ideogram.ai/api-reference/api-reference/remix-v3#request)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->repaint( $image, 'Use a van Goch style' );

$image = $fileResponse->binary();
```

### uncrop

[](#uncrop)

Extend/outpaint the image.

```
public function uncrop( Image $image,  int $top, int $right, int $bottom, int $left, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **int** `$top` Number of pixels to extend to the top
- @param **int** `$right` Number of pixels to extend to the right
- @param **int** `$bottom` Number of pixels to extend to the bottom
- @param **int** `$left` Number of pixels to extend to the left
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- [Black Forest Labs](https://docs.bfl.ai/api-reference/models/expand-an-image-by-adding-pixels-on-any-side)
- Clipdrop
- [StabilityAI](https://platform.stability.ai/docs/api-reference#tag/Edit/paths/~1v2beta~1stable-image~1edit~1outpaint/post)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->uncrop( $image, 100, 200, 0, 50 );

$image = $fileResponse->binary();
```

### upscale

[](#upscale)

Scale up the image.

```
public function upscale( Image $image, int $factor, array $options = [] ) : FileResponse
```

- @param **Image** `$image` Input image object
- @param **int** `$factor` Upscaling factor between 2 and the maximum value supported by the provider
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **FileResponse** Response file

**Supported options:**

- Clipdrop
- [Ideogram](https://developer.ideogram.ai/api-reference/api-reference/upscale#request)
- [VertexAI](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/imagen-upscale-api#parameters)
- [StabilityAI](https://platform.stability.ai/docs/api-reference#tag/Upscale)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$image = Image::fromUrl( 'https://example.com/image.png' );

$fileResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->upscale( $image, 4 );

$image = $fileResponse->binary();
```

### vectorize

[](#vectorize)

Creates embedding vectors of the images' content.

```
public function vectorize( array $images, ?int $size = null, array $options = [] ) : VectorResponse
```

- @param **array&lt;int, \\Aimeos\\Prisma\\Files\\Image&gt;** `$images` List of input image objects
- @param **int|null** `$size` Size of the resulting vector or null for provider default
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **VectorResponse** Response vector object

**Supported options:**

- [Alibaba](https://www.alibabacloud.com/help/en/model-studio/multimodal-embedding-api-reference)
- Bedrock
- [Cohere](https://docs.cohere.com/reference/embed#request)
- VertexAI
- [VoyageAI](https://docs.voyageai.com/reference/multimodal-embeddings-api)

**Example:**

```
use Aimeos\Prisma\Prisma;
use \Aimeos\Prisma\Files\Image;

$images = [
    Image::fromUrl( 'https://example.com/image.png' ),
    Image::fromUrl( 'https://example.com/image2.png' ),
];

$vectorResponse = Prisma::image()
    ->using( '', ['api_key' => 'xxx'])
    ->vectorize( $images, 512 );

$vectors = $vectorResponse->vectors();
```

Text API
--------

[](#text-api)

### stream

[](#stream)

Generate text from the given prompt and stream it token by token. The returned `TextResponse` is backed by a live stream: iterate `TextResponse::stream()` to consume each chunk as it arrives. The text accessors (`text()`, `texts()`, `first()`, `output()`) and iterating the response drain the stream for you, so you can also ignore the live chunks and use the response like a non-streamed one. Streaming uses the same endpoint the provider's [write()](#write) method uses, so tools, system prompts, [conversation history](#withmessages) and options work identically.

> **Consume the stream before reading body metadata.** `usage()`, `steps()`, `meta()`, `citations()`, `reason()` and `structured()` are only populated **after** the stream has been consumed - either iterate `stream()` to completion or call one of the text accessors first (e.g. `text()`/`output()`). Read before the stream is drained, they return empty/default values. `rateLimit()` is the exception: it comes from the response headers and is available immediately, as are HTTP/auth errors, which surface from the `stream()` call itself rather than during iteration.

```
public function stream( string $prompt, array $files = [], array $options = [] ) : TextResponse
```

- @param **string** `$prompt` Input prompt for text generation
- @param **array&lt;int, File&gt;** `$files` Files for multimodal input (images, audio, documents)
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Streamed response text

Iterating `$response->stream()` yields:

- a **string** for every streamed text delta, and
- a **Step** for every executed tool call - once before it runs (`done() === false`) and once after it completed (`done() === true`). A tool that hit its call limit is not executed and is reported once (completed).

> The stream is single-pass and the same `Step` instance is reused for both notifications, so read `done()` / `result()` **inside** the loop (a stored reference reflects the final state).

**Supported providers:**

- [Alibaba](https://www.alibabacloud.com/help/en/model-studio/model-api-reference/)
- [Anthropic](https://docs.anthropic.com/en/api/messages-streaming)
- [Azure](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions)
- [Deepseek](https://api-docs.deepseek.com/api/create-chat-completion)
- [Gemini](https://ai.google.dev/gemini-api/docs/text-generation)
- [Groq](https://console.groq.com/docs/text-chat)
- [Mistral](https://docs.mistral.ai/api/#tag/chat/operation/chat_completion_v1_chat_completions_post)
- [Ollama](https://github.com/ollama/ollama/blob/main/docs/openai.md)
- [OpenAI](https://platform.openai.com/docs/api-reference/responses-streaming)
- [Openrouter](https://openrouter.ai/docs/api-reference/streaming)
- [Perplexity](https://docs.perplexity.ai/api-reference/chat-completions)
- [xAI](https://docs.x.ai/api/endpoints#chat-completions)

**Example:**

```
use Aimeos\Prisma\Prisma;

$textResponse = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->ensure( 'stream' )
    ->stream( 'Summarize the benefits of renewable energy' );

foreach( $textResponse->stream() as $delta ) {
    echo $delta; // print each token as it arrives
}

$full = $textResponse->text();  // the complete answer
$usage = $textResponse->usage(); // token usage
```

**Multi-turn conversation:**

Pass the earlier turns with [withMessages()](#withmessages); the current prompt is appended as the next user message.

```
use Aimeos\Prisma\Prisma;

$textResponse = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->withMessages( [
        ['role' => 'user', 'content' => 'Recommend a laptop'],
        ['role' => 'assistant', 'content' => 'Sure - what is your budget?'],
    ] )
    ->ensure( 'stream' )
    ->stream( 'Around $1500' );

foreach( $textResponse->stream() as $delta ) {
    echo $delta;
}
```

**Streaming with tools:**

```
use Aimeos\Prisma\Prisma;
use Aimeos\Prisma\Tools\Step;

$textResponse = Prisma::text()
    ->using( 'anthropic', ['api_key' => 'xxx'] )
    ->withTools( [$weatherTool] )
    ->ensure( 'stream' )
    ->stream( 'What is the weather in Berlin?' );

foreach( $textResponse->stream() as $chunk ) {
    if( !$chunk instanceof Step ) {
        echo $chunk;                                                        // text delta
    } elseif( $chunk->done() ) {
        printf( "\n[%s -> %s]\n", $chunk->name(), $chunk->result() );       // tool result
    } else {
        printf( "\n[calling %s(%s)]\n", $chunk->name(), json_encode( $chunk->arguments() ) ); // tool call
    }
}

$steps = $textResponse->steps(); // executed tool steps, same as write()
```

> **Performance:** the loop body runs once per token, synchronously in the read loop. For high-frequency sinks (broadcast, WebSocket, database), coalesce deltas - buffer them and flush every ~50ms or every N characters - instead of doing a round trip per token.

**Laravel SSE (`response()->eventStream()`):**

Because `stream()` returns an iterable response, it plugs straight into Laravel's native SSE helper - just delegate to its generator:

```
use Aimeos\Prisma\Prisma;

Route::get( '/chat', function () {
    $response = Prisma::text()
        ->using( 'openai', config( 'services.openai' ) )
        ->ensure( 'stream' )
        ->stream( 'Summarize the benefits of renewable energy' );

    return response()->eventStream( function () use ( $response ) {
        foreach( $response->stream() as $chunk ) {
            if( is_string( $chunk ) ) {
                yield $chunk;
            }
        }
    } );
} );
```

### structure

[](#structure)

Generate structured output from the given prompt and schema. The response JSON is parsed and available via the `structured()` method on the response object.

```
public function structure( string $prompt, Schema $schema, array $files = [], array $options = [] ) : TextResponse
```

- @param **string** `$prompt` Input prompt for structured text generation
- @param **Schema** `$schema` Schema definition for the structured output
- @param **array&lt;int, File&gt;** `$files` Files for multimodal input (images, audio, documents)
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Response text with structured data

> Supply prior conversation turns with [withMessages()](#withmessages); the current `$prompt` is appended as the final user message.

**Supported options:**

- [Alibaba](https://www.alibabacloud.com/help/en/model-studio/developer-reference/api-details)
- [Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/structured-output)
- [Azure](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions)
- [Bedrock](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html)
- [Cohere](https://docs.cohere.com/reference/chat)
- [Deepseek](https://api-docs.deepseek.com/api/create-chat-completion)
- [Gemini](https://ai.google.dev/gemini-api/docs/structured-output)
- [Groq](https://console.groq.com/docs/api-reference#chat-create)
- [Mistral](https://docs.mistral.ai/api/#tag/chat/operation/chat_completion_v1_chat_completions_post)
- [Ollama](https://github.com/ollama/ollama/blob/main/docs/openai.md)
- [OpenAI](https://platform.openai.com/docs/api-reference/chat/create)
- [Openrouter](https://openrouter.ai/docs/api-reference/chat-completions)
- [Perplexity](https://docs.perplexity.ai/api-reference/chat-completions)
- [xAI](https://docs.x.ai/api/endpoints#chat-completions)

**Example:**

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

$schema = Schema::for( 'person', [
    'name' => Schema::string(),
    'age' => Schema::integer(),
] );

$textResponse = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->ensure( 'structure' )
    ->structure( 'Extract the person from: John is 30 years old', $schema );

$data = $textResponse->structured(); // ['name' => 'John', 'age' => 30]
$json = $textResponse->text(); // '{"name":"John","age":30}'
```

**Output mode:**

By default the schema is enforced by the provider's native structured-output API (strict mode). Pass `['mode' => 'json']` to instead embed the schema in the prompt and parse the JSON from the response — useful when a schema is too large or deeply nested for a provider's strict-mode limits. `['mode' => 'structured']` selects native mode explicitly; any other value throws a `BadRequestException`. Providers without a native strict mode (Bedrock, Cohere, Deepseek, Ollama) always use JSON mode and ignore the option.

```
$textResponse = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'] )
    ->ensure( 'structure' )
    ->structure( 'Extract the person', $schema, [], ['mode' => 'json'] );
```

> **Warning:** `structured()` is the model's output parsed as-is — always treat it as untrusted. Guard two separate things:
>
> - **Shape** — it is not validated against your schema. Native strict mode is provider-enforced, but JSON mode (`['mode' => 'json']`, and the JSON-only providers above) gives no guarantee the result matches the schema. Check it with `$schema->validate( $data )` (returns `[]` when valid).
> - **Values** — even a schema-conformant result contains model-generated text. `validate()` verifies types and constraints, not safety, so never drop a value straight into SQL, a shell command, a file path, or markup. Use bound parameters, escaping, or allow-lists, exactly as you would for any user input.
>
> ```
> $data = $textResponse->structured();
> $errors = $schema->validate( $data ); // [] when valid
> if( $errors ) {
>     // reject, retry, or handle the mismatch
> }
> ```

### translate

[](#translate)

Translate one or more texts from one language to another.

```
public function translate( array $texts, string $to, ?string $from = null, ?string $context = null, array $options = [] ) : TextResponse
```

- @param **array&lt;string&gt;** `$texts` Input texts to be translated
- @param **string** `$to` ISO language code to translate the text into
- @param **string|null** `$from` ISO language code of the input text (optional, auto-detected if omitted)
- @param **string|null** `$context` Context for the translation (optional)
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Response text

**Supported options:**

- [DeepL](https://developers.deepl.com/docs/api-reference/translate/openapi-spec-for-text-translation)
- [Google](https://docs.cloud.google.com/translate/docs/reference/rest/v2/translate#authorization)

**Example:**

```
use Aimeos\Prisma\Prisma;

$textResponse = Prisma::text()
    ->using( 'deepl', ['api_key' => 'xxx'])
    ->ensure( 'translate' )
    ->translate( ['Hello', 'World'], 'de', 'en' );

$texts = $textResponse->texts(); // ['Hallo', 'Welt']
```

### vectorize

[](#vectorize-1)

Creates embedding vectors of the texts' content.

```
public function vectorize( array $texts, ?int $size = null, array $options = [] ) : VectorResponse
```

- @param **array&lt;int, string&gt;** `$texts` List of input texts
- @param **int|null** `$size` Size of the resulting vector or null for provider default
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **VectorResponse** Response vector object

**Supported options:**

- [Alibaba](https://www.alibabacloud.com/help/en/model-studio/embedding-api-details)
- [Azure](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#embeddings)
- [Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html)
- [Cohere](https://docs.cohere.com/reference/embed)
- [Gemini](https://ai.google.dev/api/embeddings)
- [Mistral](https://docs.mistral.ai/api/#tag/embeddings)
- [Ollama](https://github.com/ollama/ollama/blob/main/docs/openai.md)
- [OpenAI](https://platform.openai.com/docs/api-reference/embeddings/create)

**Example:**

```
use Aimeos\Prisma\Prisma;

$vectorResponse = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'])
    ->ensure( 'vectorize' )
    ->vectorize( ['The quick brown fox', 'jumps over the lazy dog'], 256 );

$vectors = $vectorResponse->vectors(); // one embedding vector per input text
```

### write

[](#write)

Generate text from the given prompt with optional multimodal file inputs (images, audio, documents).

```
public function write( string $prompt, array $files = [], array $options = [] ) : TextResponse
```

- @param **string** `$prompt` Input prompt for text generation
- @param **array&lt;int, File&gt;** `$files` Files for multimodal input (images, audio, documents)
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Response text

> Supply prior conversation turns with [withMessages()](#withmessages); the current `$prompt` is appended as the final user message.

**Supported options:**

- [Alibaba](https://www.alibabacloud.com/help/en/model-studio/model-api-reference/)
- [Anthropic](https://docs.anthropic.com/en/api/messages)
- [Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-call.html)
- [Cohere](https://docs.cohere.com/reference/chat)
- [Deepseek](https://api-docs.deepseek.com/api/create-chat-completion)
- [Gemini](https://ai.google.dev/gemini-api/docs/text-generation)
- [Groq](https://console.groq.com/docs/text-chat)
- [Mistral](https://docs.mistral.ai/api/#tag/chat/operation/chat_completion_v1_chat_completions_post)
- [Ollama](https://github.com/ollama/ollama/blob/main/docs/openai.md)
- [OpenAI](https://platform.openai.com/docs/api-reference/chat/create)
- [Openrouter](https://openrouter.ai/docs/api-reference/chat-completions)
- [Perplexity](https://docs.perplexity.ai/api-reference/chat-completions)
- [xAI](https://docs.x.ai/api/endpoints#chat-completions)

**Example:**

```
use Aimeos\Prisma\Prisma;

$textResponse = Prisma::text()
    ->using( 'openai', ['api_key' => 'xxx'])
    ->ensure( 'write' )
    ->write( 'Summarize the benefits of renewable energy' );

$texts = $textResponse->texts(); // ['Renewable energy offers...']
```

Video API
---------

[](#video-api)

### describe

[](#describe-2)

Describe the content of a video file.

```
public function describe( Video $video, ?string $lang = null, array $options = [] ) : TextResponse
```

- @param **Video** `$video` Input video object
- @param **string|null** `$lang` ISO language code the description should be generated in
- @param **array&lt;string, mixed&gt;** `$options` Provider specific options
- @return **TextResponse** Response text

**Supported options:**

- Gemini

###  Health Score

54

—

FairBetter than 96% of packages

Maintenance99

Actively maintained with recent releases

Popularity37

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~17 days

Recently: every ~7 days

Total

14

Last Release

4d ago

PHP version history (2 changes)0.1.0PHP ^8.1

0.4.0PHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/ccdc84033584516c77fb6aa7f85dc7048f2fbcd08867fc6abf115d094bab5eef?d=identicon)[aimeos](/maintainers/aimeos)

---

Top Contributors

[![aimeos](https://avatars.githubusercontent.com/u/8647429?v=4)](https://github.com/aimeos "aimeos (326 commits)")

---

Tags

apillmmediamultimedia

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/aimeos-prisma/health.svg)

```
[![Health](https://phpackages.com/badges/aimeos-prisma/health.svg)](https://phpackages.com/packages/aimeos-prisma)
```

###  Alternatives

[statamic/cms

The Statamic CMS Core Package

4.8k3.6M985](/packages/statamic-cms)[tencentcloud/tencentcloud-sdk-php

TencentCloudApi php sdk

3741.3M46](/packages/tencentcloud-tencentcloud-sdk-php)[neuron-core/neuron-ai

The PHP Agentic Framework.

2.0k656.1k38](/packages/neuron-core-neuron-ai)[avalara/avataxclient

Client library for Avalara's AvaTax suite of business tax calculation and processing services. Uses the REST v2 API.

528.5M7](/packages/avalara-avataxclient)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

273.0k](/packages/eslazarev-wildberries-sdk)[files.com/files-php-sdk

Files.com PHP SDK

2481.1k](/packages/filescom-files-php-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
