Skip to content

Commit 267ca19

Browse files
cyppesixlive
andauthored
Add Image Support to Embeddings API (#789)
Co-authored-by: TJ Miller <tj@themillers.co>
1 parent 321bf91 commit 267ca19

File tree

8 files changed

+280
-9
lines changed

8 files changed

+280
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ phpunit.xml
1212
ray.php
1313
CLAUDE.md
1414
.phpunit.result.cache
15+
.ddev/

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"pestphp/pest-plugin-laravel": "^3.0",
3939
"phpstan/extension-installer": "^1.3",
4040
"phpstan/phpstan-deprecation-rules": "^2.0",
41-
"rector/rector": "2.2.7",
41+
"rector/rector": "2.2.14",
4242
"projektgopher/whisky": "^0.7.0",
4343
"orchestra/testbench": "^10",
4444
"mockery/mockery": "^1.6",

docs/core-concepts/embeddings.md

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Embeddings
22

3-
Transform your text into powerful vector representations! Embeddings let you add semantic search, recommendation systems, and other advanced natural language features to your applications.
3+
Transform your content into powerful vector representations! Embeddings let you add semantic search, recommendation systems, and other advanced features to your applications - whether you're working with text or images.
44

55
## Quick Start
66

@@ -86,6 +86,71 @@ $response = Prism::embeddings()
8686
> [!NOTE]
8787
> Make sure your file exists and is readable. The generator will throw a helpful `PrismException` if there's any issue accessing the file.
8888
89+
## Image Embeddings
90+
91+
Some providers support image embeddings, enabling powerful use cases like visual similarity search, cross-modal retrieval, and multimodal applications. Prism makes it easy to generate embeddings from images using the same fluent API.
92+
93+
> [!IMPORTANT]
94+
> Image embeddings require a provider and model that supports image input (such as CLIP-based models or multimodal embedding models like BGE-VL). Check your provider's documentation to confirm image embedding support.
95+
96+
### Single Image
97+
98+
Generate an embedding from a single image:
99+
100+
```php
101+
use Prism\Prism\Facades\Prism;
102+
use Prism\Prism\ValueObjects\Media\Image;
103+
104+
$response = Prism::embeddings()
105+
->using('provider', 'model')
106+
->fromImage(Image::fromLocalPath('/path/to/product.jpg'))
107+
->asEmbeddings();
108+
109+
$embedding = $response->embeddings[0]->embedding;
110+
```
111+
112+
### Multiple Images
113+
114+
Process multiple images in a single request:
115+
116+
```php
117+
use Prism\Prism\Facades\Prism;
118+
use Prism\Prism\ValueObjects\Media\Image;
119+
120+
$response = Prism::embeddings()
121+
->using('provider', 'model')
122+
->fromImages([
123+
Image::fromLocalPath('/path/to/image1.jpg'),
124+
Image::fromUrl('https://example.com/image2.png'),
125+
])
126+
->asEmbeddings();
127+
128+
foreach ($response->embeddings as $embedding) {
129+
// Process each image embedding
130+
$vector = $embedding->embedding;
131+
}
132+
```
133+
134+
### Multimodal: Text + Image
135+
136+
Combine text and images for cross-modal search scenarios. This is particularly useful for applications like "find products similar to this image that match this description":
137+
138+
```php
139+
use Prism\Prism\Facades\Prism;
140+
use Prism\Prism\ValueObjects\Media\Image;
141+
142+
$response = Prism::embeddings()
143+
->using('provider', 'model')
144+
->fromInput('Find similar products in red')
145+
->fromImage(Image::fromBase64($productImage, 'image/png'))
146+
->asEmbeddings();
147+
```
148+
149+
You can chain `fromImage()` and `fromInput()` in any order - Prism handles both gracefully.
150+
151+
> [!TIP]
152+
> The `Image` class supports multiple input sources: `fromLocalPath()`, `fromUrl()`, `fromBase64()`, `fromStoragePath()`, and `fromRawContent()`. See the [Images documentation](/input-modalities/images.html) for details.
153+
89154
## Common Settings
90155

91156
Just like with text generation, you can fine-tune your embeddings requests:
@@ -146,7 +211,7 @@ try {
146211
}
147212
```
148213

149-
## Pro Tips 🌟
214+
## Pro Tips
150215

151216
**Vector Storage**: Consider using a vector database like Milvus, Qdrant, or pgvector to store and query your embeddings efficiently.
152217

src/Embeddings/PendingRequest.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Prism\Prism\Concerns\ConfiguresProviders;
1010
use Prism\Prism\Concerns\HasProviderOptions;
1111
use Prism\Prism\Exceptions\PrismException;
12+
use Prism\Prism\ValueObjects\Media\Image;
1213

1314
class PendingRequest
1415
{
@@ -19,6 +20,9 @@ class PendingRequest
1920
/** @var array<string> */
2021
protected array $inputs = [];
2122

23+
/** @var array<Image> */
24+
protected array $images = [];
25+
2226
public function fromInput(string $input): self
2327
{
2428
$this->inputs[] = $input;
@@ -53,6 +57,33 @@ public function fromFile(string $path): self
5357
return $this;
5458
}
5559

60+
/**
61+
* Add an image for embedding generation.
62+
*
63+
* Note: Not all providers support image embeddings. Check the provider's
64+
* documentation to ensure the model you're using supports image input.
65+
* Common providers that support image embeddings include CLIP-based models
66+
* and multimodal embedding models like BGE-VL.
67+
*/
68+
public function fromImage(Image $image): self
69+
{
70+
$this->images[] = $image;
71+
72+
return $this;
73+
}
74+
75+
/**
76+
* Add multiple images for embedding generation.
77+
*
78+
* @param array<Image> $images
79+
*/
80+
public function fromImages(array $images): self
81+
{
82+
$this->images = array_merge($this->images, $images);
83+
84+
return $this;
85+
}
86+
5687
/**
5788
* @deprecated Use `asEmbeddings` instead.
5889
*/
@@ -63,8 +94,8 @@ public function generate(): Response
6394

6495
public function asEmbeddings(): Response
6596
{
66-
if ($this->inputs === []) {
67-
throw new PrismException('Embeddings input is required');
97+
if ($this->inputs === [] && $this->images === []) {
98+
throw new PrismException('Embeddings input is required (text or images)');
6899
}
69100

70101
$request = $this->toRequest();
@@ -82,6 +113,7 @@ protected function toRequest(): Request
82113
model: $this->model,
83114
providerKey: $this->providerKey(),
84115
inputs: $this->inputs,
116+
images: $this->images,
85117
clientOptions: $this->clientOptions,
86118
clientRetry: $this->clientRetry,
87119
providerOptions: $this->providerOptions

src/Embeddings/Request.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
use Prism\Prism\Concerns\ChecksSelf;
99
use Prism\Prism\Concerns\HasProviderOptions;
1010
use Prism\Prism\Contracts\PrismRequest;
11+
use Prism\Prism\ValueObjects\Media\Image;
1112

1213
class Request implements PrismRequest
1314
{
1415
use ChecksSelf, HasProviderOptions;
1516

1617
/**
1718
* @param array<string> $inputs
19+
* @param array<Image> $images
1820
* @param array<string, mixed> $clientOptions
1921
* @param array{0: array<int, int>|int, 1?: Closure|int, 2?: ?callable, 3?: bool} $clientRetry
2022
* @param array<string, mixed> $providerOptions
@@ -23,6 +25,7 @@ public function __construct(
2325
protected string $model,
2426
protected string $providerKey,
2527
protected array $inputs,
28+
protected array $images,
2629
protected array $clientOptions,
2730
protected array $clientRetry,
2831
array $providerOptions = [],
@@ -54,6 +57,32 @@ public function inputs(): array
5457
return $this->inputs;
5558
}
5659

60+
/**
61+
* Get image inputs for embedding generation.
62+
*
63+
* @return array<Image>
64+
*/
65+
public function images(): array
66+
{
67+
return $this->images;
68+
}
69+
70+
/**
71+
* Check if the request contains image inputs.
72+
*/
73+
public function hasImages(): bool
74+
{
75+
return $this->images !== [];
76+
}
77+
78+
/**
79+
* Check if the request contains text inputs.
80+
*/
81+
public function hasInputs(): bool
82+
{
83+
return $this->inputs !== [];
84+
}
85+
5786
#[\Override]
5887
public function model(): string
5988
{

src/Facades/Prism.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public function __construct(
4848
private readonly PrismFake $fake
4949
) {}
5050

51-
public function resolve(ProviderEnum|string $name, array $providerConfig = []): Provider
51+
public function resolve(ProviderEnum|string $name, array $providerConfig = []): PrismFake
5252
{
5353
$this->fake->setProviderConfig($providerConfig);
5454

src/ValueObjects/Media/Media.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,6 @@ public function mimeType(): ?string
256256
}
257257

258258
/**
259-
* Get a file resource suitable for HTTP multipart uploads
260-
*
261259
* @return resource
262260
*/
263261
public function resource()
@@ -290,7 +288,6 @@ public function fetchUrlContent(): void
290288
return;
291289
}
292290

293-
/** @var \Illuminate\Http\Client\Response $response */
294291
$response = Http::get($this->url);
295292
$content = $response->body();
296293

0 commit comments

Comments
 (0)