mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
fix(rate_limiter): skip non-Latin-1 x-litellm-priority header on /v1/messages (#40636)
A team or key priority that is not Latin-1 encodable (for example CJK text) was attached as a response header by the dynamic rate limiter v3 post-call hook, and Starlette then raised UnicodeEncodeError while writing headers, turning a successful /v1/messages call into HTTP 500. The header is now omitted for such values while x-litellm-rate-limiter-version and the v3 rate limit headers are still attached. Co-authored-by: yucheng <yucheng@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
c7a41c35d5
commit
985ac6b6a5
2 changed files with 38 additions and 1 deletions
|
|
@ -59,6 +59,10 @@ def _get_priority_settings() -> "PriorityReservationSettings":
|
|||
return settings
|
||||
|
||||
|
||||
def _is_latin1_encodable(value: object) -> bool:
|
||||
return all(ord(char) < 256 for char in str(value))
|
||||
|
||||
|
||||
class _PROXY_DynamicRateLimitHandlerV3(CustomLogger):
|
||||
"""
|
||||
Saturation-aware priority-based rate limiter using v3 infrastructure.
|
||||
|
|
@ -666,7 +670,13 @@ class _PROXY_DynamicRateLimitHandlerV3(CustomLogger):
|
|||
if response_has_hidden_params(response):
|
||||
priority: Final = self._get_priority_from_user_api_key_dict(user_api_key_dict=user_api_key_dict)
|
||||
additional_headers: Final = ensure_response_additional_headers(response)
|
||||
additional_headers["x-litellm-priority"] = priority or "default"
|
||||
priority_header: Final = priority or "default"
|
||||
if _is_latin1_encodable(priority_header):
|
||||
additional_headers["x-litellm-priority"] = priority_header
|
||||
else:
|
||||
verbose_proxy_logger.debug(
|
||||
"Skipping x-litellm-priority header: priority %r is not Latin-1 encodable", priority
|
||||
)
|
||||
additional_headers["x-litellm-rate-limiter-version"] = "v3"
|
||||
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -1918,3 +1918,30 @@ async def test_post_call_success_hook_leaves_raw_provider_dict_untouched():
|
|||
)
|
||||
|
||||
assert response == {"id": "msg_123", "type": "message", "role": "assistant", "content": []}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
("team_metadata", "expected_priority_header"),
|
||||
[
|
||||
({"priority": "优先"}, None),
|
||||
({"priority": "high"}, "high"),
|
||||
({}, "default"),
|
||||
],
|
||||
)
|
||||
async def test_post_call_success_hook_priority_header_is_always_http_encodable(team_metadata, expected_priority_header):
|
||||
from starlette.responses import Response
|
||||
|
||||
handler = DynamicRateLimitHandler(internal_usage_cache=DualCache())
|
||||
response = {"id": "msg_123", "type": "message", "role": "assistant", "content": [], "_hidden_params": {}}
|
||||
|
||||
await handler.async_post_call_success_hook(
|
||||
data={"model": "anthropic-haiku"},
|
||||
user_api_key_dict=UserAPIKeyAuth(team_id="team-1", team_metadata=team_metadata),
|
||||
response=response,
|
||||
)
|
||||
|
||||
additional_headers = response["_hidden_params"]["additional_headers"]
|
||||
http_response = Response(headers={key: str(value) for key, value in additional_headers.items()})
|
||||
assert http_response.headers.get("x-litellm-priority") == expected_priority_header
|
||||
assert http_response.headers["x-litellm-rate-limiter-version"] == "v3"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue