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
This commit is contained in:
Yash Raj Pandey 2026-08-27 11:25:42 -04:00
parent 03a79b6a4d
commit 6fa72652fc

View file

@ -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