mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
Address reviewer feedback for Avian provider
This commit is contained in:
parent
8d7c735f13
commit
85ce757860
3 changed files with 127 additions and 0 deletions
|
|
@ -4,6 +4,8 @@ Dynamic configuration class generator for JSON-based providers.
|
|||
|
||||
from typing import Any, Coroutine, List, Literal, Optional, Tuple, Union, overload
|
||||
|
||||
import httpx
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.litellm_core_utils.prompt_templates.common_utils import (
|
||||
handle_messages_with_content_list_to_str_conversion,
|
||||
|
|
@ -165,6 +167,61 @@ def create_config_class(provider: SimpleProviderConfig):
|
|||
|
||||
return optional_params
|
||||
|
||||
def should_retry_llm_api_inside_llm_translation_on_http_error(
|
||||
self, e: httpx.HTTPStatusError, litellm_params: dict
|
||||
) -> bool:
|
||||
"""
|
||||
Retry on 422 UnprocessableEntity when drop_params is enabled.
|
||||
|
||||
OpenAI-compatible providers may reject unknown parameters.
|
||||
When drop_params is set, strip the offending params and retry.
|
||||
"""
|
||||
import litellm as _litellm
|
||||
|
||||
should_drop = litellm_params.get("drop_params") or _litellm.drop_params
|
||||
if should_drop and e.response.status_code == 422:
|
||||
error_text = e.response.text
|
||||
if "Extra inputs are not permitted" in error_text:
|
||||
return True
|
||||
return super().should_retry_llm_api_inside_llm_translation_on_http_error(
|
||||
e=e, litellm_params=litellm_params
|
||||
)
|
||||
|
||||
@property
|
||||
def max_retry_on_unprocessable_entity_error(self) -> int:
|
||||
return 2
|
||||
|
||||
def transform_request_on_unprocessable_entity_error(
|
||||
self, e: httpx.HTTPStatusError, request_data: dict
|
||||
) -> dict:
|
||||
"""
|
||||
On 422, attempt to extract the offending field name from the error
|
||||
response and remove it from the request data before retrying.
|
||||
"""
|
||||
import json as _json
|
||||
|
||||
try:
|
||||
error_body = _json.loads(e.response.text)
|
||||
error_msg = str(error_body)
|
||||
except Exception:
|
||||
error_msg = e.response.text
|
||||
|
||||
if "Extra inputs are not permitted" in error_msg:
|
||||
# Try to extract field name from error message pattern like:
|
||||
# "field_name: Extra inputs are not permitted"
|
||||
for line in error_msg.split(","):
|
||||
line = line.strip()
|
||||
if "Extra inputs are not permitted" in line:
|
||||
field = line.split(":")[0].strip().strip("'\"{ ")
|
||||
if field and field in request_data:
|
||||
verbose_logger.debug(
|
||||
f"Removing unsupported param '{field}' from "
|
||||
f"request to {provider.slug} on retry"
|
||||
)
|
||||
request_data.pop(field, None)
|
||||
|
||||
return request_data
|
||||
|
||||
@property
|
||||
def custom_llm_provider(self) -> Optional[str]:
|
||||
return provider.slug
|
||||
|
|
|
|||
|
|
@ -3327,6 +3327,13 @@ class LlmProviders(str, Enum):
|
|||
NANOGPT = "nano-gpt"
|
||||
POE = "poe"
|
||||
CHUTES = "chutes"
|
||||
AVIAN = "avian"
|
||||
ABLITERATION = "abliteration"
|
||||
LLAMAGATE = "llamagate"
|
||||
GMI = "gmi"
|
||||
SARVAM = "sarvam"
|
||||
AIHUBMIX = "aihubmix"
|
||||
VENICEAI = "veniceai"
|
||||
XIAOMI_MIMO = "xiaomi_mimo"
|
||||
LITELLM_AGENT = "litellm_agent"
|
||||
CURSOR = "cursor"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ Unit tests for the Avian OpenAI-like provider.
|
|||
import os
|
||||
import sys
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../.."))
|
||||
)
|
||||
|
|
@ -48,3 +51,63 @@ def test_avian_complete_url_appends_endpoint():
|
|||
stream=False,
|
||||
)
|
||||
assert url == f"{AVIAN_BASE_URL}/chat/completions"
|
||||
|
||||
|
||||
def test_avian_in_provider_list():
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
assert LlmProviders.AVIAN.value == "avian"
|
||||
|
||||
|
||||
def test_avian_retry_on_422_with_drop_params():
|
||||
config = _get_config()
|
||||
mock_request = httpx.Request(method="POST", url=AVIAN_BASE_URL)
|
||||
mock_response = httpx.Response(
|
||||
status_code=422,
|
||||
text='{"detail": "max_completion_tokens: Extra inputs are not permitted"}',
|
||||
request=mock_request,
|
||||
)
|
||||
error = httpx.HTTPStatusError(
|
||||
message="422", request=mock_request, response=mock_response
|
||||
)
|
||||
assert config.should_retry_llm_api_inside_llm_translation_on_http_error(
|
||||
e=error, litellm_params={"drop_params": True}
|
||||
)
|
||||
|
||||
|
||||
def test_avian_no_retry_without_drop_params():
|
||||
config = _get_config()
|
||||
mock_request = httpx.Request(method="POST", url=AVIAN_BASE_URL)
|
||||
mock_response = httpx.Response(
|
||||
status_code=422,
|
||||
text='{"detail": "Extra inputs are not permitted"}',
|
||||
request=mock_request,
|
||||
)
|
||||
error = httpx.HTTPStatusError(
|
||||
message="422", request=mock_request, response=mock_response
|
||||
)
|
||||
assert not config.should_retry_llm_api_inside_llm_translation_on_http_error(
|
||||
e=error, litellm_params={}
|
||||
)
|
||||
|
||||
|
||||
def test_avian_max_retry_count():
|
||||
config = _get_config()
|
||||
assert config.max_retry_on_unprocessable_entity_error == 2
|
||||
|
||||
|
||||
@pytest.mark.flaky(retries=3, delay=1)
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("AVIAN_API_KEY"),
|
||||
reason="AVIAN_API_KEY not set",
|
||||
)
|
||||
def test_avian_chat_completion():
|
||||
"""Live API test — requires AVIAN_API_KEY."""
|
||||
import litellm
|
||||
|
||||
response = litellm.completion(
|
||||
model="avian/deepseek/deepseek-v3.2",
|
||||
messages=[{"role": "user", "content": "Say hello in one word."}],
|
||||
max_tokens=10,
|
||||
)
|
||||
assert response.choices[0].message.content is not None
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue