From ab92d54f64ea8dd640feacd8e734e15d1a8dbc6b Mon Sep 17 00:00:00 2001 From: fzowl Date: Sun, 26 Jul 2026 23:34:40 +0200 Subject: [PATCH 1/6] feat(voyage): add voyage-context-4 and voyage-4 series models - Add voyage-context-4 contextual embedding model (120k ctx) - Add voyage-4, voyage-4-large, voyage-4-lite, voyage-4-nano - Add voyage-multimodal-3.5 - Normalize contextual embedding input: pass list[str] to API, keep list[list[str]] only when caller provides it - Add tests for context-4 detection and input normalization --- .../embedding/transformation_contextual.py | 9 +++- ...odel_prices_and_context_window_backup.json | 48 +++++++++++++++++++ model_prices_and_context_window.json | 48 +++++++++++++++++++ tests/llm_translation/test_voyage_ai.py | 28 +++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/litellm/llms/voyage/embedding/transformation_contextual.py b/litellm/llms/voyage/embedding/transformation_contextual.py index 4df2fa4ba31..842f62ed3e8 100644 --- a/litellm/llms/voyage/embedding/transformation_contextual.py +++ b/litellm/llms/voyage/embedding/transformation_contextual.py @@ -105,8 +105,15 @@ def transform_embedding_request( optional_params: dict, headers: dict, ) -> dict: + # Voyage contextualized embeddings API accepts `inputs` as a list[str] + # (a single document's chunks). Pass list[list[str]] through unchanged + # only when the caller already provided that nested shape. + if isinstance(input, str): + inputs: Union[List[str], List[List[str]]] = [input] + else: + inputs = input return { - "inputs": input, + "inputs": inputs, "model": model, **optional_params, } diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index c584deb683a..3d2650d72a6 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -27345,6 +27345,38 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-4": { + "input_cost_per_token": 6e-08, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, + "voyage/voyage-4-large": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, + "voyage/voyage-4-lite": { + "input_cost_per_token": 2e-08, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, + "voyage/voyage-4-nano": { + "input_cost_per_token": 1e-08, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-code-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", @@ -27369,6 +27401,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-context-4": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 120000, + "max_tokens": 120000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-finance-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", @@ -27417,6 +27457,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-multimodal-3.5": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "wandb/openai/gpt-oss-120b": { "max_tokens": 131072, "max_input_tokens": 131072, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index c584deb683a..3d2650d72a6 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -27345,6 +27345,38 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-4": { + "input_cost_per_token": 6e-08, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, + "voyage/voyage-4-large": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, + "voyage/voyage-4-lite": { + "input_cost_per_token": 2e-08, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, + "voyage/voyage-4-nano": { + "input_cost_per_token": 1e-08, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-code-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", @@ -27369,6 +27401,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-context-4": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 120000, + "max_tokens": 120000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-finance-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", @@ -27417,6 +27457,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-multimodal-3.5": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "wandb/openai/gpt-oss-120b": { "max_tokens": 131072, "max_input_tokens": 131072, diff --git a/tests/llm_translation/test_voyage_ai.py b/tests/llm_translation/test_voyage_ai.py index a0b9ee0a44b..7af6a54ba61 100644 --- a/tests/llm_translation/test_voyage_ai.py +++ b/tests/llm_translation/test_voyage_ai.py @@ -142,6 +142,7 @@ def test_contextual_embedding_model_detection(self): # Test contextual model detection assert config.is_contextualized_embeddings("voyage-context-3") is True + assert config.is_contextualized_embeddings("voyage-context-4") is True assert config.is_contextualized_embeddings("voyage-context-2") is True assert config.is_contextualized_embeddings("context-model") is True @@ -198,6 +199,33 @@ def test_contextual_embedding_request_transformation(self): assert transformed["model"] == "voyage-context-3" assert transformed["encoding_format"] == "float" + def test_contextual_embedding_request_input_normalization(self): + """Contextual API is called with list[str]; list[list[str]] only when received.""" + from litellm.llms.voyage.embedding.transformation_contextual import ( + VoyageContextualEmbeddingConfig, + ) + + config = VoyageContextualEmbeddingConfig() + + # A bare string is normalized to a single-element list[str] + transformed = config.transform_embedding_request( + "voyage-context-4", "Hello", {}, {} + ) + assert transformed["inputs"] == ["Hello"] + + # A list[str] is passed through unchanged (not wrapped) + transformed = config.transform_embedding_request( + "voyage-context-4", ["Hello", "world"], {}, {} + ) + assert transformed["inputs"] == ["Hello", "world"] + + # A list[list[str]] is passed through unchanged + nested = [["Hello", "world"], ["Test"]] + transformed = config.transform_embedding_request( + "voyage-context-4", nested, {}, {} + ) + assert transformed["inputs"] == nested + def test_contextual_embedding_response_transformation(self): """Test response transformation for contextual embeddings""" from litellm.llms.voyage.embedding.transformation_contextual import ( From d7944c603546ffb5e755c26500df36cb4768a646 Mon Sep 17 00:00:00 2001 From: fzowl Date: Sun, 26 Jul 2026 23:40:51 +0200 Subject: [PATCH 2/6] fix(voyage): set voyage-4-nano cost to 0.0 (open-weight, no listed API price) --- litellm/model_prices_and_context_window_backup.json | 2 +- model_prices_and_context_window.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 3d2650d72a6..0dcd949c542 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -27370,7 +27370,7 @@ "output_cost_per_token": 0.0 }, "voyage/voyage-4-nano": { - "input_cost_per_token": 1e-08, + "input_cost_per_token": 0.0, "litellm_provider": "voyage", "max_input_tokens": 32000, "max_tokens": 32000, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 3d2650d72a6..0dcd949c542 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -27370,7 +27370,7 @@ "output_cost_per_token": 0.0 }, "voyage/voyage-4-nano": { - "input_cost_per_token": 1e-08, + "input_cost_per_token": 0.0, "litellm_provider": "voyage", "max_input_tokens": 32000, "max_tokens": 32000, From d4b82637189cde096cba3167b51b0b970d69477c Mon Sep 17 00:00:00 2001 From: fzowl Date: Sun, 26 Jul 2026 23:45:39 +0200 Subject: [PATCH 3/6] fix(voyage): remove voyage-4-nano (open-source, runs locally, no API) --- litellm/model_prices_and_context_window_backup.json | 8 -------- model_prices_and_context_window.json | 8 -------- 2 files changed, 16 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 0dcd949c542..09a83d44ce7 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -27369,14 +27369,6 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, - "voyage/voyage-4-nano": { - "input_cost_per_token": 0.0, - "litellm_provider": "voyage", - "max_input_tokens": 32000, - "max_tokens": 32000, - "mode": "embedding", - "output_cost_per_token": 0.0 - }, "voyage/voyage-code-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 0dcd949c542..09a83d44ce7 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -27369,14 +27369,6 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, - "voyage/voyage-4-nano": { - "input_cost_per_token": 0.0, - "litellm_provider": "voyage", - "max_input_tokens": 32000, - "max_tokens": 32000, - "mode": "embedding", - "output_cost_per_token": 0.0 - }, "voyage/voyage-code-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", From ec9f93e8016a028a89e221c9cefd3f472c79407c Mon Sep 17 00:00:00 2001 From: fzowl Date: Sun, 26 Jul 2026 23:48:12 +0200 Subject: [PATCH 4/6] feat(voyage): add voyage-4-nano (open-source, local, cost 0.0) --- litellm/model_prices_and_context_window_backup.json | 8 ++++++++ model_prices_and_context_window.json | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 09a83d44ce7..0dcd949c542 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -27369,6 +27369,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-4-nano": { + "input_cost_per_token": 0.0, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-code-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 09a83d44ce7..0dcd949c542 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -27369,6 +27369,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-4-nano": { + "input_cost_per_token": 0.0, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-code-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", From 6a2879642bfa0316f5ac9c8c09a9173bb03533da Mon Sep 17 00:00:00 2001 From: fzowl Date: Mon, 27 Jul 2026 13:58:03 +0200 Subject: [PATCH 5/6] feat(voyage): add missing voyage-multilingual-2 and voyage-large-2-instruct Fill in remaining Voyage embedding models absent from the price map. Synced across both root and backup model price maps. --- .../model_prices_and_context_window_backup.json | 16 ++++++++++++++++ model_prices_and_context_window.json | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 0dcd949c542..cb2e86ddff9 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -27425,6 +27425,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-large-2-instruct": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 16000, + "max_tokens": 16000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-law-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", @@ -27465,6 +27473,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-multilingual-2": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "wandb/openai/gpt-oss-120b": { "max_tokens": 131072, "max_input_tokens": 131072, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 0dcd949c542..cb2e86ddff9 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -27425,6 +27425,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-large-2-instruct": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 16000, + "max_tokens": 16000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "voyage/voyage-law-2": { "input_cost_per_token": 1.2e-07, "litellm_provider": "voyage", @@ -27465,6 +27473,14 @@ "mode": "embedding", "output_cost_per_token": 0.0 }, + "voyage/voyage-multilingual-2": { + "input_cost_per_token": 1.2e-07, + "litellm_provider": "voyage", + "max_input_tokens": 32000, + "max_tokens": 32000, + "mode": "embedding", + "output_cost_per_token": 0.0 + }, "wandb/openai/gpt-oss-120b": { "max_tokens": 131072, "max_input_tokens": 131072, From 9e626029e1054bcf8f83088780c4f5e586f57cb9 Mon Sep 17 00:00:00 2001 From: fzowl Date: Mon, 27 Jul 2026 15:35:36 +0200 Subject: [PATCH 6/6] docs(voyage): document voyage-context-4 and voyage-4 embedding models - Feature voyage-context-4 as the recommended contextual model - Add voyage-4 / voyage-4-large / voyage-4-lite to the supported models and model selection tables with pricing --- docs/my-website/docs/providers/voyage.md | 25 +++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/my-website/docs/providers/voyage.md b/docs/my-website/docs/providers/voyage.md index 43369cd6ab7..31b6a1bf07c 100644 --- a/docs/my-website/docs/providers/voyage.md +++ b/docs/my-website/docs/providers/voyage.md @@ -54,6 +54,9 @@ All models listed here https://docs.voyageai.com/embeddings/#models-and-specific | Model Name | Function Call | |-------------------------|------------------------------------------------------------| +| voyage-4 | `embedding(model="voyage/voyage-4", input)` | +| voyage-4-large | `embedding(model="voyage/voyage-4-large", input)` | +| voyage-4-lite | `embedding(model="voyage/voyage-4-lite", input)` | | voyage-3.5 | `embedding(model="voyage/voyage-3.5", input)` | | voyage-3.5-lite | `embedding(model="voyage/voyage-3.5-lite", input)` | | voyage-3-large | `embedding(model="voyage/voyage-3-large", input)` | @@ -72,9 +75,9 @@ All models listed here https://docs.voyageai.com/embeddings/#models-and-specific | voyage-lite-01 | `embedding(model="voyage/voyage-lite-01", input)` | | voyage-lite-01-instruct | `embedding(model="voyage/voyage-lite-01-instruct", input)` | -## Contextual Embeddings (voyage-context-3) +## Contextual Embeddings (voyage-context-4) -VoyageAI's `voyage-context-3` model provides contextualized chunk embeddings, where each chunk is embedded with awareness of its surrounding document context. This significantly improves retrieval quality compared to standard context-agnostic embeddings. +VoyageAI's contextual models (`voyage-context-4`, and the previous generation `voyage-context-3`) provide contextualized chunk embeddings, where each chunk is embedded with awareness of its surrounding document context. This significantly improves retrieval quality compared to standard context-agnostic embeddings. `voyage-context-4` is the latest model and is recommended over `voyage-context-3`. ### Key Benefits - Chunks understand their position and role within the full document @@ -94,7 +97,7 @@ os.environ['VOYAGE_API_KEY'] = "your-api-key" # Single document with multiple chunks response = embedding( - model="voyage/voyage-context-3", + model="voyage/voyage-context-4", input=[ [ "Chapter 1: Introduction to AI", @@ -107,7 +110,7 @@ print(f"Number of chunk groups: {len(response.data)}") # Multiple documents response = embedding( - model="voyage/voyage-context-3", + model="voyage/voyage-context-4", input=[ ["Paris is the capital of France.", "It is known for the Eiffel Tower."], ["Tokyo is the capital of Japan.", "It is a major economic hub."] @@ -117,17 +120,17 @@ print(f"Processed {len(response.data)} documents") ``` ### Specifications -- Model: `voyage-context-3` -- Context length: 32,000 tokens per document +- Model: `voyage-context-4` (recommended), `voyage-context-3` +- Context length: 32,000 tokens per chunk - Output dimensions: 256, 512, 1024 (default), or 2048 - Max inputs: 1,000 per request - Max total tokens: 120,000 - Max chunks: 16,000 -- Pricing: $0.18 per million tokens +- Pricing: `voyage-context-4` $0.12 / `voyage-context-3` $0.18 per million tokens ### When to Use Contextual Embeddings -**Use `voyage-context-3` when:** +**Use `voyage-context-4` when:** - Processing long documents split into chunks - Document structure and flow are important - References between sections matter @@ -143,13 +146,17 @@ print(f"Processed {len(response.data)} documents") | Model | Best For | Context Length | Price/M Tokens | |-------|----------|----------------|----------------| +| voyage-4 | General-purpose, multilingual | 32K | $0.06 | +| voyage-4-large | Best overall quality | 32K | $0.12 | +| voyage-4-lite | Latency-sensitive applications | 32K | $0.02 | | voyage-3.5 | General-purpose, multilingual | 32K | $0.06 | | voyage-3.5-lite | Latency-sensitive applications | 32K | $0.02 | | voyage-3-large | Best overall quality | 32K | $0.18 | | voyage-code-3 | Code retrieval and search | 32K | $0.18 | | voyage-finance-2 | Financial documents | 32K | $0.12 | | voyage-law-2 | Legal documents | 16K | $0.12 | -| voyage-context-3 | Contextual document embeddings | 32K | $0.18 | +| voyage-context-4 | Contextual document embeddings (recommended) | 32K/chunk | $0.12 | +| voyage-context-3 | Contextual document embeddings (previous gen) | 32K/chunk | $0.18 | ## Rerank