This commit is contained in:
Ramzi Alashmali 2026-11-06 15:53:11 -05:00 • committed by GitHub
commit 98bead3985
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 123 additions and 5 deletions

View file

@ -1408,8 +1408,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(
@ -1428,7 +1428,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,
)
@ -1438,7 +1438,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,
)

View file

@ -8,7 +8,7 @@ import os
from collections.abc import Awaitable, Callable, Mapping, Sequence
from types import ModuleType
from typing import Final, Literal, Protocol
from unittest.mock import MagicMock, Mock, patch
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import httpx
import pytest
@ -1773,6 +1773,124 @@ 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]}]
def _unavailable_embedding_response(request: httpx.Request) -> httpx.Response:
return httpx.Response(503, json={"error": {"message": "temporarily unavailable"}}, request=request)
@pytest.mark.parametrize("max_retries,expected_retries", [(0, 0), (3, 3), (None, litellm.DEFAULT_MAX_RETRIES)])
def test_embedding_limits_requests_after_retryable_failure(max_retries: int | None, expected_retries: int) -> None:
requests: Final = Mock(side_effect=_unavailable_embedding_response)
with (
patch("openai._base_client.time.sleep"),
OpenAI(
api_key="transport-only",
base_url="https://embedding.example/v1",
max_retries=7,
http_client=httpx.Client(transport=httpx.MockTransport(requests)),
) as client,
pytest.raises(litellm.ServiceUnavailableError) as error,
):
litellm.embedding(
model="openai/embedding-test",
input=["retry control"],
client=client,
max_retries=max_retries,
num_retries=0,
api_key="transport-only",
api_base=str(client.base_url),
)
assert error.value.status_code == 503
assert requests.call_count == expected_retries + 1
@pytest.mark.asyncio
@pytest.mark.parametrize("max_retries,expected_retries", [(0, 0), (3, 3), (None, litellm.DEFAULT_MAX_RETRIES)])
async def test_aembedding_limits_requests_after_retryable_failure(
max_retries: int | None, expected_retries: int
) -> None:
requests: Final = Mock(side_effect=_unavailable_embedding_response)
with patch("openai._base_client.anyio.sleep", new_callable=AsyncMock):
async with AsyncOpenAI(
api_key="transport-only",
base_url="https://embedding.example/v1",
max_retries=7,
http_client=httpx.AsyncClient(transport=httpx.MockTransport(requests)),
) as client:
with pytest.raises(litellm.ServiceUnavailableError) as error:
await litellm.aembedding(
model="openai/embedding-test",
input=["retry control"],
client=client,
max_retries=max_retries,
num_retries=0,
api_key="transport-only",
api_base=str(client.base_url),
)
assert error.value.status_code == 503
assert requests.call_count == expected_retries + 1
@pytest.fixture()
def _vcr_outcome_gate(request, vcr):
install_live_call_probe(request, vcr)