From 7817f92d1952f78cec8c17711032c0c8ef5cec50 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 3 Apr 2026 01:46:40 +0530 Subject: [PATCH] fix team routing --- litellm/proxy/health_check.py | 2 +- litellm/router.py | 11 ++++++++--- litellm/utils.py | 35 +++++++++++++++++++++++++++++++---- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/health_check.py b/litellm/proxy/health_check.py index bd2f4734fb2..5a98daec5f9 100644 --- a/litellm/proxy/health_check.py +++ b/litellm/proxy/health_check.py @@ -348,7 +348,7 @@ async def perform_health_check( source, cycle_id, ) - return [], [] + return [], [], {} cycle_start_time = time.monotonic() requested_model_count = len(model_list) diff --git a/litellm/router.py b/litellm/router.py index 26daf2b1e95..f390d2c70fc 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -5315,11 +5315,16 @@ class Router: e, (litellm.ContextWindowExceededError, litellm.ContentPolicyViolationError), ) - all_deployments = self._get_all_deployments(model_name=original_model_group) + _request_team_id: Optional[str] = ( + kwargs.get("metadata", {}) or {} + ).get("user_api_key_team_id") + all_deployments = self._get_all_deployments( + model_name=original_model_group, team_id=_request_team_id + ) _order_set: set = { - d.get("litellm_params", {}).get("order") + litellm.utils._get_deployment_order(d) for d in all_deployments - if d.get("litellm_params", {}).get("order") is not None + if litellm.utils._get_deployment_order(d) is not None } order_values: list = sorted(_order_set) if len(order_values) > 1 and not _skip_order_fallback: diff --git a/litellm/utils.py b/litellm/utils.py index 088ee07d630..c68a935c640 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -4866,12 +4866,39 @@ def calculate_max_parallel_requests( return None -def _get_order_filtered_deployments(healthy_deployments: List[Dict]) -> List: +def _get_deployment_order(deployment: Dict) -> Optional[int]: + """ + Returns the routing order for a deployment. + + Checks litellm_params first (static config), then model_info (dynamic/team + models added via API where order lives in model_info, not litellm_params). + """ + order = deployment.get("litellm_params", {}).get("order") + if order is None: + order = deployment.get("model_info", {}).get("order") + return order + + +def _get_order_filtered_deployments( + healthy_deployments: List[Dict], target_order: Optional[int] = None +) -> List: + if target_order is not None: + filtered = [ + d + for d in healthy_deployments + if _get_deployment_order(d) == target_order + ] + if filtered: + return filtered + # target_order doesn't match any deployment (e.g., external fallback model) — return all + return healthy_deployments + + # Default: pick min order group min_order = min( ( - deployment["litellm_params"]["order"] + _get_deployment_order(deployment) for deployment in healthy_deployments - if "order" in deployment["litellm_params"] + if _get_deployment_order(deployment) is not None ), default=None, ) @@ -4880,7 +4907,7 @@ def _get_order_filtered_deployments(healthy_deployments: List[Dict]) -> List: filtered_deployments = [ deployment for deployment in healthy_deployments - if deployment["litellm_params"].get("order") == min_order + if _get_deployment_order(deployment) == min_order ] return filtered_deployments