From 8473b70dd8a9f00bde84b2a191cb89a399faa7f3 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 1 May 2026 16:26:17 +0530 Subject: [PATCH 1/6] feat(embedding): default OpenAI-path encoding_format to float Made-with: Cursor --- litellm/main.py | 11 ++-- ...penai_embedding_encoding_format_default.py | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 tests/test_litellm/test_openai_embedding_encoding_format_default.py diff --git a/litellm/main.py b/litellm/main.py index 0079bd750c..8cff8c0b9a 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4920,11 +4920,12 @@ def embedding( # noqa: PLR0915 if headers is not None and headers != {}: optional_params["extra_headers"] = headers - if encoding_format is not None: - optional_params["encoding_format"] = encoding_format - else: - # Omiting causes openai sdk to add default value of "float" - optional_params["encoding_format"] = None + optional_params["encoding_format"] = ( + encoding_format + or optional_params.get("encoding_format") + or get_secret_str("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT") + or "float" + ) api_version = None diff --git a/tests/test_litellm/test_openai_embedding_encoding_format_default.py b/tests/test_litellm/test_openai_embedding_encoding_format_default.py new file mode 100644 index 0000000000..dfeb997564 --- /dev/null +++ b/tests/test_litellm/test_openai_embedding_encoding_format_default.py @@ -0,0 +1,50 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from litellm import embedding + + +@pytest.mark.parametrize( + "set_env, env_value, expected", + [ + (False, None, "float"), + (True, "base64", "base64"), + ], +) +def test_openai_embedding_encoding_format_default( + monkeypatch, set_env, env_value, expected +): + monkeypatch.delenv("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT", raising=False) + if set_env: + monkeypatch.setenv("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT", env_value) + + mock_response = MagicMock() + mock_response.parse.return_value = MagicMock( + model_dump=lambda: { + "data": [{"embedding": [0.1, 0.2, 0.3], "index": 0}], + "model": "text-embedding-ada-002", + "object": "list", + "usage": {"prompt_tokens": 1, "total_tokens": 1}, + } + ) + mock_response.headers = {} + + with patch( + "litellm.llms.openai.openai.OpenAIChatCompletion._get_openai_client" + ) as mock_get_client: + mock_client_instance = MagicMock() + mock_get_client.return_value = mock_client_instance + mock_client_instance.embeddings.with_raw_response.create.return_value = ( + mock_response + ) + + embedding( + model="text-embedding-ada-002", + input="Hello world", + ) + + call_kwargs = ( + mock_client_instance.embeddings.with_raw_response.create.call_args[1] + ) + assert call_kwargs["encoding_format"] == expected From a1f08233930158a02ee2ffc3553ed9860eefe38a Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 1 May 2026 16:27:13 +0530 Subject: [PATCH 2/6] test(embedding): align local_testing OpenAI encoding_format default Made-with: Cursor --- tests/local_testing/test_embedding.py | 30 +++++++-------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/tests/local_testing/test_embedding.py b/tests/local_testing/test_embedding.py index 3f1a397ebc..82510b6f4f 100644 --- a/tests/local_testing/test_embedding.py +++ b/tests/local_testing/test_embedding.py @@ -1257,22 +1257,13 @@ def test_jina_ai_img_embeddings(input_data, expected_payload_input): assert sent_data["input"] == expected_payload_input -def test_encoding_format_none_not_omitted_from_openai_sdk(): +def test_encoding_format_defaults_to_float_for_openai_sdk(monkeypatch): """ - Test that encoding_format=None is explicitly sent to OpenAI SDK. + When encoding_format is not provided, LiteLLM sends `float` for OpenAI-path embeddings. - This test verifies that when encoding_format is not provided by the user, - liteLLM explicitly sets it to None rather than omitting it. This prevents - the OpenAI SDK from adding its default value of 'base64'. - - Without this fix: - - OpenAI SDK adds encoding_format='base64' as default when parameter is missing - - This causes issues with providers that don't support encoding_format (like Gemini) - - With this fix: - - encoding_format=None is explicitly passed - - OpenAI SDK respects the explicit None and doesn't add defaults + Optional global override: `LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT`. """ + monkeypatch.delenv("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT", raising=False) with patch( "litellm.llms.openai.openai.OpenAIChatCompletion._get_openai_client" ) as mock_get_client: @@ -1310,17 +1301,12 @@ def test_encoding_format_none_not_omitted_from_openai_sdk(): call_kwargs = call_args[1] # Get kwargs - # The key assertion: encoding_format should be in the request with value None - # This prevents OpenAI SDK from adding its default 'base64' value - assert "encoding_format" in call_kwargs, ( - "encoding_format should be explicitly passed to OpenAI SDK " - "(even if None) to prevent SDK from adding default value" - ) + assert "encoding_format" in call_kwargs assert ( - call_kwargs["encoding_format"] is None - ), "encoding_format should be None when not provided by user" + call_kwargs["encoding_format"] == "float" + ), "encoding_format should default to float when not provided by user" - print("✅ PASS: encoding_format=None is correctly passed to OpenAI SDK") + print("✅ PASS: encoding_format='float' is correctly passed to OpenAI SDK") def test_encoding_format_explicit_value_preserved(): From 809b83cfdd1cdf0c85626f82c99274dbe3a5b007 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 1 May 2026 16:28:30 +0530 Subject: [PATCH 3/6] docs(embedding): document encoding_format default and env override Made-with: Cursor --- litellm/llms/hosted_vllm/embedding/README.md | 13 ++++++++++++- litellm/main.py | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/litellm/llms/hosted_vllm/embedding/README.md b/litellm/llms/hosted_vllm/embedding/README.md index f82b3c77a6..2c58e16fc2 100644 --- a/litellm/llms/hosted_vllm/embedding/README.md +++ b/litellm/llms/hosted_vllm/embedding/README.md @@ -2,4 +2,15 @@ No transformation is required for hosted_vllm embedding. VLLM is a superset of OpenAI's `embedding` endpoint. -To pass provider-specific parameters, see [this](https://docs.litellm.ai/docs/completion/provider_specific_params) \ No newline at end of file +## `encoding_format` + +For OpenAI-compatible embedding calls (including `openai/...` with a custom `api_base` pointing at vLLM), LiteLLM resolves `encoding_format` when it is not set on the request: + +1. Explicit value on the embedding call (`encoding_format=...`). +2. Model config (`litellm_params.encoding_format` on the proxy `model_list` entry). +3. Environment variable `LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT` (e.g. in `.env` or container env). +4. Default **`float`**. + +That avoids forwarding `encoding_format=None` to the provider/SDK where some servers behave poorly. + +To pass provider-specific parameters, see [provider-specific params](https://docs.litellm.ai/docs/completion/provider_specific_params). \ No newline at end of file diff --git a/litellm/main.py b/litellm/main.py index 8cff8c0b9a..9d38575474 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4700,7 +4700,10 @@ def embedding( # noqa: PLR0915 Parameters: - model: The embedding model to use. - input: The input for which embeddings are to be generated. - - encoding_format: Optional[str] The format to return the embeddings in. Can be either `float` or `base64` + - encoding_format: Optional[str] The format to return the embeddings in. Can be either `float` or `base64`. + For OpenAI-compatible embedding routing (`openai`, custom base URL, etc.), when omitted LiteLLM resolves: + explicit argument → mapped optional params (e.g. proxy `litellm_params`) → environment variable + `LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT` → default `"float"`. - dimensions: The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models. - timeout: The timeout value for the API call, default 10 mins - litellm_call_id: The call ID for litellm logging. From 5c72a95289a0b342fc0e5a9a7b379a9095a41042 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 1 May 2026 16:35:19 +0530 Subject: [PATCH 4/6] Fix code --- litellm/main.py | 19 +++++----- ...penai_embedding_encoding_format_default.py | 36 +++++++++++++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/litellm/main.py b/litellm/main.py index 9d38575474..6d5f9f9b05 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4700,10 +4700,7 @@ def embedding( # noqa: PLR0915 Parameters: - model: The embedding model to use. - input: The input for which embeddings are to be generated. - - encoding_format: Optional[str] The format to return the embeddings in. Can be either `float` or `base64`. - For OpenAI-compatible embedding routing (`openai`, custom base URL, etc.), when omitted LiteLLM resolves: - explicit argument → mapped optional params (e.g. proxy `litellm_params`) → environment variable - `LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT` → default `"float"`. + - encoding_format: Optional[str] The format to return the embeddings in. Can be either `float` or `base64` - dimensions: The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models. - timeout: The timeout value for the API call, default 10 mins - litellm_call_id: The call ID for litellm logging. @@ -4923,12 +4920,14 @@ def embedding( # noqa: PLR0915 if headers is not None and headers != {}: optional_params["extra_headers"] = headers - optional_params["encoding_format"] = ( - encoding_format - or optional_params.get("encoding_format") - or get_secret_str("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT") - or "float" - ) + if encoding_format is not None: + optional_params["encoding_format"] = encoding_format + else: + optional_params["encoding_format"] = ( + optional_params.get("encoding_format") + or get_secret_str("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT") + or "float" + ) api_version = None diff --git a/tests/test_litellm/test_openai_embedding_encoding_format_default.py b/tests/test_litellm/test_openai_embedding_encoding_format_default.py index dfeb997564..6f34963f61 100644 --- a/tests/test_litellm/test_openai_embedding_encoding_format_default.py +++ b/tests/test_litellm/test_openai_embedding_encoding_format_default.py @@ -48,3 +48,39 @@ def test_openai_embedding_encoding_format_default( mock_client_instance.embeddings.with_raw_response.create.call_args[1] ) assert call_kwargs["encoding_format"] == expected + + +def test_openai_embedding_encoding_format_explicit_overrides_env(monkeypatch): + """Request `encoding_format` wins over LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT.""" + monkeypatch.setenv("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT", "float") + + mock_response = MagicMock() + mock_response.parse.return_value = MagicMock( + model_dump=lambda: { + "data": [{"embedding": [0.1, 0.2, 0.3], "index": 0}], + "model": "text-embedding-ada-002", + "object": "list", + "usage": {"prompt_tokens": 1, "total_tokens": 1}, + } + ) + mock_response.headers = {} + + with patch( + "litellm.llms.openai.openai.OpenAIChatCompletion._get_openai_client" + ) as mock_get_client: + mock_client_instance = MagicMock() + mock_get_client.return_value = mock_client_instance + mock_client_instance.embeddings.with_raw_response.create.return_value = ( + mock_response + ) + + embedding( + model="text-embedding-ada-002", + input="Hello world", + encoding_format="base64", + ) + + call_kwargs = ( + mock_client_instance.embeddings.with_raw_response.create.call_args[1] + ) + assert call_kwargs["encoding_format"] == "base64" From df41973110b44667af1d8540f0c7b851c53b9bf4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 14:51:19 +0000 Subject: [PATCH 5/6] Remove dead embedding encoding fallback --- litellm/main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/litellm/main.py b/litellm/main.py index 6d5f9f9b05..979dbe19d9 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4924,8 +4924,7 @@ def embedding( # noqa: PLR0915 optional_params["encoding_format"] = encoding_format else: optional_params["encoding_format"] = ( - optional_params.get("encoding_format") - or get_secret_str("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT") + get_secret_str("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT") or "float" ) From 4523f6af3d41ddb2d8a58ed9752dd8eadb78ea65 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 1 May 2026 23:03:13 +0530 Subject: [PATCH 6/6] fix(embeddings): allow omitting encoding_format via env sentinel none Greptile: LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT=none (case-insensitive) strips encoding_format so OpenAI-compatible backends can use provider defaults. Preserves optional_params passthrough when env is unset. Co-authored-by: Cursor --- litellm/main.py | 15 ++++++-- ...penai_embedding_encoding_format_default.py | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/litellm/main.py b/litellm/main.py index 979dbe19d9..0553cf9d42 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4923,10 +4923,17 @@ def embedding( # noqa: PLR0915 if encoding_format is not None: optional_params["encoding_format"] = encoding_format else: - optional_params["encoding_format"] = ( - get_secret_str("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT") - or "float" - ) + env_fmt = get_secret_str("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT") + if env_fmt is not None and env_fmt.strip().lower() == "none": + optional_params.pop("encoding_format", None) + else: + _default_fmt = ( + optional_params.get("encoding_format") or env_fmt or "float" + ) + if _default_fmt.strip().lower() == "none": + optional_params.pop("encoding_format", None) + else: + optional_params["encoding_format"] = _default_fmt api_version = None diff --git a/tests/test_litellm/test_openai_embedding_encoding_format_default.py b/tests/test_litellm/test_openai_embedding_encoding_format_default.py index 6f34963f61..94e4e3c81e 100644 --- a/tests/test_litellm/test_openai_embedding_encoding_format_default.py +++ b/tests/test_litellm/test_openai_embedding_encoding_format_default.py @@ -50,6 +50,44 @@ def test_openai_embedding_encoding_format_default( assert call_kwargs["encoding_format"] == expected +@pytest.mark.parametrize("env_none", ["none", "NONE", " none "]) +def test_openai_embedding_encoding_format_env_none_omits_param( + monkeypatch, env_none +): + """LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT=none omits encoding_format (provider default).""" + monkeypatch.setenv("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT", env_none) + + mock_response = MagicMock() + mock_response.parse.return_value = MagicMock( + model_dump=lambda: { + "data": [{"embedding": [0.1, 0.2, 0.3], "index": 0}], + "model": "text-embedding-ada-002", + "object": "list", + "usage": {"prompt_tokens": 1, "total_tokens": 1}, + } + ) + mock_response.headers = {} + + with patch( + "litellm.llms.openai.openai.OpenAIChatCompletion._get_openai_client" + ) as mock_get_client: + mock_client_instance = MagicMock() + mock_get_client.return_value = mock_client_instance + mock_client_instance.embeddings.with_raw_response.create.return_value = ( + mock_response + ) + + embedding( + model="text-embedding-ada-002", + input="Hello world", + ) + + call_kwargs = ( + mock_client_instance.embeddings.with_raw_response.create.call_args[1] + ) + assert "encoding_format" not in call_kwargs + + def test_openai_embedding_encoding_format_explicit_overrides_env(monkeypatch): """Request `encoding_format` wins over LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT.""" monkeypatch.setenv("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT", "float")