Address reviewer feedback for Avian provider

This commit is contained in:
Kyle D 2026-05-14 02:33:16 +01:00
parent 8d7c735f13
commit 85ce757860
3 changed files with 127 additions and 0 deletions

View file

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

View file

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

View file

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