From d8df34876ebcdbdafbf58188494b1ebc1eef119c Mon Sep 17 00:00:00 2001 From: Joshua Valluru <326636767+joshua-berri@users.noreply.github.com> Date: Sat, 31 Oct 2026 16:08:23 -0700 Subject: [PATCH] fix(accounting): retain input pixel rates for image replay --- litellm/types/utils.py | 1 + litellm/utils.py | 1 + .../spend_tracking/test_cost_compiler.py | 24 ++++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/litellm/types/utils.py b/litellm/types/utils.py index a5e2bb1eb68..e47eaa57112 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -437,6 +437,7 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False): output_cost_per_token_above_512k_tokens: float | None # MiniMax-M3: prompts >512K priced at 2x output output_cost_per_character_above_128k_tokens: float | None # only for vertex ai models output_cost_per_image: float | None + input_cost_per_pixel: ReadOnly[float | None] output_cost_per_pixel: ReadOnly[float | None] output_cost_per_image_token: float | None output_cost_per_image_token_batches: ReadOnly[float | None] diff --git a/litellm/utils.py b/litellm/utils.py index 5fb27ad6ab6..38494cd1d99 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -7327,6 +7327,7 @@ def get_model_info_helper( output_cost_per_second_4k=_model_info.get("output_cost_per_second_4k", None), output_cost_per_video_per_second=_model_info.get("output_cost_per_video_per_second", None), output_cost_per_image=_model_info.get("output_cost_per_image", None), + input_cost_per_pixel=_model_info.get("input_cost_per_pixel", None), output_cost_per_pixel=_model_info.get("output_cost_per_pixel", None), output_cost_per_image_token=_model_info.get("output_cost_per_image_token", None), output_cost_per_image_token_batches=_model_info.get("output_cost_per_image_token_batches", None), diff --git a/tests/unit/proxy/spend_tracking/test_cost_compiler.py b/tests/unit/proxy/spend_tracking/test_cost_compiler.py index 59be2d64d3f..b898012c57b 100644 --- a/tests/unit/proxy/spend_tracking/test_cost_compiler.py +++ b/tests/unit/proxy/spend_tracking/test_cost_compiler.py @@ -1618,6 +1618,7 @@ async def test_live_adjustment_reload_is_captured_and_replay_remains_frozen(monk ("pixels", 2049, 0.6), ("tiered", 2048, 1.5), ("tiered-request", 2048, 1.5), + ("azure-tiered", 2048, 1.5), ), ) async def test_image_capture_replays_the_existing_image_calculator( @@ -1638,30 +1639,41 @@ async def test_image_capture_replays_the_existing_image_calculator( "pixels": "fal-ai/flux/dev", "tiered": "dall-e-capture", "tiered-request": "dall-e-capture", + "azure-tiered": "dall-e-3", }[pricing] - provider: Final = "fal_ai" if pricing == "pixels" else "openai" + provider: Final = "fal_ai" if pricing == "pixels" else "azure" if pricing == "azure-tiered" else "openai" + catalog_provider: Final = provider catalog_key: Final = f"{provider}/{model}" if pricing == "pixels" else model monkeypatch.setitem( litellm.model_cost, catalog_key, { - "litellm_provider": provider, + "litellm_provider": catalog_provider, "mode": "image_generation", **{ "tokens": {"input_cost_per_token": 0.01, "output_cost_per_image_token": 0.02}, "images": {"output_cost_per_image": 0.25}, "tiered": {"output_cost_per_image": 0.25}, "tiered-request": {"output_cost_per_image": 0.25}, + "azure-tiered": {"input_cost_per_token": 0.0, "output_cost_per_token": 0.0}, "pixels": {"output_cost_per_image": 0.1, "output_cost_per_pixel": 0.1 / (1024 * 1024)}, }[pricing], }, ) - tier_key: Final = f"hd/1024-x-1024/{model}" - if pricing.startswith("tiered"): + tier_key: Final = f"{provider}/hd/1024-x-1024/{model}" if pricing == "azure-tiered" else f"hd/1024-x-1024/{model}" + if "tiered" in pricing: monkeypatch.setitem( litellm.model_cost, tier_key, - {"litellm_provider": provider, "mode": "image_generation", "output_cost_per_image": 0.75}, + { + "litellm_provider": catalog_provider, + "mode": "image_generation", + **( + {"input_cost_per_pixel": 0.75 / (1024 * 1024)} + if pricing == "azure-tiered" + else {"output_cost_per_image": 0.75} + ), + }, ) logger: Final = _logger("aimage_generation") logger.model = model @@ -1687,7 +1699,7 @@ async def test_image_capture_replays_the_existing_image_calculator( ] * 2, "size": None if pricing == "tiered-request" else "1024x1024", - "quality": None if pricing == "tiered-request" else "hd" if pricing == "tiered" else "standard", + "quality": None if pricing == "tiered-request" else "hd" if "tiered" in pricing else "standard", "usage": { "input_tokens": 10, "output_tokens": 5,