Last updated 2026-05-17
Voyage AI Provider
The voyage provider implements
Embedder
only. Voyage AI specialises in retrieval-grade embeddings and is
the
recommended embedding partner
for Anthropic Claude users — Anthropic does not offer first-party
embeddings, and llmrouter pairs Claude with Voyage by
default in its examples.
Import path
import ( "github.com/elloloop/llmrouter" "github.com/elloloop/llmrouter/providers/voyage")Default endpoint
With no WithBaseURL override, the provider targets
https://api.voyageai.com/v1. Embedding requests go to
{BaseURL}/embeddings.
Construction
p, err := voyage.New( llmrouter.WithAPIKey(os.Getenv("VOYAGE_API_KEY")),)if err != nil { log.Fatal(err)}
Authentication is via Authorization: Bearer <key>.
Models
-
voyage-3— current general-purpose model (1024 dims, supports 256/512/1024/2048 viaDimensions). The recommended default for most retrieval tasks. -
voyage-3-large— higher quality, also 1024 dims with the same dimension options. Use this when retrieval accuracy matters more than cost. -
voyage-3-lite— 512 dims, lowest cost. Strong baseline for small-scale RAG. -
voyage-code-3— tuned for source code, identifiers, and technical documentation. -
voyage-finance-2— tuned for financial documents, filings, and earnings transcripts. -
voyage-law-2— tuned for legal documents and caselaw. -
voyage-multilingual-2— multilingual support across 29 languages, 1024 dims. Use this for non-English corpora.
Input type
Voyage distinguishes query embeddings from
document embeddings — using the wrong one significantly
hurts retrieval recall. The library maps
TaskType
as follows:
Canonical TaskType | Voyage input_type |
|---|---|
RETRIEVAL_QUERY / QUESTION_ANSWERING | query |
RETRIEVAL_DOCUMENT | document |
SEMANTIC_SIMILARITY | document |
CLASSIFICATION | document |
CLUSTERING | document |
Empty TaskType sends no input_type,
which Voyage treats as document.
Output dimension
Voyage 3-family models support
output_dimension — the library forwards
EmbedRequest.Dimensions as that field. Valid values
depend on the model:
voyage-3,voyage-3-large: 256, 512, 1024 (default), 2048.voyage-3-lite: 512 (fixed).voyage-code-3: 256, 512, 1024 (default), 2048.voyage-multilingual-2: 1024 (fixed).
RAG example: embed documents, embed query, retrieve
package main
import ( "context" "fmt" "log" "math" "os" "sort"
"github.com/elloloop/llmrouter" "github.com/elloloop/llmrouter/providers/voyage")
func main() { p, err := voyage.New(llmrouter.WithAPIKey(os.Getenv("VOYAGE_API_KEY"))) if err != nil { log.Fatal(err) } ctx := context.Background()
docs := []string{ "The mitochondrion is the powerhouse of the cell.", "ATP synthase produces adenosine triphosphate.", "Photosynthesis converts light energy into chemical energy.", }
docEmb, err := p.Embed(ctx, llmrouter.EmbedRequest{ Model: "voyage-3", Inputs: docs, Dimensions: 1024, TaskType: "RETRIEVAL_DOCUMENT", }) if err != nil { log.Fatal(err) }
queryEmb, err := p.Embed(ctx, llmrouter.EmbedRequest{ Model: "voyage-3", Inputs: []string{"What makes ATP?"}, TaskType: "RETRIEVAL_QUERY", }) if err != nil { log.Fatal(err) }
type hit struct { idx int sim float32 } var hits []hit for i, dv := range docEmb.Embeddings { hits = append(hits, hit{i, cosine(queryEmb.Embeddings[0], dv)}) } sort.Slice(hits, func(i, j int) bool { return hits[i].sim > hits[j].sim }) for _, h := range hits { fmt.Printf("%.3f %s\n", h.sim, docs[h.idx]) }}
func cosine(a, b []float32) float32 { var dot, na, nb float64 for i := range a { dot += float64(a[i]) * float64(b[i]) na += float64(a[i]) * float64(a[i]) nb += float64(b[i]) * float64(b[i]) } return float32(dot / (math.Sqrt(na) * math.Sqrt(nb)))}Pairing with Anthropic Claude
Anthropic does not offer a first-party embedding endpoint and explicitly recommends Voyage. The canonical pairing:
import ( "github.com/elloloop/llmrouter/providers/anthropic" "github.com/elloloop/llmrouter/providers/voyage")
chat, _ := anthropic.New(llmrouter.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")))embed, _ := voyage.New(llmrouter.WithAPIKey(os.Getenv("VOYAGE_API_KEY")))
// embed.Embed(...) for retrieval; chat.CompletionStream(...) for generation.Truncation
Voyage truncates inputs that exceed the model's context window by
default. To error instead of truncating, pass
truncation=false via Raw:
raw := []byte(`{ "input": ["..."], "input_type": "document", "truncation": false}`)
resp, err := p.Embed(ctx, llmrouter.EmbedRequest{ Model: "voyage-3", Raw: raw,})Error handling
Non-2xx responses surface as
*llmrouter.ErrUpstream
with Provider == "voyage". Common cases:
401— bad or missing API key.400— invalidoutput_dimensionfor the chosen model, or input over context window withtruncation=false.429— rate-limit (Voyage's rate limits are per-minute token throughput).
Caveats
- Embeddings only. No chat, no TTS, no STT. Compose with a chat provider for full pipelines.
- Batch size limit. Voyage caps requests at 128
inputs per call (1000 for
voyage-3-lite). The provider does not auto-batch; chunk client-side. - Asymmetric query/document. Always set
TaskTypecorrectly — the difference is large (5-10% recall on standard benchmarks).
See also
- Embeddings concept — Embedder semantics.
- Anthropic provider — canonical Voyage chat partner.
- OpenAI provider — alternative embeddings.
- Cohere provider — alternative embeddings with input-type support.