Amazon Bedrock

Amazon Bedrock serves embedding models from Amazon, Cohere, and TwelveLabs through the AWS SDKs, inside your AWS account. Call Bedrock from your application and send the vectors to Qdrant.

Models

ModelModel IDDimensionsMax InputInput
Amazon Titan Text Embeddings V2amazon.titan-embed-text-v2:01024 (default), 512, 2568,192 tokensText
Amazon Nova Multimodal Embeddingsamazon.nova-2-multimodal-embeddings-v1:03072 (default), 1024, 384, 2568K tokens, or 30 seconds of video or audio per requestText, document image, image, video, audio
Cohere Embed v4cohere.embed-v4:01536 (default), 1024, 512, 256128K tokensText, image
TwelveLabs Marengo Embed 3.0twelvelabs.marengo-embed-3-0-v1:0512500 tokens of textText, image, video, audio

Each model has its own request and response format, documented on its model card. This page uses Titan Text Embeddings V2 for text and Nova Multimodal Embeddings for images. Titan Text Embeddings V2 succeeds Titan Embeddings G1 - Text (amazon.titan-embed-text-v1). Titan Multimodal Embeddings G1 (amazon.titan-embed-image-v1) and Cohere Embed English and Multilingual v3 (cohere.embed-english-v3, cohere.embed-multilingual-v3) are also previous generations. Use Nova Multimodal Embeddings or Cohere Embed v4 in their place.

Titan Text Embeddings V2 returns normalized vectors by default (normalize: true), so the Cosine and Dot distance metrics rank results identically. This page uses Cosine.

Setup

Install the AWS SDK and the Qdrant client:

  • Python: pip install boto3 qdrant-client
  • TypeScript: npm install @aws-sdk/client-bedrock-runtime @qdrant/js-client-rest

The AWS SDKs read credentials from the default credential chain, such as the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables, the shared credentials file, AWS IAM Identity Center, or an IAM role when the code runs on AWS. They also accept an Amazon Bedrock API key in the AWS_BEARER_TOKEN_BEDROCK environment variable. The identity needs the bedrock:InvokeModel permission. For details, see Credentials for Python and Setting Credentials in Node.js for JavaScript.

The examples use us-east-1, where both models on this page are available. Create both clients, a helper that embeds one text, and a collection sized for the model:

import json

import boto3
from qdrant_client import QdrantClient, models

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
client = QdrantClient(url="http://localhost:6333")

MODEL_ID = "amazon.titan-embed-text-v2:0"

def embed(text: str) -> list[float]:
    response = bedrock.invoke_model(
        modelId=MODEL_ID,
        contentType="application/json",
        body=json.dumps({"inputText": text, "dimensions": 1024, "normalize": True}),
    )
    return json.loads(response["body"].read())["embedding"]

client.create_collection(
    collection_name="{collection_name}",
    vectors_config=models.VectorParams(size=1024, distance=models.Distance.COSINE),
)
import {
  BedrockRuntimeClient,
  InvokeModelCommand,
} from "@aws-sdk/client-bedrock-runtime";
import { QdrantClient } from "@qdrant/js-client-rest";

const bedrock = new BedrockRuntimeClient({ region: "us-east-1" });
const client = new QdrantClient({ url: "http://localhost:6333" });

const MODEL_ID = "amazon.titan-embed-text-v2:0";

async function embed(text: string): Promise<number[]> {
  const response = await bedrock.send(
    new InvokeModelCommand({
      modelId: MODEL_ID,
      contentType: "application/json",
      body: JSON.stringify({ inputText: text, dimensions: 1024, normalize: true }),
    })
  );
  return JSON.parse(new TextDecoder().decode(response.body)).embedding;
}

await client.createCollection("{collection_name}", {
  vectors: { size: 1024, distance: "Cosine" },
});

Embed Documents

Titan Text Embeddings V2 embeds one text per request, so this example calls the helper for each document and uses the input order as the point ID:

documents = [
    "Qdrant is a vector search engine.",
    "Amazon Bedrock serves embedding models through the AWS SDKs.",
]

client.upsert(
    collection_name="{collection_name}",
    points=[
        models.PointStruct(id=idx, vector=embed(doc), payload={"text": doc})
        for idx, doc in enumerate(documents)
    ],
)
const documents = [
  "Qdrant is a vector search engine.",
  "Amazon Bedrock serves embedding models through the AWS SDKs.",
];

await client.upsert("{collection_name}", {
  points: await Promise.all(
    documents.map(async (doc, idx) => ({
      id: idx,
      vector: await embed(doc),
      payload: { text: doc },
    }))
  ),
});

Each text can be up to 8,192 tokens or 50,000 characters. For large datasets, batch inference processes many records as one job.

Embed Queries

Titan Text Embeddings V2 has no separate query mode. Embed the query with the same model and dimensions as the documents:

hits = client.query_points(
    collection_name="{collection_name}",
    query=embed("What is Qdrant?"),
    limit=3,
).points
const { points: hits } = await client.query("{collection_name}", {
  query: await embed("What is Qdrant?"),
  limit: 3,
});

Embed Images

Nova Multimodal Embeddings maps text, images, documents, video, and audio into one vector space, so a text query can find an image. Its vectors don’t share a space with Titan’s, so store them in a separate collection.

Set embeddingPurpose to GENERIC_INDEX when you index content of any type. For queries, pick the purpose that matches the indexed content, such as IMAGE_RETRIEVAL for images or GENERIC_RETRIEVAL for a mix of types. This example uses 1024 dimensions:

import base64

NOVA_ID = "amazon.nova-2-multimodal-embeddings-v1:0"

def nova_embed(purpose: str, content: dict[str, object]) -> list[float]:
    body = {
        "taskType": "SINGLE_EMBEDDING",
        "singleEmbeddingParams": {
            "embeddingPurpose": purpose,
            "embeddingDimension": 1024,
            **content,
        },
    }
    response = bedrock.invoke_model(
        modelId=NOVA_ID, contentType="application/json", body=json.dumps(body)
    )
    return json.loads(response["body"].read())["embeddings"][0]["embedding"]

client.create_collection(
    collection_name="{image_collection_name}",
    vectors_config=models.VectorParams(size=1024, distance=models.Distance.COSINE),
)

with open("photo.jpg", "rb") as f:
    image = {"format": "jpeg", "source": {"bytes": base64.b64encode(f.read()).decode()}}

client.upsert(
    collection_name="{image_collection_name}",
    points=[
        models.PointStruct(
            id=1,
            vector=nova_embed("GENERIC_INDEX", {"image": image}),
            payload={"file": "photo.jpg"},
        )
    ],
)

image_hits = client.query_points(
    collection_name="{image_collection_name}",
    query=nova_embed(
        "IMAGE_RETRIEVAL",
        {"text": {"truncationMode": "END", "value": "A dog on a beach"}},
    ),
    limit=3,
).points
import { readFile } from "node:fs/promises";

const NOVA_ID = "amazon.nova-2-multimodal-embeddings-v1:0";

async function novaEmbed(purpose: string, content: object): Promise<number[]> {
  const response = await bedrock.send(
    new InvokeModelCommand({
      modelId: NOVA_ID,
      contentType: "application/json",
      body: JSON.stringify({
        taskType: "SINGLE_EMBEDDING",
        singleEmbeddingParams: {
          embeddingPurpose: purpose,
          embeddingDimension: 1024,
          ...content,
        },
      }),
    })
  );
  return JSON.parse(new TextDecoder().decode(response.body)).embeddings[0].embedding;
}

await client.createCollection("{image_collection_name}", {
  vectors: { size: 1024, distance: "Cosine" },
});

const image = {
  format: "jpeg",
  source: { bytes: (await readFile("photo.jpg")).toString("base64") },
};

await client.upsert("{image_collection_name}", {
  points: [
    {
      id: 1,
      vector: await novaEmbed("GENERIC_INDEX", { image }),
      payload: { file: "photo.jpg" },
    },
  ],
});

const { points: imageHits } = await client.query("{image_collection_name}", {
  query: await novaEmbed("IMAGE_RETRIEVAL", {
    text: { truncationMode: "END", value: "A dog on a beach" },
  }),
  limit: 3,
});

An inline request can be up to 25 MB after base64 encoding. Larger files, and video or audio longer than 30 seconds, go through the asynchronous StartAsyncInvoke API with input and output in Amazon S3. See Using Amazon Nova Embeddings and the request and response schema.

Store and Search the Vectors

Next Steps

Was this page useful?

Thank you for your feedback! 🙏

We are sorry to hear that. 😔 You can edit this page on GitHub, or create a GitHub issue.