From bdada02d47bf29976e594480152c7e9ef86828bf Mon Sep 17 00:00:00 2001 From: Srivatsa03 Date: Thu, 20 Aug 2026 19:37:32 -0500 Subject: [PATCH] fix(proxy): restore model pricing in /v2/model/info after a request A deployment that has served a request carries its cost keys as explicit None, since Deployment normalises model_info through ModelInfo and materialises every unset field. The enrichment pass backfilled only keys that were absent, so a None never healed and the Costs column went blank for exactly the models being used while untouched models kept their prices. Cost tracking was never affected, only what this endpoint reported. Treat None as unset when backfilling Fixes #37637 --- litellm/proxy/proxy_server.py | 2 +- .../proxy_server/test_routes_model_info.py | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 4b97cade7f0..09cf2fb720a 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -12475,7 +12475,7 @@ def _enrich_model_info_with_litellm_data( except Exception: litellm_model_info = {} for k, v in litellm_model_info.items(): - if k not in model_info: + if model_info.get(k) is None: model_info[k] = v model["model_info"] = model_info # don't return the api key / vertex credentials diff --git a/tests/test_litellm/proxy/proxy_server/test_routes_model_info.py b/tests/test_litellm/proxy/proxy_server/test_routes_model_info.py index b0e8a85d3fa..f60ca7ca1fd 100644 --- a/tests/test_litellm/proxy/proxy_server/test_routes_model_info.py +++ b/tests/test_litellm/proxy/proxy_server/test_routes_model_info.py @@ -69,6 +69,31 @@ def test_v2_model_info_in_openapi_schema(): assert "get" in schema["paths"]["/v2/model/info"] +def test_v2_model_info_enrichment_heals_none_valued_cost_keys(): + """A deployment that has served a request carries its cost keys as explicit None, because + Deployment materialises every unset ModelInfo field. Backfilling only absent keys left those + None values unhealed, so this route reported no pricing for exactly the models in use.""" + from litellm.proxy.proxy_server import _enrich_model_info_with_litellm_data + + base = { + "model_name": "claude-haiku-4-5", + "litellm_params": {"model": "anthropic/claude-haiku-4-5-20251001"}, + } + + absent = _enrich_model_info_with_litellm_data(model={**base, "model_info": {"id": "abc"}}) + expected = absent["model_info"]["input_cost_per_token"] + assert expected is not None + + served = _enrich_model_info_with_litellm_data( + model={ + **base, + "model_info": {"id": "abc", "input_cost_per_token": None, "output_cost_per_token": None}, + } + ) + assert served["model_info"]["input_cost_per_token"] == expected + assert served["model_info"]["output_cost_per_token"] is not None + + # --------------------------------------------------------------------------- # GET /v1/model/info, GET /model/info # ---------------------------------------------------------------------------