This commit is contained in:
xuan 2026-11-04 05:17:15 +00:00 • committed by GitHub
commit c3abb7fe07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 49 additions and 0 deletions

View file

@ -3569,6 +3569,8 @@ class PythonCore(DeprecatedRouterMembers):
else:
model_client = potential_model_client
self.total_calls[model_name] += 1
### DEPLOYMENT-SPECIFIC PRE-CALL CHECKS ### (e.g. update rpm pre-call. Raise error, if deployment over limit)
## only run if model group given, not model id
if model in self._core_model_names or not self._core_has_model_id(model):
@ -3603,6 +3605,8 @@ class PythonCore(DeprecatedRouterMembers):
):
response.fetch_sync_stream()
self.success_calls[model_name] += 1
# Wrap streaming responses so MidStreamFallbackError (raised
# during iteration) triggers the Router's fallback chain.
if isinstance(response, CustomStreamWrapper):
@ -3616,6 +3620,8 @@ class PythonCore(DeprecatedRouterMembers):
return response
except Exception as e:
verbose_router_logger.info("litellm.completion(model=%s)\x1b[31m Exception %s\x1b[0m", model_name, e)
if model_name is not None:
self.fail_calls[model_name] += 1
# Set per-deployment num_retries on exception for retry logic
if deployment is not None:
self._set_deployment_num_retries_on_exception(e, deployment)

View file

@ -27738,6 +27738,7 @@ class TestRouterIndexManagement:
f"{'=' * 70}\n"
)
def test_model_names_is_set(self):
"""Verify that model_names uses a set for O(1) lookups, not a list (O(n))"""
router = Router(model_list=[])
@ -32396,3 +32397,45 @@ def test_image_routing_inspects_native_request_content_without_translation(reque
original: Final = copy.deepcopy(request_kwargs)
assert _candidate_ids(router, request_kwargs=request_kwargs) == ["vision"]
assert request_kwargs == original
def test_sync_completion_tracks_router_metrics():
"""
Regression: sync router.completion() must update router.total_calls /
router.success_calls / router.fail_calls, same as router.acompletion() does.
Before the fix, _completion() (the sync entry point that completion()
reaches through function_with_fallbacks -> async_function_with_retries)
never touched any of the three counters, so the router metrics stayed at
zero for every sync call.
"""
router = Router(
model_list=[
{
"model_name": "gpt-4o",
"litellm_params": {"model": "openai/gpt-4o", "num_retries": 0},
}
],
num_retries=0,
)
## success: total/success track the deployment model
router.completion(
model="gpt-4o",
messages=[{"role": "user", "content": "hi"}],
mock_response="Hello",
)
assert router.total_calls["openai/gpt-4o"] == 1
assert router.success_calls["openai/gpt-4o"] == 1
assert router.fail_calls.get("openai/gpt-4o", 0) == 0
## failure: fail tracks the deployment model
with pytest.raises(Exception):
router.completion(
model="gpt-4o",
messages=[{"role": "user", "content": "hi"}],
mock_response="litellm.InternalServerError",
)
assert router.fail_calls["openai/gpt-4o"] == 1
assert router.total_calls["openai/gpt-4o"] == 2
assert router.success_calls["openai/gpt-4o"] == 1