Skip to content

Commit 2d85e7c

Browse files
authored
refactor: better media handling (#321)
1 parent fb21a8a commit 2d85e7c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1340
-498
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
],
2020
"require": {
2121
"php": "^8.2",
22+
"ext-fileinfo": "*",
2223
"laravel/framework": "^11.0|^12.0"
2324
},
2425
"config": {

docs/components/ProviderSupport.vue

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -169,20 +169,10 @@ export default {
169169
"Documents",
170170
],
171171
providers: [
172-
{
173-
name: "Amazon Bedrock",
174-
text: Planned,
175-
streaming: Unsupported,
176-
structured: Unsupported,
177-
embeddings: Planned,
178-
image: Planned,
179-
tools: Planned,
180-
documents: Unsupported,
181-
},
182172
{
183173
name: "Anthropic",
184174
text: Supported,
185-
streaming: Planned,
175+
streaming: Supported,
186176
structured: Adapted,
187177
embeddings: Unsupported,
188178
image: Supported,
@@ -200,12 +190,42 @@ export default {
200190
documents: Unsupported,
201191
},
202192
{
203-
name: "DeepSeek",
193+
name: "Bedrock - Anthropic",
204194
text: Supported,
205195
streaming: Unsupported,
206196
structured: Supported,
207197
embeddings: Unsupported,
198+
image: Supported,
199+
tools: Supported,
200+
documents: Unsupported,
201+
},
202+
{
203+
name: "Bedrock - Cohere",
204+
text: Unsupported,
205+
streaming: Unsupported,
206+
structured: Unsupported,
207+
embeddings: Supported,
208208
image: Unsupported,
209+
tools: Unsupported,
210+
documents: Unsupported,
211+
},
212+
{
213+
name: "Bedrock - Converse",
214+
text: Supported,
215+
streaming: Unsupported,
216+
structured: Supported,
217+
embeddings: Unsupported,
218+
image: Supported,
219+
tools: Supported,
220+
documents: Supported,
221+
},
222+
{
223+
name: "DeepSeek",
224+
text: Supported,
225+
streaming: Unsupported,
226+
structured: Supported,
227+
embeddings: Unsupported,
228+
image: Supported,
209229
tools: Supported,
210230
documents: Unsupported,
211231
},
@@ -257,7 +277,7 @@ export default {
257277
embeddings: Supported,
258278
image: Supported,
259279
tools: Supported,
260-
documents: Unsupported,
280+
documents: Supported,
261281
},
262282
{
263283
name: "VoyageAI",

docs/input-modalities/documents.md

Lines changed: 95 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
11
# Documents
22

3-
Prism currently supports documents with Gemini and Anthropic.
3+
Prism supports including documents in your messages with some providers.
4+
5+
See the [provider support table](/getting-started/introduction.html#provider-support) to check whether Prism supports your chosen provider.
6+
7+
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.
48

59
## Supported file types
610

7-
Different providers support different document types.
8-
9-
At the time of writing:
10-
- Anthropic supports
11-
- pdf (application/pdf)
12-
- txt (text/plain)
13-
- md (text/md)
14-
- Gemini supports:
15-
- pdf (application/pdf)
16-
- javascript (text/javascript)
17-
- python (text/x-python)
18-
- txt (text/plain)
19-
- html (text/html)
20-
- css (text/css)
21-
- md (text/md)
22-
- csv (text/csv)
23-
- xml (text/xml)
24-
- rtf (text/rtf)
25-
- Mistral supports:
26-
- PDF (application/pdf)
27-
- CSV (text/csv)
28-
- text files (text/plain)
29-
- OpenAI supports:
30-
- PDF (application/pdf)
31-
- `file_id` (previously uploaded pdf file id.)
32-
33-
All of these formats should work with Prism.
11+
> [!TIP]
12+
> If provider interoperability is important to your app, we recommend converting documents to markdown.
13+
14+
Please check provider documentation for supported file/mime types, as support differs widely.
15+
16+
The most supported file types are pdf and text/plain (which may include markdown).
17+
18+
## Transfer mediums
19+
20+
> [!TIP]
21+
> If provider interoperability is important to your app, we recommend using rawContent or base64.
22+
23+
Providers are not consistent in their support of sending file raw contents, base64 and/or URLs.
24+
25+
Prism tries to smooth over these rough edges, but its not always possible.
26+
27+
### Supported conversions
28+
- Where a provider does not support URLs: Prism will fetch the URL and use base64 or rawContent.
29+
- Where you provide a file, base64 or rawContent: Prism will switch between base64 and rawContent depending on what the provider accepts.
30+
31+
### Limitations
32+
33+
- 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.
34+
- Chunks cannot be passed between providers, as they could be in different formats (however, currently only Anthropic supports them).
3435

3536
## Getting started
3637

37-
To add an image to your message, add a `Document` value object to the `additionalContent` property:
38+
To add a document to your message, add a `Document` value object to the `additionalContent` property:
3839

3940
```php
4041
use Prism\Prism\Enums\Provider;
@@ -44,29 +45,82 @@ use Prism\Prism\ValueObjects\Messages\Support\Document;
4445
use Prism\Prism\ValueObjects\Messages\Support\OpenAIFile;
4546

4647
Prism::text()
47-
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
48+
->using('my-provider', 'my-model')
4849
->withMessages([
50+
// From a local path
51+
new UserMessage('Here is the document from a local path', [
52+
Document::fromLocalPath(
53+
path: 'tests/Fixtures/test-pdf.pdf',
54+
title: 'My document title' // optional
55+
),
56+
]),
57+
// From a storage path
58+
new UserMessage('Here is the document from a storage path', [
59+
Document::fromStoragePath(
60+
path: 'mystoragepath/file.pdf',
61+
disk: 'my-disk', // optional - omit/null for default disk
62+
title: 'My document title' // optional
63+
),
64+
]),
4965
// From base64
5066
new UserMessage('Here is the document from base64', [
51-
Document::fromBase64(base64_encode(file_get_contents('tests/Fixtures/test-pdf.pdf')), 'application/pdf'),
67+
Document::fromBase64(
68+
base64: $baseFromDB,
69+
mimeType: 'optional/mimetype', // optional
70+
title: 'My document title' // optional
71+
),
5272
]),
53-
// Or from a path
54-
new UserMessage('Here is the document from a local path', [
55-
Document::fromPath('tests/Fixtures/test-pdf.pdf'),
73+
// From raw content
74+
new UserMessage('Here is the document from raw content', [
75+
Document::fromRawContent(
76+
rawContent: $rawContent,
77+
mimeType: 'optional/mimetype', // optional
78+
title: 'My document title' // optional
79+
),
5680
]),
57-
// Or from a text string
81+
// From a text string
5882
new UserMessage('Here is the document from a text string (e.g. from your database)', [
59-
Document::fromText('Hello world!'),
83+
Document::fromText(
84+
text: 'Hello world!',
85+
title: 'My document title' // optional
86+
),
6087
]),
61-
// Or from an URL
62-
new UserMessage('Here is the document from a url (make sure this is publically accessable)', [
63-
Document::fromUrl('https://example.com/test-pdf.pdf'),
88+
// From an URL
89+
new UserMessage('Here is the document from a url (make sure this is publically accessible)', [
90+
Document::fromUrl(
91+
url: 'https://example.com/test-pdf.pdf',
92+
title: 'My document title' // optional
93+
),
6494
]),
65-
// Or from a file_id
95+
// From chunks
96+
new UserMessage('Here is a chunked document', [
97+
Document::fromChunks(
98+
chunks: [
99+
'chunk one',
100+
'chunk two'
101+
],
102+
title: 'My document title' // optional
103+
),
104+
]),
105+
])
106+
->asText();
107+
108+
```
109+
110+
Or, if using an OpenAI file_id - add an `OpenAIFile`:
111+
112+
```php
113+
use Prism\Enums\Provider;
114+
use Prism\Prism\Prism;
115+
use Prism\Prism\ValueObjects\Messages\UserMessage;
116+
use Prism\Prism\ValueObjects\Messages\Support\OpenAIFile;
117+
118+
Prism::text()
119+
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
120+
->withMessages([
66121
new UserMessage('Here is the document from file_id', [
67122
new OpenAIFile('file-lsfgSXyV2xEb8gw8fYjXU6'),
68123
]),
69124
])
70125
->asText();
71-
72126
```

docs/input-modalities/images.md

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Prism supports including images in your messages for vision analysis for most pr
44

55
See the [provider support table](/getting-started/introduction.html#provider-support) to check whether Prism supports your chosen provider.
66

7-
Note however that not all models with a supported provider support vision. If you are running into issues with not supported messages, double check the provider model documentation for support.
7+
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.
88

99
## Getting started
1010

@@ -14,39 +14,54 @@ To add an image to your message, add an `Image` value object to the `additionalC
1414
use Prism\Prism\ValueObjects\Messages\UserMessage;
1515
use Prism\Prism\ValueObjects\Messages\Support\Image;
1616

17-
// From a local file
17+
// From a local path
1818
$message = new UserMessage(
1919
"What's in this image?",
20-
[Image::fromPath('/path/to/image.jpg')]
20+
[Image::fromLocalPath(path: '/path/to/image.jpg')]
21+
);
22+
23+
// From a path on a storage disk
24+
$message = new UserMessage(
25+
"What's in this image?",
26+
[Image::fromStoragePath(
27+
path: '/path/to/image.jpg',
28+
disk: 'my-disk' // optional - omit/null for default disk
29+
)]
2130
);
2231

2332
// From a URL
2433
$message = new UserMessage(
2534
'Analyze this diagram:',
26-
[Image::fromUrl('https://example.com/diagram.png')]
35+
[Image::fromUrl(url: 'https://example.com/diagram.png')]
2736
);
2837

29-
// From a URL which does not end in the image format,
30-
// you can pass the mime type as the second argument
31-
// e.g. if you are generating a temporary URL
38+
// From base64
3239
$message = new UserMessage(
3340
'Analyze this diagram:',
34-
[Image::fromUrl(
35-
'https://storage.example.com/diagram.png?AccessID=test&Expires=1742330260&Signature=dVQaFcIk9FJWIVnvV1%2FWu',
36-
'image/png'
37-
)]
41+
[Image::fromBase64(base64: base64_encode(file_get_contents('/path/to/image.jpg')))]
3842
);
3943

40-
// From a Base64
41-
$image = base64_encode(file_get_contents('/path/to/image.jpg'));
42-
44+
// From raw content
4345
$message = new UserMessage(
4446
'Analyze this diagram:',
45-
[Image::fromBase64($image)]
47+
[Image::fromRawContent(rawContent: file_get_contents('/path/to/image.jpg'))]
4648
);
4749

4850
$response = Prism::text()
4951
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
5052
->withMessages([$message])
5153
->generate();
5254
```
55+
56+
## Transfer mediums
57+
58+
Providers are not consistent in their support of sending raw contents, base64 and/or URLs (as noted above).
59+
60+
Prism tries to smooth over these rough edges, but its not always possible.
61+
62+
### Supported conversions
63+
- Where a provider does not support URLs: Prism will fetch the URL and use base64 or rawContent.
64+
- Where you provide a file, base64 or rawContent: Prism will switch between base64 and rawContent depending on what the provider accepts.
65+
66+
### Limitations
67+
- 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.

src/Contracts/ProviderMediaMapper.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Prism\Prism\Contracts;
4+
5+
use Prism\Prism\Enums\Provider;
6+
use Prism\Prism\Exceptions\PrismException;
7+
use Prism\Prism\ValueObjects\Messages\Support\Media;
8+
9+
abstract class ProviderMediaMapper
10+
{
11+
public function __construct(public readonly Media $media)
12+
{
13+
if ($this->validateMedia() === false) {
14+
$providerName = $this->provider() instanceof Provider ? $this->provider()->value : $this->provider();
15+
16+
$calledClass = static::class;
17+
18+
throw new PrismException("The $providerName provider does not support the mediums available in the provided `$calledClass`. Pleae consult the Prism documentation for more information on which mediums the $providerName provider supports.");
19+
}
20+
}
21+
22+
abstract public function toPayload(): mixed;
23+
24+
abstract protected function provider(): string|Provider;
25+
26+
abstract protected function validateMedia(): bool;
27+
}

0 commit comments

Comments
 (0)