mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
changes after ai review
This commit is contained in:
parent
3c550d2708
commit
ae1ad3b089
4 changed files with 179 additions and 22 deletions
|
|
@ -9428,12 +9428,17 @@ class Router:
|
|||
|
||||
Allows all cache calls to be made async => 10x perf impact (8rps -> 100 rps).
|
||||
"""
|
||||
# Support per-request routing_strategy override from key/team config
|
||||
routing_strategy_to_use = (
|
||||
request_kwargs.pop("routing_strategy", None) or self.routing_strategy
|
||||
)
|
||||
|
||||
if (
|
||||
self.routing_strategy != "usage-based-routing-v2"
|
||||
and self.routing_strategy != "simple-shuffle"
|
||||
and self.routing_strategy != "cost-based-routing"
|
||||
and self.routing_strategy != "latency-based-routing"
|
||||
and self.routing_strategy != "least-busy"
|
||||
routing_strategy_to_use != "usage-based-routing-v2"
|
||||
and routing_strategy_to_use != "simple-shuffle"
|
||||
and routing_strategy_to_use != "cost-based-routing"
|
||||
and routing_strategy_to_use != "latency-based-routing"
|
||||
and routing_strategy_to_use != "least-busy"
|
||||
): # prevent regressions for other routing strategies, that don't have async get available deployments implemented.
|
||||
return self.get_available_deployment(
|
||||
model=model,
|
||||
|
|
@ -9481,7 +9486,7 @@ class Router:
|
|||
|
||||
start_time = time.time()
|
||||
if (
|
||||
self.routing_strategy == "usage-based-routing-v2"
|
||||
routing_strategy_to_use == "usage-based-routing-v2"
|
||||
and self.lowesttpm_logger_v2 is not None
|
||||
):
|
||||
deployment = (
|
||||
|
|
@ -9493,7 +9498,7 @@ class Router:
|
|||
)
|
||||
)
|
||||
elif (
|
||||
self.routing_strategy == "cost-based-routing"
|
||||
routing_strategy_to_use == "cost-based-routing"
|
||||
and self.lowestcost_logger is not None
|
||||
):
|
||||
deployment = (
|
||||
|
|
@ -9505,7 +9510,7 @@ class Router:
|
|||
)
|
||||
)
|
||||
elif (
|
||||
self.routing_strategy == "latency-based-routing"
|
||||
routing_strategy_to_use == "latency-based-routing"
|
||||
and self.lowestlatency_logger is not None
|
||||
):
|
||||
deployment = (
|
||||
|
|
@ -9517,14 +9522,14 @@ class Router:
|
|||
request_kwargs=request_kwargs,
|
||||
)
|
||||
)
|
||||
elif self.routing_strategy == "simple-shuffle":
|
||||
elif routing_strategy_to_use == "simple-shuffle":
|
||||
return simple_shuffle(
|
||||
llm_router_instance=self,
|
||||
healthy_deployments=healthy_deployments,
|
||||
model=model,
|
||||
)
|
||||
elif (
|
||||
self.routing_strategy == "least-busy"
|
||||
routing_strategy_to_use == "least-busy"
|
||||
and self.leastbusy_logger is not None
|
||||
):
|
||||
deployment = (
|
||||
|
|
@ -9642,8 +9647,14 @@ class Router:
|
|||
|
||||
# 5. Apply load balancing strategy
|
||||
start_time = time.perf_counter()
|
||||
# For now, pass-through endpoints don't support routing_strategy override
|
||||
# TODO: Add routing_strategy override support for pass-through endpoints
|
||||
routing_strategy_to_use = (
|
||||
self.routing_strategy
|
||||
) # Pass-through uses global routing strategy
|
||||
|
||||
if (
|
||||
self.routing_strategy == "usage-based-routing-v2"
|
||||
routing_strategy_to_use == "usage-based-routing-v2"
|
||||
and self.lowesttpm_logger_v2 is not None
|
||||
):
|
||||
deployment = (
|
||||
|
|
@ -9655,7 +9666,7 @@ class Router:
|
|||
)
|
||||
)
|
||||
elif (
|
||||
self.routing_strategy == "latency-based-routing"
|
||||
routing_strategy_to_use == "latency-based-routing"
|
||||
and self.lowestlatency_logger is not None
|
||||
):
|
||||
deployment = (
|
||||
|
|
@ -9667,14 +9678,14 @@ class Router:
|
|||
request_kwargs=request_kwargs,
|
||||
)
|
||||
)
|
||||
elif self.routing_strategy == "simple-shuffle":
|
||||
elif routing_strategy_to_use == "simple-shuffle":
|
||||
return simple_shuffle(
|
||||
llm_router_instance=self,
|
||||
healthy_deployments=pass_through_deployments,
|
||||
model=model,
|
||||
)
|
||||
elif (
|
||||
self.routing_strategy == "least-busy"
|
||||
routing_strategy_to_use == "least-busy"
|
||||
and self.leastbusy_logger is not None
|
||||
):
|
||||
deployment = (
|
||||
|
|
@ -9683,8 +9694,6 @@ class Router:
|
|||
healthy_deployments=pass_through_deployments, # type: ignore
|
||||
)
|
||||
)
|
||||
else:
|
||||
deployment = None
|
||||
|
||||
if deployment is None:
|
||||
exception = await async_raise_no_deployment_exception(
|
||||
|
|
@ -9791,6 +9800,11 @@ class Router:
|
|||
# users need to explicitly call a specific deployment, by setting `specific_deployment = True` as completion()/embedding() kwarg
|
||||
# When this was no explicit we had several issues with fallbacks timing out
|
||||
|
||||
# Support per-request routing_strategy override from key/team config
|
||||
routing_strategy_to_use = (request_kwargs or {}).pop(
|
||||
"routing_strategy", None
|
||||
) or self.routing_strategy
|
||||
|
||||
model, healthy_deployments = self._common_checks_available_deployment(
|
||||
model=model,
|
||||
messages=messages,
|
||||
|
|
@ -9859,11 +9873,14 @@ class Router:
|
|||
cooldown_list=_cooldown_list,
|
||||
)
|
||||
|
||||
if self.routing_strategy == "least-busy" and self.leastbusy_logger is not None:
|
||||
if (
|
||||
routing_strategy_to_use == "least-busy"
|
||||
and self.leastbusy_logger is not None
|
||||
):
|
||||
deployment = self.leastbusy_logger.get_available_deployments(
|
||||
model_group=model, healthy_deployments=healthy_deployments # type: ignore
|
||||
)
|
||||
elif self.routing_strategy == "simple-shuffle":
|
||||
elif routing_strategy_to_use == "simple-shuffle":
|
||||
# if users pass rpm or tpm, we do a random weighted pick - based on rpm/tpm
|
||||
############## Check 'weight' param set for weighted pick #################
|
||||
return simple_shuffle(
|
||||
|
|
@ -9881,7 +9898,7 @@ class Router:
|
|||
request_kwargs=request_kwargs,
|
||||
)
|
||||
elif (
|
||||
self.routing_strategy == "usage-based-routing"
|
||||
routing_strategy_to_use == "usage-based-routing"
|
||||
and self.lowesttpm_logger is not None
|
||||
):
|
||||
deployment = self.lowesttpm_logger.get_available_deployments(
|
||||
|
|
@ -9891,7 +9908,7 @@ class Router:
|
|||
input=input,
|
||||
)
|
||||
elif (
|
||||
self.routing_strategy == "usage-based-routing-v2"
|
||||
routing_strategy_to_use == "usage-based-routing-v2"
|
||||
and self.lowesttpm_logger_v2 is not None
|
||||
):
|
||||
deployment = self.lowesttpm_logger_v2.get_available_deployments(
|
||||
|
|
@ -9900,6 +9917,16 @@ class Router:
|
|||
messages=messages,
|
||||
input=input,
|
||||
)
|
||||
elif (
|
||||
routing_strategy_to_use == "cost-based-routing"
|
||||
and self.lowestcost_logger is not None
|
||||
):
|
||||
deployment = self.lowestcost_logger.get_available_deployments(
|
||||
model_group=model,
|
||||
healthy_deployments=healthy_deployments, # type: ignore
|
||||
messages=messages,
|
||||
input=input,
|
||||
)
|
||||
else:
|
||||
deployment = None
|
||||
|
||||
|
|
|
|||
132
litellm/tests/test_litellm/test_routing_strategy_override.py
Normal file
132
litellm/tests/test_litellm/test_routing_strategy_override.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
"""
|
||||
Test routing_strategy override functionality for issue #21993
|
||||
"""
|
||||
import pytest
|
||||
|
||||
|
||||
def test_routing_strategy_per_request_override():
|
||||
"""
|
||||
Test that routing_strategy can be overridden per-request.
|
||||
|
||||
This tests the core fix: routing_strategy should be extracted from request_kwargs
|
||||
and used instead of self.routing_strategy for deployment selection.
|
||||
"""
|
||||
# Test the logic of extracting routing_strategy from kwargs
|
||||
DEFAULT_ROUTING = "least-busy"
|
||||
|
||||
# Test 1: When routing_strategy is provided in kwargs
|
||||
request_kwargs = {"routing_strategy": "cost-based-routing", "messages": ["test"]}
|
||||
routing_strategy_to_use = request_kwargs.pop("routing_strategy", None) or DEFAULT_ROUTING
|
||||
|
||||
assert routing_strategy_to_use == "cost-based-routing", "Should use override routing_strategy"
|
||||
assert "routing_strategy" not in request_kwargs, "routing_strategy should be popped from kwargs (not forwarded to LLM API)"
|
||||
|
||||
# Test 2: When routing_strategy is NOT provided
|
||||
request_kwargs = {"messages": ["test"]}
|
||||
routing_strategy_to_use = request_kwargs.pop("routing_strategy", None) or DEFAULT_ROUTING
|
||||
|
||||
assert routing_strategy_to_use == DEFAULT_ROUTING, "Should use default routing_strategy when no override"
|
||||
assert "routing_strategy" not in request_kwargs, "routing_strategy should be popped from kwargs"
|
||||
|
||||
|
||||
def test_routing_strategy_all_strategies():
|
||||
"""
|
||||
Test that all routing strategies can be overridden.
|
||||
"""
|
||||
strategies = [
|
||||
"simple-shuffle",
|
||||
"least-busy",
|
||||
"usage-based-routing",
|
||||
"latency-based-routing",
|
||||
"cost-based-routing",
|
||||
"usage-based-routing-v2",
|
||||
]
|
||||
|
||||
DEFAULT_ROUTING = "simple-shuffle"
|
||||
|
||||
for strategy in strategies:
|
||||
request_kwargs = {"routing_strategy": strategy}
|
||||
routing_strategy_to_use = request_kwargs.pop("routing_strategy", None) or DEFAULT_ROUTING
|
||||
|
||||
assert routing_strategy_to_use == strategy, f"Expected {strategy}, got {routing_strategy_to_use}"
|
||||
assert "routing_strategy" not in request_kwargs, f"routing_strategy should be popped (not forwarded to API) for {strategy}"
|
||||
|
||||
|
||||
def test_routing_strategy_none_override():
|
||||
"""
|
||||
Test that explicit None override uses default routing_strategy.
|
||||
"""
|
||||
DEFAULT_ROUTING = "least-busy"
|
||||
|
||||
# Test with explicit None
|
||||
request_kwargs = {"routing_strategy": None}
|
||||
routing_strategy_to_use = request_kwargs.pop("routing_strategy", None) or DEFAULT_ROUTING
|
||||
|
||||
assert routing_strategy_to_use == DEFAULT_ROUTING, "None override should use default"
|
||||
|
||||
|
||||
def test_routing_strategy_empty_string_override():
|
||||
"""
|
||||
Test that empty string override uses default routing_strategy.
|
||||
"""
|
||||
DEFAULT_ROUTING = "simple-shuffle"
|
||||
|
||||
# Test with empty string
|
||||
request_kwargs = {"routing_strategy": ""}
|
||||
routing_strategy_to_use = request_kwargs.pop("routing_strategy", None) or DEFAULT_ROUTING
|
||||
|
||||
assert routing_strategy_to_use == DEFAULT_ROUTING, "Empty string override should use default"
|
||||
|
||||
|
||||
def test_routing_strategy_doesnt_affect_other_kwargs():
|
||||
"""
|
||||
Test that popping routing_strategy doesn't affect other kwargs.
|
||||
"""
|
||||
DEFAULT_ROUTING = "least-busy"
|
||||
|
||||
request_kwargs = {
|
||||
"routing_strategy": "cost-based-routing",
|
||||
"messages": ["test message"],
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 100,
|
||||
}
|
||||
|
||||
routing_strategy_to_use = request_kwargs.pop("routing_strategy", None) or DEFAULT_ROUTING
|
||||
|
||||
assert routing_strategy_to_use == "cost-based-routing", "Should use override routing_strategy"
|
||||
assert "routing_strategy" not in request_kwargs, "routing_strategy should be popped"
|
||||
assert request_kwargs["messages"] == ["test message"], "Other kwargs should be preserved"
|
||||
assert request_kwargs["temperature"] == 0.7, "Temperature should be preserved"
|
||||
assert request_kwargs["max_tokens"] == 100, "Max tokens should be preserved"
|
||||
|
||||
|
||||
def test_routing_strategy_prevents_api_forwarding():
|
||||
"""
|
||||
Test that routing_strategy is NOT forwarded to LLM API.
|
||||
|
||||
This addresses P1 concern from reviewer: "routing_strategy gets forwarded to litellm.acompletion and then to the underlying LLM API"
|
||||
"""
|
||||
DEFAULT_ROUTING = "simple-shuffle"
|
||||
|
||||
# Test that routing_strategy is properly popped (removed) from kwargs
|
||||
request_kwargs = {
|
||||
"routing_strategy": "cost-based-routing",
|
||||
"messages": ["test"],
|
||||
"temperature": 0.7,
|
||||
}
|
||||
|
||||
routing_strategy_to_use = request_kwargs.pop("routing_strategy", None) or DEFAULT_ROUTING
|
||||
|
||||
# routing_strategy should be gone from kwargs
|
||||
assert "routing_strategy" not in request_kwargs, "routing_strategy should be popped from kwargs (not forwarded to API)"
|
||||
|
||||
# Other kwargs should be preserved
|
||||
assert "messages" in request_kwargs, "Messages should be preserved"
|
||||
assert "temperature" in request_kwargs, "Temperature should be preserved"
|
||||
|
||||
# Verify routing was used for the request
|
||||
assert routing_strategy_to_use == "cost-based-routing", "Should use override routing_strategy"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v", "-s"])
|
||||
|
|
@ -607,7 +607,6 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
|||
value !== null &&
|
||||
value !== undefined &&
|
||||
value !== "" &&
|
||||
value !== false &&
|
||||
!(Array.isArray(value) && value.length === 0);
|
||||
|
||||
const hasNewValues = Object.values(currentRouterSettings.router_settings).some(isMeaningfulValue);
|
||||
|
|
|
|||
|
|
@ -292,7 +292,6 @@ export function KeyEditView({
|
|||
value !== null &&
|
||||
value !== undefined &&
|
||||
value !== "" &&
|
||||
value !== false &&
|
||||
!(Array.isArray(value) && value.length === 0);
|
||||
|
||||
const hasNewValues = Object.values(currentRouterSettings.router_settings).some(isMeaningfulValue);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue