---
url: /providers/anthropic.md
---
# Anthropic
## Configuration
```php
'anthropic' => [
'api_key' => env('ANTHROPIC_API_KEY', ''),
'version' => env('ANTHROPIC_API_VERSION', '2023-06-01'),
'default_thinking_budget' => env('ANTHROPIC_DEFAULT_THINKING_BUDGET', 1024),
// Include beta strings as a comma separated list.
'anthropic_beta' => env('ANTHROPIC_BETA', null),
]
```
## Prompt caching
Anthropic's prompt caching feature allows you to drastically reduce latency and your API bill when repeatedly re-using blocks of content within five minutes of each other.
We support Anthropic prompt caching on:
* System Messages (text only)
* User Messages (Text, Image and PDF (pdf only))
* Assistant Messages (text only)
* Tools
The API for enabling prompt caching is the same for all, enabled via the `withProviderOptions()` method. Where a UserMessage contains both text and an image or document, both will be cached.
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\Tool;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Messages\SystemMessage;
Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withMessages([
(new SystemMessage('I am a long re-usable system message.'))
->withProviderOptions(['cacheType' => 'ephemeral']),
(new UserMessage('I am a long re-usable user message.'))
->withProviderOptions(['cacheType' => 'ephemeral'])
])
->withTools([
Tool::as('cache me')
->withProviderOptions(['cacheType' => 'ephemeral'])
])
->asText();
```
If you prefer, you can use the `AnthropicCacheType` Enum like so:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Providers\Anthropic\Enums\AnthropicCacheType;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Document;
(new UserMessage('I am a long re-usable user message.'))->withProviderOptions(['cacheType' => AnthropicCacheType::ephemeral])
```
Note that you must use the `withMessages()` method in order to enable prompt caching, rather than `withPrompt()` or `withSystemPrompt()`.
### Tool result caching
In addition to caching prompts and tool definitions, Prism supports caching tool results. This is particularly useful when making multiple tool calls where results might be referenced repeatedly.
To enable tool result caching, use the `tool_result_cache_type` provider option on your request:
```php
use Prism\Prism\Prism;
$response = Prism::text()
->using('anthropic', 'claude-3-5-sonnet-20241022')
->withMaxSteps(30)
->withTools([new WeatherTool()])
->withProviderOptions([
'tool_result_cache_type' => 'ephemeral'
])
->withPrompt('Check the weather in New York, London, Tokyo, Paris, and Sydney')
->asText();
```
When multiple tool results are returned, Prism automatically applies caching to only the last result, which caches all preceding results as well. This avoids Anthropic's 4-cache-breakpoint limitation.
Please ensure you read Anthropic's [prompt caching documentation](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching), which covers some important information on e.g. minimum cacheable tokens and message order consistency.
## Extended thinking
Claude Sonnet 3.7 supports an optional extended thinking mode, where it will reason before returning its answer. Please ensure your consider [Anthropic's own extended thinking documentation](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) before using extended thinking with caching and/or tools, as there are some important limitations and behaviours to be aware of.
### Enabling extended thinking and setting budget
Prism supports thinking mode for text and structured with the same API:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::text()
->using('anthropic', 'claude-3-7-sonnet-latest')
->withPrompt('What is the meaning of life, the universe and everything in popular fiction?')
// enable thinking
->withProviderOptions(['thinking' => ['enabled' => true]])
->asText();
```
By default Prism will set the thinking budget to the value set in config, or where that isn't set, the minimum allowed (1024).
You can overide the config (or its default) using `withProviderOptions`:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::text()
->using('anthropic', 'claude-3-7-sonnet-latest')
->withPrompt('What is the meaning of life, the universe and everything in popular fiction?')
// Enable thinking and set a budget
->withProviderOptions([
'thinking' => [
'enabled' => true,
'budgetTokens' => 2048
]
]);
```
Note that thinking tokens count towards output tokens, so you will be billed for them and your token budget must be less than the max tokens you have set for the request.
If you expect a long response, you should ensure there's enough tokens left for the response - i.e. does (maxTokens - thinkingBudget) leave a sufficient remainder.
### Inspecting the thinking block
Anthropic returns the thinking block with its response.
You can access it via the additionalContent property on either the Response or the relevant step.
On the Response (easiest if not using tools):
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::text()
->using('anthropic', 'claude-3-7-sonnet-latest')
->withPrompt('What is the meaning of life, the universe and everything in popular fiction?')
->withProviderOptions(['thinking' => ['enabled' => true']])
->asText();
$response->additionalContent['thinking'];
```
On the Step (necessary if using tools, as Anthropic returns the thinking block on the ToolCall step):
```php
$tools = [...];
$response = Prism::text()
->using('anthropic', 'claude-3-7-sonnet-latest')
->withTools($tools)
->withMaxSteps(3)
->withPrompt('What time is the tigers game today and should I wear a coat?')
->withProviderOptions(['thinking' => ['enabled' => true]])
->asText();
$response->steps->first()->additionalContent->thinking;
```
### Extended output mode
Claude Sonnet 3.7 also brings extended output mode which increase the output limit to 128k tokens.
This feature is currently in beta, so you will need to enable to by adding `output-128k-2025-02-19` to your Anthropic anthropic\_beta config (see [Configuration](#configuration) above).
## Documents
Anthropic supports PDF, text and markdown documents. Note that Anthropic uses vision to process PDFs under the hood, and consequently there are some limitations detailed in their [feature documentation](https://docs.anthropic.com/en/docs/build-with-claude/pdf-support).
See the [Documents](/input-modalities/documents.html) on how to get started using them.
Anthropic also supports "custom content documents", separately documented below, which are primarily for use with citations.
### Custom content documents
Custom content documents are primarily for use with citations (see below), if you need citations to reference your own chunking strategy.
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Document;
Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withMessages([
new UserMessage(
content: "Is the grass green and the sky blue?",
additionalContent: [
Document::fromChunks(["The grass is green.", "Flamingos are pink.", "The sky is blue."])
]
)
])
->asText();
```
## Citations
Prism supports [Anthropic's citations feature](https://docs.anthropic.com/en/docs/build-with-claude/citations) for both text and structured.
Please note however that due to Anthropic not supporting "native" structured output, and Prism's workaround for this, the output can be unreliable. You should therefore ensure you implement proper error handling for the scenario where Anthropic does not return a valid decodable schema.
## Code execution
Anthropic offers built-in code execution capabilities that allow your AI to run code in a secure environment. This is a provider tool that executes code using Anthropic's infrastructure. For more information about the difference between custom tools and provider tools, see [Tools & Function Calling](/core-concepts/tools-function-calling#provider-tools).
To enable code execution, you will first need to enable the beta feature.
Either in prism/config.php:
```php
'anthropic' => [
...
'anthropic_beta' => 'code-execution-2025-05-22',
],
```
Or in your env file (assuming config/prism.php reflects the default prism setup):
```
ANTHROPIC_BETA="code-execution-2025-05-22"
```
You may then use code execution as follows:
```php
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\ProviderTool;
Prism::text()
->using('anthropic', 'claude-3-5-haiku-latest')
->withPrompt('Solve the equation 3x + 10 = 14.')
->withProviderTools([new ProviderTool(type: 'code_execution_20250522', name: 'code_execution')])
->asText();
```
### Enabling citations
Anthropic require citations to be enabled on all documents in a request. To enable them, using the `withProviderOptions()` method when building your request:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Document;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withMessages([
new UserMessage(
content: "Is the grass green and the sky blue?",
additionalContent: [
Document::fromChunks(
chunks: ["The grass is green.", "Flamingos are pink.", "The sky is blue."],
title: 'The colours of nature',
context: 'The go-to textbook on the colours found in nature!'
)
]
)
])
->withProviderOptions(['citations' => true])
->asText();
```
### Accessing citations
You can access the chunked output with its citations via the additionalContent property on a response, which returns an array of `Providers\Anthropic\ValueObjects\MessagePartWithCitations`s.
As a rough worked example, let's assume you want to implement footnotes. You'll need to loop through those chunks and (1) re-construct the message with links to the footnotes; and (2) build an array of footnotes to loop through in your frontend.
```php
use Prism\Prism\Providers\Anthropic\ValueObjects\MessagePartWithCitations;
use Prism\Prism\Providers\Anthropic\ValueObjects\Citation;
$messageChunks = $response->additionalContent['messagePartsWithCitations'];
$text = '';
$footnotes = [];
$footnoteId = 1;
/** @var MessagePartWithCitations $messageChunk */
foreach ($messageChunks as $messageChunk) {
$text .= $messageChunk->text;
/** @var Citation $citation */
foreach ($messageChunk->citations as $citation) {
$footnotes[] = [
'id' => $footnoteId,
'document_title' => $citation->documentTitle,
'reference_start' => $citation->startIndex,
'reference_end' => $citation->endIndex
];
$text .= ''.$footnoteId.'';
$footnoteId++;
}
}
```
Note that when using streaming, Anthropic does not stream citations in the same way. Instead, of building the context as above, yield text to the browser in the usual way and pair text up with the relevant footnote using the `citationIndex` on the text chunk's additionalContent parameter.
## Considerations
### Message Order
* Message order matters. Anthropic is strict about the message order being:
1. `UserMessage`
2. `AssistantMessage`
3. `UserMessage`
### Structured Output
While Anthropic models don't have native JSON mode or structured output like some providers, Prism implements two approaches for structured output:
#### Default JSON Mode (Prompt-based)
* We automatically append instructions to your prompt that guide the model to output valid JSON matching your schema
* If the response isn't valid JSON, Prism will raise a PrismException
* This method can sometimes struggle with complex JSON containing quotes, especially in non-English languages
#### Tool Calling Mode (Recommended)
For more reliable structured output, especially when dealing with complex content or non-English text that may contain quotes, you can enable tool calling mode:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
$response = Prism::structured()
->withSchema(new ObjectSchema(
'weather_report',
'Weather forecast with recommendations',
[
new StringSchema('forecast', 'The weather forecast'),
new StringSchema('recommendation', 'Clothing recommendation')
],
['forecast', 'recommendation']
))
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withPrompt('What\'s the weather like and what should I wear?')
->withProviderOptions(['use_tool_calling' => true])
->asStructured();
```
**Benefits of tool calling mode:**
* More reliable JSON parsing, especially with quotes and special characters
* Better handling of non-English content (Chinese, Japanese, etc.)
* Reduced risk of malformed JSON responses
* Compatible with thinking mode
**Limitations:**
* Cannot be used with citations (citations are not supported in tool calling mode)
* Slightly more complex under the hood but identical API usage
## Limitations
### Messages
Most providers' API include system messages in the messages array with a "system" role. Anthropic does not support the system role, and instead has a "system" property, separate from messages.
Therefore, for Anthropic we:
* Filter all `SystemMessage`s out, omitting them from messages.
* Always submit the prompt defined with `->withSystemPrompt()` at the top of the system prompts array.
* Move all `SystemMessage`s to the system prompts array in the order they were declared.
### Images
Does not support `Image::fromURL`
---
---
url: /input-modalities/audio.md
---
# Audio
Prism supports including audio files in your messages for advanced analysis with supported providers like Gemini.
See the [provider support table](/getting-started/introduction.html#provider-support) to check whether Prism supports your chosen provider.
Note however that provider support may differ by model. If you receive error messages with a provider that Prism indicates is supported, check the provider's documentation as to whether the model you are using supports audio files.
::: tip
For other input modalities like videos and images, see their respective documentation pages:
* [Video documentation](/input-modalities/video.html)
* [Images documentation](/input-modalities/images.html)
:::
## Getting started
To add an audio file to your prompt, use the `withPrompt` method with an `Audio` value object:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Media\Audio;
// From a local path
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
"What's in this audio?",
[Audio::fromLocalPath(path: '/path/to/audio.mp3')]
)
->asText();
// From a path on a storage disk
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
"What's in this audio?",
[Audio::fromStoragePath(
path: '/path/to/audio.mp3',
diskName: 'my-disk' // optional - omit/null for default disk
)]
)
->asText();
// From a URL
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Analyze this audio:',
[Audio::fromUrl(url: 'https://example.com/audio.mp3')]
)
->asText();
// From base64
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Analyze this audio:',
[Audio::fromBase64(
base64: base64_encode(file_get_contents('/path/to/audio.mp3')),
mimeType: 'audio/mpeg'
)]
)
->asText();
// From raw content
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Analyze this audio:',
[Audio::fromRawContent(
rawContent: file_get_contents('/path/to/audio.mp3'),
mimeType: 'audio/mpeg'
)]
)
->asText();
```
## Alternative: Using withMessages
You can also include audio files using the message-based approach:
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Audio;
$message = new UserMessage(
"What's in this audio?",
[Audio::fromLocalPath(path: '/path/to/audio.mp3')]
);
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withMessages([$message])
->asText();
```
## Supported Audio Types
Prism supports a variety of audio formats, including:
* MP3 (audio/mpeg)
* WAV (audio/x-wav, audio/wav)
* AAC (audio/aac)
* FLAC (audio/flac)
The specific supported formats depend on the provider. Gemini is currently the main provider with comprehensive audio analysis capabilities. Check the provider's documentation for a complete list of supported formats.
## Transfer mediums
Providers are not consistent in their support of sending raw contents, base64 and/or URLs.
Prism tries to smooth over these rough edges, but its not always possible.
### Supported conversions
* Where a provider does not support URLs: Prism will fetch the URL and use base64 or rawContent.
* Where you provide a file, base64 or rawContent: Prism will switch between base64 and rawContent depending on what the provider accepts.
### Limitations
* Where a provider only supports URLs: if you provide a file path, raw contents or base64, for security reasons Prism does not create a URL for you and your request will fail.
---
---
url: /core-concepts/audio.md
---
# Audio Processing
Transform text into speech and speech into text using AI-powered audio models. Prism provides a unified API for audio processing across different providers, enabling both text-to-speech (TTS) and speech-to-text (STT) functionality.
## Getting Started
### Text-to-Speech
Convert text into natural-sounding speech:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::audio()
->using(Provider::OpenAI, 'tts-1')
->withInput('Hello, this is a test of text-to-speech functionality.')
->withVoice('alloy')
->asAudio();
$audio = $response->audio;
if ($audio->hasBase64()) {
file_put_contents('output.mp3', base64_decode($audio->base64));
echo "Audio saved as: output.mp3";
}
```
### Speech-to-Text
Convert audio files into text transcriptions:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Media\Audio;
$audioFile = Audio::fromPath('/path/to/audio.mp3');
$response = Prism::audio()
->using(Provider::OpenAI, 'whisper-1')
->withInput($audioFile)
->asText();
echo "Transcription: " . $response->text;
```
## Provider Support
Currently, Prism supports audio processing through:
* **OpenAI**: TTS-1, TTS-1-HD (text-to-speech) and Whisper-1 (speech-to-text)
* **Groq**: PlayAI TTS models (text-to-speech) and Whisper Large V3 models (speech-to-text)
Additional providers will be added in future releases as the ecosystem evolves.
## Basic Usage
### Simple Text-to-Speech
Generate speech from text input:
```php
$response = Prism::audio()
->using('openai', 'tts-1')
->withInput('Welcome to our application!')
->withVoice('alloy')
->asAudio();
$audio = $response->audio;
echo "Audio type: " . $audio->getMimeType(); // audio/mpeg
echo "Has audio data: " . ($audio->hasBase64() ? 'Yes' : 'No');
```
### Simple Speech-to-Text
Transcribe audio files to text:
```php
use Prism\Prism\ValueObjects\Media\Audio;
// From file path
$audioFile = Audio::fromPath('/path/to/recording.wav');
// From URL
$audioFile = Audio::fromUrl('https://example.com/audio.mp3');
// From base64 data
$audioFile = Audio::fromBase64($base64AudioData, 'audio/wav');
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->asText();
// Access the transcription
echo $response->text;
```
## Working with Audio Files
### Creating Audio Objects
The `Audio` class provides several ways to work with audio files:
```php
use Prism\Prism\ValueObjects\Media\Audio;
// From local file
$audio = Audio::fromPath('/path/to/audio.mp3');
// From remote URL
$audio = Audio::fromUrl('https://example.com/speech.wav');
// From base64 encoded data
$audio = Audio::fromBase64($base64Data, 'audio/mpeg');
// From raw binary content
$audio = Audio::fromContent($binaryData, 'audio/wav');
```
### Audio Properties
Access audio file information:
```php
$audio = Audio::fromPath('/path/to/audio.mp3');
echo "MIME type: " . $audio->mimeType();
echo "Has local path: " . ($audio->hasLocalPath() ? 'Yes' : 'No');
echo "File size: " . $audio->size() . " bytes";
```
## Working with Responses
### Text-to-Speech Responses
```php
$response = Prism::audio()
->using('openai', 'tts-1-hd')
->withInput('This is high-quality text-to-speech.')
->withVoice('nova')
->asAudio();
// Access the generated audio
$audio = $response->audio;
if ($audio->hasBase64()) {
// Save to file
$audioData = base64_decode($audio->base64);
file_put_contents('speech.mp3', $audioData);
// Get MIME type
echo "Content type: " . $audio->getMimeType();
}
// Access additional response data
foreach ($response->additionalContent as $key => $value) {
echo "{$key}: {$value}\n";
}
```
### Speech-to-Text Responses
```php
$audioFile = Audio::fromPath('/path/to/speech.mp3');
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->asText();
// Access the transcription
$text = $response->text;
echo "Transcription: " . $text;
// Check token usage
if ($response->usage) {
echo "Prompt tokens: " . $response->usage->promptTokens;
echo "Completion tokens: " . $response->usage->completionTokens;
}
// Access raw response data
print_r($response->additionalContent);
```
## Voice Selection
Prism provides a dedicated `withVoice()` method for selecting voices in text-to-speech, making voice selection a first-class citizen in the API:
```php
$response = Prism::audio()
->using('openai', 'tts-1')
->withInput('Hello, how are you today?')
->withVoice('alloy') // Voice options vary by provider
->asAudio();
```
## Provider-Specific Options
While Prism provides a consistent API, you can access provider-specific features using the `withProviderOptions()` method.
### OpenAI Text-to-Speech Options
Customize format, speed, and other options:
```php
$response = Prism::audio()
->using('openai', 'tts-1')
->withInput('Hello, how are you today?')
->withVoice('nova')
->withProviderOptions([
'response_format' => 'mp3', // mp3, opus, aac, flac, wav, pcm
'speed' => 1.0, // 0.25 to 4.0
])
->asAudio();
```
### OpenAI Speech-to-Text Options
Configure transcription settings:
```php
$audioFile = Audio::fromPath('/path/to/multilingual.mp3');
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'language' => 'en',
'prompt' => 'Previous context...'
])
->asText();
```
### Response Formats
Different response formats provide varying levels of detail:
```php
// Verbose JSON format includes timestamps and confidence scores
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'response_format' => 'verbose_json',
])
->asText();
// Access additional metadata
$metadata = $response->additionalContent;
if (isset($metadata['segments'])) {
foreach ($metadata['segments'] as $segment) {
echo "Segment: " . $segment['text'] . "\n";
echo "Start: " . $segment['start'] . "s\n";
echo "End: " . $segment['end'] . "s\n";
}
}
```
## Advanced Usage
Audio can be integrated into multi-modal conversations:
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Audio;
use Prism\Prism\ValueObjects\Media\Text;
$audioFile = Audio::fromPath('/path/to/question.mp3');
// First transcribe the audio
$transcription = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->asText();
// Then use in a text conversation
$response = Prism::text()
->using('openai', 'gpt-4')
->withMessages([
new UserMessage('', [
new Text('User asked: '),
new Text($transcription->text),
new Text(' - Please provide a detailed response.')
])
])
->asText();
// Convert response back to speech
$speechResponse = Prism::audio()
->using('openai', 'tts-1')
->withInput($response->text)
->withVoice('alloy')
->asAudio();
```
## Configuration Options
### Client Configuration
Configure HTTP client options for audio processing:
```php
$response = Prism::audio()
->using('openai', 'tts-1')
->withInput('This might take a while to process.')
->withClientOptions([
'timeout' => 60, // Increase timeout for large files
'connect_timeout' => 10, // Connection timeout
])
->withClientRetry(3, 1000) // Retry 3 times with 1s delay
->asAudio();
```
### Provider Configuration
Override provider configuration for multi-tenant applications:
```php
$customConfig = [
'api_key' => 'user-specific-api-key',
'organization' => 'user-org-id',
];
$response = Prism::audio()
->using('openai', 'whisper-1')
->usingProviderConfig($customConfig)
->withInput($audioFile)
->asText();
```
## Error Handling
Handle potential errors in audio processing:
```php
use Prism\Prism\Exceptions\PrismException;
use Throwable;
try {
$response = Prism::audio()
->using('openai', 'tts-1')
->withInput('Text to convert to speech')
->withVoice('alloy')
->asAudio();
// Process successful response
file_put_contents('output.mp3', base64_decode($response->audio->base64));
} catch (PrismException $e) {
Log::error('Audio processing failed:', ['error' => $e->getMessage()]);
// Handle Prism-specific errors
} catch (Throwable $e) {
Log::error('General error:', ['error' => $e->getMessage()]);
// Handle any other errors
}
```
## Testing
Prism provides convenient fakes for testing audio functionality:
```php
use Prism\Prism\Prism;
use Prism\Prism\Testing\PrismFake;
use Prism\Prism\Audio\AudioResponse;
use Prism\Prism\Audio\TextResponse;
use Prism\Prism\ValueObjects\GeneratedAudio;
test('can generate text-to-speech', function () {
$fakeAudio = new AudioResponse(
audio: new GeneratedAudio(
base64: base64_encode('fake-audio-data'),
type: 'audio/mpeg'
)
);
Prism::fake([$fakeAudio]);
$response = Prism::audio()
->using('openai', 'tts-1')
->withInput('Test audio generation')
->withVoice('alloy')
->asAudio();
expect($response->audio->hasBase64())->toBeTrue();
expect($response->audio->getMimeType())->toBe('audio/mpeg');
});
test('can transcribe speech-to-text', function () {
$fakeTranscription = new TextResponse(
text: 'This is a fake transcription'
);
Prism::fake([$fakeTranscription]);
$audioFile = Audio::fromPath('/fake/path/test.mp3');
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->asText();
expect($response->text)->toBe('This is a fake transcription');
});
```
---
---
url: /getting-started/configuration.md
---
# Configuration
Prism's flexible configuration allows you to easily set up and switch between different AI providers. Let's dive into how you can configure Prism to work with your preferred providers.
## Configuration File
After installation, you'll find the Prism configuration file at `config/prism.php`. If you haven't published it yet, you can do so with:
```bash
php artisan vendor:publish --tag=prism-config
```
Let's break down the key sections of this configuration file:
```php
return [
'prism_server' => [
'enabled' => env('PRISM_SERVER_ENABLED', false),
],
'providers' => [
// Provider configurations here
],
];
```
## Provider Configuration
Prism uses a straightforward provider configuration system that lets you set up multiple AI providers in one place. Each provider has its own section in the configuration file where you can specify:
* API credentials
* Base URLs (useful for self-hosted instances or custom endpoints)
* Other Provider-specific settings
Here's a general template for how providers are configured:
```php
'providers' => [
'provider-name' => [
'api_key' => env('PROVIDER_API_KEY', ''),
'url' => env('PROVIDER_URL', 'https://api.provider.com'),
// Other provider-specific settings
],
],
```
## Environment Variables
Prism follows Laravel's environment configuration best practices. All sensitive or environment-specific values should be stored in your `.env` file. Here's how it works:
1. Each provider's configuration pulls values from environment variables
2. Default values are provided as fallbacks
3. Environment variables follow a predictable naming pattern:
* API keys: `PROVIDER_API_KEY`
* URLs: `PROVIDER_URL`
* Other settings: `PROVIDER_SETTING_NAME`
For example:
```shell
# Prism Server Configuration
PRISM_SERVER_ENABLED=true
# Provider Configuration
PROVIDER_API_KEY=your-api-key-here
PROVIDER_URL=https://custom-endpoint.com
```
> \[!NOTE]
> Remember to always refer to your chosen provider's documentation pages for the most up-to-date configuration options and requirements specific to that provider.
## Overriding config in your code
You can override config in your code in two ways:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
// Via the third parameter of `using()`
$response = Prism::text()
->using(Provider::OpenAI, 'claude-3-5-sonnet-20241022', [
'url' => 'new-base-url'
])
->withPrompt('Explain quantum computing.')
->asText();
// Or via `usingProviderConfig()` (note that this will re-resolve the provider).
$response = Prism::text()
->using(Provider::OpenAI, 'claude-3-5-sonnet-20241022')
->usingProviderConfig([
'url' => 'new-base-url'
])
->withPrompt('Explain quantum computing.')
->asText();
```
---
---
url: /advanced/custom-providers.md
---
# Custom Providers
Want to add support for a new AI provider in Prism? This guide will walk you through creating and registering your own custom provider implementation.
## Building Your Provider
All providers must extend the `Prism\Prism\Providers\Provider` abstract class. This base class provides default implementations for all required methods, throwing exceptions for unsupported actions.
When creating your provider, you'll only need to override the methods for the features you want to support:
* `text()` - For text generation
* `structured()` - For structured output generation
* `embeddings()` - For creating embeddings
* `images()` - For image generation
* `stream()` - For streaming text responses
Here's what that looks like in practice:
```php
namespace App\Prism\Providers;
use Prism\Prism\Providers\Provider;
use Prism\Prism\Text\Request as TextRequest;
use Prism\Prism\Text\Response as TextResponse;
class MyCustomProvider extends Provider
{
public function __construct(
protected string $apiKey,
) {}
public function text(TextRequest $request): TextResponse
{
// Your text generation logic here
// Make API calls, process the response, and return a TextResponse
}
// Only override the methods you need!
}
```
## Registration Process
Once you've created your provider, you'll need to register it with Prism. Let's add it to a service provider:
```php
namespace App\Providers;
use App\Prism\Providers\MyCustomProvider;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->app['prism-manager']->extend('my-custom-provider', function ($app, $config) {
return new MyCustomProvider(
apiKey: $config['api_key'] ?? '',
);
});
}
}
```
Next, add your provider configuration to `config/prism.php`:
```php
return [
'providers' => [
// ... other providers ...
'my-custom-provider' => [
'api_key' => env('MY_CUSTOM_PROVIDER_API_KEY'),
],
],
];
```
That's it! You're ready to use your custom provider:
```php
use Prism\Prism\Facades\Prism;
$response = Prism::text()
->using('my-custom-provider', 'model-name')
->withPrompt('Hello, custom AI!')
->asText();
```
## Custom Error Handling
Your provider inherits a default `handleRequestException` method that handles common HTTP status codes. You can override this method to handle provider-specific errors or add custom logic:
```php
use Illuminate\Http\Client\RequestException;
use Prism\Prism\Exceptions\PrismException;
class MyCustomProvider extends Provider
{
// ... other methods ...
public function handleRequestException(string $model, RequestException $e): never
{
// Handle provider-specific error codes
match ($e->response->getStatusCode()) {
429 => throw PrismRateLimitedException::make(
rateLimits: $this->processRateLimits($e->response),
retryAfter: $e->response->header('retry-after') === ''
? null
: (int) $e->response->header('retry-after'),
),
default => parent::handleRequestException($model, $e),
};
}
}
```
The method must throw an exception (return type `never`). If you don't handle a specific status code, make sure to call the parent method to maintain the default error handling.
## Best Practices
* **Start small**: Begin by implementing just the methods you need. You don't have to support every feature right away.
* **Handle errors gracefully**: Leverage the inherited error handling or override `handleRequestException()` for provider-specific errors (see Custom Error Handling section above).
* **Test thoroughly**: Make sure to test your provider with various inputs and edge cases.
* **Document your models**: Let users know which models your provider supports and any special parameters they can use.
> \[!TIP]
> Looking at existing provider implementations in Prism's source code can give you great insights into best practices and patterns to follow.
---
---
url: /providers/deepseek.md
---
# DeepSeek
## Configuration
```php
'deepseek' => [
'api_key' => env('DEEPSEEK_API_KEY', ''),
'url' => env('DEEPSEEK_URL', 'https://api.deepseek.com/v1')
]
```
## Provider-specific options
## Limitations
### Embeddings
Does not support embeddings.
### Tool Choice
Does not support tool choice.
### Images
Does not support images.
---
---
url: /input-modalities/documents.md
---
# Documents
Prism supports including documents in your messages with some providers.
See the [provider support table](/getting-started/introduction.html#provider-support) to check whether Prism supports your chosen provider.
Note however that provider support may differ by model. If you receive error messages with a provider that Prism indicates is supported, check the provider's documentation as to whether the model you are using supports documents.
## Supported file types
> \[!TIP]
> If provider interoperability is important to your app, we recommend converting documents to markdown.
Please check provider documentation for supported file/mime types, as support differs widely.
The most supported file types are pdf and text/plain (which may include markdown).
## Transfer mediums
> \[!TIP]
> If provider interoperability is important to your app, we recommend using rawContent or base64.
Providers are not consistent in their support of sending file raw contents, base64 and/or URLs.
Prism tries to smooth over these rough edges, but its not always possible.
### Supported conversions
* Where a provider does not support URLs: Prism will fetch the URL and use base64 or rawContent.
* Where you provide a file, base64 or rawContent: Prism will switch between base64 and rawContent depending on what the provider accepts.
### Limitations
* Where a provider only supports URLs: if you provide a file path, raw contents, base64 or chunks, for security reasons Prism does not create a URL for you and your request will fail.
* Chunks cannot be passed between providers, as they could be in different formats (however, currently only Anthropic supports them).
## Getting started
To add a document to your prompt, use the `withPrompt` method with a `Document` value object:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\Media\Document;
// From a local path
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Analyze this document',
[Document::fromLocalPath(
path: 'tests/Fixtures/test-pdf.pdf',
title: 'My document title' // optional
)]
)
->asText();
// From a storage path
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Summarize this document',
[Document::fromStoragePath(
path: 'mystoragepath/file.pdf',
diskName: 'my-disk', // optional - omit/null for default disk
title: 'My document title' // optional
)]
)
->asText();
// From base64
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Extract key points from this document',
[Document::fromBase64(
base64: $baseFromDB,
mimeType: 'optional/mimetype', // optional
title: 'My document title' // optional
)]
)
->asText();
// From raw content
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Review this document',
[Document::fromRawContent(
rawContent: $rawContent,
mimeType: 'optional/mimetype', // optional
title: 'My document title' // optional
)]
)
->asText();
// From a text string
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Process this text document',
[Document::fromText(
text: 'Hello world!',
title: 'My document title' // optional
)]
)
->asText();
// From an URL
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Analyze this document from URL',
[Document::fromUrl(
url: 'https://example.com/test-pdf.pdf',
title: 'My document title' // optional
)]
)
->asText();
// From chunks
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Process this chunked document',
[Document::fromChunks(
chunks: [
'chunk one',
'chunk two'
],
title: 'My document title' // optional
)]
)
->asText();
// From a provider file ID
$response = Prism::text()
->using('my-provider', 'my-model')
->withPrompt(
'Analyze this document from provider file',
[Document::fromFileId(
fileId: 'my-provider-file-id'
)]
)
->asText();
```
## Alternative: Using withMessages
You can also include documents using the message-based approach:
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Document;
$message = new UserMessage(
'Analyze this document',
[Document::fromLocalPath(
path: 'tests/Fixtures/test-pdf.pdf',
title: 'My document title' // optional
)]
);
$response = Prism::text()
->using('my-provider', 'my-model')
->withMessages([$message])
->asText();
```
Or, if using a provider file\_id - use fromFileId:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\Media\Document;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
'Analyze this OpenAI file',
[Document::fromFileId(
fileId: 'file-lsfgSXyV2xEb8gw8fYjXU6'
)]
)
->asText();
```
---
---
url: /providers/elevenlabs.md
---
# ElevenLabs
## Configuration
```php
'elevenlabs' => [
'api_key' => env('ELEVENLABS_API_KEY', ''),
'url' => env('ELEVENLABS_URL', 'https://api.elevenlabs.io/v1/'),
]
```
## Speech-to-Text
ElevenLabs provides speech-to-text through their Scribe model with support for diarization and audio event tagging.
### Basic Usage
```php
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\Media\Audio;
$audioFile = Audio::fromPath('/path/to/recording.mp3');
$response = Prism::audio()
->using('elevenlabs', 'scribe_v1')
->withInput($audioFile)
->asText();
```
## Provider-specific Options
### Language Detection
```php
$response = Prism::audio()
->using('elevenlabs', 'scribe_v1')
->withInput($audioFile)
->withProviderOptions([
'language_code' => 'en',
])
->asText();
```
### Speaker Diarization
```php
$response = Prism::audio()
->using('elevenlabs', 'scribe_v1')
->withInput($audioFile)
->withProviderOptions([
'diarize' => true,
'num_speakers' => 2,
])
->asText();
```
### Audio Event Tagging
```php
$response = Prism::audio()
->using('elevenlabs', 'scribe_v1')
->withInput($audioFile)
->withProviderOptions([
'tag_audio_events' => true,
])
->asText();
```
## Limitations
* Text-to-speech is not yet implemented
---
---
url: /core-concepts/embeddings.md
---
# Embeddings
Transform your text into powerful vector representations! Embeddings let you add semantic search, recommendation systems, and other advanced natural language features to your applications.
## Quick Start
Here's how to generate an embedding with just a few lines of code:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::embeddings()
->using(Provider::OpenAI, 'text-embedding-3-large')
->fromInput('Your text goes here')
->asEmbeddings();
// Get your embeddings vector
$embeddings = $response->embeddings[0]->embedding;
// Check token usage
echo $response->usage->tokens;
```
## Generating multiple embeddings
You can generate multiple embeddings at once with all providers that support embeddings, other than Gemini:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::embeddings()
->using(Provider::OpenAI, 'text-embedding-3-large')
// First embedding
->fromInput('Your text goes here')
// Second embedding
->fromInput('Your second text goes here')
// Third and fourth embeddings
->fromArray([
'Third',
'Fourth'
])
->asEmbeddings();
/** @var Embedding $embedding */
foreach ($embeddings as $embedding) {
// Do something with your embeddings
$embedding->embedding;
}
// Check token usage
echo $response->usage->tokens;
```
## Input Methods
You've got two convenient ways to feed text into the embeddings generator:
### Direct Text Input
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::embeddings()
->using(Provider::OpenAI, 'text-embedding-3-large')
->fromInput('Analyze this text')
->asEmbeddings();
```
### From File
Need to analyze a larger document? No problem:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::embeddings()
->using(Provider::OpenAI, 'text-embedding-3-large')
->fromFile('/path/to/your/document.txt')
->asEmbeddings();
```
> \[!NOTE]
> Make sure your file exists and is readable. The generator will throw a helpful `PrismException` if there's any issue accessing the file.
## Common Settings
Just like with text generation, you can fine-tune your embeddings requests:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::embeddings()
->using(Provider::OpenAI, 'text-embedding-3-large')
->fromInput('Your text here')
->withClientOptions(['timeout' => 30]) // Adjust request timeout
->withClientRetry(3, 100) // Add automatic retries
->asEmbeddings();
```
## Response Handling
The embeddings response gives you everything you need:
```php
namespace Prism\Prism\ValueObjects\Embedding;
// Get an array of Embedding value objects
$embeddings = $response->embeddings;
// Just get first embedding
$firstVectorSet = $embeddings[0]->embedding;
// Loop over all embeddings
/** @var Embedding $embedding */
foreach ($embeddings as $embedding) {
$vectorSet = $embedding->embedding;
}
// Check token usage
$tokenCount = $response->usage->tokens;
```
## Error Handling
Always handle potential errors gracefully:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Exceptions\PrismException;
try {
$response = Prism::embeddings()
->using(Provider::OpenAI, 'text-embedding-3-large')
->fromInput('Your text here')
->asEmbeddings();
} catch (PrismException $e) {
Log::error('Embeddings generation failed:', [
'error' => $e->getMessage()
]);
}
```
## Pro Tips 🌟
**Vector Storage**: Consider using a vector database like Milvus, Qdrant, or pgvector to store and query your embeddings efficiently.
**Text Preprocessing**: For best results, clean and normalize your text before generating embeddings. This might include:
* Removing unnecessary whitespace
* Converting to lowercase
* Removing special characters
* Handling Unicode normalization
> \[!IMPORTANT]
> Different providers and models produce vectors of different dimensions. Always check your provider's documentation for specific details about the embedding model you're using.
---
---
url: /advanced/error-handling.md
---
# Error handling
By default, Prism throws a `PrismException` for Prism errors, or a `PrismServerException` for Prism Server errors.
For production use cases, you may find yourself needing to catch Exceptions more granularly, for instance to provide more useful error messages to users or to implement failover or retry logic.
Prism has begun rolling out more specific exceptions as use cases arise.
## Provider agnostic exceptions
* `PrismStructuredDecodingException` where a provider has returned invalid JSON for a structured request.
## Exceptions based on provider feedback
Prism currently supports three exceptions based on provider feedback:
* `PrismRateLimitedException` where you have hit a rate limit or quota (see [Handling rate limits](/advanced/rate-limits.html) for more info).
* `PrismProviderOverloadedException` where the provider is unable to fulfil your request due to capacity issues.
* `PrismRequestTooLargeException` where your request is too large.
However, as providers all handle errors differently, support is being rolled out incrementally. If you'd like to make your first contribution, adding one or more of these exceptions for a provider would make a great first contribution. If you'd like to discuss, start an issue on Github, or just jump straight into a pull request.
---
---
url: /providers/gemini.md
---
# Gemini
## Configuration
```php
'gemini' => [
'api_key' => env('GEMINI_API_KEY', ''),
'url' => env('GEMINI_URL', 'https://generativelanguage.googleapis.com/v1beta/models'),
],
```
## Search grounding
Google Gemini offers built-in search grounding capabilities that allow your AI to search the web for real-time information. This is a provider tool that uses Google's search infrastructure. For more information about the difference between custom tools and provider tools, see [Tools & Function Calling](/core-concepts/tools-function-calling#provider-tools).
You may enable Google search grounding on text requests using withProviderTools:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\ProviderTool;
$response = Prism::text()
->using(Provider::Gemini, 'gemini-2.0-flash')
->withPrompt('What is the stock price of Google right now?')
// Enable search grounding
->withProviderTools([
new ProviderTool('google_search')
])
->asText();
```
If you use search groundings, Google require you meet certain [display requirements](https://ai.google.dev/gemini-api/docs/grounding/search-suggestions).
The data you need to meet these display requirements, and to build e.g. footnote functionality will be saved to the response's `additionalContent` property.
```php
// The Google supplied and styled widget to click through to results.
$response->additionalContent['searchEntryPoint'];
// The search queries made by the model
$response->additionalContent['searchQueries'];
// The detail needed to build your citations
$response->additionalContent['groundingSupports'];
```
`groundingSupports` is an array of `MessagePartWithSearchGroundings`, which you can use to build up footnotes as follows:
```php
use Prism\Prism\Providers\Gemini\ValueObjects\MessagePartWithSearchGroundings;
use Prism\Prism\Providers\Gemini\ValueObjects\SearchGrounding;
$text = '';
$footnotes = [];
$footnoteId = 1;
/** @var MessagePartWithSearchGrounding $part */
foreach ($response->additionalContent['groundingSupports'] as $part) {
$text .= $part->text;
/** @var SearchGrounding $grounding */
foreach ($part->groundings as $grounding) {
$footnotes[] = [
'id' => $footnoteId,
'firstCharacter' => $part->startIndex,
'lastCharacter' => $part->endIndex,
'title' => $grounding->title,
'uri' => $grounding->uri,
'confidence' => $grounding->confidence // Float 0-1
];
$text .= ''.$footnoteId.'';
$footnoteId++;
}
}
// Pass $text and $footnotes to your frontend.
```
## Caching
Prism supports Gemini prompt caching, though due to Gemini requiring you first upload the cached content, it works a little differently to other providers.
To store content in the cache, use the Gemini provider cache method as follows:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\Providers\Gemini\Gemini;
use Prism\Prism\ValueObjects\Media\Document;
use Prism\Prism\ValueObjects\Messages\SystemMessage;
use Prism\Prism\ValueObjects\Messages\UserMessage;
/** @var Gemini */
$provider = Prism::provider(Provider::Gemini);
$object = $provider->cache(
model: 'gemini-1.5-flash-002',
messages: [
new UserMessage('', [
Document::fromLocalPath('tests/Fixtures/long-document.pdf'),
]),
],
systemPrompts: [
new SystemMessage('You are a legal analyst.'),
],
ttl: 60
);
```
Then reference that object's name in your request using withProviderOptions:
```php
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash-002')
->withProviderOptions(['cachedContentName' => $object->name])
->withPrompt('In no more than 100 words, what is the document about?')
->asText();
```
## Embeddings
You can customize your Gemini embeddings request with additional parameters using `->withProviderOptions()`.
### Title
You can add a title to your embedding request. Only applicable when TaskType is `RETRIEVAL_DOCUMENT`
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::embeddings()
->using(Provider::Gemini, 'text-embedding-004')
->fromInput('The food was delicious and the waiter...')
->withProviderOptions(['title' => 'Restaurant Review'])
->asEmbeddings();
```
### Task Type
Gemini allows you to specify the task type for your embeddings to optimize them for specific use cases:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::embeddings()
->using(Provider::Gemini, 'text-embedding-004')
->fromInput('The food was delicious and the waiter...')
->withProviderOptions(['taskType' => 'RETRIEVAL_QUERY'])
->asEmbeddings();
```
[Available task types](https://ai.google.dev/api/embeddings#tasktype)
### Output Dimensionality
You can control the dimensionality of your embeddings:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::embeddings()
->using(Provider::Gemini, 'text-embedding-004')
->fromInput('The food was delicious and the waiter...')
->withProviderOptions(['outputDimensionality' => 768])
->asEmbeddings();
```
### Thinking Mode
Gemini 2.5 series models use an internal "thinking process" during response generation. Thinking is on by default as these models have the ability to automatically decide when and how much to think based on the prompt. If you would like to customize how many tokens the model may use for thinking, or disable thinking altogether, utilize the `withProviderOptions()` method, and pass through an array with a key value pair with `thinkingBudget` and an integer representing the budget of tokens. Set this value to `0` to disable thinking.
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Gemini, 'gemini-2.5-flash-preview')
->withPrompt('Explain the concept of Occam\'s Razor and provide a simple, everyday example.')
// Set thinking budget
->withProviderOptions(['thinkingBudget' => 300])
->asText();
```
> \[!NOTE]
> Do not specify a `thinkingBudget` on 2.0 or prior series Gemini models as your request will fail.
## Media Support
Gemini has robust support for processing multimedia content:
### Video Analysis
Gemini can process and analyze video content including standard video files and YouTube videos. Prism implements this through the `Video` value object which maps to Gemini's video processing capabilities.
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Video;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withMessages([
new UserMessage(
'What is happening in this video?',
additionalContent: [
Video::fromUrl('https://example.com/sample-video.mp4'),
],
),
])
->asText();
```
### YouTube Integration
Gemini has special support for YouTube videos. You can easily `analyze/summarize` YouTube content by providing the URL:
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Video;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withMessages([
new UserMessage(
'Summarize this YouTube video:',
additionalContent: [
Video::fromUrl('https://www.youtube.com/watch?v=dQw4w9WgXcQ'),
],
),
])
->asText();
```
### Audio Processing
Gemini can analyze audio files for various tasks like transcription, content analysis, and audio scene understanding. The implementation in Prism uses the `Audio` value object which is specifically designed for Gemini's audio processing capabilities.
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Audio;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withMessages([
new UserMessage(
'Transcribe this audio file:',
additionalContent: [
Audio::fromLocalPath('/path/to/audio.mp3'),
],
),
])
->asText();
```
---
---
url: /providers/groq.md
---
# Groq
## Configuration
```php
'groq' => [
'api_key' => env('GROQ_API_KEY', ''),
'url' => env('GROQ_URL', 'https://api.groq.com/openai/v1'),
],
```
## Audio Processing
Groq provides high-performance audio processing capabilities through their ultra-fast Language Processing Unit (LPU) architecture, enabling both text-to-speech (TTS) and speech-to-text (STT) functionality with exceptional speed and quality.
### Text-to-Speech
Groq offers PlayAI TTS models that can convert text into natural-sounding speech with support for multiple languages and voices.
#### Basic TTS Usage
```php
use Prism\Prism\Prism;
$response = Prism::audio()
->using('groq', 'playai-tts')
->withInput('Hello, welcome to our application!')
->withVoice('Fritz-PlayAI')
->asAudio();
// Save the audio file
$audioData = base64_decode($response->audio->base64);
file_put_contents('welcome.wav', $audioData);
```
#### TTS Configuration Options
Control audio format and quality:
```php
$response = Prism::audio()
->using('groq', 'playai-tts')
->withInput('Testing different audio settings.')
->withVoice('Celeste-PlayAI')
->withProviderOptions([
'response_format' => 'wav', // wav (default)
'speed' => 1.2, // Speed: 0.5 to 5.0
'sample_rate' => 48000, // Sample rate options: 8000, 16000, 22050, 24000, 32000, 44100, 48000
])
->asAudio();
echo "Audio type: " . $response->audio->getMimeType();
```
#### Arabic Text-to-Speech
```php
$response = Prism::audio()
->using('groq', 'playai-tts-arabic')
->withInput('مرحبا بكم في تطبيقنا')
->withVoice('Amira-PlayAI')
->asAudio();
file_put_contents('arabic_speech.wav', base64_decode($response->audio->base64));
```
### Speech-to-Text
Groq provides ultra-fast speech recognition using Whisper models, offering exceptional speed with real-time factors of up to 299x.
#### Basic STT Usage
```php
use Prism\Prism\ValueObjects\Media\Audio;
$audioFile = Audio::fromPath('/path/to/recording.mp3');
$response = Prism::audio()
->using('groq', 'whisper-large-v3')
->withInput($audioFile)
->asText();
echo "Transcription: " . $response->text;
```
#### Model Selection Guide
Choose the right model for your use case:
```php
$response = Prism::audio()
->using('groq', 'whisper-large-v3')
->withInput($audioFile)
->asText();
// For fastest English-only transcription
$response = Prism::audio()
->using('groq', 'distil-whisper-large-v3-en')
->withInput($audioFile)
->asText();
// For balanced speed and multilingual capability
$response = Prism::audio()
->using('groq', 'whisper-large-v3-turbo')
->withInput($audioFile)
->asText();
```
#### Language Detection and Specification
Whisper can automatically detect languages or you can specify them:
```php
$response = Prism::audio()
->using('groq', 'whisper-large-v3')
->withInput($audioFile)
->withProviderOptions([
'language' => 'es', // ISO-639-1 code (optional)
'temperature' => 0.2, // Lower for more focused results
])
->asText();
```
#### Response Formats
Get transcriptions in different formats:
```php
// Standard JSON response
$response = Prism::audio()
->using('groq', 'whisper-large-v3')
->withInput($audioFile)
->withProviderOptions([
'response_format' => 'json', // json, text, verbose_json
])
->asText();
// Verbose JSON includes timestamps and segments
$response = Prism::audio()
->using('groq', 'whisper-large-v3')
->withInput($audioFile)
->withProviderOptions([
'response_format' => 'verbose_json',
'timestamp_granularities' => ['segment'], // word, segment
])
->asText();
// Access detailed segment information
$segments = $response->additionalContent['segments'] ?? [];
foreach ($segments as $segment) {
echo "Text: " . $segment['text'] . "\n";
echo "Start: " . $segment['start'] . "s\n";
echo "End: " . $segment['end'] . "s\n";
}
```
#### Context and Prompts
Improve transcription accuracy with context:
```php
$response = Prism::audio()
->using('groq', 'whisper-large-v3')
->withInput($audioFile)
->withProviderOptions([
'prompt' => 'This is a technical discussion about machine learning and artificial intelligence.',
'language' => 'en',
'temperature' => 0.1, // Lower temperature for technical content
])
->asText();
```
#### Creating Audio Objects
```php
use Prism\Prism\ValueObjects\Media\Audio;
// From local file path
$audio = Audio::fromPath('/path/to/audio.mp3');
// From remote URL (recommended for large files)
$audio = Audio::fromUrl('https://example.com/recording.wav');
// From base64 encoded data
$audio = Audio::fromBase64($base64AudioData, 'audio/mpeg');
// From binary content
$audioContent = file_get_contents('/path/to/audio.wav');
$audio = Audio::fromContent($audioContent, 'audio/wav');
```
---
---
url: /advanced/rate-limits.md
---
# Handling Rate Limits
Hitting issues with rate limits? We've got you covered!
In this guide we will look at handling:
* situations where you actually hit a rate limit (i.e. HTTP 429); and
* dynamic rate limiting (figuring out when you can make your next request, from a successful request).
## Provider support
Prism throws a `PrismRateLimitedException` for all providers other than DeepSeek (which does not have rate limits).
Prism provides an array of `ProviderRateLimit` value objects on the exception and on meta for all providers other than OpenAI, Gemini, xAI and VoyageAI - as they do not provide the necessary headers to do so.
## The ProviderRateLimit value object
Throughout this guide, we'll talk about the `ProviderRateLimit` value object.
Each `ProviderRateLimit` has four properties:
* name - the name given to that rate limit by the provider - e.g. "input-tokens"
* limit - the current limit set on your API key by the provider - e.g. for input-tokens, perhaps 80000
* remaining - how many you have left - e.g. for input-tokens if you have used 30000 out of your 80000 limit - this will be 50000
* resetsAt - a Carbon instance with the date and time at which remaining will reset to limit
## Handling a rate limit hit
Prism throws a `PrismRateLimitedException` when you hit a rate limit.
You can catch that exception, gracefully fail and inspect the `rateLimits` property which contains an array of `ProviderRateLimit`s.
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\ProviderRateLimit;
use Prism\Prism\Exceptions\PrismRateLimitedException;
try {
Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt('Hello world!')
->asText();
}
catch (PrismRateLimitedException $e) {
/** @var ProviderRateLimit $rate_limit */
foreach ($e->rateLimits as $rate_limit) {
// Loop through rate limits...
}
// Log, fail gracefully, etc.
}
```
### Figuring out which rate limit you have hit
In a simple world, they'd only be one rate limit.
However most providers implement various rate limits (e.g. request, input tokens, output tokens, etc.) and provide you with information on all of them on all requests, regardless of which you have hit.
For simple rate limits like "requests", the `remaining` property on `ProviderRateLimit` will be 0 if you have hit it. These are easy to find:
```php
use Prism\Prism\ValueObjects\ProviderRateLimit;
use Illuminate\Support\Arr;
try {
// Your request
}
catch (PrismRateLimitedException $e) {
$hit_limit = Arr::first($e->rateLimits, fn(ProviderRateLimit $rate_limit) => $rate_limit->remaining === 0);
}
```
For less simple rate limits like input tokens, the `remaining` property may not be zero. For instance, if you have 5,000 input tokens remaining and submit a request requiring 6,000 tokens, you'll be rate limited but remaining will still show 5,000.
Here, you may need to implement some logic to approximate how many tokens your request will use before sending it, and then test against that:
```php
use Prism\Prism\ValueObjects\ProviderRateLimit;
use Illuminate\Support\Arr;
try {
// Your request
}
catch (PrismRateLimitedException $e) {
$input_token_limit = Arr::first($e->rateLimits, fn(ProviderRateLimit $rate_limit) => $rate_limit->name === 'input-tokens');
if ($input_token_limit < $your_token_estimate) {
// Handle
}
}
```
To help with approximating input token usage, we plan to implement Anthopic's token counting endpoint in a future release.
For providers that don't have a token counting endpoint, you could either roll your own token counter or use something like [tiktoken](https://github.com/openai/tiktoken) if you are comfortable calling out to Python.
Once you know which rate limit you have hit, you'll want to ensure your app does not continue making requests until after the `ProviderRateLimit` `resetsAt` property.
If you aren't sure where to start with that, check out the [What should you do with rate limit information](#what-should-you-do-with-rate-limit-information) section below.
## Dynamic rate limiting
Prism adds the same rate limit information to every successful request:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\ProviderRateLimit;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt('Hello world!')
->asText();
/** @var ProviderRateLimit $rate_limit */
foreach ($response->meta->rateLimits as $rate_limit) {
// Handle
}
```
Armed with that information, you'll probably want to [update your app's rate limiter(s)](#what-should-you-do-with-rate-limit-information).
## What should you do with rate limit information?
You'll likely want to implement a rate limiter within your app. Thankfully Laravel, as always, makes this very easy!
You should take a look at the [rate limiting](https://laravel.com/docs/11.x/rate-limiting) docs, and if you are firing requests from your queue, check out the [job middleware](https://laravel.com/docs/11.x/queues#job-middleware) docs.
You should implement a rate limiter / job middleware for each of the provider rate limits your application typically hits.
---
---
url: /core-concepts/image-generation.md
---
# Image Generation
Generate stunning images from text prompts using AI-powered models. Prism provides a clean, consistent API for image generation across different providers, starting with comprehensive OpenAI support.
## Getting Started
Creating images with Prism is as simple as describing what you want:
```php
use Prism\Prism\Prism;
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('A cute baby sea otter floating on its back in calm blue water')
->generate();
$image = $response->firstImage();
echo $image->url; // https://oaidalleapiprodscus.blob.core.windows.net/...
```
## Provider Support
Currently, Prism supports image generation through:
* **OpenAI**: DALL-E 2, DALL-E 3, and GPT-Image-1 models
Additional providers will be added in future releases as the ecosystem evolves.
## Basic Usage
### Simple Generation
The most straightforward way to generate an image:
```php
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('A serene mountain landscape at sunset')
->generate();
// Access the generated image
$image = $response->firstImage();
if ($image->hasUrl()) {
echo "Image URL: " . $image->url;
}
if ($image->hasBase64()) {
echo "Base64 Image Data: " . $image->base64;
}
```
### Working with Responses
The response object provides helpful methods for accessing generated content:
```php
$response = Prism::image()
->using('openai', 'dall-e-2')
->withPrompt('Abstract geometric patterns in vibrant colors')
->generate();
// Check if images were generated
if ($response->hasImages()) {
echo "Generated {$response->imageCount()} image(s)";
// Access all images
foreach ($response->images as $image) {
if ($image->hasUrl()) {
echo "Image: {$image->url}\n";
}
if ($image->hasBase64()) {
echo "Base64 Image: " . substr($image->base64, 0, 50) . "...\n";
}
if ($image->hasRevisedPrompt()) {
echo "Revised prompt: {$image->revisedPrompt}\n";
}
}
// Or just get the first one
$firstImage = $response->firstImage();
}
// Check usage information
echo "Prompt tokens: {$response->usage->promptTokens}";
echo "Model used: {$response->meta->model}";
```
## Provider-Specific Options
While Prism provides a consistent API, you can access provider-specific features using the `withProviderOptions()` method.
### OpenAI Options
OpenAI offers various customization options depending on the model:
#### DALL-E 3 Options
```php
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('A beautiful sunset over mountains')
->withProviderOptions([
'size' => '1792x1024', // 1024x1024, 1024x1792, 1792x1024
'quality' => 'hd', // standard, hd
'style' => 'vivid', // vivid, natural
'response_format' => 'url', // url, b64_json
])
->generate();
```
#### GPT-Image-1 (Base64 Only)
The GPT-Image-1 model always returns base64-encoded images, regardless of the `response_format` setting:
```php
$response = Prism::image()
->using('openai', 'gpt-image-1')
->withPrompt('A cute baby sea otter floating on its back')
->withProviderOptions([
'size' => '1024x1024', // 1024x1024, 1536x1024, 1024x1536, auto
'quality' => 'high', // auto, high, medium, low
'background' => 'transparent', // transparent, opaque, auto
'output_format' => 'png', // png, jpeg, webp
'output_compression' => 90, // 0-100 (for jpeg/webp)
])
->generate();
$image = $response->firstImage();
if ($image->hasBase64()) {
// Save the base64 image to a file
file_put_contents('generated-image.png', base64_decode($image->base64));
echo "Base64 image saved to generated-image.png";
}
```
#### Base64 vs URL Responses
Different models return images in different formats:
* **GPT-Image-1**: Always returns base64-encoded images in the `base64` property
* **DALL-E 2 & 3**: Return URLs by default, but can return base64 when `response_format` is set to `'b64_json'`
```php
// Request base64 format from DALL-E 3
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('Abstract art')
->withProviderOptions([
'response_format' => 'b64_json',
])
->generate();
$image = $response->firstImage();
if ($image->hasBase64()) {
echo "Received base64 image data";
}
```
## Testing
Prism provides convenient fakes for testing image generation:
```php
use Prism\Prism\Prism;
use Prism\Prism\Testing\PrismFake;
test('can generate images', function () {
$fake = PrismFake::create()->image();
Prism::fake($fake);
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('Test image')
->generate();
expect($response->hasImages())->toBeTrue();
expect($response->firstImage()->url)->toContain('fake-image-url');
});
```
Need help with a specific provider or use case? Check the [provider documentation](/providers/openai) for detailed configuration options and examples.
---
---
url: /input-modalities/images.md
---
# Images
Prism supports including images in your messages for vision analysis for most providers.
See the [provider support table](/getting-started/introduction.html#provider-support) to check whether Prism supports your chosen provider.
Note however that provider support may differ by model. If you receive error messages with a provider that Prism indicates is supported, check the provider's documentation as to whether the model you are using supports images.
## Getting started
To add an image to your prompt, use the `withPrompt` method with an `Image` value object:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Media\Image;
// From a local path
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
"What's in this image?",
[Image::fromLocalPath(path: '/path/to/image.jpg')]
)
->asText();
// From a path on a storage disk
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
"What's in this image?",
[Image::fromStoragePath(
path: '/path/to/image.jpg',
diskName: 'my-disk' // optional - omit/null for default disk
)]
)
->asText();
// From a URL
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
'Analyze this diagram:',
[Image::fromUrl(url: 'https://example.com/diagram.png')]
)
->asText();
// From base64
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
'Analyze this diagram:',
[Image::fromBase64(base64: base64_encode(file_get_contents('/path/to/image.jpg')))]
)
->asText();
// From raw content
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
'Analyze this diagram:',
[Image::fromRawContent(rawContent: file_get_contents('/path/to/image.jpg'))]
)
->asText();
```
## Alternative: Using withMessages
You can also include images using the message-based approach:
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Image;
$message = new UserMessage(
"What's in this image?",
[Image::fromLocalPath(path: '/path/to/image.jpg')]
);
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withMessages([$message])
->asText();
```
## Transfer mediums
Providers are not consistent in their support of sending raw contents, base64 and/or URLs (as noted above).
Prism tries to smooth over these rough edges, but its not always possible.
### Supported conversions
* Where a provider does not support URLs: Prism will fetch the URL and use base64 or rawContent.
* Where you provide a file, base64 or rawContent: Prism will switch between base64 and rawContent depending on what the provider accepts.
### Limitations
* Where a provider only supports URLs: if you provide a file path, raw contents or base64, for security reasons Prism does not create a URL for you and your request will fail.
---
---
url: /getting-started/installation.md
---
# Installation
Getting started with Prism is a breeze.
## Requirements
Before we dive in, make sure your project meets these requirements:
* PHP 8.2 or higher
* Laravel 11.0 or higher
## Step 1: Composer Installation
::: tip
Prism is actively evolving. To prevent unexpected issues from breaking changes, we strongly recommend pinning your installation to a specific version. Example: "prism-php/prism": "^0.3.0".
:::
First, let's add Prism to your project using Composer. Open your terminal, navigate to your project directory, and run:
```bash
composer require prism-php/prism
```
This command will download Prism and its dependencies into your project.
## Step 2: Publish the Configuration
Prism comes with a configuration file that you'll want to customize. Publish it to your config directory by running:
```bash
php artisan vendor:publish --tag=prism-config
```
This will create a new file at `config/prism.php`. We'll explore how to configure Prism in the next section.
---
---
url: /getting-started/introduction.md
---
# Introduction
Large Language Models (LLMs) have revolutionized how we interact with artificial intelligence, enabling applications to understand, generate, and manipulate human language with unprecedented sophistication. These powerful models open up exciting possibilities for developers, from creating chatbots and content generators to building complex AI-driven applications.
Prism **simplifies the process of integrating LLMs into your Laravel projects**, providing a unified interface to work with various AI providers. This allows you to focus on crafting innovative AI features for your users, rather than getting bogged down in the intricacies of different APIs and implementation details.
Here's a quick example of how you can generate text using Prism:
::: code-group
```php [Anthropic]
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-7-sonnet-latest')
->withSystemPrompt(view('prompts.system'))
->withPrompt('Explain quantum computing to a 5-year-old.')
->asText();
echo $response->text;
```
```php [Mistral]
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Mistral, 'mistral-medium')
->withSystemPrompt(view('prompts.system'))
->withPrompt('Explain quantum computing to a 5-year-old.')
->asText();
echo $response->text;
```
```php [Ollama]
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Ollama, 'llama2')
->withSystemPrompt(view('prompts.system'))
->withPrompt('Explain quantum computing to a 5-year-old.')
->asText();
echo $response->text;
```
```php [OpenAI]
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::OpenAI, 'gpt-4')
->withSystemPrompt(view('prompts.system'))
->withPrompt('Explain quantum computing to a 5-year-old.')
->asText();
echo $response->text;
```
:::
Prism draws significant inspiration from the [Vercel AI SDK](https://sdk.vercel.ai/docs/ai-sdk-core), adapting its powerful concepts and developer-friendly approach to the Laravel ecosystem.
## Key Features
* **Unified Provider Interface**: Switch seamlessly between AI providers like OpenAI, Anthropic, and Ollama without changing your application code.
* **Tool System**: Extend AI capabilities by defining custom tools that can interact with your application's business logic.
* **Image Support**: Work with multi-modal models that can process both text and images.
Prism also provides a fluent `prism()` helper function to resolve the `Prism` instance from the application container.
```php
prism()
->text()
->using(Provider::OpenAI, 'gpt-4')
->withPrompt('Explain quantum computing to a 5-year-old.')
->asText();
```
## Providers
We currently offer first-party support for these leading AI providers:
* [Anthropic](/providers/anthropic.md)
* [DeepSeek](/providers/deepseek.md)
* [Groq](/providers/groq.md)
* [Mistral](/providers/mistral.md)
* [Ollama](/providers/ollama.md)
* [OpenAI](/providers/openai.md)
* [xAI](/providers/xai.md)
Each provider brings its own strengths to the table, and Prism makes it easy to use them all through a consistent, elegant interface.
## Provider Support
Make sure you check the dedicated provider pages for considerations, limitations, and options. Support may be model dependant, check with your provider for model specific features and support.
---
---
url: /providers/mistral.md
---
# Mistral
## Configuration
```php
'mistral' => [
'api_key' => env('MISTRAL_API_KEY', ''),
'url' => env('MISTRAL_URL', 'https://api.mistral.ai/v1'),
],
```
## Provider-specific options
## Audio Processing
Mistral provides advanced speech-to-text capabilities through their Voxtral models, offering state-of-the-art transcription accuracy with native multilingual support and audio understanding features.
### Speech-to-Text
Mistral's Voxtral models deliver exceptional speech recognition performance, outperforming industry standards like Whisper large-v3 across multiple languages and acoustic environments.
#### Basic STT Usage
```php
use Prism\Prism\ValueObjects\Media\Audio;
$audioFile = Audio::fromPath('/path/to/recording.mp3');
$response = Prism::audio()
->using('mistral', 'voxtral-mini-2507')
->withInput($audioFile)
->asText();
echo "Transcription: " . $response->text;
```
#### Model Selection Guide
Choose the right Voxtral model for your use case:
```php
// For production transcription with highest accuracy
$response = Prism::audio()
->using('mistral', 'voxtral-small-latest')
->withInput($audioFile)
->asText();
// For efficient transcription and edge deployment
$response = Prism::audio()
->using('mistral', 'voxtral-mini-latest')
->withInput($audioFile)
->asText();
// For optimized transcription-only service
$response = Prism::audio()
->using('mistral', 'voxtral-mini-2507')
->withInput($audioFile)
->asText();
```
#### Language Detection and Specification
Voxtral automatically detects languages or you can specify them for better accuracy:
```php
$response = Prism::audio()
->using('mistral', 'voxtral-mini-2507')
->withInput($audioFile)
->withProviderOptions([
'language' => 'en', // ISO-639-1 code (optional)
'temperature' => 0.0, // Lower for more deterministic results
])
->asText();
// Multilingual support - single model handles multiple languages
$response = Prism::audio()
->using('mistral', 'voxtral-mini-2507')
->withInput($multilingualAudioFile)
->withProviderOptions([
// Auto-detection works well for mixed-language content
'temperature' => 0.1,
])
->asText();
```
#### Timestamps and Segmentation
Get detailed timing information with your transcriptions:
```php
$response = Prism::audio()
->using('mistral', 'voxtral-mini-2507')
->withInput($audioFile)
->withProviderOptions([
'timestamp_granularities' => ['segment'], // Available: word, segment
'response_format' => 'json',
])
->asText();
// Access segment information with timestamps
$segments = $response->additionalContent['segments'] ?? [];
foreach ($segments as $segment) {
echo "Text: " . $segment['text'] . "\n";
echo "Start: " . $segment['start'] . "s\n";
echo "End: " . $segment['end'] . "s\n";
}
```
#### Context and Prompts
Improve transcription accuracy with contextual information:
```php
$response = Prism::audio()
->using('mistral', 'voxtral-mini-2507')
->withInput($audioFile)
->withProviderOptions([
'prompt' => 'This is a medical consultation discussing patient symptoms and treatment options.',
'language' => 'en',
'temperature' => 0.0, // Deterministic for medical content
])
->asText();
```
#### Long-form Audio Processing
Voxtral handles extended audio without chunking:
```php
// Process up to 30 minutes of audio in a single request
$longAudioFile = Audio::fromPath('/path/to/long_meeting.wav');
$response = Prism::audio()
->using('mistral', 'voxtral-small-latest')
->withInput($longAudioFile)
->withProviderOptions([
'timestamp_granularities' => ['segment'],
'language' => 'en',
])
->asText();
echo "Full transcription: " . $response->text;
// Access usage information
if ($response->usage) {
echo "Audio duration: " . $response->usage->promptTokens . " tokens\n";
echo "Total tokens: " . $response->usage->totalTokens . "\n";
}
```
#### Creating Audio Objects
```php
use Prism\Prism\ValueObjects\Media\Audio;
// From local file path
$audio = Audio::fromPath('/path/to/audio.mp3');
// From remote URL
$audio = Audio::fromUrl('https://example.com/recording.wav');
// From base64 encoded data
$audio = Audio::fromBase64($base64AudioData, 'audio/mpeg');
// From binary content
$audioContent = file_get_contents('/path/to/audio.wav');
$audio = Audio::fromContent($audioContent, 'audio/wav');
```
## Documents
The text generation part of the exposed Facade only allows documents to be passed in through via URL.
See the [documents](./../input-modalities/documents.md) on how to do that.
## OCR
Mistral provides an OCR endpoint which can be used to extract text from documents.
This OCR endpoint can be used like this:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
use Prism\Prism\Tool;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Messages\SystemMessage;
use Prism\Prism\Providers\Mistral\Mistral;
use Prism\Prism\Providers\Mistral\ValueObjects\OCRResponse;
/** @var Mistral $provider */
$provider = Prism::provider(\Prism\Prism\Enums\Provider::Mistral);
/** @var OCRResponse $ocrResponse */
$ocrResponse = $provider->ocr(
'mistral-ocr-latest',
Document::fromUrl('https://prismphp.com/storage/prism-text-generation.pdf')
);
/**
* Just need the full text of all the pages combined? Use the toText() method.
*/
$text = $ocrResponse->toText();
```
::: tip
The OCR endpoint response time can vary depending on the size of the document. We recommend doing this in the background like a queue with a longer timeout.
:::
---
---
url: /providers/ollama.md
---
# Ollama
## Configuration
```php
'ollama' => [
'url' => env('OLLAMA_URL', 'http://localhost:11434/v1'),
],
```
## Ollama Options
Ollama allows you to customize how the model is run via [options](https://github.com/ollama/ollama/blob/main/docs/modelfile.md#parameter). These options can be passed via the `->withProviderOptions()` method.
```php
Prism::text() // [!code focus]
->using(Provider::Ollama, 'gemma3:1b')
->withPrompt('Who are you?')
->withClientOptions(['timeout' => 60])
->withProviderOptions([ // [!code focus]
'top_p' => 0.9, // [!code focus]
'num_ctx' => 4096, // [!code focus]
]) // [!code focus]
```
> \[!NOTE]
> Using `withProviderOptions` will override settings like `topP` and `temperature`
## Considerations
### Timeouts
Depending on your configuration, responses tend to time out. You may need to extend the client's timeout using `->withClientOptions(['timeout' => $seconds])`.
```php
Prism::text() // [!code focus]
->using(Provider::Ollama, 'gemma3:1b')
->withPrompt('Who are you?')
->withClientOptions(['timeout' => 60]) // [!code focus]
```
### Structured Output
Ollama doesn't have native JSON mode or structured output like some providers, Prism implements a robust workaround for structured output:
* We automatically append instructions to your prompt that guide the model to output valid JSON matching your schema
* If the response isn't valid JSON, Prism will raise a PrismException
## Limitations
### Image URL
Ollama does not support images using `Image::fromUrl()`.
### Tool Choice
Ollama does not currently support tool choice / required tools.
---
---
url: /providers/openai.md
---
# OpenAI
## Configuration
```php
'openai' => [
'url' => env('OPENAI_URL', 'https://api.openai.com/v1'),
'api_key' => env('OPENAI_API_KEY', ''),
'organization' => env('OPENAI_ORGANIZATION', null),
]
```
## Provider-specific options
### Strict Tool Schemas
Prism supports OpenAI's [function calling with Structured Outputs](https://platform.openai.com/docs/guides/function-calling#function-calling-with-structured-outputs) via provider-specific meta.
```php
Tool::as('search') // [!code focus]
->for('Searching the web')
->withStringParameter('query', 'the detailed search query')
->using(fn (): string => '[Search results]')
->withProviderOptions([ // [!code focus]
'strict' => true, // [!code focus]
]); // [!code focus]
```
### Strict Structured Output Schemas
```php
$response = Prism::structured()
->withProviderOptions([ // [!code focus]
'schema' => [ // [!code focus]
'strict' => true // [!code focus]
] // [!code focus]
]) // [!code focus]
```
### Metadata
```php
$response = Prism::structured()
->withProviderOptions([ // [!code focus]
'metadata' => [ // [!code focus]
'project_id' => 23 // [!code focus]
] // [!code focus]
]) // [!code focus]
```
### Previous Responses
Prism supports OpenAI's [conversation state](https://platform.openai.com/docs/guides/conversation-state#openai-apis-for-conversation-state) with the `previous_response_id` parameter.
```php
$response = Prism::structured()
->withProviderOptions([ // [!code focus]
'previous_response_id' => 'response_id' // [!code focus]
]) // [!code focus]
```
### Truncation
```php
$response = Prism::structured()
->withProviderOptions([ // [!code focus]
'truncation' => 'auto' // [!code focus]
]) // [!code focus]
```
### Reasoning Models
OpenAI's reasoning models like `gpt-5`, `gpt-5-mini`, and `gpt-5-nano` use advanced reasoning capabilities to think through complex problems before responding. These models excel at multi-step problem solving, coding, scientific reasoning, and complex analysis tasks.
#### Reasoning Effort
Control how much reasoning the model performs before generating a response using the `reasoning` parameter:
```php
$response = Prism::text()
->using('openai', 'gpt-5')
->withPrompt('Write a PHP function to implement a binary search algorithm with proper error handling')
->withProviderOptions([ // [!code focus]
'reasoning' => ['effort' => 'high'] // [!code focus]
]) // [!code focus]
->asText();
```
Available reasoning effort levels:
* **`low`**: Faster responses with economical token usage, suitable for simpler tasks
* **`medium`**: Balanced approach between speed and reasoning depth (default)
* **`high`**: More thorough reasoning for complex problems requiring deep analysis
> \[!NOTE]
> Reasoning models generate internal "reasoning tokens" that help them think through problems. These tokens are included in your usage costs but aren't visible in the response.
#### Reasoning Token Usage
You can track reasoning token usage through the response's usage information:
```php
$response = Prism::text()
->using('openai', 'gpt-5-mini')
->withPrompt('Refactor this PHP code to use dependency injection')
->withProviderOptions([
'reasoning' => ['effort' => 'medium']
])
->asText();
// Access reasoning token usage
$usage = $response->firstStep()->usage;
echo "Reasoning tokens: " . $usage->thoughtTokens;
echo "Total completion tokens: " . $usage->completionTokens;
```
### Caching
Automatic caching does not currently work with JsonMode. Please ensure you use StructuredMode if you wish to utilise automatic caching.
## Provider Tools
OpenAI offers built-in provider tools that can be used alongside your custom tools. These tools are executed by OpenAI's infrastructure and provide specialized capabilities. For more information about the difference between custom tools and provider tools, see [Tools & Function Calling](/core-concepts/tools-function-calling#provider-tools).
### Code Interpreter
The OpenAI code interpreter allows your AI to execute Python code in a secure, sandboxed environment. This is particularly useful for mathematical calculations, data analysis, and code execution tasks.
```php
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\ProviderTool;
Prism::text()
->using('openai', 'gpt-4.1')
->withPrompt('Solve the equation 3x + 10 = 14.')
->withProviderTools([
new ProviderTool(type: 'code_interpreter', options: ['container' => ['type' => 'auto']])
])
->asText();
```
#### Configuration Options
* **container**: Configure the execution environment
* `type`: Set to `'auto'` for automatic environment selection
## Additional Message Attributes
Adding optional parameters to a `UserMessage` like the `name` field can be done through the `additionalAttributes` parameter.
```php
Prism::text()
->using('openai', 'gpt-4.1')
->withMessages([
new UserMessage('Who are you?', additionalAttributes: ['name' => 'TJ']),
])
->asText()
```
## Image Generation
OpenAI provides powerful image generation capabilities through multiple models. Prism supports all of OpenAI's image generation models with their full feature sets.
### Supported Models
| Model | Description |
|-------|-------------|
| `dall-e-3` | Latest DALL-E model |
| `dall-e-2` | Previous generation |
| `gpt-image-1` | GPT-based image model |
### Basic Usage
```php
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('A serene mountain landscape at sunset')
->generate();
$image = $response->firstImage();
echo $image->url; // Generated image URL
```
### DALL-E 3 Options
DALL-E 3 is the most advanced model with the highest quality output:
```php
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('A futuristic cityscape with flying cars')
->withProviderOptions([
'size' => '1792x1024', // 1024x1024, 1024x1792, 1792x1024
'quality' => 'hd', // standard, hd
'style' => 'vivid', // vivid, natural
])
->generate();
// DALL-E 3 automatically revises prompts for better results
if ($response->firstImage()->hasRevisedPrompt()) {
echo "Revised prompt: " . $response->firstImage()->revisedPrompt;
}
```
### DALL-E 2 Options
DALL-E 2 supports generating multiple images and is more cost-effective:
```php
$response = Prism::image()
->using('openai', 'dall-e-2')
->withPrompt('Abstract geometric patterns')
->withProviderOptions([
'n' => 4, // Number of images (1-10)
'size' => '1024x1024', // 256x256, 512x512, 1024x1024
'response_format' => 'url', // url only
'user' => 'user-123', // Optional user identifier
])
->generate();
// Process multiple images
foreach ($response->images as $image) {
echo "Image: {$image->url}\n";
}
```
### GPT-Image-1 Options
GPT-Image-1 offers advanced features including image editing and format control:
```php
$response = Prism::image()
->using('openai', 'gpt-image-1')
->withPrompt('A detailed architectural rendering of a modern house')
->withProviderOptions([
'size' => '1536x1024', // Various sizes supported
'quality' => 'high', // standard, high
'output_format' => 'webp', // png, webp, jpeg
'output_compression' => 85, // Compression level (0-100)
'background' => 'transparent', // transparent, white, black
'moderation' => true, // Enable content moderation
])
->generate();
```
### Image Editing with GPT-Image-1
GPT-Image-1 supports sophisticated image editing operations:
```php
$originalImage = fopen('tests/Fixtures/diamond.png', 'r');
$mask = fopen('tests/Fixtures/diamond-mask.png', 'r');
$response = Prism::image()
->using('openai', 'gpt-image-1')
->withPrompt('Add a vaporwave sunset to the background')
->withProviderOptions([
'image' => $originalImage,
'mask' => $mask,
'size' => '1024x1024',
'output_format' => 'png',
'quality' => 'high',
])
->withClientOptions(['timeout' => 9999])
->generate();
file_put_contents('edited-image.png', base64_decode($response->firstImage()->base64));
```
### Response Format
Generated images are returned as URLs:
```php
$response = Prism::image()
->using('openai', 'dall-e-3')
->withPrompt('Digital artwork')
->generate();
$image = $response->firstImage();
if ($image->hasUrl()) {
echo "
";
}
```
## Audio Processing
OpenAI provides comprehensive audio processing capabilities through their TTS (Text-to-Speech) and Whisper (Speech-to-Text) models. Prism supports all of OpenAI's audio models with their full feature sets.
### Text-to-Speech
Convert text into natural-sounding speech with various voice options:
#### Basic TTS Usage
```php
use Prism\Prism\Prism;
$response = Prism::audio()
->using('openai', 'gpt-4o-mini-tts')
->withInput('Hello, welcome to our application!')
->withVoice('alloy')
->asAudio();
// Save the audio file
$audioData = base64_decode($response->audio->base64);
file_put_contents('welcome.mp3', $audioData);
```
\\
#### High-Definition Audio
For higher quality audio output, use the model:
```php
$response = Prism::audio()
->using('openai', 'gpt-4o-mini-tts')
->withInput('This is high-quality audio generation.')
->withProviderOptions([
'voice' => 'nova',
'response_format' => 'wav', // Higher quality format
])
->asAudio();
```
#### Audio Format Options
Control the output format and quality:
```php
$response = Prism::audio()
->using('openai', 'gpt-4o-mini-tts')
->withInput('Testing different audio formats.')
->withProviderOptions([
'voice' => 'echo',
'response_format' => 'opus', // mp3, opus, aac, flac, wav, pcm
'speed' => 1.25, // Speed: 0.25 to 4.0
])
->asAudio();
echo "Audio type: " . $response->audio->getMimeType();
```
For more information on the available options, please refer to the [OpenAI API documentation](https://platform.openai.com/docs/guides/text-to-speech).
### Speech-to-Text
Convert audio files into accurate text transcriptions using Whisper:
#### Basic STT Usage
```php
use Prism\Prism\ValueObjects\Media\Audio;
$audioFile = Audio::fromPath('/path/to/recording.mp3');
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->asText();
echo "Transcription: " . $response->text;
```
#### Language Detection
Whisper can automatically detect the language or you can specify it:
```php
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'language' => 'es', // ISO-639-1 code (optional)
'temperature' => 0.2, // Lower temperature for more focused results
])
->asText();
```
#### Response Formats
Get transcriptions in different formats with varying detail levels:
```php
// Standard JSON response
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'response_format' => 'json', // json, text, srt, verbose_json, vtt
])
->asText();
// Verbose JSON includes timestamps and confidence scores
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'response_format' => 'verbose_json',
])
->asText();
// Access detailed segment information
$segments = $response->additionalContent['segments'] ?? [];
foreach ($segments as $segment) {
echo "Text: " . $segment['text'] . "\n";
echo "Start: " . $segment['start'] . "s\n";
echo "End: " . $segment['end'] . "s\n";
echo "Confidence: " . ($segment['no_speech_prob'] ?? 'N/A') . "\n\n";
}
```
#### Subtitle Generation
Generate subtitle files directly:
```php
// SRT format subtitles
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'response_format' => 'srt',
])
->asText();
file_put_contents('subtitles.srt', $response->text);
// VTT format subtitles
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'response_format' => 'vtt',
])
->asText();
file_put_contents('subtitles.vtt', $response->text);
```
#### Context and Prompts
Improve transcription accuracy with context:
```php
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audioFile)
->withProviderOptions([
'prompt' => 'This is a technical discussion about machine learning and artificial intelligence.',
'language' => 'en',
'temperature' => 0.1, // Lower temperature for technical content
])
->asText();
```
### Audio File Handling
#### Creating Audio Objects
Load audio from various sources:
```php
use Prism\Prism\ValueObjects\Media\Audio;
// From local file path
$audio = Audio::fromPath('/path/to/audio.mp3');
// From remote URL
$audio = Audio::fromUrl('https://example.com/recording.wav');
// From base64 encoded data
$audio = Audio::fromBase64($base64AudioData, 'audio/mpeg');
// From binary content
$audioContent = file_get_contents('/path/to/audio.wav');
$audio = Audio::fromContent($audioContent, 'audio/wav');
```
#### File Size Considerations
Whisper has a file size limit of 25 MB. For larger files, consider:
```php
// Check file size before processing
$audio = Audio::fromPath('/path/to/large-audio.mp3');
if ($audio->size() > 25 * 1024 * 1024) { // 25 MB
echo "File too large for processing";
} else {
$response = Prism::audio()
->using('openai', 'whisper-1')
->withInput($audio)
->asText();
}
```
For more information on the available options, please refer to the [OpenAI API documentation](https://platform.openai.com/docs/guides/speech-to-text).
---
---
url: /providers/openrouter.md
---
# OpenRouter
OpenRouter provides access to multiple AI models through a single API. This provider allows you to use various models from different providers through OpenRouter's routing system.
## Configuration
Add your OpenRouter configuration to `config/prism.php`:
```php
'providers' => [
'openrouter' => [
'api_key' => env('OPENROUTER_API_KEY'),
'url' => env('OPENROUTER_URL', 'https://openrouter.ai/api/v1'),
],
],
```
## Environment Variables
Set your OpenRouter API key and URL in your `.env` file:
```env
OPENROUTER_API_KEY=your_api_key_here
OPENROUTER_URL=https://openrouter.ai/api/v1
```
## Usage
### Text Generation
```php
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::OpenRouter, 'openai/gpt-4-turbo')
->withPrompt('Tell me a story about AI.')
->generate();
echo $response->text;
```
### Structured Output
```php
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
$schema = new ObjectSchema('person', 'Person information', [
new StringSchema('name', 'The person\'s name'),
new StringSchema('occupation', 'The person\'s occupation'),
]);
$response = Prism::structured()
->using(Provider::OpenRouter, 'openai/gpt-4-turbo')
->withPrompt('Generate a person profile for John Doe.')
->withSchema($schema)
->generate();
echo $response->text;
```
### Tool Calling
```php
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Tool;
$weatherTool = Tool::as('get_weather')
->for('Get the current weather for a location')
->withStringParameter('location', 'The location to get weather for')
->using(function (string $location) {
return "The weather in {$location} is sunny and 72°F";
});
$response = Prism::text()
->using(Provider::OpenRouter, 'openai/gpt-4-turbo')
->withPrompt('What is the weather like in New York?')
->withTools([$weatherTool])
->generate();
echo $response->text;
```
### Streaming
```php
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
$stream = Prism::text()
->using(Provider::OpenRouter, 'openai/gpt-4-turbo')
->withPrompt('Tell me a long story about AI.')
->asStream();
foreach ($stream as $chunk) {
echo $chunk->text;
}
```
### Streaming with Tools
```php
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Tool;
$weatherTool = Tool::as('get_weather')
->for('Get the current weather for a location')
->withStringParameter('location', 'The location to get weather for')
->using(function (string $location) {
return "The weather in {$location} is sunny and 72°F";
});
$stream = Prism::text()
->using(Provider::OpenRouter, 'openai/gpt-4-turbo')
->withPrompt('What is the weather like in multiple cities?')
->withTools([$weatherTool])
->asStream();
foreach ($stream as $chunk) {
echo $chunk->text;
// Handle tool calls
if ($chunk->toolCalls) {
foreach ($chunk->toolCalls as $toolCall) {
echo "Tool called: {$toolCall->name}\n";
}
}
// Handle tool results
if ($chunk->toolResults) {
foreach ($chunk->toolResults as $result) {
echo "Tool result: {$result->result}\n";
}
}
}
```
### Reasoning/Thinking Tokens
Some models (like OpenAI's o1 series) support reasoning tokens that show the model's thought process:
```php
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Enums\ChunkType;
$stream = Prism::text()
->using(Provider::OpenRouter, 'openai/o1-preview')
->withPrompt('Solve this complex math problem: What is the derivative of x^3 + 2x^2 - 5x + 1?')
->asStream();
foreach ($stream as $chunk) {
if ($chunk->chunkType === ChunkType::Thinking) {
// This is the model's reasoning/thinking process
echo "Thinking: " . $chunk->text . "\n";
} else {
// This is the final answer
echo $chunk->text;
}
}
```
#### Reasoning Effort
Control how much reasoning the model performs before generating a response using the `reasoning` parameter. The way this is structured depends on the underlying model you are calling:
```php
$response = Prism::text()
->using(Provider::OpenRouter, 'openai/gpt-5-mini')
->withPrompt('Write a PHP function to implement a binary search algorithm with proper error handling')
->withProviderOptions([
'reasoning' => [
'effort' => 'high', // Can be "high", "medium", or "low" (OpenAI-style)
'max_tokens' => 2000, // Specific token limit (Gemini / Anthropic-style)
// Optional: Default is false. All models support this.
'exclude': false, // Set to true to exclude reasoning tokens from response
// Or enable reasoning with the default parameters:
'enabled': true // Default: inferred from `effort` or `max_tokens`
]
])
->asText();
```
## Available Models
OpenRouter supports many models from different providers. Some popular options include:
* `openai/gpt-4-turbo`
* `openai/gpt-3.5-turbo`
* `anthropic/claude-3-5-sonnet`
* `meta-llama/llama-3.1-70b`
* `google/gemini-pro`
* `mistralai/mistral-7b-instruct`
Visit [OpenRouter's models page](https://openrouter.ai/models) for a complete list of available models.
## Features
* ✅ Text Generation
* ✅ Structured Output
* ✅ Tool Calling
* ✅ Multiple Model Support
* ✅ Provider Routing
* ✅ Streaming
* ✅ Reasoning/Thinking Tokens (for compatible models)
* ❌ Embeddings (not yet implemented)
* ❌ Image Generation (not yet implemented)
## API Reference
For detailed API documentation, visit [OpenRouter's API documentation](https://openrouter.ai/docs/api-reference/chat-completion).
## Error Handling
The OpenRouter provider includes standard error handling for common issues:
* Rate limiting
* Request too large
* Provider overload
* Invalid API key
Errors are automatically mapped to appropriate Prism exceptions for consistent error handling across all providers.
---
---
url: /core-concepts/prism-server.md
---
# Prism Server
Prism Server is a powerful feature that allows you to expose your Prism-powered AI models through a standardized API. This makes it easy to integrate your custom AI solutions into various applications, including chat interfaces and other tools that support OpenAI-compatible APIs.
## How It Works
Prism Server acts as a middleware, translating requests from OpenAI-compatible clients into Prism-specific operations. This means you can use tools like ChatGPT web UIs or any OpenAI SDK to interact with your custom Prism models.
## Setting Up Prism Server
### 1. Enable Prism Server
First, make sure Prism Server is enabled in your `config/prism.php` file:
```php
'prism_server' => [
// The middleware that will be applied to the Prism Server routes.
'middleware' => [],
'enabled' => env('PRISM_SERVER_ENABLED', false),
]
```
### 2. Register Your Prisms
To make your Prism models available through the server, you need to register them. This is typically done in a service provider, such as `AppServiceProvider`:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Facades\PrismServer;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
PrismServer::register(
'my-custom-model',
fn () => Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withSystemPrompt('You are a helpful assistant.')
);
}
}
```
In this example, we're registering a model named `my-custom-model` that uses the Anthropic Claude 3 Sonnet model with a custom system message.
## Using Prism Server
Once set up, Prism Server exposes two main endpoints:
### Chat Completions
To generate text using your registered Prism models:
```bash
curl -X POST "http://your-app.com/prism/openai/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "my-custom-model",
"messages": [
{"role": "user", "content": "Hello, who are you?"}
]
}'
```
### List Available Models
To get a list of all registered Prism models:
```bash
curl "http://your-app.com/prism/openai/v1/models"
```
## Integration with Open WebUI
Prism Server works seamlessly with OpenAI-compatible chat interfaces like [Open WebUI](https://openwebui.com). Here's an example Docker Compose configuration:
```yaml
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main
ports:
- "3000:8080"
environment:
OPENAI_API_BASE_URLS: "http://laravel:8080/prism/openai/v1"
WEBUI_SECRET_KEY: "your-secret-key"
laravel:
image: serversideup/php:8.3-fpm-nginx
volumes:
- ".:/var/www/html"
environment:
OPENAI_API_KEY: ${OPENAI_API_KEY}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
depends_on:
- open-webui
```
With this setup, you can access your Prism models through a user-friendly chat interface at `http://localhost:3000`.
By leveraging Prism Server, you can create powerful, custom AI experiences while maintaining compatibility with a wide ecosystem of tools and libraries. Whether you're building a chatbot, a content generation tool, or something entirely new, Prism Server provides the flexibility and standardization you need to succeed.
## Adding Middleware
You can add middleware to the Prism Server routes by setting the `middleware` option in your `config/prism.php` file:
```php
'prism_server' => [
'middleware' => ['api'],
],
```
---
---
url: /advanced/provider-interoperability.md
---
# Provider Interoperability
When working with Prism, you might need to customize requests based on which provider you're using. Different providers have unique capabilities, configuration options, and requirements that can affect how you structure your requests for optimal results.
## Using the `whenProvider` Method
The `whenProvider` method lets you easily customize your requests for specific providers while maintaining clean, readable code.
```php
$response = Prism::text()
->using(Provider::OpenAI, 'gpt-4o')
->withPrompt('Who are you?')
->whenProvider(
Provider::Anthropic,
fn ($request) => $request
->withProviderOptions([
'cacheType' => 'ephemeral',
])
)
->asText();
```
In this example, the `withProviderOptions` settings will only be applied when using Anthropic's provider. If you're using OpenAI (as specified in the `using` method), these customizations are simply skipped.
## Key Benefits
* **Cleaner Code**: Keep your provider-specific customizations encapsulated and only apply them when needed
* **Easy Provider Switching**: Swap between providers without rewriting your configuration code
* **Maintainable Applications**: Define provider-specific behaviors in one place
## Advanced Usage
You can chain multiple `whenProvider` calls to handle different provider scenarios:
```php
$response = Prism::text()
->using(Provider::OpenAI, 'gpt-4o')
->withPrompt('Generate a creative story about robots.')
->whenProvider(
Provider::Anthropic,
fn ($request) => $request
->withMaxTokens(4000)
->withProviderOptions(['cacheType' => 'ephemeral'])
)
->whenProvider(
Provider::OpenAI,
fn ($request) => $request
->withMaxTokens(2000)
->withProviderOptions(['response_format' => ['type' => 'text']])
)
->asText();
```
## Using Invokable Classes
For more complex provider-specific configurations, you can use invokable classes instead of closures:
```php
class AnthropicConfigurator
{
public function __invoke($request)
{
return $request
->withMaxTokens(4000)
->withProviderOptions([
'cacheType' => 'ephemeral',
'citations' => true,
]);
}
}
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-sonnet')
->withPrompt('Explain the theory of relativity.')
->whenProvider(Provider::Anthropic, new AnthropicConfigurator())
->asText();
```
This approach can be especially helpful when you have complex or reusable provider configurations.
> \[!TIP]
> The `whenProvider` method works with all request types in Prism including text, structured output, and embeddings requests.
## Best Practices
### Avoiding SystemMessages with Multiple Providers
When working with multiple providers, it's best to avoid using `SystemMessages` directly in your `withMessages` array. Instead, use the `withSystemPrompt` method as it offers better provider interoperability.
```php
// Avoid this when switching between providers
$response = Prism::text()
->using(Provider::OpenAI, 'gpt-4o')
->withMessages([
new SystemMessage('You are a helpful assistant.'),
new UserMessage('Tell me about AI'),
])
->asText();
// Prefer this instead
$response = Prism::text()
->using(Provider::OpenAI, 'gpt-4o')
->withSystemPrompt('You are a helpful assistant.')
->withPrompt('Tell me about AI')
->asText();
```
This approach allows Prism to handle the provider-specific formatting of system messages, making your code more portable across different LLM providers.
---
---
url: /core-concepts/schemas.md
---
# Schemas
Schemas are the blueprints that help you define the shape of your data in Prism. Whether you're building tool parameters or crafting structured outputs, schemas help you clearly communicate what your data should look like.
## Quick Start
Let's dive right in with a practical example:
```php
use Prism\Prism\Schema\ArraySchema;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
$userSchema = new ObjectSchema(
name: 'user',
description: 'A user profile with their hobbies',
properties: [
new StringSchema('name', 'The user\'s full name'),
new ArraySchema(
name: 'hobbies',
description: 'The user\'s list of hobbies',
items: new ObjectSchema(
name: 'hobby',
description: 'A detailed hobby entry',
properties: [
new StringSchema('name', 'The name of the hobby'),
new StringSchema('description', 'A brief description of the hobby'),
],
requiredFields: ['name', 'description']
)
),
],
requiredFields: ['name', 'hobbies']
);
```
## Available Schema Types
### StringSchema
For text values of any length. Perfect for names, descriptions, or any textual data.
```php
use Prism\Prism\Schema\StringSchema;
$nameSchema = new StringSchema(
name: 'full_name',
description: 'The user\'s full name including first and last name'
);
```
### NumberSchema
Handles both integers and floating-point numbers. Great for ages, quantities, or measurements.
```php
use Prism\Prism\Schema\NumberSchema;
$ageSchema = new NumberSchema(
name: 'age',
description: 'The user\'s age in years'
);
```
### BooleanSchema
For simple true/false values. Perfect for flags and toggles.
```php
use Prism\Prism\Schema\BooleanSchema;
$activeSchema = new BooleanSchema(
name: 'is_active',
description: 'Whether the user account is active'
);
```
### ArraySchema
For lists of items, where each item follows a specific schema.
```php
use Prism\Prism\Schema\ArraySchema;
use Prism\Prism\Schema\StringSchema;
$tagsSchema = new ArraySchema(
name: 'tags',
description: 'List of tags associated with the post',
items: new StringSchema('tag', 'A single tag')
);
```
### EnumSchema
When you need to restrict values to a specific set of options.
```php
use Prism\Prism\Schema\EnumSchema;
$statusSchema = new EnumSchema(
name: 'status',
description: 'The current status of the post',
options: ['draft', 'published', 'archived']
);
```
### ObjectSchema
For complex, nested data structures. The Swiss Army knife of schemas!
```php
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
use Prism\Prism\Schema\NumberSchema;
$profileSchema = new ObjectSchema(
name: 'profile',
description: 'A user\'s public profile information',
properties: [
new StringSchema('username', 'The unique username'),
new StringSchema('bio', 'A short biography'),
new NumberSchema('joined_year', 'Year the user joined'),
],
requiredFields: ['username']
);
```
## Nullable Fields
Sometimes, not every field is required. You can make any schema nullable by setting the `nullable` parameter to `true`:
```php
use Prism\Prism\Schema\StringSchema;
$bioSchema = new StringSchema(
name: 'bio',
description: 'Optional user biography',
nullable: true
);
```
> \[!NOTE]
> When using OpenAI in strict mode, all fields must be marked as required, so optional fields must be marked as nullable.
## Required vs Nullable Fields
Understanding the difference between required fields and nullable fields is crucial when working with schemas in Prism:
### Required Fields
Required fields are specified at the object level using the `requiredFields` parameter. They indicate which properties must be present in the data structure:
```php
$userSchema = new ObjectSchema(
name: 'user',
description: 'User profile',
properties: [
new StringSchema('email', 'Primary email address'),
new StringSchema('name', 'User\'s full name'),
new StringSchema('bio', 'User biography'),
],
requiredFields: ['email', 'name'] // email and name must be present
);
```
### Nullable Fields
Nullable fields, on the other hand, are specified at the individual field level using the `nullable` parameter. They indicate that a field can contain a `null` value:
```php
$userSchema = new ObjectSchema(
name: 'user',
description: 'User profile',
properties: [
new StringSchema('email', 'Primary email address'),
new StringSchema('name', 'User\'s full name'),
new StringSchema('bio', 'User biography', nullable: true), // bio can be null
],
requiredFields: ['email', 'name', 'bio'] // bio must be present, but can be null
);
```
### Key Differences
1. **Required vs Present**:
* A required field must be present in the data structure
* A non-nullable field must contain a non-null value when present
* A field can be required but nullable (must be present, can be null)
* A field can be non-required and non-nullable (when present, cannot be null)
2. **Common Patterns**:
```php
// Required and Non-nullable (most strict)
new StringSchema('email', 'Primary email', nullable: false);
// requireFields: ['email']
// Required but Nullable (must be present, can be null)
new StringSchema('bio', 'User bio', nullable: true);
// requireFields: ['bio']
// Optional and Non-nullable (can be omitted, but if present cannot be null)
new StringSchema('phone', 'Phone number', nullable: false);
// requireFields: []
// Optional and Nullable (most permissive)
new StringSchema('website', 'Personal website', nullable: true);
// requireFields: []
```
### Provider Considerations
When working with providers that support strict mode (like OpenAI), you'll want to be especially careful with these settings:
```php
// For OpenAI strict mode:
// - All fields should be required
// - Use nullable: true for optional fields
$userSchema = new ObjectSchema(
name: 'user',
description: 'User profile',
properties: [
new StringSchema('email', 'Required email address'),
new StringSchema('bio', 'Optional biography', nullable: true),
],
requiredFields: ['email', 'bio'] // Note: bio is required but nullable
);
```
> \[!TIP]
> When in doubt, be explicit about both requirements. Specify both the `nullable` status of each field AND which fields are required in your object schemas. This makes your intentions clear to both other developers and AI providers.
## Best Practices
1. **Clear Descriptions**: Write clear, concise descriptions for each field. Future you (and other developers) will thank you!
```php
// ❌ Not helpful
new StringSchema('name', 'the name');
// ✅ Much better
new StringSchema('name', 'The user\'s display name (2-50 characters)');
```
2. **Thoughtful Required Fields**: Only mark fields as required if they're truly necessary:
```php
new ObjectSchema(
name: 'user',
description: 'User profile',
properties: [
new StringSchema('email', 'Primary email address'),
new StringSchema('phone', 'Optional phone number', nullable: true),
],
requiredFields: ['email']
);
```
3. **Nested Organization**: Keep your schemas organized when dealing with complex structures:
```php
// Define child schemas first
$addressSchema = new ObjectSchema(/*...*/);
$contactSchema = new ObjectSchema(/*...*/);
// Then use them in your parent schema
$userSchema = new ObjectSchema(
name: 'user',
description: 'Complete user profile',
properties: [$addressSchema, $contactSchema]
);
```
> \[!NOTE]
> Remember that while schemas help define the structure of your data, Prism doesn't currently validate the data against these schemas. Schema validation is planned for a future release!
---
---
url: /core-concepts/streaming-output.md
---
# Streaming Output
Want to show AI responses to your users in real-time? Streaming lets you display text as it's generated, creating a more responsive and engaging user experience.
> \[!WARNING]
> When using Laravel Telescope or other packages that intercept Laravel's HTTP client events, they may consume the stream before Prism can emit the stream chunks. This can cause streaming to appear broken or incomplete. Consider disabling such interceptors when using streaming functionality, or configure them to ignore Prism's HTTP requests.
## Basic Streaming
At its simplest, streaming works like this:
```php
use Prism\Prism\Prism;
$response = Prism::text()
->using('openai', 'gpt-4')
->withPrompt('Tell me a story about a brave knight.')
->asStream();
// Process each chunk as it arrives
foreach ($response as $chunk) {
echo $chunk->text;
// Flush the output buffer to send text to the browser immediately
ob_flush();
flush();
}
```
## Understanding Chunks
Each chunk from the stream contains a piece of the generated content:
```php
foreach ($response as $chunk) {
// The text fragment in this chunk
echo $chunk->text;
if ($chunk->usage) {
echo "Prompt tokens: " . $chunk->usage->promptTokens;
echo "Completion tokens: " . $chunk->usage->completionTokens;
}
// Check if this is the final chunk
if ($chunk->finishReason === FinishReason::Stop) {
echo "Generation complete: " . $chunk->finishReason->name;
}
}
```
## Streaming with Tools
Streaming works seamlessly with tools, allowing real-time interaction:
```php
use Prism\Prism\Facades\Tool;
use Prism\Prism\Prism;
$weatherTool = Tool::as('weather')
->for('Get current weather information')
->withStringParameter('city', 'City name')
->using(function (string $city) {
return "The weather in {$city} is sunny and 72°F.";
});
$response = Prism::text()
->using('openai', 'gpt-4o')
->withTools([$weatherTool])
->withMaxSteps(3) // Control maximum number of back-and-forth steps
->withPrompt('What\'s the weather like in San Francisco today?')
->asStream();
$fullResponse = '';
foreach ($response as $chunk) {
// Append each chunk to build the complete response
$fullResponse .= $chunk->text;
// Check for tool calls
if ($chunk->chunkType === ChunkType::ToolCall) {
foreach ($chunk->toolCalls as $call) {
echo "Tool called: " . $call->name;
}
}
// Check for tool results
if ($chunk->chunkType === ChunkType::ToolResult) {
foreach ($chunk->toolResults as $result) {
echo "Tool result: " . $result->result;
}
}
}
echo "Final response: " . $fullResponse;
```
## Configuration Options
Streaming supports the same configuration options as regular [text generation](/core-concepts/text-generation#generation-parameters).
## Handling Streaming in Web Applications
Here's how to integrate streaming in a Laravel controller:
Alternatively, you might consider using Laravel's [Broadcasting feature](https://laravel.com/docs/12.x/broadcasting) to send the chunks to your frontend.
```php
use Prism\Prism\Prism;
use Illuminate\Http\Response;
public function streamResponse()
{
return response()->stream(function () {
$stream = Prism::text()
->using('openai', 'gpt-4')
->withPrompt('Explain quantum computing step by step.')
->asStream();
foreach ($stream as $chunk) {
echo $chunk->text;
ob_flush();
flush();
}
}, 200, [
'Cache-Control' => 'no-cache',
'Content-Type' => 'text/event-stream',
'X-Accel-Buffering' => 'no', // Prevents Nginx from buffering
]);
}
```
### Laravel 12 Event Streams
Stream the output via Laravel event streams ([docs](https://laravel.com/docs/12.x/responses#event-streams)).
```php
Route::get('/chat', function () {
return response()->eventStream(function () {
$stream = Prism::text()
->using('openai', 'gpt-4')
->withPrompt('Explain quantum computing step by step.')
->asStream();
foreach ($stream as $response) {
yield $response->text;
}
});
});
```
Streaming gives your users a more responsive experience by showing AI-generated content as it's created, rather than making them wait for the complete response. This approach feels more natural and keeps users engaged, especially for longer responses or complex interactions with tools.
---
---
url: /core-concepts/structured-output.md
---
# Structured Output
Want your AI responses as neat and tidy as a Marie Kondo-approved closet? Structured output lets you define exactly how you want your data formatted, making it perfect for building APIs, processing forms, or any time you need data in a specific shape.
## Quick Start
Here's how to get structured data from your AI:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
$schema = new ObjectSchema(
name: 'movie_review',
description: 'A structured movie review',
properties: [
new StringSchema('title', 'The movie title'),
new StringSchema('rating', 'Rating out of 5 stars'),
new StringSchema('summary', 'Brief review summary')
],
requiredFields: ['title', 'rating', 'summary']
);
$response = Prism::structured()
->using(Provider::OpenAI, 'gpt-4o')
->withSchema($schema)
->withPrompt('Review the movie Inception')
->asStructured();
// Access your structured data
$review = $response->structured;
echo $review['title']; // "Inception"
echo $review['rating']; // "5 stars"
echo $review['summary']; // "A mind-bending..."
```
> \[!TIP]
> This is just a basic example of schema usage. Check out our [dedicated schemas guide](/core-concepts/schemas) to learn about all available schema types, nullable fields, and best practices for structuring your data.
## Understanding Output Modes
Different AI providers handle structured output in two main ways:
1. **Structured Mode**: Some providers support strict schema validation, ensuring responses perfectly match your defined structure.
2. **JSON Mode**: Other providers simply guarantee valid JSON output that approximately matches your schema.
> \[!NOTE]
> Check your provider's documentation to understand which mode they support. Provider support can vary by model, so always verify capabilities for your specific use case.
## Provider-Specific Options
Providers may offer additional options for structured output:
### OpenAI: Strict Mode
OpenAI supports a "strict mode" for even tighter schema validation:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::structured()
->withProviderOptions([
'schema' => [
'strict' => true
]
])
// ... rest of your configuration
```
### Anthropic: Tool Calling Mode
Anthropic doesn't have native structured output, but Prism provides two approaches. For more reliable JSON parsing, especially with complex content or non-English text, use tool calling mode:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::structured()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withSchema($schema)
->withPrompt('天氣怎麼樣?應該穿什麼?') // Chinese text with potential quotes
->withProviderOptions(['use_tool_calling' => true])
->asStructured();
```
**When to use tool calling mode with Anthropic:**
* Working with non-English content that may contain quotes
* Complex JSON structures that might confuse prompt-based parsing
* When you need the most reliable structured output possible
> \[!NOTE]
> Tool calling mode cannot be used with Anthropic's citations feature.
> \[!TIP]
> Check the provider-specific documentation pages for additional options and features that might be available for structured output.
## Response Handling
When working with structured responses, you have access to both the structured data and metadata about the generation:
```php
use Prism\Prism\Prism;
$response = Prism::structured()
->withSchema($schema)
->asStructured();
// Access the structured data as a PHP array
$data = $response->structured;
// Get the raw response text if needed
echo $response->text;
// Check why the generation stopped
echo $response->finishReason->name;
// Get token usage statistics
echo "Prompt tokens: {$response->usage->promptTokens}";
echo "Completion tokens: {$response->usage->completionTokens}";
// Access provider-specific response data
$rawResponse = $response->response;
```
> \[!TIP]
> Always validate the structured data before using it in your application:
```php
if ($response->structured === null) {
// Handle parsing failure
}
if (!isset($response->structured['required_field'])) {
// Handle missing required data
}
```
## Common Settings
Structured output supports several configuration options to fine-tune your generations:
### Model Configuration
* `maxTokens` - Set the maximum number of tokens to generate
* `temperature` - Control output randomness (provider-dependent)
* `topP` - Alternative to temperature for controlling randomness (provider-dependent)
### Input Methods
* `withPrompt` - Single prompt for generation
* `withMessages` - Message history for more context
* `withSystemPrompt` - System-level instructions
### Request Configuration
* `withClientOptions` - Set HTTP client options (e.g., timeouts)
* `withClientRetry` - Configure automatic retries on failures
* `usingProviderConfig` - Override provider configuration
* `withProviderOptions` - Set provider-specific options
> \[!NOTE]
> Unlike text generation, structured output does not support tools/function calling. For those features, use the text generation API instead.
See the [Text Generation](./text-generation.md) documentation for comparison with standard text generation capabilities.
## Error Handling
When working with structured output, it's especially important to handle potential errors:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Exceptions\PrismException;
try {
$response = Prism::structured()
->using('anthropic', 'claude-3-sonnet')
->withSchema($schema)
->withPrompt('Generate product data')
->asStructured();
} catch (PrismException $e) {
// Handle validation or generation errors
Log::error('Structured generation failed:', [
'error' => $e->getMessage()
]);
}
```
> \[!IMPORTANT]
> Always validate the structured response before using it in your application, as different providers may have varying levels of schema adherence.
---
---
url: /core-concepts/testing.md
---
# Testing
Want to make sure your Prism integrations work flawlessly? Let's dive into testing! Prism provides a powerful fake implementation that makes it a breeze to test your AI‑powered features.
## Basic Test Setup
First, let's look at how to set up basic response faking:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Usage;
use Prism\Prism\Testing\TextResponseFake;
it('can generate text', function () {
$fakeResponse = TextResponseFake::make()
->withText('Hello, I am Claude!')
->withUsage(new Usage(10, 20));
// Set up the fake
$fake = Prism::fake([$fakeResponse]);
// Run your code
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withPrompt('Who are you?')
->asText();
// Make assertions
expect($response->text)->toBe('Hello, I am Claude!');
});
```
The response fakes create a new response with default values and let you fluently set the values you need for your test.
## Testing Multiple Responses
When testing conversations or tool usage, you might need to simulate multiple responses:
```php
use Prism\Prism\ValueObjects\Usage;
use Prism\Prism\ValueObjects\ToolCall;
use Prism\Prism\Testing\TextResponseFake;
use Prism\Prism\ValueObjects\Meta;
it('can handle tool calls', function () {
$responses = [
TextResponseFake::make()
->withToolCalls([
new ToolCall(
id: 'call_1',
name: 'search',
arguments: ['query' => 'Latest news']
)
])
->withUsage(new Usage(15, 25))
->withMeta(new Meta('fake-1', 'fake-model')),
TextResponseFake::make()
->withText('Here are the latest news...')
->withUsage(new Usage(20, 30))
->withMeta(new Meta('fake-2', 'fake-model')),
];
$fake = Prism::fake($responses);
});
```
## Using the ResponseBuilder
If you need to test a richer response object, e.g. with Steps, you may find it easier to use the `ResponseBuilder` together with the fake Step helpers.
This is especially useful when you want to test complex streamed responses.
```php
use Prism\Prism\Text\ResponseBuilder;
use Prism\Prism\Testing\TextStepFake;
use Prism\Prism\ValueObjects\Usage;
use Prism\Prism\ValueObjects\Meta;
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\ValueObjects\ToolCall;
use Prism\Prism\ValueObjects\ToolResult;
use Prism\Prism\ValueObjects\Messages\{UserMessage,AssistantMessage,SystemMessage};
use Prism\Prism\ValueObjects\Media\Document;
Prism::fake([
(new ResponseBuilder)
->addStep(
TextStepFake::make()
->withText('Step 1 response text')
->withFinishReason(FinishReason::Stop)
->withToolCalls([/* tool calls */])
->withToolResults([/* tool results */])
->withUsage(new Usage(1000, 750))
->withMeta(new Meta('step1', 'test-model'))
->withMessages([
new UserMessage('Test message 1', [
new Document(
document: '',
mimeType: 'text/plain',
dataFormat: 'text',
documentTitle: 'Test document',
documentContext: 'Test context'
),
]),
new AssistantMessage('Test message 2')
])
->withSystemPrompts([
new SystemMessage('Test system')
])
->withAdditionalContent(['test' => 'additional'])
)
->addStep(
TextStepFake::make()
->withText('Step 2 response text')
->withFinishReason(FinishReason::Stop)
->withToolCalls([/* tool calls */])
->withToolResults([/* tool results */])
->withUsage(new Usage(1000, 750))
->withMeta(new Meta(id: 123, model: 'test-model'))
->withMessages([/* Second step messages */])
->withSystemPrompts([/* Second step system prompts */])
->withAdditionalContent([/* Second step additional data */])
)
->toResponse()
]);
```
## Testing Tools
```php
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Facades\Tool;
use Prism\Prism\Prism;
use Prism\Prism\Testing\TextStepFake;
use Prism\Prism\Text\ResponseBuilder;
use Prism\Prism\ValueObjects\Meta;
use Prism\Prism\ValueObjects\ToolCall;
use Prism\Prism\ValueObjects\ToolResult;
use Prism\Prism\ValueObjects\Usage;
it('can use weather tool', function () {
// Define the expected tool call and response sequence
$responses = [
(new ResponseBuilder)
->addStep(
// First response: AI decides to use the weather tool
TextStepFake::make()
->withToolCalls([
new ToolCall(
id: 'call_123',
name: 'weather',
arguments: ['city' => 'Paris']
),
])
->withFinishReason(FinishReason::ToolCalls)
->withUsage(new Usage(15, 25))
->withMeta(new Meta('fake-1', 'fake-model'))
)
->addStep(
// Second response: AI uses the tool result to form a response
TextStepFake::make()
->withText('Based on current conditions, the weather in Paris is sunny with a temperature of 72°F.')
->withToolResults([
new ToolResult(
toolCallId: 'call_123',
toolName: 'weather',
args: ['city' => 'Paris'],
result: 'Sunny, 72°F'
),
])
->withFinishReason(FinishReason::Stop)
->withUsage(new Usage(20, 30))
->withMeta(new Meta('fake-2', 'fake-model')),
)
->toResponse(),
];
// Set up the fake
Prism::fake($responses);
// Create the weather tool
$weatherTool = Tool::as('weather')
->for('Get weather information')
->withStringParameter('city', 'City name')
->using(fn (string $city) => "The weather in {$city} is sunny with a temperature of 72°F");
// Run the actual test
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withPrompt('What\'s the weather in Paris?')
->withTools([$weatherTool])
->withMaxSteps(2)
->asText();
// Assert the response has the correct number of steps
expect($response->steps)->toHaveCount(2);
// Assert tool calls were made correctly
expect($response->steps[0]->toolCalls)->toHaveCount(1);
expect($response->steps[0]->toolCalls[0]->name)->toBe('weather');
expect($response->steps[0]->toolCalls[0]->arguments())->toBe(['city' => 'Paris']);
// Assert tool results were processed
expect($response->toolResults)->toHaveCount(1);
expect($response->toolResults[0]->result)
->toBe('Sunny, 72°F');
// Assert final response
expect($response->text)
->toBe('Based on current conditions, the weather in Paris is sunny with a temperature of 72°F.');
});
```
## Testing Streamed Responses
To test streamed responses, you can use any text response for a fake. The fake Provider will turn the text response into a fake stream of text chunks.
It will always finish with an empty chunk including your given finish reason.
```php
Prism::fake([
TextResponseFake::make()
->withText('fake response text') // text to be streamed
->withFinishReason(FinishReason::Stop), // finish reason for final chunk
]);
$text = Prism::text()
->using('anthropic', 'claude-3-sonnet')
->withPrompt('What is the meaning of life?')
->asStream();
$outputText = '';
foreach ($text as $chunk) {
$outputText .= $chunk->text; // will be ['fake ', 'respo', 'nse t', 'ext', ''];
}
expect($outputText)->toBe('fake response text');
```
You can adjust the chunk size by using `withFakeChunkSize` on the fake.
```php
Prism::fake([
TextResponseFake::make()->withText('fake response text'),
])->withFakeChunkSize(1);
```
Now, the text will be streamed in chunks of one character (`['f', 'a', 'k', ...]`).
### Testing Tool Calling while Streaming
When testing streamed responses with tool calls, you can use the `ResponseBuilder` to create a more complex response.
Given a text response with steps, the fake provider will not only generate text chunks, but also include chunks for tool calls and results.
```php
Prism::fake([
(new ResponseBuilder)
->addStep(
TextStepFake::make()
->withToolCalls(
[
new ToolCall('id-123', 'tool', ['input' => 'value']),
]
)
)
->addStep(
TextStepFake::make()
->withToolResults(
[
new ToolResult('id-123', 'tool', ['input' => 'value'], 'result'),
]
)
)
->addStep(
TextStepFake::make()
->withText('fake response text')
)
->toResponse(),
]);
$text = Prism::text()
->using('anthropic', 'claude-3-sonnet')
->withPrompt('What is the meaning of life?')
->asStream();
$outputText = '';
$toolCalls = [];
$toolResults = [];
foreach ($text as $chunk) {
$outputText .= $chunk->text;
// Accumulate tool calls
if ($chunk->toolCalls) {
foreach ($chunk->toolCalls as $call) {
$toolCalls[] = $call;
}
}
// Accumulate tool results
if ($chunk->toolResults) {
foreach ($chunk->toolResults as $result) {
$toolResults[] = $result;
}
}
}
expect($outputText)->toBe('fake response text')
->and($toolCalls)->toHaveCount(1)
->and($toolCalls[0])->toBeInstanceOf(ToolCall::class)
->and($toolCalls[0]->id)->toBe('id-123')
->and($toolCalls[0]->name)->toBe('tool')
->and($toolCalls[0]->arguments())->toBe(['input' => 'value'])
->and($toolResults)->toHaveCount(1)
->and($toolResults[0])->toBeInstanceOf(ToolResult::class)
->and($toolResults[0]->toolCallId)->toBe('id-123')
->and($toolResults[0]->toolName)->toBe('tool')
->and($toolResults[0]->args)->toBe(['input' => 'value'])
->and($toolResults[0]->result)->toBe('result');
```
## Testing Structured Output
```php
use Prism\Prism\Prism;
use Prism\Prism\Testing\StructuredResponseFake;
use Prism\Prism\ValueObjects\Usage;
use Prism\Prism\ValueObjects\Meta;
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
it('can generate structured response', function () {
$schema = new ObjectSchema(
name: 'user',
description: 'A user object, because we love organizing things!',
properties: [
new StringSchema('name', 'The user\'s name (hopefully not "test test")'),
new StringSchema('bio', 'A brief bio (no novels, please)'),
],
requiredFields: ['name', 'bio']
);
$fakeResponse = StructuredResponseFake::make()
->withText(json_encode([
'name' => 'Alice Tester',
'bio' => 'Professional bug hunter and code wrangler'
], JSON_THROW_ON_ERROR))
->withStructured([
'name' => 'Alice Tester',
'bio' => 'Professional bug hunter and code wrangler'
])
->withFinishReason(FinishReason::Stop)
->withUsage(new Usage(10, 20))
->withMeta(new Meta('fake-1', 'fake-model'));
$fake = Prism::fake([$fakeResponse]);
$response = Prism::structured()
->using('anthropic', 'claude-3-sonnet')
->withPrompt('Generate a user profile')
->withSchema($schema)
->asStructured();
// Assertions
expect($response->structured)->toBeArray();
expect($response->structured['name'])->toBe('Alice Tester');
expect($response->structured['bio'])->toBe('Professional bug hunter and code wrangler');
});
```
## Testing Embeddings
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Embedding;
use Prism\Prism\ValueObjects\EmbeddingsUsage;
use Prism\Prism\Testing\EmbeddingsResponseFake;
use Prism\Prism\ValueObjects\Meta;
it('can generate embeddings', function () {
$fakeResponse = EmbeddingsResponseFake::make()
->withEmbeddings([Embedding::fromArray(array_fill(0, 1536, 0.1))])
->withUsage(new EmbeddingsUsage(10))
->withMeta(new Meta('fake-emb-1', 'fake-model'));
Prism::fake([$fakeResponse]);
$response = Prism::embeddings()
->using(Provider::OpenAI, 'text-embedding-3-small')
->fromInput('Test content for embedding generation.')
->asEmbeddings();
expect($response->embeddings)->toHaveCount(1)
->and($response->embeddings[0]->embedding)
->toBeArray()
->toHaveCount(1536);
});
```
## Assertions
`PrismFake` provides several helpful assertion methods:
```php
// Assert specific prompt was sent
$fake->assertPrompt('Who are you?');
// Assert number of calls made
$fake->assertCallCount(2);
// Assert detailed request properties
$fake->assertRequest(function ($requests) {
expect($requests[0]->provider())->toBe('anthropic');
expect($requests[0]->model())->toBe('claude-3-sonnet');
});
// Assert provider configuration
$fake->assertProviderConfig(['api_key' => 'sk-1234']);
```
## Using the real response classes
While the fake helpers make tests concise, you can still build responses with the real
classes if you know you will need to test against all the properties of the response:
```php
use Prism\Prism\Text\Response;
use Illuminate\Support\Collection;
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\ValueObjects\Usage;
use Prism\Prism\ValueObjects\Meta;
$response = new Response(
steps: collect([]),
responseMessages: collect([]),
text: 'The meaning of life is 42',
finishReason: FinishReason::Stop,
toolCalls: [],
toolResults: [],
usage: new Usage(42, 42),
meta: new Meta('resp_1', 'real-model'),
messages: collect([]),
additionalContent: [],
);
```
This approach is perfectly valid—but for most tests the fake builders are shorter and
easier to read.
---
---
url: /core-concepts/text-generation.md
---
# Text Generation
Prism provides a powerful interface for generating text using Large Language Models (LLMs). This guide covers everything from basic usage to advanced features like multi-modal interactions and response handling.
## Basic Text Generation
At its simplest, you can generate text with just a few lines of code:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt('Tell me a short story about a brave knight.')
->asText();
echo $response->text;
```
## System Prompts and Context
System prompts help set the behavior and context for the AI. They're particularly useful for maintaining consistent responses or giving the LLM a persona:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withSystemPrompt('You are an expert mathematician who explains concepts simply.')
->withPrompt('Explain the Pythagorean theorem.')
->asText();
```
You can also use Laravel views for complex system prompts:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withSystemPrompt(view('prompts.math-tutor'))
->withPrompt('What is calculus?')
->asText();
```
You an also pass a View to the `withPrompt` method.
## Multi-Modal Input
Prism supports including images, documents, audio, and video files in your prompts for rich multi-modal analysis:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Media\Image;
use Prism\Prism\ValueObjects\Media\Document;
use Prism\Prism\ValueObjects\Media\Audio;
use Prism\Prism\ValueObjects\Media\Video;
// Analyze an image
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
'What objects do you see in this image?',
[Image::fromLocalPath('/path/to/image.jpg')]
)
->asText();
// Process a document
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt(
'Summarize the key points from this document',
[Document::fromLocalPath('/path/to/document.pdf')]
)
->asText();
// Analyze audio content
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'What is being discussed in this audio?',
[Audio::fromLocalPath('/path/to/audio.mp3')]
)
->asText();
// Process video content
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Describe what happens in this video',
[Video::fromUrl('https://www.youtube.com/watch?v=dQw4w9WgXcQ')]
)
->asText();
// Multiple media types in one prompt
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Compare this image with the information in this document',
[
Image::fromLocalPath('/path/to/chart.png'),
Document::fromLocalPath('/path/to/report.pdf')
]
)
->asText();
```
## Message Chains and Conversations
For interactive conversations, use message chains to maintain context:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Messages\AssistantMessage;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withMessages([
new UserMessage('What is JSON?'),
new AssistantMessage('JSON is a lightweight data format...'),
new UserMessage('Can you show me an example?')
])
->asText();
```
### Message Types
* `SystemMessage`
* `UserMessage`
* `AssistantMessage`
* `ToolResultMessage`
> \[!NOTE]
> Some providers, like Anthropic, do not support the `SystemMessage` type. In those cases we convert `SystemMessage` to `UserMessage`.
## Generation Parameters
Fine-tune your generations with various parameters:
`withMaxTokens`
Maximum number of tokens to generate.
`usingTemperature`
Temperature setting.
The value is passed through to the provider. The range depends on the provider and model. For most providers, 0 means almost deterministic results, and higher values mean more randomness.
> \[!TIP]
> It is recommended to set either temperature or topP, but not both.
`usingTopP`
Nucleus sampling.
The value is passed through to the provider. The range depends on the provider and model. For most providers, nucleus sampling is a number between 0 and 1. E.g. 0.1 would mean that only tokens with the top 10% probability mass are considered.
> \[!TIP]
> It is recommended to set either temperature or topP, but not both.
`withClientOptions`
Under the hood we use Laravel's [HTTP client](https://laravel.com/docs/11.x/http-client#main-content). You can use this method to pass any of Guzzles [request options](https://docs.guzzlephp.org/en/stable/request-options.html) e.g. `->withClientOptions(['timeout' => 30])`.
`withClientRetry`
Under the hood we use Laravel's [HTTP client](https://laravel.com/docs/11.x/http-client#main-content). You can use this method to set [retries](https://laravel.com/docs/11.x/http-client#retries) e.g. `->withClientRetry(3, 100)`.
`usingProviderConfig`
This allows for complete or partial override of the providers configuration. This is great for multi-tenant applications where users supply their own API keys. These values are merged with the original configuration allowing for partial or complete config override.
## Response Handling
The response object provides rich access to the generation results:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt('Explain quantum computing.')
->asText();
// Access the generated text
echo $response->text;
// Check why the generation stopped
echo $response->finishReason->name;
// Get token usage statistics
echo "Prompt tokens: {$response->usage->promptTokens}";
echo "Completion tokens: {$response->usage->completionTokens}";
// For multi-step generations, examine each step
foreach ($response->steps as $step) {
echo "Step text: {$step->text}";
echo "Step tokens: {$step->usage->completionTokens}";
}
// Access message history
foreach ($response->responseMessages as $message) {
if ($message instanceof AssistantMessage) {
echo $message->content;
}
}
```
### Finish Reasons
```php
FinishReason::Stop;
FinishReason::Length;
FinishReason::ContentFilter;
FinishReason::ToolCalls;
FinishReason::Error;
FinishReason::Other;
FinishReason::Unknown;
```
## Error Handling
Remember to handle potential errors in your generations:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Exceptions\PrismException;
use Throwable;
try {
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withPrompt('Generate text...')
->asText();
} catch (PrismException $e) {
Log::error('Text generation failed:', ['error' => $e->getMessage()]);
} catch (Throwable $e) {
Log::error('Generic error:', ['error' => $e->getMessage()]);
}
```
---
---
url: /core-concepts/tools-function-calling.md
---
# Tools & Function Calling
Need your AI assistant to check the weather, search a database, or call your API? Tools are here to help! They let you extend your AI's capabilities by giving it access to specific functions it can call.
## Tool Concept Overview
Think of tools as special functions that your AI assistant can use when it needs to perform specific tasks. Just like how Laravel's facades provide a clean interface to complex functionality, Prism tools give your AI a clean way to interact with external services and data sources.
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Facades\Tool;
$weatherTool = Tool::as('weather')
->for('Get current weather conditions')
->withStringParameter('city', 'The city to get weather for')
->using(function (string $city): string {
// Your weather API logic here
return "The weather in {$city} is sunny and 72°F.";
});
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withMaxSteps(2)
->withPrompt('What is the weather like in Paris?')
->withTools([$weatherTool])
->asText();
```
## Max Steps
Prism defaults to allowing a single step. To use Tools, you'll need to increase this using `withMaxSteps`:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
// Increase max steps to at least 2
->withMaxSteps(2)
->withPrompt('What is the weather like in Paris?')
->withTools([$weatherTool])
->asText();
```
You should use a higher number of max steps if you expect your initial prompt to make multiple tool calls.
## Creating Basic Tools
Creating tools in Prism is straightforward and fluent. Here's how you can create a simple tool:
```php
use Prism\Prism\Facades\Tool;
$searchTool = Tool::as('search')
->for('Search for current information')
->withStringParameter('query', 'The search query')
->using(function (string $query): string {
// Your search implementation
return "Search results for: {$query}";
});
```
Tools can take a variety of parameters, but must always return a string.
## Error Handling
By default, tools handle invalid parameters gracefully by returning error messages instead of throwing exceptions. This helps AI assistants understand and potentially correct their mistakes.
```php
$tool = Tool::as('calculate')
->for('Add two numbers')
->withNumberParameter('a', 'First number')
->withNumberParameter('b', 'Second number')
->using(fn (int $a, int $b): string => (string) ($a + $b));
// If AI provides invalid parameters, it receives:
// "Parameter validation error: Type mismatch. Expected: [a (NumberSchema, required), b (NumberSchema, required)]. Received: {"a":"five","b":10}"
```
### Opting Out
If you prefer exceptions for invalid parameters:
```php
// Per-tool
$tool->withoutErrorHandling();
// Per-request
Prism::text()->withoutToolErrorHandling();
```
**Best Practice**: Use default error handling for conversational AI. Disable it only when you need strict validation that stops execution.
## Parameter Definition
Prism offers multiple ways to define tool parameters, from simple primitives to complex objects.
### String Parameters
Perfect for text inputs:
```php
use Prism\Prism\Facades\Tool;
$tool = Tool::as('search')
->for('Search for information')
->withStringParameter('query', 'The search query')
->using(function (string $query): string {
return "Search results for: {$query}";
});
```
### Number Parameters
For integer or floating-point values:
```php
use Prism\Prism\Facades\Tool;
$tool = Tool::as('calculate')
->for('Perform calculations')
->withNumberParameter('value', 'The number to process')
->using(function (float $value): string {
return "Calculated result: {$value * 2}";
});
```
### Boolean Parameters
For true/false flags:
```php
use Prism\Prism\Facades\Tool;
$tool = Tool::as('feature_toggle')
->for('Toggle a feature')
->withBooleanParameter('enabled', 'Whether to enable the feature')
->using(function (bool $enabled): string {
return "Feature is now " . ($enabled ? 'enabled' : 'disabled');
});
```
### Array Parameters
For handling lists of items:
```php
use Prism\Prism\Facades\Tool;
$tool = Tool::as('process_tags')
->for('Process a list of tags')
->withArrayParameter(
'tags',
'List of tags to process',
new StringSchema('tag', 'A single tag')
)
->using(function (array $tags): string {
return "Processing tags: " . implode(', ', $tags);
});
```
### Enum Parameters
When you need to restrict values to a specific set:
```php
use Prism\Prism\Facades\Tool;
$tool = Tool::as('set_status')
->for('Set the status')
->withEnumParameter(
'status',
'The new status',
['draft', 'published', 'archived']
)
->using(function (string $status): string {
return "Status set to: {$status}";
});
```
### Object Parameters
For complex objects without needing to create separate schema instances:
```php
use Prism\Prism\Facades\Tool;
use Prism\Prism\Schema\StringSchema;
use Prism\Prism\Schema\NumberSchema;
$tool = Tool::as('update_user')
->for('Update a user profile')
->withObjectParameter(
'user',
'The user profile data',
[
new StringSchema('name', 'User\'s full name'),
new NumberSchema('age', 'User\'s age'),
new StringSchema('email', 'User\'s email address')
],
requiredFields: ['name', 'email']
)
->using(function (array $user): string {
return "Updated user profile for: {$user['name']}";
});
```
### Schema-based Parameters
For complex, nested data structures, you can use Prism's schema system:
```php
use Prism\Prism\Facades\Tool;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
use Prism\Prism\Schema\NumberSchema;
$tool = Tool::as('create_user')
->for('Create a new user profile')
->withParameter(new ObjectSchema(
name: 'user',
description: 'The user profile data',
properties: [
new StringSchema('name', 'User\'s full name'),
new NumberSchema('age', 'User\'s age'),
new StringSchema('email', 'User\'s email address')
],
requiredFields: ['name', 'email']
))
->using(function (array $user): string {
return "Created user profile for: {$user['name']}";
});
```
> \[!TIP]
> For more complex parameter definitions, Prism provides a powerful schema system. See our [complete schemas guide](/core-concepts/schemas) to learn how to define complex nested objects, arrays, enums, and more.
## Complex Tool Implementation
For more sophisticated tools, you can create dedicated classes:
```php
namespace App\Tools;
use Prism\Prism\Tool;
use Illuminate\Support\Facades\Http;
class SearchTool extends Tool
{
public function __construct()
{
$this
->as('search')
->for('useful when you need to search for current events')
->withStringParameter('query', 'Detailed search query. Best to search one topic at a time.')
->using($this);
}
public function __invoke(string $query): string
{
$response = Http::get('https://serpapi.com/search', [
'engine' => 'google',
'q' => $query,
'google_domain' => 'google.com',
'gl' => 'us',
'hl' => 'en',
'api_key' => config('services.serpapi.api_key'),
]);
$results = collect($response->json('organic_results'));
$results->map(function ($result) {
return [
'title' => $result['title'],
'link' => $result['link'],
'snippet' => $result['snippet'],
];
})->take(4);
return view('prompts.search-tool-results', [
'results' => $results,
])->render();
}
}
```
## Tool Choice Options
You can control how the AI uses tools with the `withToolChoice` method:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Enums\ToolChoice;
$prism = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withMaxSteps(2)
->withPrompt('How is the weather in Paris?')
->withTools([$weatherTool])
// Let the AI decide whether to use tools
->withToolChoice(ToolChoice::Auto)
// Force the AI to use a tool
->withToolChoice(ToolChoice::Any)
// Force the AI to use a specific tool
->withToolChoice('weather');
```
> \[!WARNING]
> Tool choice support varies by provider. Check your provider's documentation for specific capabilities.
## Response Handling with Tools
When your AI uses tools, you can inspect the results and see how it arrived at its answer:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-latest')
->withMaxSteps(2)
->withPrompt('What is the weather like in Paris?')
->withTools([$weatherTool])
->asText();
// Get the final answer
echo $response->text;
// ->text is empty for tool calls
// Inspect tool usage
if ($response->toolResults) {
foreach ($response->toolResults as $toolResult) {
echo "Tool: " . $toolResult->toolName . "\n";
echo "Result: " . $toolResult->result . "\n";
}
}
foreach ($response->steps as $step) {
if ($step->toolCalls) {
foreach ($step->toolCalls as $toolCall) {
echo "Tool: " . $toolCall->name . "\n";
echo "Arguments: " . json_encode($toolCall->arguments()) . "\n";
}
}
}
```
## Provider Tools
In addition to custom tools that you define, Prism supports **provider tools** - built-in capabilities offered directly by AI providers. These are specialized tools that leverage the provider's own infrastructure and services.
### Understanding Provider Tools vs Custom Tools
**Custom Tools** (covered above) are functions you define and implement yourself:
* You control the logic and implementation
* Called by the AI, executed by your code
* Can access your databases, APIs, and services
**Provider Tools** are built-in capabilities offered by the AI provider:
* Implemented and executed by the provider
* Access the provider's own services and infrastructure
* Enable capabilities like code execution, web search, and more
### Using Provider Tools
Provider tools are added to your requests using the `withProviderTools()` method with `ProviderTool` objects:
```php
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\ProviderTool;
$response = Prism::text()
->using('anthropic', 'claude-3-5-sonnet-latest')
->withPrompt('Calculate the fibonacci sequence up to 100')
->withProviderTools([
new ProviderTool(type: 'code_execution_20250522', name: 'code_execution')
])
->asText();
```
### Available Provider Tools
Each provider offers different built-in capabilities. Check the provider-specific documentation for detailed information about available tools, configuration options, and usage examples.
### ProviderTool Object
The `ProviderTool` class accepts three parameters:
```php
new ProviderTool(
type: 'code_execution_20250522', // Required: The provider tool identifier
name: 'code_execution', // Optional: Custom name for the tool
options: [] // Optional: Provider-specific options
)
```
* **type**: The provider-specific tool identifier (required)
* **name**: Optional custom name for the tool
* **options**: Additional provider-specific configuration options
### Combining Provider Tools and Custom Tools
You can use both provider tools and custom tools in the same request:
```php
use Prism\Prism\Prism;
use Prism\Prism\ValueObjects\ProviderTool;
use Prism\Prism\Facades\Tool;
$customTool = Tool::as('database_lookup')
->for('Look up user information')
->withStringParameter('user_id', 'The user ID to look up')
->using(function (string $userId): string {
// Your database lookup logic
return "User data for ID: {$userId}";
});
$response = Prism::text()
->using('anthropic', 'claude-3-5-sonnet-latest')
->withMaxSteps(5)
->withPrompt('Look up user 123 and calculate their usage statistics')
->withTools([$customTool])
->withProviderTools([
new ProviderTool(type: 'code_execution_20250522', name: 'code_execution')
])
->asText();
```
---
---
url: /index.md
---
---
---
url: /input-modalities/video.md
---
# Video
Prism supports including video files and YouTube videos in your messages for advanced analysis with supported providers like Gemini.
See the [provider support table](/getting-started/introduction.html#provider-support) to check whether Prism supports your chosen provider.
Note however that provider support may differ by model. If you receive error messages with a provider that Prism indicates is supported, check the provider's documentation as to whether the model you are using supports video files.
::: tip
For other input modalities like audio and images, see their respective documentation pages:
* [Audio documentation](/input-modalities/audio.html)
* [Images documentation](/input-modalities/images.html)
:::
## Getting started
To add a video to your prompt, use the `withPrompt` method with a `Video` value object:
```php
use Prism\Prism\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Media\Video;
// From a local path
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
"What's in this video?",
[Video::fromLocalPath(path: '/path/to/video.mp4')]
)
->asText();
// From a path on a storage disk
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
"What's in this video?",
[Video::fromStoragePath(
path: '/path/to/video.mp4',
diskName: 'my-disk' // optional - omit/null for default disk
)]
)
->asText();
// From a URL
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Analyze this video:',
[Video::fromUrl(url: 'https://example.com/video.mp4')]
)
->asText();
// From a YouTube URL (automatically extracts the video ID)
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'What is this YouTube video about?',
[Video::fromUrl(url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ')]
)
->asText();
// From shortened YouTube URL
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'What is this YouTube video about?',
[Video::fromUrl(url: 'https://youtu.be/dQw4w9WgXcQ')]
)
->asText();
// From base64
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Analyze this video:',
[Video::fromBase64(
base64: base64_encode(file_get_contents('/path/to/video.mp4')),
mimeType: 'video/mp4'
)]
)
->asText();
// From raw content
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'Analyze this video:',
[Video::fromRawContent(
rawContent: file_get_contents('/path/to/video.mp4'),
mimeType: 'video/mp4'
)]
)
->asText();
```
## Alternative: Using withMessages
You can also include videos using the message-based approach:
```php
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Media\Video;
$message = new UserMessage(
"What's in this video?",
[Video::fromLocalPath(path: '/path/to/video.mp4')]
);
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withMessages([$message])
->asText();
```
## Supported Video Types
Prism supports a variety of video formats, including:
* MP4 (video/mp4)
* MOV (video/quicktime)
* WEBM (video/webm)
* AVI (video/x-msvideo)
* YouTube videos (via URL)
The specific supported formats depend on the provider. Gemini is currently the main provider with comprehensive video analysis capabilities. Check the provider's documentation for a complete list of supported formats.
## YouTube Video Support
Prism provides seamless support for YouTube videos. When you pass a YouTube URL to `Video::fromUrl()`, Prism automatically extracts the video ID and sends it to the provider in the appropriate format.
Supported YouTube URL formats:
* Standard: `https://www.youtube.com/watch?v=VIDEO_ID`
* Shortened: `https://youtu.be/VIDEO_ID`
Example:
```php
$response = Prism::text()
->using(Provider::Gemini, 'gemini-1.5-flash')
->withPrompt(
'What is this YouTube video about?',
[Video::fromUrl('https://www.youtube.com/watch?v=dQw4w9WgXcQ')]
)
->asText();
```
## Transfer mediums
Providers are not consistent in their support of sending raw contents, base64 and/or URLs.
Prism tries to smooth over these rough edges, but its not always possible.
### Supported conversions
* Where a provider does not support URLs: Prism will fetch the URL and use base64 or rawContent.
* Where you provide a file, base64 or rawContent: Prism will switch between base64 and rawContent depending on what the provider accepts.
### Limitations
* Where a provider only supports URLs: if you provide a file path, raw contents or base64, for security reasons Prism does not create a URL for you and your request will fail.
---
---
url: /providers/voyageai.md
---
# Voyage AI
## Configuration
```php
'voyageai' => [
'api_key' => env('VOYAGEAI_API_KEY', ''),
'url' => env('VOYAGEAI_URL', 'https://api.voyageai.com/v1'),
],
```
## Provider specific options
You can change some options on your request specific to Voyage AI by using `->withProviderOptions()`.
### Input type
By default, Voyage AI generates general purpose vectors.
However, they taylor your vectors for the task they are intended for - for search ("query") or for retrieval ("document"):
For search / querying:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::embeddings()
->using(Provider::VoyageAI, 'voyage-3-lite')
->fromInput('The food was delicious and the waiter...')
->withProviderOptions(['inputType' => 'query'])
->asEmbeddings();
```
For document retrieval:
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::embeddings()
->using(Provider::VoyageAI, 'voyage-3-lite')
->fromInput('The food was delicious and the waiter...')
->withProviderOptions(['inputType' => 'document'])
->asEmbeddings();
```
### Truncation
By default, Voyage AI truncates inputs that are over the context length.
You can force it to throw an error instead by setting truncation to false.
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
Prism::embeddings()
->using(Provider::VoyageAI, 'voyage-3-lite')
->fromInput('The food was delicious and the waiter...')
->withProviderOptions(['truncation' => false])
->asEmbeddings();
```
---
---
url: /providers/xai.md
---
# xAI
## Configuration
```php
'xai' => [
'api_key' => env('XAI_API_KEY', ''),
'url' => env('XAI_URL', 'https://api.x.ai/v1'),
],
```
## Provider-specific options
### Extended Thinking/Reasoning
xAI's Grok models support an optional extended thinking mode, where the model will reason through problems before returning its answer. This is particularly useful for complex mathematical problems, logical reasoning, and detailed analysis tasks.
#### Enabling thinking mode
```php
use Prism\Prism\Enums\Provider;
use Prism\Prism\Prism;
$response = Prism::text()
->using(Provider::XAI, 'grok-4')
->withPrompt('Solve this complex equation: 3x² + 5x - 2 = 0')
->withProviderOptions([
'thinking' => ['enabled' => true]
])
->asText();
```
Thinking content is automatically extracted when present in the response and can be accessed through streaming chunks.
If you prefer not to process thinking content, you can disable it. Set the `thinking` option to `false`.
#### Streaming thinking content
When using streaming, thinking content is yielded as separate chunks with `ChunkType::Thinking`:
```php
use Prism\Prism\Enums\ChunkType;
$stream = Prism::text()
->using(Provider::XAI, 'grok-4')
->withPrompt('Explain quantum entanglement in detail')
->asStream();
foreach ($stream as $chunk) {
if ($chunk->chunkType === ChunkType::Thinking) {
echo $chunk->text . PHP_EOL; // Outputs: Thinking...
} elseif ($chunk->chunkType === ChunkType::Text) {
echo $chunk->text;
}
}
```
### Structured Output
xAI supports structured output through JSON schema validation. The following models support structured output:
* `grok-3`
* `grok-4`
```php
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
use Prism\Prism\Schema\BooleanSchema;
$schema = new ObjectSchema(
'weather_report',
'Weather forecast with recommendations',
[
new StringSchema('forecast', 'The weather forecast'),
new StringSchema('clothing', 'Clothing recommendation'),
new BooleanSchema('coat_required', 'Whether a coat is needed'),
],
['forecast', 'clothing', 'coat_required']
);
$response = Prism::structured()
->withSchema($schema)
->using(Provider::XAI, 'grok-4')
->withPrompt('What\'s the weather like in Detroit and should I wear a coat?')
->asStructured();
// Access structured data
echo $response->structured['forecast']; // "75° and sunny"
echo $response->structured['coat_required']; // false
```
#### Strict schema mode
Enable strict schema validation for more reliable structured output:
```php
$response = Prism::structured()
->withSchema($schema)
->using(Provider::XAI, 'grok-4')
->withProviderOptions([
'schema' => ['strict' => true]
])
->withPrompt('Analyze this data')
->asStructured();
```
### Tool Calling
xAI supports function calling with tools. Tools can be used alongside thinking mode for complex problem-solving scenarios.
```php
use Prism\Prism\Facades\Tool;
$tools = [
Tool::as('calculator')
->for('Perform mathematical calculations')
->withStringParameter('expression', 'Mathematical expression to calculate')
->using(fn (string $expression): string => "Result: " . eval("return $expression;")),
Tool::as('weather')
->for('Get current weather information')
->withStringParameter('city', 'City name')
->using(fn (string $city): string => "Weather in {$city}: 72°F and sunny"),
];
$response = Prism::text()
->using(Provider::XAI, 'grok-4')
->withTools($tools)
->withMaxSteps(3)
->withPrompt('Calculate 15 * 23 and tell me the weather in Detroit')
->asText();
```
### Model Parameters
#### Temperature Control
Control the randomness of responses:
```php
$response = Prism::text()
->using(Provider::XAI, 'grok-4')
->withTemperature(0.7) // 0.0 = deterministic, 1.0 = very creative
->withPrompt('Write a creative story')
->asText();
```
#### Top-P Sampling
Control nucleus sampling:
```php
$response = Prism::text()
->using(Provider::XAI, 'grok-4')
->withTopP(0.9) // Consider top 90% probability mass
->withPrompt('Generate diverse responses')
->asText();
```
#### Token Limits
Set maximum output tokens:
```php
$response = Prism::text()
->using(Provider::XAI, 'grok-4')
->withMaxTokens(1000)
->withPrompt('Write a detailed explanation')
->asText();
```
## Advanced Examples
### Complex Analysis with Thinking
```php
$response = Prism::text()
->using(Provider::XAI, 'grok-4')
->withPrompt('
Analyze the economic implications of implementing a universal basic income program.
Consider both potential benefits and drawbacks, and provide specific examples.
')
->asStream();
$analysis = '';
$reasoning = '';
foreach ($response as $chunk) {
if ($chunk->chunkType === ChunkType::Thinking) {
$reasoning .= $chunk->text;
} else {
$analysis .= $chunk->text;
echo $chunk->text; // Stream to user
}
}
// Save the reasoning process for later review
file_put_contents('analysis_reasoning.txt', $reasoning);
```
### Structured Data Extraction
```php
use Prism\Prism\Schema\ArraySchema;
use Prism\Prism\Schema\IntegerSchema;
use Prism\Prism\Schema\NumberSchema;
$schema = new ObjectSchema(
'financial_analysis',
'Complete financial analysis result',
[
new StringSchema('summary', 'Executive summary'),
new NumberSchema('total_revenue', 'Total revenue amount'),
new NumberSchema('profit_margin', 'Profit margin percentage'),
new ArraySchema('recommendations', 'List of recommendations',
new StringSchema('recommendation', 'Individual recommendation')
),
new ObjectSchema('risk_assessment', 'Risk analysis', [
new StringSchema('level', 'Risk level (low/medium/high)'),
new IntegerSchema('score', 'Risk score from 1-10'),
], ['level', 'score']),
],
['summary', 'total_revenue', 'profit_margin', 'recommendations', 'risk_assessment']
);
$response = Prism::structured()
->withSchema($schema)
->using(Provider::XAI, 'grok-4')
->withPrompt('
Analyze this financial data:
Q1 Revenue: $1.2M, Q1 Costs: $800K
Q2 Revenue: $1.5M, Q2 Costs: $900K
Provide a complete analysis with recommendations.
')
->asStructured();
$analysis = $response->structured;
echo "Revenue: $" . number_format($analysis['total_revenue']);
echo "Risk Level: " . $analysis['risk_assessment']['level'];
```
### Model Validation
Structured output is only supported on specific models. Prism will throw an exception for unsupported models:
```php
use Prism\Prism\Exceptions\PrismException;
try {
$response = Prism::structured()
->withSchema($schema)
->using(Provider::XAI, 'unsupported-model')
->asStructured();
} catch (PrismException $e) {
// Handle unsupported model error
echo "Error: " . $e->getMessage();
}
```
## Considerations
### Thinking Content Processing
* Thinking content is automatically filtered to remove repetitive "Thinking..." patterns
* Only meaningful reasoning content is yielded in thinking chunks
* Thinking content appears before regular response content in streams
* Thinking can be disabled if not needed to reduce processing overhead
### API Compatibility
xAI uses an OpenAI-compatible API structure, which means:
* Request/response formats are similar to OpenAI
* Tool calling follows OpenAI's function calling specification
* Structured output uses JSON schema format
* Streaming follows server-sent events (SSE) format
### Token Management
* Thinking tokens count toward your total token usage
* Set appropriate `maxTokens` limits when expecting long thinking sequences
* Monitor usage through the response objects for cost tracking