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
This commit is contained in:
Austin Varga 2026-04-09 09:31:12 -06:00
parent 97f722f558
commit 098f95a9f7
2 changed files with 63 additions and 18 deletions

View file

@ -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

View file

@ -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 = [