From 098f95a9f73758b174e37f0003111defc85c6436 Mon Sep 17 00:00:00 2001 From: Austin Varga <64624232+avarga1@users.noreply.github.com> Date: Thu, 9 Apr 2026 09:31:12 -0600 Subject: [PATCH] fix(embeddings): only set encoding_format=None for canonical OpenAI provider litellm/main.py unconditionally set encoding_format=None in optional_params when no encoding_format was provided, even for third-party providers like together_ai and nvidia_nim that share the same embedding code path. These providers reject the null value with a 400 error. The None sentinel only needs to be set for the canonical OpenAI provider to prevent the OpenAI Python SDK from injecting its own default of "float". Scoping it to custom_llm_provider == "openai" (or None for the open_ai_embedding_models fallback) leaves third-party providers unaffected. Closes #25388 --- litellm/main.py | 6 ++- tests/test_litellm/test_utils.py | 75 +++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/litellm/main.py b/litellm/main.py index ddd37b47536..33b26b5e4ac 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4913,8 +4913,10 @@ def embedding( # noqa: PLR0915 if encoding_format is not None: optional_params["encoding_format"] = encoding_format - else: - # Omiting causes openai sdk to add default value of "float" + elif custom_llm_provider == "openai" or custom_llm_provider is None: + # Omitting causes openai sdk to add default value of "float". + # Only suppress for the canonical OpenAI provider — third-party + # providers (together_ai, nvidia_nim, etc.) reject encoding_format=null. optional_params["encoding_format"] = None api_version = None diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index 50dc3c6c6ec..367fdae7173 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -486,6 +486,57 @@ def test_cohere_embedding_optional_params(): assert optional_params is not None +def test_encoding_format_none_not_sent_to_third_party_providers(): + """ + together_ai / nvidia_nim / etc. reject encoding_format=null. + When no encoding_format is given, the None sentinel should only be set + for the canonical OpenAI provider, not third-party providers that share + the same embedding code path. + """ + from unittest.mock import MagicMock, patch + + import litellm + + mock_response = litellm.EmbeddingResponse( + model="text-embedding-3-small", + data=[{"embedding": [0.1, 0.2, 0.3], "index": 0, "object": "embedding"}], + usage=litellm.Usage(prompt_tokens=1, total_tokens=1), + ) + + def get_optional_params(call_args): + return call_args.kwargs.get( + "optional_params", call_args[1].get("optional_params", {}) + ) + + with patch("litellm.main.openai_chat_completions") as mock_oai: + mock_oai.embedding.return_value = mock_response + + # OpenAI — encoding_format=None should be set to suppress SDK default + litellm.embedding(model="text-embedding-3-small", input="hello") + params = get_optional_params(mock_oai.embedding.call_args) + assert "encoding_format" in params + assert params["encoding_format"] is None + + # together_ai — encoding_format key should be absent entirely + mock_oai.reset_mock() + litellm.embedding( + model="together_ai/togethercomputer/m2-bert-80M-8k-retrieval", + input="hello", + ) + params = get_optional_params(mock_oai.embedding.call_args) + assert "encoding_format" not in params + + # Explicit encoding_format should always be forwarded + mock_oai.reset_mock() + litellm.embedding( + model="together_ai/togethercomputer/m2-bert-80M-8k-retrieval", + input="hello", + encoding_format="float", + ) + params = get_optional_params(mock_oai.embedding.call_args) + assert params.get("encoding_format") == "float" + + def validate_model_cost_values(model_data, exceptions=None): """ Validates that cost values in model data do not exceed 1. @@ -1412,8 +1463,7 @@ class TestProxyFunctionCalling: assert result is True, "Resolvable model names work with fallback logic" # Documentation notes: - print( - """ + print(""" PROXY MODEL RESOLUTION BEHAVIOR: ✅ WORKS (with current fallback logic): @@ -1428,8 +1478,7 @@ class TestProxyFunctionCalling: 💡 SOLUTION: Use LiteLLM proxy server with proper model_list configuration that maps custom names to underlying models. - """ - ) + """) @pytest.mark.parametrize( "proxy_model_with_hints,expected_result", @@ -1791,8 +1840,7 @@ class TestProxyFunctionCalling: This test provides documentation on how the proxy server configuration would typically map custom model names to underlying models. """ - print( - """ + print(""" REAL-WORLD PROXY SERVER CONFIGURATION EXAMPLE: =============================================== @@ -1845,8 +1893,7 @@ class TestProxyFunctionCalling: - Consistent request/response format - Enhanced streaming support for function calls - """ - ) + """) # Verify that direct underlying models work as expected bedrock_models = [ @@ -2060,8 +2107,7 @@ class TestProxyFunctionCalling: This test provides documentation on how the proxy server configuration would typically map custom model names to underlying models. """ - print( - """ + print(""" REAL-WORLD PROXY SERVER CONFIGURATION EXAMPLE: =============================================== @@ -2114,8 +2160,7 @@ class TestProxyFunctionCalling: - Consistent request/response format - Enhanced streaming support for function calls - """ - ) + """) # Verify that direct underlying models work as expected bedrock_models = [ @@ -2329,8 +2374,7 @@ class TestProxyFunctionCalling: This test provides documentation on how the proxy server configuration would typically map custom model names to underlying models. """ - print( - """ + print(""" REAL-WORLD PROXY SERVER CONFIGURATION EXAMPLE: =============================================== @@ -2383,8 +2427,7 @@ class TestProxyFunctionCalling: - Consistent request/response format - Enhanced streaming support for function calls - """ - ) + """) # Verify that direct underlying models work as expected bedrock_models = [