mirror of
https://github.com/qdrant/landing_page.git
synced 2026-09-25 14:08:30 +02:00
Merge pull request #2656 from qdrant/clelia/multimodal-tutorial-revamp
chore: update multimodal search tutorial
This commit is contained in:
@@ -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
|
||||
```
|
||||
|
||||
+140
@@ -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
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```csharp
|
||||
var client = new QdrantClient(
|
||||
host: QDRANT_URL,
|
||||
https: true,
|
||||
apiKey: QDRANT_API_KEY
|
||||
);
|
||||
```
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```go
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: QDRANT_URL,
|
||||
APIKey: QDRANT_API_KEY,
|
||||
UseTLS: true,
|
||||
})
|
||||
```
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```java
|
||||
QdrantClient client =
|
||||
new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder(QDRANT_URL, 6334, true)
|
||||
.withApiKey(QDRANT_API_KEY)
|
||||
.build());
|
||||
```
|
||||
+11
@@ -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,
|
||||
)
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
```rust
|
||||
let client = Qdrant::from_url(&std::env::var("QDRANT_URL")?)
|
||||
.api_key(std::env::var("QDRANT_API_KEY")?)
|
||||
.build()?;
|
||||
```
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
```typescript
|
||||
const client = new QdrantClient({
|
||||
url: process.env.QDRANT_URL,
|
||||
apiKey: process.env.QDRANT_API_KEY,
|
||||
});
|
||||
```
|
||||
+18
@@ -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 },
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
+22
@@ -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,
|
||||
},
|
||||
},
|
||||
),
|
||||
})
|
||||
}
|
||||
```
|
||||
+28
@@ -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();
|
||||
}
|
||||
```
|
||||
+12
@@ -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),
|
||||
}
|
||||
)
|
||||
```
|
||||
+13
@@ -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?;
|
||||
}
|
||||
```
|
||||
+12
@@ -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" },
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
+118
@@ -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"]);
|
||||
```
|
||||
+17
@@ -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" },
|
||||
};
|
||||
```
|
||||
+23
@@ -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"},
|
||||
}
|
||||
```
|
||||
+24
@@ -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")
|
||||
);
|
||||
```
|
||||
+16
@@ -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"},
|
||||
]
|
||||
```
|
||||
+20
@@ -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" },
|
||||
];
|
||||
```
|
||||
+15
@@ -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" },
|
||||
];
|
||||
```
|
||||
+152
@@ -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"])
|
||||
```
|
||||
+17
@@ -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"]);
|
||||
```
|
||||
+21
@@ -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"])
|
||||
```
|
||||
+19
@@ -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"));
|
||||
```
|
||||
+16
@@ -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"])
|
||||
```
|
||||
+21
@@ -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"));
|
||||
```
|
||||
+12
@@ -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);
|
||||
```
|
||||
+172
@@ -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"));
|
||||
```
|
||||
+17
@@ -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"]);
|
||||
```
|
||||
+19
@@ -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"])
|
||||
```
|
||||
+19
@@ -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"));
|
||||
```
|
||||
+16
@@ -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"])
|
||||
```
|
||||
+18
@@ -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"));
|
||||
```
|
||||
+12
@@ -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);
|
||||
```
|
||||
+112
@@ -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"])
|
||||
```
|
||||
+136
@@ -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"));
|
||||
```
|
||||
+18
@@ -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"]);
|
||||
```
|
||||
+19
@@ -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"])
|
||||
```
|
||||
+19
@@ -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"));
|
||||
```
|
||||
+18
@@ -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"])
|
||||
```
|
||||
+18
@@ -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"));
|
||||
```
|
||||
+12
@@ -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);
|
||||
```
|
||||
+82
@@ -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);
|
||||
```
|
||||
+27
@@ -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);
|
||||
```
|
||||
+38
@@ -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,
|
||||
})
|
||||
```
|
||||
+37
@@ -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());
|
||||
```
|
||||
+29
@@ -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)
|
||||
]
|
||||
)
|
||||
```
|
||||
+37
@@ -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?;
|
||||
```
|
||||
+16
@@ -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,
|
||||
})),
|
||||
})
|
||||
);
|
||||
```
|
||||
+199
@@ -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
|
||||
}
|
||||
+195
@@ -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
|
||||
}
|
||||
}
|
||||
+144
@@ -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
|
||||
+152
@@ -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(())
|
||||
}
|
||||
+94
@@ -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)|[](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:**
|
||||
|
||||

|
||||
|
||||
### 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:**
|
||||
|
||||

|
||||
|
||||
### Image-to-Text
|
||||
|
||||
Now run a reverse search, starting from this image:
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||
<!--  -->
|
||||
|
||||
| Time: 15 min | Level: Beginner |Output: [GitHub](https://github.com/qdrant/examples/blob/master/multimodal-search/Multimodal_Search_with_LlamaIndex.ipynb)|[](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:**
|
||||
|
||||

|
||||
|
||||
### Image-to-Text
|
||||
|
||||
Now, let's do a reverse search with the following image:
|
||||
|
||||

|
||||
|
||||
```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!
|
||||
Reference in New Issue
Block a user