Integration: Voyage AI
Use text embeddings and rerankers from Voyage AI
Table of Contents
- Installation
- Usage
- Supported Models
- Example
- Retrieve and Rerank
- Use Voyage Search as an Agent Tool
- Contextualized Embeddings Example
- Multimodal Embeddings
Voyage AI’s embedding and ranking models are state-of-the-art in retrieval accuracy. The integration supports the following models:
voyage-4-large,voyage-4, andvoyage-4-lite- Latest general-purpose embedding models with shared embedding space and MoE architecturevoyage-3.5andvoyage-3.5-lite- General-purpose embedding models with superior performancevoyage-code-3- Optimized for code retrievalvoyage-context-3- Contextualized chunk embedding model that preserves document context for improved retrieval accuracyvoyage-multimodal-3.5- Multimodal model supporting text, images, and video (preview)
For the complete list of available models, see the Embeddings Documentation and Contextualized Chunk Embeddings.
Supported Models
Text Embedding Models
| Model | Description | Dimensions |
|---|---|---|
voyage-4-large |
The best general-purpose and multilingual retrieval quality | 1024 (default), 256, 512, 2048 |
voyage-4 |
Optimized for general-purpose and multilingual retrieval quality | 1024 (default), 256, 512, 2048 |
voyage-4-lite |
Optimized for latency and cost | 1024 (default), 256, 512, 2048 |
voyage-3.5 |
General-purpose embedding model | 1024 |
voyage-3.5-lite |
Efficient model with lower latency | 1024 |
voyage-code-3 |
Optimized for code retrieval | 1024 |
Multimodal Embedding Models
| Model | Description | Dimensions | Modalities |
|---|---|---|---|
voyage-multimodal-3 |
Multimodal embedding model | 1024 | Text, Images |
voyage-multimodal-3.5 |
Multimodal embedding model (preview) | 256, 512, 1024, 2048 | Text, Images, Video |
Reranker Models
| Model | Description |
|---|---|
rerank-2.5 |
Latest reranker model with the best accuracy |
rerank-2 |
High-accuracy reranker model |
rerank-2-lite |
Efficient reranker with lower latency |
Installation
pip install voyage-embedders-haystack
Usage
You can use Voyage models with five components:
- VoyageTextEmbedder - For embedding query text
- VoyageDocumentEmbedder - For embedding documents
-
VoyageContextualizedDocumentEmbedder - For contextualized chunk embeddings with
voyage-context-3 -
VoyageMultimodalEmbedder - For multimodal embeddings with
voyage-multimodal-3.5 - VoyageRanker - For reranking documents
Standard Embeddings
To create semantic embeddings for documents, use VoyageDocumentEmbedder in your indexing pipeline. For generating embeddings for queries, use VoyageTextEmbedder. For reranking, use VoyageRanker with
Voyage Rerankers.
Contextualized Embeddings
For improved retrieval quality, use VoyageContextualizedDocumentEmbedder with the voyage-context-3 model. This component preserves context between related document chunks by grouping them together during embedding, reducing context loss that occurs when chunks are embedded independently
Important: You must explicitly specify the model parameter when initializing any component. Choose from the available models listed in the
Embeddings Documentation. Recommended choices include:
voyage-4-large- Best general-purpose and multilingual retrieval qualityvoyage-4- Balanced general-purpose and multilingual retrieval qualityvoyage-4-lite- Optimized for latency and costvoyage-context-3- Contextualized embeddings for improved retrieval (use withVoyageContextualizedDocumentEmbedder)
You can set the environment variable VOYAGE_API_KEY instead of passing the API key as an argument. To get an API key, please see the
Voyage AI website.
Note (v1.7.0+): The
modelparameter is required and must be explicitly specified. Earlier versions defaulted tovoyage-3for embedders andrerank-2for the ranker.
Example
Below is the example Semantic Search pipeline that uses the
Simple Wikipedia Dataset from HuggingFace. You can find more examples in the
examples folder.
Load the dataset:
# Install HuggingFace Datasets using "pip install datasets"
from datasets import load_dataset
from haystack import Pipeline
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
from haystack.components.writers import DocumentWriter
from haystack.dataclasses import Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
# Import Voyage Embedders
from haystack_integrations.components.embedders.voyage_embedders import VoyageDocumentEmbedder, VoyageTextEmbedder
# Load first 100 rows of the Simple Wikipedia Dataset from HuggingFace
dataset = load_dataset("pszemraj/simple_wikipedia", split="validation[:100]")
docs = [
Document(
content=doc["text"],
meta={
"title": doc["title"],
"url": doc["url"],
},
)
for doc in dataset
]
Index the documents to the InMemoryDocumentStore using the VoyageDocumentEmbedder and DocumentWriter:
doc_store = InMemoryDocumentStore(embedding_similarity_function="cosine")
retriever = InMemoryEmbeddingRetriever(document_store=doc_store)
doc_writer = DocumentWriter(document_store=doc_store)
doc_embedder = VoyageDocumentEmbedder(
model="voyage-4",
input_type="document",
)
text_embedder = VoyageTextEmbedder(model="voyage-4", input_type="query")
# Indexing Pipeline
indexing_pipeline = Pipeline()
indexing_pipeline.add_component(instance=doc_embedder, name="DocEmbedder")
indexing_pipeline.add_component(instance=doc_writer, name="DocWriter")
indexing_pipeline.connect("DocEmbedder", "DocWriter")
indexing_pipeline.run({"DocEmbedder": {"documents": docs}})
print(f"Number of documents in Document Store: {len(doc_store.filter_documents())}")
print(f"First Document: {doc_store.filter_documents()[0]}")
print(f"Embedding of first Document: {doc_store.filter_documents()[0].embedding}")
Query the Semantic Search Pipeline using the InMemoryEmbeddingRetriever and VoyageTextEmbedder:
text_embedder = VoyageTextEmbedder(model="voyage-4", input_type="query")
# Query Pipeline
query_pipeline = Pipeline()
query_pipeline.add_component(instance=text_embedder, name="TextEmbedder")
query_pipeline.add_component(instance=retriever, name="Retriever")
query_pipeline.connect("TextEmbedder.embedding", "Retriever.query_embedding")
# Search
results = query_pipeline.run({"TextEmbedder": {"text": "Which year did the Joker movie release?"}})
# Print text from top result
top_result = results["Retriever"]["documents"][0].content
print("The top search result is:")
print(top_result)
Retrieve and Rerank
Embedding retrieval is fast and cheap, but a reranker can meaningfully improve the order of the top results. The VoyageRanker re-scores the candidate documents returned by the retriever using Voyage AI’s rerank-2.5 model. Building on the doc_store and indexed corpus from the
Example above, add a VoyageRanker to the query pipeline:
from haystack_integrations.components.rankers.voyage.ranker import VoyageRanker
# Query pipeline: embed -> retrieve -> rerank
rerank_pipeline = Pipeline()
rerank_pipeline.add_component(instance=VoyageTextEmbedder(model="voyage-4", input_type="query"), name="TextEmbedder")
rerank_pipeline.add_component(instance=InMemoryEmbeddingRetriever(document_store=doc_store, top_k=5), name="Retriever")
rerank_pipeline.add_component(instance=VoyageRanker(model="rerank-2.5", top_k=3), name="Ranker")
# Connect embedding -> retriever -> ranker
rerank_pipeline.connect("TextEmbedder.embedding", "Retriever.query_embedding")
rerank_pipeline.connect("Retriever.documents", "Ranker.documents")
# VoyageTextEmbedder does not output `text`, so pass the query to both
# the embedder and the ranker explicitly.
query = "Which year did the Joker movie release?"
results = rerank_pipeline.run({
"TextEmbedder": {"text": query},
"Ranker": {"query": query},
})
# Print the top 3 reranked results
for i, doc in enumerate(results["Ranker"]["documents"]):
print(f"{i + 1}. score={doc.score:.4f} | {doc.content[:120]}")
Use Voyage Search as an Agent Tool
You can expose a Voyage-powered search pipeline as a tool for a Haystack Agent, which decides when to search the corpus and grounds its answer in the retrieved passages. The two examples below show this with a retrieve-and-rerank pipeline and with a lighter retrieve-only pipeline.
Retrieve and Rerank with ComponentTool
Wrap the retrieve-and-rerank pipeline from above in a SuperComponent, then turn it into a tool with ComponentTool. The Agent calls the tool with a query, and the reranked passages are formatted back into text for the model:
from haystack import SuperComponent
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import ComponentTool
# Expose the retrieve-and-rerank pipeline as a single searchable component.
# input_mapping sends the query to both the embedder and the ranker;
# output_mapping surfaces the reranked documents.
search_component = SuperComponent(
pipeline=rerank_pipeline,
input_mapping={"query": ["TextEmbedder.text", "Ranker.query"]},
output_mapping={"Ranker.documents": "documents"},
)
# Format the retrieved documents into the text the Agent's LLM will read.
def format_documents(documents):
return "\n\n".join(f"[{i + 1}] {doc.content}" for i, doc in enumerate(documents))
# ComponentTool auto-generates the tool's JSON schema from the component inputs.
search_tool = ComponentTool(
component=search_component,
name="voyage_search",
description="Search the indexed Wikipedia corpus for passages relevant to a query.",
outputs_to_string={"source": "documents", "handler": format_documents},
)
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
tools=[search_tool],
system_prompt=(
"You are a helpful assistant. Use the voyage_search tool to find relevant passages "
"from Wikipedia, then answer the user's question based only on those passages. "
"If the search results don't contain the answer, say you couldn't find it."
),
)
result = agent.run(
messages=[ChatMessage.from_user("Which year did the Joker movie release?")]
)
print(result["messages"][-1].text)
Retrieve-only with PipelineTool
If you don’t need reranking, a plain embed-and-retrieve pipeline makes a lighter search tool. Here PipelineTool wraps the Pipeline directly. Building on the doc_store indexed in the
Example above:
from haystack import Pipeline
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
from haystack.dataclasses import ChatMessage
from haystack.tools import PipelineTool
# Query pipeline: embed -> retrieve (no reranker)
search_pipeline = Pipeline()
search_pipeline.add_component(instance=VoyageTextEmbedder(model="voyage-4", input_type="query"), name="TextEmbedder")
search_pipeline.add_component(instance=InMemoryEmbeddingRetriever(document_store=doc_store, top_k=3), name="Retriever")
search_pipeline.connect("TextEmbedder.embedding", "Retriever.query_embedding")
# Format the retrieved documents into the text the Agent's LLM will read.
def format_documents(documents):
return "\n\n".join(f"[{i + 1}] {doc.content}" for i, doc in enumerate(documents))
# PipelineTool wraps the Pipeline directly - no SuperComponent needed.
search_tool = PipelineTool(
pipeline=search_pipeline,
input_mapping={"query": ["TextEmbedder.text"]},
output_mapping={"Retriever.documents": "documents"},
name="voyage_search",
description="Search the indexed Wikipedia corpus for passages relevant to a query.",
outputs_to_string={"source": "documents", "handler": format_documents},
)
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
tools=[search_tool],
system_prompt=(
"You are a helpful assistant. Use the voyage_search tool to find relevant passages "
"from Wikipedia, then answer the user's question based only on those passages. "
"If the search results don't contain the answer, say you couldn't find it."
),
)
result = agent.run(
messages=[ChatMessage.from_user("Which year did the Joker movie release?")]
)
print(result["messages"][-1].text)
Contextualized Embeddings Example
The voyage-context-3 model enables contextualized chunk embeddings, which preserve relationships between document chunks for better retrieval accuracy. Documents with the same source_id are embedded together in context:
from haystack import Document
from haystack_integrations.components.embedders.voyage_embedders import VoyageContextualizedDocumentEmbedder
# Create documents with source_id to group related chunks
docs = [
# Chunks from the same document (source_id: "doc1")
Document(
content="Apple Inc. released their Q1 earnings report.",
meta={"source_id": "doc1", "title": "Apple News"}
),
Document(
content="Revenue increased by 12% year over year.",
meta={"source_id": "doc1", "title": "Apple News"}
),
# Chunks from another document (source_id: "doc2")
Document(
content="Tesla announced new vehicle production targets.",
meta={"source_id": "doc2", "title": "Tesla Update"}
),
]
# Use VoyageContextualizedDocumentEmbedder for voyage-context-3
embedder = VoyageContextualizedDocumentEmbedder(
model="voyage-context-3",
input_type="document",
)
result = embedder.run(documents=docs)
# Chunks with the same source_id are embedded together, preserving context
# This improves retrieval - e.g., searching "Apple revenue growth" will better match
# the second chunk because it maintains its connection to "Apple Inc."
For more examples, see the contextualized embedder example.
Multimodal Embeddings
Voyage AI’s voyage-multimodal-3.5 model transforms unstructured data from multiple modalities (text, images, video) into a shared vector space. This enables mixed-media document retrieval and cross-modal semantic search.
Features
- Multiple modalities: Supports text, images, and video in a single input
- Variable dimensions: Output dimensions of 256, 512, 1024 (default), or 2048
- Interleaved content: Mix text, images, and video in single inputs
- No preprocessing required: Process documents with embedded images directly
Limits
- Images: Max 20MB, 16 million pixels
- Video: Max 20MB
- Context: 32,000 tokens
- Token counting: 560 image pixels = 1 token, 1120 video pixels = 1 token
Basic Multimodal Example
Use the VoyageMultimodalEmbedder component for multimodal embeddings. Each input is a list of content items (text, images, or videos):
from haystack.dataclasses import ByteStream
from haystack_integrations.components.embedders.voyage_embedders import VoyageMultimodalEmbedder
# Text-only embedding
embedder = VoyageMultimodalEmbedder(model="voyage-multimodal-3.5")
result = embedder.run(inputs=[["A sunset over the ocean"]])
print(f"Embedding dimensions: {len(result['embeddings'][0])}")
# Mixed text and image embedding
image_bytes = ByteStream.from_file_path("image.jpg")
result = embedder.run(inputs=[["Product photo for online store", image_bytes]])
print(f"Tokens used: {result['meta']['total_tokens']}")
Multimodal Example with Custom Dimensions
from haystack.dataclasses import ByteStream
from haystack_integrations.components.embedders.voyage_embedders import VoyageMultimodalEmbedder
# Configure output dimensions (256, 512, 1024, or 2048)
embedder = VoyageMultimodalEmbedder(
model="voyage-multimodal-3.5",
output_dimension=2048, # Higher dimensions for better accuracy
input_type="document", # Optimize for document retrieval
)
# Embed multiple inputs at once
image1 = ByteStream.from_file_path("doc1.jpg")
image2 = ByteStream.from_file_path("doc2.jpg")
result = embedder.run(inputs=[
["Document about machine learning", image1],
["Technical diagram", image2],
])
print(f"Number of embeddings: {len(result['embeddings'])}")
print(f"Image pixels processed: {result['meta']['image_pixels']}")
Video Embedding Example
Video inputs require the voyageai.video_utils module:
from voyageai.video_utils import Video
from haystack_integrations.components.embedders.voyage_embedders import VoyageMultimodalEmbedder
embedder = VoyageMultimodalEmbedder(model="voyage-multimodal-3.5")
# Load video using VoyageAI's Video utility
video = Video.from_path("video.mp4", model="voyage-multimodal-3.5")
# Embed video with optional text context
result = embedder.run(inputs=[["Machine learning tutorial", video]])
print(f"Embedding dimensions: {len(result['embeddings'][0])}")
print(f"Video pixels processed: {result['meta']['video_pixels']}")
print(f"Total tokens: {result['meta']['total_tokens']}")
Use Cases
- Mixed-media document retrieval (PDFs, slides with images)
- Image-text similarity search
- Video content retrieval and search
- Cross-modal semantic search
For more information, see the Multimodal Embeddings Documentation.
License
voyage-embedders-haystack is distributed under the terms of the
Apache-2.0 license.
