From 500e267b3f7d8fc5d0444ca5e9288d0fb6779bc8 Mon Sep 17 00:00:00 2001 From: Ramzi Alashmali <186906047+RAMZI0TO99@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:13:48 +0300 Subject: [PATCH] fix(openai): honor zero retries for embeddings --- litellm/llms/openai/openai.py | 8 ++-- tests/unit/llms/openai/test_openai.py | 59 +++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/litellm/llms/openai/openai.py b/litellm/llms/openai/openai.py index 2461fb4e1af..c9faff270e0 100644 --- a/litellm/llms/openai/openai.py +++ b/litellm/llms/openai/openai.py @@ -1332,8 +1332,8 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): super().embedding() try: data: Final = {"model": model, "input": input, **optional_params} - max_retries = max_retries or litellm.DEFAULT_MAX_RETRIES - if not isinstance(max_retries, int): + effective_max_retries: Final = litellm.DEFAULT_MAX_RETRIES if max_retries is None else max_retries + if not isinstance(effective_max_retries, int): raise OpenAIError(status_code=422, message="max retries must be an int") ## LOGGING logging_obj.pre_call( @@ -1352,7 +1352,7 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): api_key=api_key, timeout=timeout, client=client, - max_retries=max_retries, + max_retries=effective_max_retries, shared_session=shared_session, litellm_params=litellm_params, ) @@ -1362,7 +1362,7 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): api_key=api_key, api_base=api_base, timeout=timeout, - max_retries=max_retries, + max_retries=effective_max_retries, client=client, litellm_params=litellm_params, ) diff --git a/tests/unit/llms/openai/test_openai.py b/tests/unit/llms/openai/test_openai.py index 2be691fa65b..b4badf6e5bc 100644 --- a/tests/unit/llms/openai/test_openai.py +++ b/tests/unit/llms/openai/test_openai.py @@ -350,3 +350,62 @@ async def test_async_audio_speech_records_provider_response_headers(): ) _assert_provider_headers_recorded(response) + + +def _embedding_transport() -> httpx.MockTransport: + return httpx.MockTransport( + lambda request: httpx.Response( + 200, + json={ + "object": "list", + "model": "embedding-test", + "data": [{"object": "embedding", "index": 0, "embedding": [0.1, 0.2]}], + "usage": {"prompt_tokens": 1, "total_tokens": 1}, + }, + ) + ) + + +@pytest.mark.parametrize("max_retries,expected_retries", [(0, 0), (3, 3), (None, litellm.DEFAULT_MAX_RETRIES)]) +def test_embedding_honors_explicit_retry_limit(max_retries: int | None, expected_retries: int) -> None: + with OpenAI( + api_key="transport-only", + base_url="https://embedding.example/v1", + max_retries=7, + http_client=httpx.Client(transport=_embedding_transport()), + ) as client: + response: Final = litellm.embedding( + model="openai/embedding-test", + input=["retry control"], + encoding_format="float", + client=client, + max_retries=max_retries, + api_key="transport-only", + api_base=str(client.base_url), + ) + + assert client.max_retries == expected_retries + assert response.data == [{"object": "embedding", "index": 0, "embedding": [0.1, 0.2]}] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("max_retries,expected_retries", [(0, 0), (3, 3), (None, litellm.DEFAULT_MAX_RETRIES)]) +async def test_aembedding_honors_explicit_retry_limit(max_retries: int | None, expected_retries: int) -> None: + async with AsyncOpenAI( + api_key="transport-only", + base_url="https://embedding.example/v1", + max_retries=7, + http_client=httpx.AsyncClient(transport=_embedding_transport()), + ) as client: + response: Final = await litellm.aembedding( + model="openai/embedding-test", + input=["retry control"], + encoding_format="float", + client=client, + max_retries=max_retries, + api_key="transport-only", + api_base=str(client.base_url), + ) + + assert client.max_retries == expected_retries + assert response.data == [{"object": "embedding", "index": 0, "embedding": [0.1, 0.2]}]