From 6fa72652fc161016ff99f5f1727d9197e12fd311 Mon Sep 17 00:00:00 2001 From: Yash Raj Pandey Date: Thu, 27 Aug 2026 11:25:42 -0400 Subject: [PATCH] test(router): pin numeric cost sorting in cost-based routing A config can supply input_cost_per_token and output_cost_per_token as strings. Before the float cast, + concatenated them and sorted ordered the cost strings lexically, so the router silently picked the costlier deployment. A missing output cost mixed a string with a float and raised TypeError Both tests fail when the float cast is reverted --- .../router_strategy/test_lowest_cost.py | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/test_litellm/router_strategy/test_lowest_cost.py diff --git a/tests/test_litellm/router_strategy/test_lowest_cost.py b/tests/test_litellm/router_strategy/test_lowest_cost.py new file mode 100644 index 00000000000..0b238eef3dd --- /dev/null +++ b/tests/test_litellm/router_strategy/test_lowest_cost.py @@ -0,0 +1,56 @@ +import pytest + +from litellm.caching.caching import DualCache +from litellm.router_strategy.lowest_cost import LowestCostLoggingHandler + + +@pytest.mark.asyncio +async def test_string_costs_are_sorted_numerically(): + handler = LowestCostLoggingHandler(router_cache=DualCache()) + deployments = [ + { + "model_name": "test-model-group", + "litellm_params": { + "model": "test/unknown-model", + "input_cost_per_token": "0.000009", + "output_cost_per_token": "0.000009", + }, + "model_info": {"id": "cheap"}, + }, + { + "model_name": "test-model-group", + "litellm_params": { + "model": "test/unknown-model", + "input_cost_per_token": "0.000008", + "output_cost_per_token": "0.000012", + }, + "model_info": {"id": "costly"}, + }, + ] + + selected = await handler.async_get_available_deployments( + model_group="test-model-group", healthy_deployments=deployments + ) + + assert selected["model_info"]["id"] == "cheap" + + +@pytest.mark.asyncio +async def test_string_input_cost_with_default_output_cost_does_not_raise(): + handler = LowestCostLoggingHandler(router_cache=DualCache()) + deployments = [ + { + "model_name": "test-model-group", + "litellm_params": { + "model": "test/unknown-model", + "input_cost_per_token": "0.000009", + }, + "model_info": {"id": "mixed"}, + } + ] + + selected = await handler.async_get_available_deployments( + model_group="test-model-group", healthy_deployments=deployments + ) + + assert selected is not None