Merge pull request #2656 from qdrant/clelia/multimodal-tutorial-revamp

chore: update multimodal search tutorial
This commit is contained in:
Clelia (Astra) Bertelli
2026-09-03 10:59:07 +02:00
committed by GitHub
65 changed files with 2594 additions and 207 deletions
@@ -5,6 +5,7 @@ edition = "2024"
[dependencies]
anyhow = "1.0.100"
base64="0.23.1"
chrono = "0.4"
csv = "1.3"
qdrant-edge = "0.8.0"
@@ -3,5 +3,6 @@
| [Qdrant Local Quickstart](/documentation/quickstart/) | Basic CRUD operations and local deployment. | <span class="pill">Any</span> | 10m | <span class="text-green">Beginner</span> |
| [Qdrant Cloud Quickstart](/documentation/cloud-quickstart/) | Basic CRUD operations on Qdrant Cloud. | <span class="pill">Any</span> | 10m | <span class="text-green">Beginner</span> |
| [Semantic Search 101](/documentation/tutorials-basics/search-beginners/) | Build a search engine for science fiction books. | <span class="pill">Any</span> | 10m | <span class="text-green">Beginner</span> |
| [Multimodal Search](/documentation/tutorials-basics/multimodal-search/) | Build a pipeline to search across text and images modalities | <span class="pill">Python</span> | 15m | <span class="text-green">Beginner</span> |
| [Hybrid Search](/documentation/tutorials-basics/cloud-inference-hybrid-search/) | Get started with hybrid search. | <span class="pill">Any</span> | 30m | <span class="text-green">Beginner</span> |
| [Hybrid Search with Reranking](/documentation/tutorials-basics/reranking-hybrid-search/) | Rerank hybrid search results for improved accuracy. | <span class="pill">Any</span> | 40m | <span class="text-yellow">Intermediate</span> |
| [Hybrid Search with Reranking](/documentation/tutorials-basics/reranking-hybrid-search/) | Rerank hybrid search results for improved accuracy. | <span class="pill">Any</span> | 40m | <span class="text-yellow">Intermediate</span> |
@@ -1,3 +1,3 @@
```csharp
Qdrant.Client
```
dotnet add package Qdrant.Client
```
@@ -1,3 +1,3 @@
```go
github.com/qdrant/go-client
```
go get github.com/qdrant/go-client
```
@@ -1,3 +1,6 @@
```java
io.qdrant:client
```
// build.gradle
dependencies {
implementation("io.qdrant:client:+") // specify the desired version
}
```
@@ -1,3 +1,3 @@
```python
qdrant-client
```
pip install qdrant-client
```
@@ -1,3 +1,3 @@
```rust
qdrant-client
```
cargo add qdrant-client
```
@@ -1,3 +1,3 @@
```typescript
qdrant/js-client-rest
```
npm install @qdrant/js-client-rest
```
@@ -0,0 +1,140 @@
using Qdrant.Client;
using Qdrant.Client.Grpc;
public class Snippet
{
public static async Task Run()
{
// @hide-start
string QDRANT_URL = "xyz-example.eu-central.aws.cloud.qdrant.io";
string QDRANT_API_KEY = "<your-api-key>";
// @hide-end
// @block-start client-connection
var client = new QdrantClient(
host: QDRANT_URL,
https: true,
apiKey: QDRANT_API_KEY
);
// @block-end client-connection
// @block-start define-dataset
static string ImageToBase64Url(string imagePath)
{
string prefix = "data:image/png;base64";
byte[] bytes = File.ReadAllBytes(imagePath);
return $"{prefix},{Convert.ToBase64String(bytes)}";
}
var documents = new[]
{
new { Caption = "An image about plane emergency safety.", Image = "images/image-1.png" },
new { Caption = "An image about airplane components.", Image = "images/image-2.png" },
new { Caption = "An image about COVID safety restrictions.", Image = "images/image-3.png" },
new { Caption = "A confidential image about UFO sightings.", Image = "images/image-4.png" },
new { Caption = "An image about unusual footprints on Aralar 2011.", Image = "images/image-5.png" },
};
// @block-end define-dataset
// @block-start create-collection
string collectionName = "multimodal-embeddings";
if (!await client.CollectionExistsAsync(collectionName))
{
await client.CreateCollectionAsync(
collectionName: collectionName,
vectorsConfig: new VectorParamsMap
{
Map =
{
["image"] = new VectorParams { Size = 512, Distance = Distance.Cosine },
["text"] = new VectorParams { Size = 512, Distance = Distance.Cosine },
}
}
);
}
// @block-end create-collection
// @block-start upload-data
string cohereApiKey = Environment.GetEnvironmentVariable("COHERE_API_KEY")!;
var points = documents.Select((doc, idx) => new PointStruct
{
Id = (ulong)idx,
Vectors = new Dictionary<string, Vector>
{
["text"] = new Document
{
Text = doc.Caption,
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
["image"] = new Image
{
Image_ = ImageToBase64Url(doc.Image),
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
},
Payload = { ["caption"] = doc.Caption, ["image"] = doc.Image }
}).ToList();
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
await client.UpsertAsync(collectionName: collectionName, points: points);
// @block-end upload-data
// @block-start text-to-image-search
IReadOnlyList<ScoredPoint> results;
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Document
{
Text = "Plane components",
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "image",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["image"]);
// @block-end text-to-image-search
// @block-start multilingual-search
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Document
{
Text = "Componenti di un aereo",
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "image",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["image"]);
// @block-end multilingual-search
// @block-start image-to-text-search
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Image
{
Image_ = ImageToBase64Url("images/image-2.png"),
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "text",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["caption"]);
// @block-end image-to-text-search
}
}
@@ -0,0 +1,7 @@
```csharp
var client = new QdrantClient(
host: QDRANT_URL,
https: true,
apiKey: QDRANT_API_KEY
);
```
@@ -0,0 +1,7 @@
```go
client, err := qdrant.NewClient(&qdrant.Config{
Host: QDRANT_URL,
APIKey: QDRANT_API_KEY,
UseTLS: true,
})
```
@@ -0,0 +1,7 @@
```java
QdrantClient client =
new QdrantClient(
QdrantGrpcClient.newBuilder(QDRANT_URL, 6334, true)
.withApiKey(QDRANT_API_KEY)
.build());
```
@@ -0,0 +1,11 @@
```python
import os
from qdrant_client import QdrantClient, models
client = QdrantClient(
url=os.getenv("QDRANT_URL"),
api_key=os.getenv("QDRANT_API_KEY"),
cloud_inference=True,
)
```
@@ -0,0 +1,5 @@
```rust
let client = Qdrant::from_url(&std::env::var("QDRANT_URL")?)
.api_key(std::env::var("QDRANT_API_KEY")?)
.build()?;
```
@@ -0,0 +1,6 @@
```typescript
const client = new QdrantClient({
url: process.env.QDRANT_URL,
apiKey: process.env.QDRANT_API_KEY,
});
```
@@ -0,0 +1,18 @@
```csharp
string collectionName = "multimodal-embeddings";
if (!await client.CollectionExistsAsync(collectionName))
{
await client.CreateCollectionAsync(
collectionName: collectionName,
vectorsConfig: new VectorParamsMap
{
Map =
{
["image"] = new VectorParams { Size = 512, Distance = Distance.Cosine },
["text"] = new VectorParams { Size = 512, Distance = Distance.Cosine },
}
}
);
}
```
@@ -0,0 +1,22 @@
```go
collectionName := "multimodal-embeddings"
exists, err := client.CollectionExists(context.Background(), collectionName)
if !exists {
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: collectionName,
VectorsConfig: qdrant.NewVectorsConfigMap(
map[string]*qdrant.VectorParams{
"image": {
Size: 512,
Distance: qdrant.Distance_Cosine,
},
"text": {
Size: 512,
Distance: qdrant.Distance_Cosine,
},
},
),
})
}
```
@@ -0,0 +1,28 @@
```java
String collectionName = "multimodal-embeddings";
if (!client.collectionExistsAsync(collectionName).get()) {
client.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName(collectionName)
.setVectorsConfig(
VectorsConfig.newBuilder()
.setParamsMap(
VectorParamsMap.newBuilder()
.putMap(
"image",
VectorParams.newBuilder()
.setSize(512)
.setDistance(Distance.Cosine)
.build())
.putMap(
"text",
VectorParams.newBuilder()
.setSize(512)
.setDistance(Distance.Cosine)
.build())
.build()))
.build()
).get();
}
```
@@ -0,0 +1,12 @@
```python
COLLECTION_NAME = "multimodal-embeddings"
if not client.collection_exists(COLLECTION_NAME):
client.create_collection(
collection_name=COLLECTION_NAME,
vectors_config={
"image": models.VectorParams(size=512, distance=models.Distance.COSINE),
"text": models.VectorParams(size=512, distance=models.Distance.COSINE),
}
)
```
@@ -0,0 +1,13 @@
```rust
let collection_name = "multimodal-embeddings";
if !client.collection_exists(collection_name).await? {
let mut vectors = VectorsConfigBuilder::default();
vectors.add_named_vector_params("image", VectorParamsBuilder::new(512, Distance::Cosine));
vectors.add_named_vector_params("text", VectorParamsBuilder::new(512, Distance::Cosine));
client
.create_collection(CreateCollectionBuilder::new(collection_name).vectors_config(vectors))
.await?;
}
```
@@ -0,0 +1,12 @@
```typescript
const collectionName = "multimodal-embeddings";
if (!(await client.collectionExists(collectionName)).exists) {
await client.createCollection(collectionName, {
vectors: {
image: { size: 512, distance: "Cosine" },
text: { size: 512, distance: "Cosine" },
},
});
}
```
@@ -0,0 +1,118 @@
```csharp
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient(
host: QDRANT_URL,
https: true,
apiKey: QDRANT_API_KEY
);
static string ImageToBase64Url(string imagePath)
{
string prefix = "data:image/png;base64";
byte[] bytes = File.ReadAllBytes(imagePath);
return $"{prefix},{Convert.ToBase64String(bytes)}";
}
var documents = new[]
{
new { Caption = "An image about plane emergency safety.", Image = "images/image-1.png" },
new { Caption = "An image about airplane components.", Image = "images/image-2.png" },
new { Caption = "An image about COVID safety restrictions.", Image = "images/image-3.png" },
new { Caption = "A confidential image about UFO sightings.", Image = "images/image-4.png" },
new { Caption = "An image about unusual footprints on Aralar 2011.", Image = "images/image-5.png" },
};
string collectionName = "multimodal-embeddings";
if (!await client.CollectionExistsAsync(collectionName))
{
await client.CreateCollectionAsync(
collectionName: collectionName,
vectorsConfig: new VectorParamsMap
{
Map =
{
["image"] = new VectorParams { Size = 512, Distance = Distance.Cosine },
["text"] = new VectorParams { Size = 512, Distance = Distance.Cosine },
}
}
);
}
string cohereApiKey = Environment.GetEnvironmentVariable("COHERE_API_KEY")!;
var points = documents.Select((doc, idx) => new PointStruct
{
Id = (ulong)idx,
Vectors = new Dictionary<string, Vector>
{
["text"] = new Document
{
Text = doc.Caption,
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
["image"] = new Image
{
Image_ = ImageToBase64Url(doc.Image),
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
},
Payload = { ["caption"] = doc.Caption, ["image"] = doc.Image }
}).ToList();
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
await client.UpsertAsync(collectionName: collectionName, points: points);
IReadOnlyList<ScoredPoint> results;
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Document
{
Text = "Plane components",
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "image",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["image"]);
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Document
{
Text = "Componenti di un aereo",
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "image",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["image"]);
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Image
{
Image_ = ImageToBase64Url("images/image-2.png"),
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "text",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["caption"]);
```
@@ -0,0 +1,17 @@
```csharp
static string ImageToBase64Url(string imagePath)
{
string prefix = "data:image/png;base64";
byte[] bytes = File.ReadAllBytes(imagePath);
return $"{prefix},{Convert.ToBase64String(bytes)}";
}
var documents = new[]
{
new { Caption = "An image about plane emergency safety.", Image = "images/image-1.png" },
new { Caption = "An image about airplane components.", Image = "images/image-2.png" },
new { Caption = "An image about COVID safety restrictions.", Image = "images/image-3.png" },
new { Caption = "A confidential image about UFO sightings.", Image = "images/image-4.png" },
new { Caption = "An image about unusual footprints on Aralar 2011.", Image = "images/image-5.png" },
};
```
@@ -0,0 +1,23 @@
```go
type Doc struct {
Caption string
Image string
}
func imageToBase64Url(imagePath string) (string, error) {
prefix := "data:image/png;base64"
bytes, err := os.ReadFile(imagePath)
if err != nil {
return "", err
}
return fmt.Sprintf("%s,%s", prefix, base64.StdEncoding.EncodeToString(bytes)), nil
}
var documents = []Doc{
{Caption: "An image about plane emergency safety.", Image: "images/image-1.png"},
{Caption: "An image about airplane components.", Image: "images/image-2.png"},
{Caption: "An image about COVID safety restrictions.", Image: "images/image-3.png"},
{Caption: "A confidential image about UFO sightings.", Image: "images/image-4.png"},
{Caption: "An image about unusual footprints on Aralar 2011.", Image: "images/image-5.png"},
}
```
@@ -0,0 +1,24 @@
```java
static class Doc {
final String caption;
final String image;
Doc(String caption, String image) {
this.caption = caption;
this.image = image;
}
}
static String imageToBase64Url(String imagePath) throws Exception {
String prefix = "data:image/png;base64";
byte[] bytes = Files.readAllBytes(Path.of(imagePath));
return prefix + "," + Base64.getEncoder().encodeToString(bytes);
}
static List<Doc> documents = List.of(
new Doc("An image about plane emergency safety.", "images/image-1.png"),
new Doc("An image about airplane components.", "images/image-2.png"),
new Doc("An image about COVID safety restrictions.", "images/image-3.png"),
new Doc("A confidential image about UFO sightings.", "images/image-4.png"),
new Doc("An image about unusual footprints on Aralar 2011.", "images/image-5.png")
);
```
@@ -0,0 +1,16 @@
```python
import base64
def image_to_base64_url(image_path: str) -> str:
prefix = "data:image/png;base64"
with open(image_path, "rb") as image_file:
return prefix + "," + base64.b64encode(image_file.read()).decode("utf-8")
documents = [
{"caption": "An image about plane emergency safety.", "image": "images/image-1.png"},
{"caption": "An image about airplane components.", "image": "images/image-2.png"},
{"caption": "An image about COVID safety restrictions.", "image": "images/image-3.png"},
{"caption": "A confidential image about UFO sightings.", "image": "images/image-4.png"},
{"caption": "An image about unusual footprints on Aralar 2011.", "image": "images/image-5.png"},
]
```
@@ -0,0 +1,20 @@
```rust
fn image_to_base64_url(image_path: &str) -> anyhow::Result<String> {
let prefix = "data:image/png;base64";
let bytes = std::fs::read(image_path)?;
Ok(format!("{prefix},{}", BASE64_STANDARD.encode(bytes)))
}
struct Doc {
caption: &'static str,
image: &'static str,
}
let documents = vec![
Doc { caption: "An image about plane emergency safety.", image: "images/image-1.png" },
Doc { caption: "An image about airplane components.", image: "images/image-2.png" },
Doc { caption: "An image about COVID safety restrictions.", image: "images/image-3.png" },
Doc { caption: "A confidential image about UFO sightings.", image: "images/image-4.png" },
Doc { caption: "An image about unusual footprints on Aralar 2011.", image: "images/image-5.png" },
];
```
@@ -0,0 +1,15 @@
```typescript
function imageToBase64Url(imagePath: string): string {
const prefix = "data:image/png;base64";
const imageBuffer = readFileSync(imagePath);
return `${prefix},${imageBuffer.toString("base64")}`;
}
const documents = [
{ caption: "An image about plane emergency safety.", image: "images/image-1.png" },
{ caption: "An image about airplane components.", image: "images/image-2.png" },
{ caption: "An image about COVID safety restrictions.", image: "images/image-3.png" },
{ caption: "A confidential image about UFO sightings.", image: "images/image-4.png" },
{ caption: "An image about unusual footprints on Aralar 2011.", image: "images/image-5.png" },
];
```
@@ -0,0 +1,152 @@
```go
import (
"context"
"encoding/base64"
"fmt"
"os"
"github.com/qdrant/go-client/qdrant"
)
type Doc struct {
Caption string
Image string
}
func imageToBase64Url(imagePath string) (string, error) {
prefix := "data:image/png;base64"
bytes, err := os.ReadFile(imagePath)
if err != nil {
return "", err
}
return fmt.Sprintf("%s,%s", prefix, base64.StdEncoding.EncodeToString(bytes)), nil
}
var documents = []Doc{
{Caption: "An image about plane emergency safety.", Image: "images/image-1.png"},
{Caption: "An image about airplane components.", Image: "images/image-2.png"},
{Caption: "An image about COVID safety restrictions.", Image: "images/image-3.png"},
{Caption: "A confidential image about UFO sightings.", Image: "images/image-4.png"},
{Caption: "An image about unusual footprints on Aralar 2011.", Image: "images/image-5.png"},
}
client, err := qdrant.NewClient(&qdrant.Config{
Host: QDRANT_URL,
APIKey: QDRANT_API_KEY,
UseTLS: true,
})
collectionName := "multimodal-embeddings"
exists, err := client.CollectionExists(context.Background(), collectionName)
if !exists {
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: collectionName,
VectorsConfig: qdrant.NewVectorsConfigMap(
map[string]*qdrant.VectorParams{
"image": {
Size: 512,
Distance: qdrant.Distance_Cosine,
},
"text": {
Size: 512,
Distance: qdrant.Distance_Cosine,
},
},
),
})
}
cohereApiKey := os.Getenv("COHERE_API_KEY")
ctx := qdrant.WithHeader(context.Background(), "cohere-api-key", cohereApiKey)
points := make([]*qdrant.PointStruct, len(documents))
for idx, doc := range documents {
imageUrl, err := imageToBase64Url(doc.Image)
points[idx] = &qdrant.PointStruct{
Id: qdrant.NewIDNum(uint64(idx)),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"text": qdrant.NewVectorDocument(&qdrant.Document{
Text: doc.Caption,
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
"image": qdrant.NewVectorImage(&qdrant.Image{
Image: qdrant.NewValueString(imageUrl),
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
}),
Payload: qdrant.NewValueMap(map[string]any{
"caption": doc.Caption,
"image": doc.Image,
}),
}
}
client.Upsert(ctx, &qdrant.UpsertPoints{
CollectionName: collectionName,
Points: points,
})
results, err := client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputDocument(&qdrant.Document{
Text: "Plane components",
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("image"),
WithPayload: qdrant.NewWithPayloadInclude("image"),
Limit: qdrant.PtrOf(uint64(1)),
})
fmt.Println(results[0].Payload["image"])
results, err = client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputDocument(&qdrant.Document{
Text: "Componenti di un aereo",
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("image"),
WithPayload: qdrant.NewWithPayloadInclude("image"),
Limit: qdrant.PtrOf(uint64(1)),
})
fmt.Println(results[0].Payload["image"])
queryImageUrl, err := imageToBase64Url("images/image-2.png")
results, err = client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputImage(&qdrant.Image{
Image: qdrant.NewValueString(queryImageUrl),
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("text"),
WithPayload: qdrant.NewWithPayloadInclude("caption"),
Limit: qdrant.PtrOf(uint64(1)),
})
fmt.Println(results[0].Payload["caption"])
```
@@ -0,0 +1,17 @@
```csharp
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Image
{
Image_ = ImageToBase64Url("images/image-2.png"),
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "text",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["caption"]);
```
@@ -0,0 +1,21 @@
```go
queryImageUrl, err := imageToBase64Url("images/image-2.png")
results, err = client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputImage(&qdrant.Image{
Image: qdrant.NewValueString(queryImageUrl),
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("text"),
WithPayload: qdrant.NewWithPayloadInclude("caption"),
Limit: qdrant.PtrOf(uint64(1)),
})
fmt.Println(results[0].Payload["caption"])
```
@@ -0,0 +1,19 @@
```java
results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Image.newBuilder()
.setImage(value(imageToBase64Url("images/image-2.png")))
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("text")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("caption"));
```
@@ -0,0 +1,16 @@
```python
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Image(
image=image_to_base64_url("images/image-2.png"),
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="text",
with_payload=["caption"],
limit=1
).points[0].payload
print(payload["caption"])
```
@@ -0,0 +1,21 @@
```rust
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
ImageBuilder::new_from_base64(
image_to_base64_url("images/image-2.png")?,
"cohere/embed-v4.0",
)
.options(options.clone())
.build(),
))
.using("text")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("caption"));
```
@@ -0,0 +1,12 @@
```typescript
const imageToTextResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { image: imageToBase64Url("images/image-2.png"), model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "text",
with_payload: ["caption"],
limit: 1,
})
);
console.log(imageToTextResults.points[0].payload!.caption);
```
@@ -0,0 +1,172 @@
```java
import static io.qdrant.client.QueryFactory.nearest;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorFactory.vector;
import static io.qdrant.client.VectorsFactory.namedVectors;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;
import io.grpc.Context;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.RequestHeaders;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorParamsMap;
import io.qdrant.client.grpc.Collections.VectorsConfig;
import io.qdrant.client.grpc.Points.Document;
import io.qdrant.client.grpc.Points.Image;
import io.qdrant.client.grpc.Points.PointStruct;
import io.qdrant.client.grpc.Points.QueryPoints;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
import java.util.Map;
static class Doc {
final String caption;
final String image;
Doc(String caption, String image) {
this.caption = caption;
this.image = image;
}
}
static String imageToBase64Url(String imagePath) throws Exception {
String prefix = "data:image/png;base64";
byte[] bytes = Files.readAllBytes(Path.of(imagePath));
return prefix + "," + Base64.getEncoder().encodeToString(bytes);
}
static List<Doc> documents = List.of(
new Doc("An image about plane emergency safety.", "images/image-1.png"),
new Doc("An image about airplane components.", "images/image-2.png"),
new Doc("An image about COVID safety restrictions.", "images/image-3.png"),
new Doc("A confidential image about UFO sightings.", "images/image-4.png"),
new Doc("An image about unusual footprints on Aralar 2011.", "images/image-5.png")
);
QdrantClient client =
new QdrantClient(
QdrantGrpcClient.newBuilder(QDRANT_URL, 6334, true)
.withApiKey(QDRANT_API_KEY)
.build());
String collectionName = "multimodal-embeddings";
if (!client.collectionExistsAsync(collectionName).get()) {
client.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName(collectionName)
.setVectorsConfig(
VectorsConfig.newBuilder()
.setParamsMap(
VectorParamsMap.newBuilder()
.putMap(
"image",
VectorParams.newBuilder()
.setSize(512)
.setDistance(Distance.Cosine)
.build())
.putMap(
"text",
VectorParams.newBuilder()
.setSize(512)
.setDistance(Distance.Cosine)
.build())
.build()))
.build()
).get();
}
String cohereApiKey = System.getenv("COHERE_API_KEY");
Context ctx = RequestHeaders.withHeader(
Context.current(), "cohere-api-key", cohereApiKey);
List<PointStruct> points = new java.util.ArrayList<>();
for (int idx = 0; idx < documents.size(); idx++) {
Doc doc = documents.get(idx);
points.add(
PointStruct.newBuilder()
.setId(io.qdrant.client.PointIdFactory.id(idx))
.setVectors(
namedVectors(
Map.of(
"text",
vector(
Document.newBuilder()
.setText(doc.caption)
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()),
"image",
vector(
Image.newBuilder()
.setImage(value(imageToBase64Url(doc.image)))
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))))
.putAllPayload(
Map.of(
"caption", value(doc.caption),
"image", value(doc.image)))
.build());
}
ctx.call(() -> client.upsertAsync(collectionName, points).get());
var results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Document.newBuilder()
.setText("Plane components")
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("image")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("image"));
results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Document.newBuilder()
.setText("Componenti di un aereo")
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("image")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("image"));
results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Image.newBuilder()
.setImage(value(imageToBase64Url("images/image-2.png")))
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("text")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("caption"));
```
@@ -0,0 +1,17 @@
```csharp
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Document
{
Text = "Componenti di un aereo",
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "image",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["image"]);
```
@@ -0,0 +1,19 @@
```go
results, err = client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputDocument(&qdrant.Document{
Text: "Componenti di un aereo",
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("image"),
WithPayload: qdrant.NewWithPayloadInclude("image"),
Limit: qdrant.PtrOf(uint64(1)),
})
fmt.Println(results[0].Payload["image"])
```
@@ -0,0 +1,19 @@
```java
results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Document.newBuilder()
.setText("Componenti di un aereo")
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("image")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("image"));
```
@@ -0,0 +1,16 @@
```python
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Document(
text="Componenti di un aereo",
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="image",
with_payload=["image"],
limit=1
).points[0].payload
Image.open(payload["image"])
```
@@ -0,0 +1,18 @@
```rust
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
DocumentBuilder::new("Componenti di un aereo", "cohere/embed-v4.0")
.options(options.clone())
.build(),
))
.using("image")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("image"));
```
@@ -0,0 +1,12 @@
```typescript
const multilingualResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { text: "Componenti di un aereo", model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "image",
with_payload: ["image"],
limit: 1,
})
);
console.log(multilingualResults.points[0].payload!.image);
```
@@ -0,0 +1,112 @@
```python
import os
from qdrant_client import QdrantClient, models
client = QdrantClient(
url=os.getenv("QDRANT_URL"),
api_key=os.getenv("QDRANT_API_KEY"),
cloud_inference=True,
)
import base64
def image_to_base64_url(image_path: str) -> str:
prefix = "data:image/png;base64"
with open(image_path, "rb") as image_file:
return prefix + "," + base64.b64encode(image_file.read()).decode("utf-8")
documents = [
{"caption": "An image about plane emergency safety.", "image": "images/image-1.png"},
{"caption": "An image about airplane components.", "image": "images/image-2.png"},
{"caption": "An image about COVID safety restrictions.", "image": "images/image-3.png"},
{"caption": "A confidential image about UFO sightings.", "image": "images/image-4.png"},
{"caption": "An image about unusual footprints on Aralar 2011.", "image": "images/image-5.png"},
]
COLLECTION_NAME = "multimodal-embeddings"
if not client.collection_exists(COLLECTION_NAME):
client.create_collection(
collection_name=COLLECTION_NAME,
vectors_config={
"image": models.VectorParams(size=512, distance=models.Distance.COSINE),
"text": models.VectorParams(size=512, distance=models.Distance.COSINE),
}
)
from qdrant_client.context_headers import headers
cohere_api_key = os.getenv("COHERE_API_KEY")
with headers({"cohere-api-key": cohere_api_key}):
client.upsert(
collection_name=COLLECTION_NAME,
points=[
models.PointStruct(
id=idx,
vector={
"text": models.Document(
text=doc["caption"],
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
"image": models.Image(
image=image_to_base64_url(doc["image"]),
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
},
payload=doc
)
for idx, doc in enumerate(documents)
]
)
from PIL import Image
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Document(
text="Plane components",
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="image",
with_payload=["image"],
limit=1
).points[0].payload
Image.open(payload["image"])
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Document(
text="Componenti di un aereo",
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="image",
with_payload=["image"],
limit=1
).points[0].payload
Image.open(payload["image"])
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Image(
image=image_to_base64_url("images/image-2.png"),
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="text",
with_payload=["caption"],
limit=1
).points[0].payload
print(payload["caption"])
```
@@ -0,0 +1,136 @@
```rust
use std::collections::HashMap;
use base64::prelude::*;
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{
CreateCollectionBuilder, Distance, DocumentBuilder, ImageBuilder, NamedVectors, PointStruct,
Query, QueryPointsBuilder, UpsertPointsBuilder, Value, VectorParamsBuilder,
VectorsConfigBuilder,
};
let client = Qdrant::from_url(&std::env::var("QDRANT_URL")?)
.api_key(std::env::var("QDRANT_API_KEY")?)
.build()?;
fn image_to_base64_url(image_path: &str) -> anyhow::Result<String> {
let prefix = "data:image/png;base64";
let bytes = std::fs::read(image_path)?;
Ok(format!("{prefix},{}", BASE64_STANDARD.encode(bytes)))
}
struct Doc {
caption: &'static str,
image: &'static str,
}
let documents = vec![
Doc { caption: "An image about plane emergency safety.", image: "images/image-1.png" },
Doc { caption: "An image about airplane components.", image: "images/image-2.png" },
Doc { caption: "An image about COVID safety restrictions.", image: "images/image-3.png" },
Doc { caption: "A confidential image about UFO sightings.", image: "images/image-4.png" },
Doc { caption: "An image about unusual footprints on Aralar 2011.", image: "images/image-5.png" },
];
let collection_name = "multimodal-embeddings";
if !client.collection_exists(collection_name).await? {
let mut vectors = VectorsConfigBuilder::default();
vectors.add_named_vector_params("image", VectorParamsBuilder::new(512, Distance::Cosine));
vectors.add_named_vector_params("text", VectorParamsBuilder::new(512, Distance::Cosine));
client
.create_collection(CreateCollectionBuilder::new(collection_name).vectors_config(vectors))
.await?;
}
let cohere_api_key = std::env::var("COHERE_API_KEY")?;
let mut options: HashMap<String, Value> = HashMap::new();
options.insert("output_dimension".to_string(), 512i64.into());
let mut points = Vec::new();
for (idx, doc) in documents.iter().enumerate() {
let vectors = NamedVectors::default()
.add_vector(
"text",
DocumentBuilder::new(doc.caption, "cohere/embed-v4.0")
.options(options.clone())
.build(),
)
.add_vector(
"image",
ImageBuilder::new_from_base64(image_to_base64_url(doc.image)?, "cohere/embed-v4.0")
.options(options.clone())
.build(),
);
points.push(PointStruct::new(
idx as u64,
vectors,
[
("caption", doc.caption.into()),
("image", doc.image.into()),
],
));
}
client
.with_header("cohere-api-key", &cohere_api_key)
.upsert_points(UpsertPointsBuilder::new(collection_name, points))
.await?;
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
DocumentBuilder::new("Plane components", "cohere/embed-v4.0")
.options(options.clone())
.build(),
))
.using("image")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("image"));
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
DocumentBuilder::new("Componenti di un aereo", "cohere/embed-v4.0")
.options(options.clone())
.build(),
))
.using("image")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("image"));
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
ImageBuilder::new_from_base64(
image_to_base64_url("images/image-2.png")?,
"cohere/embed-v4.0",
)
.options(options.clone())
.build(),
))
.using("text")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("caption"));
```
@@ -0,0 +1,18 @@
```csharp
IReadOnlyList<ScoredPoint> results;
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
results = await client.QueryAsync(
collectionName: collectionName,
query: new Document
{
Text = "Plane components",
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
usingVector: "image",
payloadSelector: true,
limit: 1
);
Console.WriteLine(results[0].Payload["image"]);
```
@@ -0,0 +1,19 @@
```go
results, err := client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputDocument(&qdrant.Document{
Text: "Plane components",
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("image"),
WithPayload: qdrant.NewWithPayloadInclude("image"),
Limit: qdrant.PtrOf(uint64(1)),
})
fmt.Println(results[0].Payload["image"])
```
@@ -0,0 +1,19 @@
```java
var results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Document.newBuilder()
.setText("Plane components")
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("image")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("image"));
```
@@ -0,0 +1,18 @@
```python
from PIL import Image
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Document(
text="Plane components",
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="image",
with_payload=["image"],
limit=1
).points[0].payload
Image.open(payload["image"])
```
@@ -0,0 +1,18 @@
```rust
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
DocumentBuilder::new("Plane components", "cohere/embed-v4.0")
.options(options.clone())
.build(),
))
.using("image")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("image"));
```
@@ -0,0 +1,12 @@
```typescript
const textToImageResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { text: "Plane components", model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "image",
with_payload: ["image"],
limit: 1,
})
);
console.log(textToImageResults.points[0].payload!.image);
```
@@ -0,0 +1,82 @@
```typescript
import { QdrantClient, Schemas, withHeaders } from "@qdrant/js-client-rest";
import { readFileSync } from "fs";
const client = new QdrantClient({
url: process.env.QDRANT_URL,
apiKey: process.env.QDRANT_API_KEY,
});
function imageToBase64Url(imagePath: string): string {
const prefix = "data:image/png;base64";
const imageBuffer = readFileSync(imagePath);
return `${prefix},${imageBuffer.toString("base64")}`;
}
const documents = [
{ caption: "An image about plane emergency safety.", image: "images/image-1.png" },
{ caption: "An image about airplane components.", image: "images/image-2.png" },
{ caption: "An image about COVID safety restrictions.", image: "images/image-3.png" },
{ caption: "A confidential image about UFO sightings.", image: "images/image-4.png" },
{ caption: "An image about unusual footprints on Aralar 2011.", image: "images/image-5.png" },
];
const collectionName = "multimodal-embeddings";
if (!(await client.collectionExists(collectionName)).exists) {
await client.createCollection(collectionName, {
vectors: {
image: { size: 512, distance: "Cosine" },
text: { size: 512, distance: "Cosine" },
},
});
}
const cohereApiKey = process.env.COHERE_API_KEY!;
await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.upsert(collectionName, {
points: documents.map((doc, idx) => ({
id: idx,
vector: {
text: { text: doc.caption, model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
image: { image: imageToBase64Url(doc.image), model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
},
payload: doc,
})),
})
);
const textToImageResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { text: "Plane components", model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "image",
with_payload: ["image"],
limit: 1,
})
);
console.log(textToImageResults.points[0].payload!.image);
const multilingualResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { text: "Componenti di un aereo", model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "image",
with_payload: ["image"],
limit: 1,
})
);
console.log(multilingualResults.points[0].payload!.image);
const imageToTextResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { image: imageToBase64Url("images/image-2.png"), model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "text",
with_payload: ["caption"],
limit: 1,
})
);
console.log(imageToTextResults.points[0].payload!.caption);
```
@@ -0,0 +1,27 @@
```csharp
string cohereApiKey = Environment.GetEnvironmentVariable("COHERE_API_KEY")!;
var points = documents.Select((doc, idx) => new PointStruct
{
Id = (ulong)idx,
Vectors = new Dictionary<string, Vector>
{
["text"] = new Document
{
Text = doc.Caption,
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
["image"] = new Image
{
Image_ = ImageToBase64Url(doc.Image),
Model = "cohere/embed-v4.0",
Options = { ["output_dimension"] = 512 },
},
},
Payload = { ["caption"] = doc.Caption, ["image"] = doc.Image }
}).ToList();
using (RequestHeaders.Use("cohere-api-key", cohereApiKey))
await client.UpsertAsync(collectionName: collectionName, points: points);
```
@@ -0,0 +1,38 @@
```go
cohereApiKey := os.Getenv("COHERE_API_KEY")
ctx := qdrant.WithHeader(context.Background(), "cohere-api-key", cohereApiKey)
points := make([]*qdrant.PointStruct, len(documents))
for idx, doc := range documents {
imageUrl, err := imageToBase64Url(doc.Image)
points[idx] = &qdrant.PointStruct{
Id: qdrant.NewIDNum(uint64(idx)),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"text": qdrant.NewVectorDocument(&qdrant.Document{
Text: doc.Caption,
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
"image": qdrant.NewVectorImage(&qdrant.Image{
Image: qdrant.NewValueString(imageUrl),
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
}),
Payload: qdrant.NewValueMap(map[string]any{
"caption": doc.Caption,
"image": doc.Image,
}),
}
}
client.Upsert(ctx, &qdrant.UpsertPoints{
CollectionName: collectionName,
Points: points,
})
```
@@ -0,0 +1,37 @@
```java
String cohereApiKey = System.getenv("COHERE_API_KEY");
Context ctx = RequestHeaders.withHeader(
Context.current(), "cohere-api-key", cohereApiKey);
List<PointStruct> points = new java.util.ArrayList<>();
for (int idx = 0; idx < documents.size(); idx++) {
Doc doc = documents.get(idx);
points.add(
PointStruct.newBuilder()
.setId(io.qdrant.client.PointIdFactory.id(idx))
.setVectors(
namedVectors(
Map.of(
"text",
vector(
Document.newBuilder()
.setText(doc.caption)
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()),
"image",
vector(
Image.newBuilder()
.setImage(value(imageToBase64Url(doc.image)))
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))))
.putAllPayload(
Map.of(
"caption", value(doc.caption),
"image", value(doc.image)))
.build());
}
ctx.call(() -> client.upsertAsync(collectionName, points).get());
```
@@ -0,0 +1,29 @@
```python
from qdrant_client.context_headers import headers
cohere_api_key = os.getenv("COHERE_API_KEY")
with headers({"cohere-api-key": cohere_api_key}):
client.upsert(
collection_name=COLLECTION_NAME,
points=[
models.PointStruct(
id=idx,
vector={
"text": models.Document(
text=doc["caption"],
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
"image": models.Image(
image=image_to_base64_url(doc["image"]),
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
},
payload=doc
)
for idx, doc in enumerate(documents)
]
)
```
@@ -0,0 +1,37 @@
```rust
let cohere_api_key = std::env::var("COHERE_API_KEY")?;
let mut options: HashMap<String, Value> = HashMap::new();
options.insert("output_dimension".to_string(), 512i64.into());
let mut points = Vec::new();
for (idx, doc) in documents.iter().enumerate() {
let vectors = NamedVectors::default()
.add_vector(
"text",
DocumentBuilder::new(doc.caption, "cohere/embed-v4.0")
.options(options.clone())
.build(),
)
.add_vector(
"image",
ImageBuilder::new_from_base64(image_to_base64_url(doc.image)?, "cohere/embed-v4.0")
.options(options.clone())
.build(),
);
points.push(PointStruct::new(
idx as u64,
vectors,
[
("caption", doc.caption.into()),
("image", doc.image.into()),
],
));
}
client
.with_header("cohere-api-key", &cohere_api_key)
.upsert_points(UpsertPointsBuilder::new(collection_name, points))
.await?;
```
@@ -0,0 +1,16 @@
```typescript
const cohereApiKey = process.env.COHERE_API_KEY!;
await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.upsert(collectionName, {
points: documents.map((doc, idx) => ({
id: idx,
vector: {
text: { text: doc.caption, model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
image: { image: imageToBase64Url(doc.image), model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
},
payload: doc,
})),
})
);
```
@@ -0,0 +1,199 @@
package snippet
import (
"context"
"encoding/base64"
"fmt"
"os"
"github.com/qdrant/go-client/qdrant"
)
// @block-start define-dataset
type Doc struct {
Caption string
Image string
}
func imageToBase64Url(imagePath string) (string, error) {
prefix := "data:image/png;base64"
bytes, err := os.ReadFile(imagePath)
if err != nil {
return "", err
}
return fmt.Sprintf("%s,%s", prefix, base64.StdEncoding.EncodeToString(bytes)), nil
}
var documents = []Doc{
{Caption: "An image about plane emergency safety.", Image: "images/image-1.png"},
{Caption: "An image about airplane components.", Image: "images/image-2.png"},
{Caption: "An image about COVID safety restrictions.", Image: "images/image-3.png"},
{Caption: "A confidential image about UFO sightings.", Image: "images/image-4.png"},
{Caption: "An image about unusual footprints on Aralar 2011.", Image: "images/image-5.png"},
}
// @block-end define-dataset
func Main() {
// @hide-start
QDRANT_URL := "xyz-example.eu-central.aws.cloud.qdrant.io"
QDRANT_API_KEY := "<your-api-key>"
// @hide-end
// @block-start client-connection
client, err := qdrant.NewClient(&qdrant.Config{
Host: QDRANT_URL,
APIKey: QDRANT_API_KEY,
UseTLS: true,
})
// @block-end client-connection
// @hide-start
if err != nil {
panic(err)
}
// @hide-end
// @block-start create-collection
collectionName := "multimodal-embeddings"
exists, err := client.CollectionExists(context.Background(), collectionName)
if err != nil { panic(err) } // @hide
if !exists {
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: collectionName,
VectorsConfig: qdrant.NewVectorsConfigMap(
map[string]*qdrant.VectorParams{
"image": {
Size: 512,
Distance: qdrant.Distance_Cosine,
},
"text": {
Size: 512,
Distance: qdrant.Distance_Cosine,
},
},
),
})
}
// @block-end create-collection
// @block-start upload-data
cohereApiKey := os.Getenv("COHERE_API_KEY")
ctx := qdrant.WithHeader(context.Background(), "cohere-api-key", cohereApiKey)
points := make([]*qdrant.PointStruct, len(documents))
for idx, doc := range documents {
imageUrl, err := imageToBase64Url(doc.Image)
if err != nil { panic(err) } // @hide
points[idx] = &qdrant.PointStruct{
Id: qdrant.NewIDNum(uint64(idx)),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"text": qdrant.NewVectorDocument(&qdrant.Document{
Text: doc.Caption,
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
"image": qdrant.NewVectorImage(&qdrant.Image{
Image: qdrant.NewValueString(imageUrl),
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
}),
Payload: qdrant.NewValueMap(map[string]any{
"caption": doc.Caption,
"image": doc.Image,
}),
}
}
client.Upsert(ctx, &qdrant.UpsertPoints{
CollectionName: collectionName,
Points: points,
})
// @block-end upload-data
// @block-start text-to-image-search
results, err := client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputDocument(&qdrant.Document{
Text: "Plane components",
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("image"),
WithPayload: qdrant.NewWithPayloadInclude("image"),
Limit: qdrant.PtrOf(uint64(1)),
})
// @hide-start
if err != nil {
panic(err)
}
// @hide-end
fmt.Println(results[0].Payload["image"])
// @block-end text-to-image-search
// @block-start multilingual-search
results, err = client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputDocument(&qdrant.Document{
Text: "Componenti di un aereo",
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("image"),
WithPayload: qdrant.NewWithPayloadInclude("image"),
Limit: qdrant.PtrOf(uint64(1)),
})
// @hide-start
if err != nil {
panic(err)
}
// @hide-end
fmt.Println(results[0].Payload["image"])
// @block-end multilingual-search
// @block-start image-to-text-search
queryImageUrl, err := imageToBase64Url("images/image-2.png")
if err != nil { panic(err) } // @hide
results, err = client.Query(ctx, &qdrant.QueryPoints{
CollectionName: collectionName,
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputImage(&qdrant.Image{
Image: qdrant.NewValueString(queryImageUrl),
Model: "cohere/embed-v4.0",
Options: qdrant.NewValueMap(map[string]any{
"output_dimension": 512,
}),
}),
),
Using: qdrant.PtrOf("text"),
WithPayload: qdrant.NewWithPayloadInclude("caption"),
Limit: qdrant.PtrOf(uint64(1)),
})
// @hide-start
if err != nil {
panic(err)
}
// @hide-end
fmt.Println(results[0].Payload["caption"])
// @block-end image-to-text-search
}
@@ -0,0 +1,195 @@
package com.example.snippets_amalgamation;
import static io.qdrant.client.QueryFactory.nearest;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorFactory.vector;
import static io.qdrant.client.VectorsFactory.namedVectors;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;
import io.grpc.Context;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.RequestHeaders;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorParamsMap;
import io.qdrant.client.grpc.Collections.VectorsConfig;
import io.qdrant.client.grpc.Points.Document;
import io.qdrant.client.grpc.Points.Image;
import io.qdrant.client.grpc.Points.PointStruct;
import io.qdrant.client.grpc.Points.QueryPoints;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
import java.util.Map;
public class Snippet {
// @block-start define-dataset
static class Doc {
final String caption;
final String image;
Doc(String caption, String image) {
this.caption = caption;
this.image = image;
}
}
static String imageToBase64Url(String imagePath) throws Exception {
String prefix = "data:image/png;base64";
byte[] bytes = Files.readAllBytes(Path.of(imagePath));
return prefix + "," + Base64.getEncoder().encodeToString(bytes);
}
static List<Doc> documents = List.of(
new Doc("An image about plane emergency safety.", "images/image-1.png"),
new Doc("An image about airplane components.", "images/image-2.png"),
new Doc("An image about COVID safety restrictions.", "images/image-3.png"),
new Doc("A confidential image about UFO sightings.", "images/image-4.png"),
new Doc("An image about unusual footprints on Aralar 2011.", "images/image-5.png")
);
// @block-end define-dataset
public static void run() throws Exception {
// @hide-start
String QDRANT_URL = "xyz-example.eu-central.aws.cloud.qdrant.io";
String QDRANT_API_KEY = "<your-api-key>";
// @hide-end
// @block-start client-connection
QdrantClient client =
new QdrantClient(
QdrantGrpcClient.newBuilder(QDRANT_URL, 6334, true)
.withApiKey(QDRANT_API_KEY)
.build());
// @block-end client-connection
// @block-start create-collection
String collectionName = "multimodal-embeddings";
if (!client.collectionExistsAsync(collectionName).get()) {
client.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName(collectionName)
.setVectorsConfig(
VectorsConfig.newBuilder()
.setParamsMap(
VectorParamsMap.newBuilder()
.putMap(
"image",
VectorParams.newBuilder()
.setSize(512)
.setDistance(Distance.Cosine)
.build())
.putMap(
"text",
VectorParams.newBuilder()
.setSize(512)
.setDistance(Distance.Cosine)
.build())
.build()))
.build()
).get();
}
// @block-end create-collection
// @block-start upload-data
String cohereApiKey = System.getenv("COHERE_API_KEY");
Context ctx = RequestHeaders.withHeader(
Context.current(), "cohere-api-key", cohereApiKey);
List<PointStruct> points = new java.util.ArrayList<>();
for (int idx = 0; idx < documents.size(); idx++) {
Doc doc = documents.get(idx);
points.add(
PointStruct.newBuilder()
.setId(io.qdrant.client.PointIdFactory.id(idx))
.setVectors(
namedVectors(
Map.of(
"text",
vector(
Document.newBuilder()
.setText(doc.caption)
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()),
"image",
vector(
Image.newBuilder()
.setImage(value(imageToBase64Url(doc.image)))
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))))
.putAllPayload(
Map.of(
"caption", value(doc.caption),
"image", value(doc.image)))
.build());
}
ctx.call(() -> client.upsertAsync(collectionName, points).get());
// @block-end upload-data
// @block-start text-to-image-search
var results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Document.newBuilder()
.setText("Plane components")
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("image")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("image"));
// @block-end text-to-image-search
// @block-start multilingual-search
results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Document.newBuilder()
.setText("Componenti di un aereo")
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("image")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("image"));
// @block-end multilingual-search
// @block-start image-to-text-search
results = ctx.call(() -> client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName(collectionName)
.setQuery(
nearest(
Image.newBuilder()
.setImage(value(imageToBase64Url("images/image-2.png")))
.setModel("cohere/embed-v4.0")
.putOptions("output_dimension", value(512))
.build()))
.setUsing("text")
.setWithPayload(enable(true))
.setLimit(1)
.build()
).get());
System.out.println(results.get(0).getPayloadMap().get("caption"));
// @block-end image-to-text-search
}
}
@@ -0,0 +1,144 @@
# @block-start client-connection
import os
from qdrant_client import QdrantClient, models
client = QdrantClient(
url=os.getenv("QDRANT_URL"),
api_key=os.getenv("QDRANT_API_KEY"),
cloud_inference=True,
)
# @block-end client-connection
# @block-start define-dataset
import base64
def image_to_base64_url(image_path: str) -> str:
prefix = "data:image/png;base64"
with open(image_path, "rb") as image_file:
return prefix + "," + base64.b64encode(image_file.read()).decode("utf-8")
documents = [
{"caption": "An image about plane emergency safety.", "image": "images/image-1.png"},
{"caption": "An image about airplane components.", "image": "images/image-2.png"},
{"caption": "An image about COVID safety restrictions.", "image": "images/image-3.png"},
{"caption": "A confidential image about UFO sightings.", "image": "images/image-4.png"},
{"caption": "An image about unusual footprints on Aralar 2011.", "image": "images/image-5.png"},
]
# @block-end define-dataset
# @block-start create-collection
COLLECTION_NAME = "multimodal-embeddings"
if not client.collection_exists(COLLECTION_NAME):
client.create_collection(
collection_name=COLLECTION_NAME,
vectors_config={
"image": models.VectorParams(size=512, distance=models.Distance.COSINE),
"text": models.VectorParams(size=512, distance=models.Distance.COSINE),
}
)
# @block-end create-collection
# @block-start upload-data
from qdrant_client.context_headers import headers
cohere_api_key = os.getenv("COHERE_API_KEY")
# @hide-start
if cohere_api_key is None:
raise RuntimeError("COHERE_API_KEY not found in the current environment")
# @hide-end
with headers({"cohere-api-key": cohere_api_key}):
client.upsert(
collection_name=COLLECTION_NAME,
points=[
models.PointStruct(
id=idx,
vector={
"text": models.Document(
text=doc["caption"],
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
"image": models.Image(
image=image_to_base64_url(doc["image"]),
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
},
payload=doc
)
for idx, doc in enumerate(documents)
]
)
# @block-end upload-data
# @block-start text-to-image-search
from PIL import Image
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Document(
text="Plane components",
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="image",
with_payload=["image"],
limit=1
).points[0].payload
# @hide-start
if payload is None:
raise RuntimeError("Payload should be not null")
# @hide-end
Image.open(payload["image"])
# @block-end text-to-image-search
# @block-start multilingual-search
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Document(
text="Componenti di un aereo",
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="image",
with_payload=["image"],
limit=1
).points[0].payload
# @hide-start
if payload is None:
raise RuntimeError("Payload should be not null")
# @hide-end
Image.open(payload["image"])
# @block-end multilingual-search
# @block-start image-to-text-search
with headers({"cohere-api-key": cohere_api_key}):
payload = client.query_points(
collection_name=COLLECTION_NAME,
query=models.Image(
image=image_to_base64_url("images/image-2.png"),
model="cohere/embed-v4.0",
options={"output_dimension": 512},
),
using="text",
with_payload=["caption"],
limit=1
).points[0].payload
# @hide-start
if payload is None:
raise RuntimeError("Payload should be not null")
# @hide-end
print(payload["caption"])
# @block-end image-to-text-search
@@ -0,0 +1,152 @@
use std::collections::HashMap;
use base64::prelude::*;
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{
CreateCollectionBuilder, Distance, DocumentBuilder, ImageBuilder, NamedVectors, PointStruct,
Query, QueryPointsBuilder, UpsertPointsBuilder, Value, VectorParamsBuilder,
VectorsConfigBuilder,
};
pub async fn main() -> anyhow::Result<()> {
// @block-start client-connection
let client = Qdrant::from_url(&std::env::var("QDRANT_URL")?)
.api_key(std::env::var("QDRANT_API_KEY")?)
.build()?;
// @block-end client-connection
// @block-start define-dataset
fn image_to_base64_url(image_path: &str) -> anyhow::Result<String> {
let prefix = "data:image/png;base64";
let bytes = std::fs::read(image_path)?;
Ok(format!("{prefix},{}", BASE64_STANDARD.encode(bytes)))
}
struct Doc {
caption: &'static str,
image: &'static str,
}
let documents = vec![
Doc { caption: "An image about plane emergency safety.", image: "images/image-1.png" },
Doc { caption: "An image about airplane components.", image: "images/image-2.png" },
Doc { caption: "An image about COVID safety restrictions.", image: "images/image-3.png" },
Doc { caption: "A confidential image about UFO sightings.", image: "images/image-4.png" },
Doc { caption: "An image about unusual footprints on Aralar 2011.", image: "images/image-5.png" },
];
// @block-end define-dataset
// @block-start create-collection
let collection_name = "multimodal-embeddings";
if !client.collection_exists(collection_name).await? {
let mut vectors = VectorsConfigBuilder::default();
vectors.add_named_vector_params("image", VectorParamsBuilder::new(512, Distance::Cosine));
vectors.add_named_vector_params("text", VectorParamsBuilder::new(512, Distance::Cosine));
client
.create_collection(CreateCollectionBuilder::new(collection_name).vectors_config(vectors))
.await?;
}
// @block-end create-collection
// @block-start upload-data
let cohere_api_key = std::env::var("COHERE_API_KEY")?;
let mut options: HashMap<String, Value> = HashMap::new();
options.insert("output_dimension".to_string(), 512i64.into());
let mut points = Vec::new();
for (idx, doc) in documents.iter().enumerate() {
let vectors = NamedVectors::default()
.add_vector(
"text",
DocumentBuilder::new(doc.caption, "cohere/embed-v4.0")
.options(options.clone())
.build(),
)
.add_vector(
"image",
ImageBuilder::new_from_base64(image_to_base64_url(doc.image)?, "cohere/embed-v4.0")
.options(options.clone())
.build(),
);
points.push(PointStruct::new(
idx as u64,
vectors,
[
("caption", doc.caption.into()),
("image", doc.image.into()),
],
));
}
client
.with_header("cohere-api-key", &cohere_api_key)
.upsert_points(UpsertPointsBuilder::new(collection_name, points))
.await?;
// @block-end upload-data
// @block-start text-to-image-search
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
DocumentBuilder::new("Plane components", "cohere/embed-v4.0")
.options(options.clone())
.build(),
))
.using("image")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("image"));
// @block-end text-to-image-search
// @block-start multilingual-search
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
DocumentBuilder::new("Componenti di un aereo", "cohere/embed-v4.0")
.options(options.clone())
.build(),
))
.using("image")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("image"));
// @block-end multilingual-search
// @block-start image-to-text-search
let results = client
.with_header("cohere-api-key", &cohere_api_key)
.query(
QueryPointsBuilder::new(collection_name)
.query(Query::new_nearest(
ImageBuilder::new_from_base64(
image_to_base64_url("images/image-2.png")?,
"cohere/embed-v4.0",
)
.options(options.clone())
.build(),
))
.using("text")
.with_payload(true)
.limit(1),
)
.await?;
println!("{:?}", results.result[0].payload.get("caption"));
// @block-end image-to-text-search
Ok(())
}
@@ -0,0 +1,94 @@
import { QdrantClient, Schemas, withHeaders } from "@qdrant/js-client-rest";
import { readFileSync } from "fs";
// @block-start client-connection
const client = new QdrantClient({
url: process.env.QDRANT_URL,
apiKey: process.env.QDRANT_API_KEY,
});
// @block-end client-connection
// @block-start define-dataset
function imageToBase64Url(imagePath: string): string {
const prefix = "data:image/png;base64";
const imageBuffer = readFileSync(imagePath);
return `${prefix},${imageBuffer.toString("base64")}`;
}
const documents = [
{ caption: "An image about plane emergency safety.", image: "images/image-1.png" },
{ caption: "An image about airplane components.", image: "images/image-2.png" },
{ caption: "An image about COVID safety restrictions.", image: "images/image-3.png" },
{ caption: "A confidential image about UFO sightings.", image: "images/image-4.png" },
{ caption: "An image about unusual footprints on Aralar 2011.", image: "images/image-5.png" },
];
// @block-end define-dataset
// @block-start create-collection
const collectionName = "multimodal-embeddings";
if (!(await client.collectionExists(collectionName)).exists) {
await client.createCollection(collectionName, {
vectors: {
image: { size: 512, distance: "Cosine" },
text: { size: 512, distance: "Cosine" },
},
});
}
// @block-end create-collection
// @block-start upload-data
const cohereApiKey = process.env.COHERE_API_KEY!;
await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.upsert(collectionName, {
points: documents.map((doc, idx) => ({
id: idx,
vector: {
text: { text: doc.caption, model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
image: { image: imageToBase64Url(doc.image), model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
},
payload: doc,
})),
})
);
// @block-end upload-data
// @block-start text-to-image-search
const textToImageResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { text: "Plane components", model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "image",
with_payload: ["image"],
limit: 1,
})
);
console.log(textToImageResults.points[0].payload!.image);
// @block-end text-to-image-search
// @block-start multilingual-search
const multilingualResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { text: "Componenti di un aereo", model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "image",
with_payload: ["image"],
limit: 1,
})
);
console.log(multilingualResults.points[0].payload!.image);
// @block-end multilingual-search
// @block-start image-to-text-search
const imageToTextResults = await withHeaders({ "cohere-api-key": cohereApiKey }, () =>
client.query(collectionName, {
query: { image: imageToBase64Url("images/image-2.png"), model: "cohere/embed-v4.0", options: { output_dimension: 512 } },
using: "text",
with_payload: ["caption"],
limit: 1,
})
);
console.log(imageToTextResults.points[0].payload!.caption);
// @block-end image-to-text-search
@@ -0,0 +1,121 @@
---
title: Multimodal Search
short_description: "Build a multimodal, multilingual vector earch application with Cohere Embed 4.0 and Qdrant Cloud Inference that searches across image and text modalities."
description: "Combine Cohere Embed 4.0 with Qdrant Cloud Inference to power multimodal, multilingual vector search application over images and text using a shared embedding space."
weight: 25
partition: develop
social_preview_image: /documentation/examples/multimodal-search/social_preview.png
aliases:
- /documentation/tutorials/multimodal-search-fastembed/
- /documentation/advanced-tutorials/multimodal-search-fastembed/
- /documentation/multimodal-search/
---
# Multimodal and Multilingual Vector Search with Cohere and Qdrant
| Time: 15 min | Level: Beginner |Output: [GitHub](https://github.com/qdrant/examples/blob/master/multimodal-search/Multimodal_Search_with_Cohere_and_Cloud_Inference.ipynb)|[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://githubtocolab.com/qdrant/examples/blob/master/multimodal-search/Multimodal_Search_with_Cohere_and_Cloud_Inference.ipynb) |
| --- | ----------- | ----------- | ----------- |
## Overview
You often understand and share information more effectively when combining different types of data. The taste of comfort food can trigger childhood memories. A song might be described with just "pam pam clap" sounds instead of a paragraph. Emojis and stickers can express a feeling or a complex idea faster than words.
Modalities of data such as **text, images, video, and audio**, in various combinations, form valuable use cases for semantic search applications.
Vector databases, being **modality-agnostic**, are well suited for building these applications.
This tutorial works with two modalities: image and text data. You can build a semantic search application with any combination of modalities, as long as you choose an embedding model that bridges the **semantic gap**.
> The **semantic gap** refers to the difference between low-level features, such as brightness, and high-level concepts, such as cuteness.
[Cohere Embed 4.0](https://cohere.com/blog/embed-4), for example, is built for multimodal and multilingual embedding, and supports more than 100 languages. Instead of running the model yourself, this tutorial calls it through [Qdrant Cloud Inference](/documentation/inference/inference-api/), so Qdrant generates the embeddings and stores them in a [collection](/documentation/manage-data/collections/) in one step.
## Setup
Install the client:
{{< code-snippet path="/documentation/headless/snippets/install-client/" >}}
<aside role="status">
You need a Cohere API key to follow along. Create a free one on the <a href="https://dashboard.cohere.com/api-keys">Cohere dashboard</a>.
</aside>
## Dataset
To make the demonstration simple, this tutorial uses a tiny dataset of images and their captions.
Download the [tutorial images](https://github.com/qdrant/examples/tree/master/multimodal-search/images) and place them in a folder named `images`, in the same folder as your code or notebook.
## Connect to Qdrant
1. **Create a client object for Qdrant, with Cloud Inference enabled**.
You'll use a [Qdrant Cloud Free Tier Cluster](/documentation/cloud/create-cluster/#free-clusters). [Create a free cluster](https://cloud.qdrant.io/), save the associated API key and endpoint URL, and instantiate the Qdrant client. Set `cloud_inference=True` so Qdrant can generate embeddings for you:
{{< code-snippet path="/documentation/headless/snippets/tutorial-multimodal-search/" block="client-connection" >}}
2. **Define the dataset and a helper to encode images**.
Cloud Inference accepts images as base64 data URLs, so convert each file before uploading it:
{{< code-snippet path="/documentation/headless/snippets/tutorial-multimodal-search/" block="define-dataset" >}}
3. **Create a collection for the images with captions**.
{{< code-snippet path="/documentation/headless/snippets/tutorial-multimodal-search/" block="create-collection" >}}
## Upload Data to Qdrant
Upload your images with captions to the collection. Each image and its caption is embedded by Cohere Embed 4.0, through [Cloud Inference](/documentation/inference/external-inference-providers/#cohere), and stored as a [point](/documentation/concepts/points/).
Pass your Cohere API key through a header, and describe each vector as a `models.Document` (for text) or `models.Image` (for the image), naming the Cohere model and the output dimension you want:
{{< code-snippet path="/documentation/headless/snippets/tutorial-multimodal-search/" block="upload-data" >}}
## Search
### Text-to-Image
See what image comes back for the query "*Plane components*". Wrap the query in a `models.Document` the same way you did while uploading, so Cloud Inference embeds it with the same model:
{{< code-snippet path="/documentation/headless/snippets/tutorial-multimodal-search/" block="text-to-image-search" >}}
**Response:**
![Diagram of airplane components](/documentation/advanced-tutorials/airplane.png)
### Multilingual Search
Now run the same query in Italian, one of the 30+ languages Cohere Embed 4.0 supports, and compare the results:
{{< code-snippet path="/documentation/headless/snippets/tutorial-multimodal-search/" block="multilingual-search" >}}
**Response:**
![Diagram of airplane components](/documentation/advanced-tutorials/airplane.png)
### Image-to-Text
Now run a reverse search, starting from this image:
![Diagram of airplane components](/documentation/advanced-tutorials/airplane.png)
Embed the image with `models.Image`, and search only among the text vectors:
{{< code-snippet path="/documentation/headless/snippets/tutorial-multimodal-search/" block="image-to-text-search" >}}
**Response:**
```text
'An image about airplane components.'
```
## Next Steps
Even image and text multimodal search alone supports many use cases: e-commerce, media management, content recommendation, emotion recognition, biomedical image retrieval, and spoken sign language transcription, among others.
Consider a shopper who has a picture of a product they want, plus a specific textual requirement, like "*in beige color*". You can search using text or images alone, or combine their embeddings through **late fusion** (summing and weighting the vectors can work surprisingly well).
Combining both modalities with [Discovery Search](/articles/discovery-search/) can also surface results that neither modality would find on its own.
Join our [Discord community](https://qdrant.to/discord), where we talk about vector search and similarity learning, experiment, and have fun!
@@ -25,4 +25,4 @@ partition: ecosystem
<!-- | [Multimodal & Multilingual RAG](/documentation/tutorials-build-essentials/multimodal-search/) | Search across image and text modalities. | <span class="pill">LlamaIndex</span> | 15m | <span class="text-green">Beginner</span> | -->
<!-- | [Agentic RAG with LangGraph](/documentation/tutorials-build-essentials/agentic-rag-langgraph/) | Build AI agents to answer library documentation. | <span class="pill">LangGraph</span> | 45m | <span class="text-yellow">Intermediate</span> | -->
<!-- | [S3 Ingestion with LangChain](/documentation/tutorials-build-essentials/data-ingestion-beginners/) | Stream data from AWS S3 to vector store. | <span class="pill">LangChain</span> | 30m | <span class="text-green">Beginner</span> | -->
<!-- | [S3 Ingestion with LangChain](/documentation/tutorials-build-essentials/data-ingestion-beginners/) | Stream data from AWS S3 to vector store. | <span class="pill">LangChain</span> | 30m | <span class="text-green">Beginner</span> | -->
@@ -1,193 +0,0 @@
---
title: Multimodal and Multilingual RAG
short_description: "Build a multimodal, multilingual RAG application with LlamaIndex and Qdrant that searches across image and text modalities."
description: "Tutorial: combine LlamaIndex with Qdrant to power multimodal, multilingual RAG over images and text using a shared embedding space and vector search."
weight: 25
hideInSidebar: true
partition: ecosystem
social_preview_image: /documentation/examples/multimodal-search/social_preview.png
aliases:
- /documentation/tutorials/multimodal-search-fastembed/
- /documentation/advanced-tutorials/multimodal-search-fastembed/
- /documentation/multimodal-search/
---
# Multimodal and Multilingual RAG with LlamaIndex and Qdrant
<!-- ![Snow prints](/documentation/examples/multimodal-search/image-1.png) -->
| Time: 15 min | Level: Beginner |Output: [GitHub](https://github.com/qdrant/examples/blob/master/multimodal-search/Multimodal_Search_with_LlamaIndex.ipynb)|[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://githubtocolab.com/qdrant/examples/blob/master/multimodal-search/Multimodal_Search_with_LlamaIndex.ipynb) |
| --- | ----------- | ----------- | ----------- |
## Overview
We often understand and share information more effectively when combining different types of data. For example, the taste of comfort food can trigger childhood memories. We might describe a song with just “pam pam clap” sounds. Instead of writing paragraphs. Sometimes, we may use emojis and stickers to express how we feel or to share complex ideas.
Modalities of data such as **text**, **images**, **video** and **audio** in various combinations form valuable use cases for Semantic Search applications.
Vector databases, being **modality-agnostic**, are perfect for building these applications.
In this simple tutorial, we are working with two simple modalities: **image** and **text** data. However, you can create a Semantic Search application with any combination of modalities if you choose the right embedding model to bridge the **semantic gap**.
> The **semantic gap** refers to the difference between low-level features (aka brightness) and high-level concepts (aka cuteness).
For example, the [vdr-2b-multi-v1 model](https://huggingface.co/llamaindex/vdr-2b-multi-v1) from LlamaIndex is designed for multilingual embedding, particularly effective for visual document retrieval across multiple languages and domains. It allows for searching and querying visually rich multilingual documents without the need for OCR or other data extraction pipelines.
## Setup
First, install the required libraries `qdrant-client`, `llama-index-embeddings-huggingface`, and `torchvision`.
```bash
pip install qdrant-client llama-index-embeddings-huggingface torchvision
```
## Dataset
To make the demonstration simple, we created a tiny dataset of images and their captions for you.
Images can be downloaded from [here](https://github.com/qdrant/examples/tree/master/multimodal-search/images). It's **important** to place them in the same folder as your code/notebook, in the folder named `images`.
## Vectorize data
`LlamaIndex`'s `vdr-2b-multi-v1` model supports cross-lingual retrieval, allowing for effective searches across languages and domains. It encodes document page screenshots into dense single-vector representations, eliminating the need for OCR and other complex data extraction processes.
Let's embed the images and their captions in the **shared embedding space**.
```python
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
model = HuggingFaceEmbedding(
model_name="llamaindex/vdr-2b-multi-v1",
device="cpu", # "mps" for mac, "cuda" for nvidia GPUs
trust_remote_code=True,
)
documents = [
{"caption": "An image about plane emergency safety.", "image": "images/image-1.png"},
{"caption": "An image about airplane components.", "image": "images/image-2.png"},
{"caption": "An image about COVID safety restrictions.", "image": "images/image-3.png"},
{"caption": "An confidential image about UFO sightings.", "image": "images/image-4.png"},
{"caption": "An image about unusual footprints on Aralar 2011.", "image": "images/image-5.png"},
]
text_embeddings = model.get_text_embedding_batch([doc["caption"] for doc in documents])
image_embeddings = model.get_image_embedding_batch([doc["image"] for doc in documents])
```
## Upload data to Qdrant
1. **Create a client object for Qdrant**.
```python
from qdrant_client import QdrantClient, models
# docker run -p 6333:6333 qdrant/qdrant
client = QdrantClient(url="http://localhost:6333/")
```
2. **Create a new collection for the images with captions**.
```python
COLLECTION_NAME = "llama-multi"
if not client.collection_exists(COLLECTION_NAME):
client.create_collection(
collection_name=COLLECTION_NAME,
vectors_config={
"image": models.VectorParams(size=len(image_embeddings[0]), distance=models.Distance.COSINE),
"text": models.VectorParams(size=len(text_embeddings[0]), distance=models.Distance.COSINE),
}
)
```
3. **Upload our images with captions to the Collection**.
```python
client.upload_points(
collection_name=COLLECTION_NAME,
points=[
models.PointStruct(
id=idx,
vector={
"text": text_embeddings[idx],
"image": image_embeddings[idx],
},
payload=doc
)
for idx, doc in enumerate(documents)
]
)
```
## Search
### Text-to-Image
Let's see what image we will get to the query "*Adventures on snow hills*".
```python
from PIL import Image
find_image = model.get_query_embedding("Adventures on snow hills")
Image.open(client.query_points(
collection_name=COLLECTION_NAME,
query=find_image,
using="image",
with_payload=["image"],
limit=1
).points[0].payload['image'])
```
Let's also run the same query in Italian and compare the results.
### Multilingual Search
Now, let's do a multilingual search using an Italian query:
```python
Image.open(client.query_points(
collection_name=COLLECTION_NAME,
query=model.get_query_embedding("Avventure sulle colline innevate"),
using="image",
with_payload=["image"],
limit=1
).points[0].payload['image'])
```
**Response:**
![Snow prints](/documentation/advanced-tutorials/snow-prints.png)
### Image-to-Text
Now, let's do a reverse search with the following image:
![Airplane](/documentation/advanced-tutorials/airplane.png)
```python
client.query_points(
collection_name=COLLECTION_NAME,
query=model.get_image_embedding("images/image-2.png"),
# Now we are searching only among text vectors with our image query
using="text",
with_payload=["caption"],
limit=1
).points[0].payload['caption']
```
**Response:**
```text
'An image about plane emergency safety.'
```
## Next steps
Use cases of even just Image & Text Multimodal Search are countless: E-Commerce, Media Management, Content Recommendation, Emotion Recognition Systems, Biomedical Image Retrieval, Spoken Sign Language Transcription, etc.
Imagine a scenario: a user wants to find a product similar to a picture they have, but they also have specific textual requirements, like "*in beige colour*". You can search using just texts or images and combine their embeddings in a **late fusion manner** (summing and weighting might work surprisingly well).
Moreover, using [Discovery Search](/articles/discovery-search/) with both modalities, you can provide users with information that is impossible to retrieve unimodally!
Join our [Discord community](https://qdrant.to/discord), where we talk about vector search and similarity learning, experiment, and have fun!