diff --git a/litellm-rust/crates/model-catalog/src/model_info.rs b/litellm-rust/crates/model-catalog/src/model_info.rs index 4924a7b37d3..361cb56e9b1 100644 --- a/litellm-rust/crates/model-catalog/src/model_info.rs +++ b/litellm-rust/crates/model-catalog/src/model_info.rs @@ -159,9 +159,6 @@ pub struct ModelInfo { pub input_cost_per_pixel: Option, #[serde(skip_serializing_if = "Option::is_none")] pub input_cost_per_query: Option, - /// USD per billable pixel of the reference images sent with an image edit (Azure AI FLUX.2 counts them in whole 1024x1024 megapixels). - #[serde(skip_serializing_if = "Option::is_none")] - pub input_cost_per_reference_pixel: Option, #[serde(skip_serializing_if = "Option::is_none")] pub input_cost_per_request: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/litellm/llms/azure_ai/image_generation/cost_calculator.py b/litellm/llms/azure_ai/image_generation/cost_calculator.py index 40324f68e84..df299cfd138 100644 --- a/litellm/llms/azure_ai/image_generation/cost_calculator.py +++ b/litellm/llms/azure_ai/image_generation/cost_calculator.py @@ -26,8 +26,7 @@ _REFERENCE_PIXELS: Final = TypeAdapter(tuple[Annotated[int, Field(strict=True, g @dataclass(frozen=True, slots=True, kw_only=True) class _Flux2MegapixelPrices: first: float - additional: float - reference: float + megapixel: float def _price(resolved: ModelInfo, cost_key: str) -> float | None: @@ -52,30 +51,19 @@ def _deployment_price(deployment: ModelInfo | None, cost_key: str) -> float | No def _flux2_prices(resolved: ModelInfo, deployment: ModelInfo | None) -> _Flux2MegapixelPrices: - # A deployment that prices the generated image itself also prices its references, unless it sets a reference - # rate: a flat per-image price covers them, and a pixel rate bills them at that rate - deployment_reference_rate: Final = _deployment_price(deployment, "input_cost_per_reference_pixel") deployment_image_price: Final = _deployment_price(deployment, "output_cost_per_image") if deployment_image_price is not None: - return _Flux2MegapixelPrices( - first=deployment_image_price, - additional=0.0, - reference=(deployment_reference_rate or 0.0) * MEGAPIXEL, - ) + return _Flux2MegapixelPrices(first=deployment_image_price, megapixel=0.0) deployment_pixel_rate: Final = _deployment_price(deployment, "input_cost_per_pixel") if deployment_pixel_rate is not None: return _Flux2MegapixelPrices( - first=deployment_pixel_rate * MEGAPIXEL, - additional=deployment_pixel_rate * MEGAPIXEL, - reference=(deployment_pixel_rate if deployment_reference_rate is None else deployment_reference_rate) - * MEGAPIXEL, + first=deployment_pixel_rate * MEGAPIXEL, megapixel=deployment_pixel_rate * MEGAPIXEL ) catalog_megapixel_rate: Final = _pixel_rate(resolved, "input_cost_per_pixel") * MEGAPIXEL catalog_first_megapixel: Final = _price(resolved, "output_cost_per_image") return _Flux2MegapixelPrices( first=catalog_megapixel_rate if catalog_first_megapixel is None else catalog_first_megapixel, - additional=catalog_megapixel_rate, - reference=_pixel_rate(resolved, "input_cost_per_reference_pixel") * MEGAPIXEL, + megapixel=catalog_megapixel_rate, ) @@ -84,8 +72,9 @@ def _billable_megapixels(pixels: int) -> int: def _billable_reference_megapixels(reference_pixels: tuple[int, ...]) -> int: - # Azure's FLUX.2 request_meta (2026-09-25) bills a lone reference at no more than 4 MP, and each reference of a - # multi-reference edit as exactly 1 MP whatever its size + # Azure's FLUX.2 request_meta (2026-09-25) bills a lone reference at no more than 4 MP, each reference of a + # multi-reference edit as exactly 1 MP whatever its size, and every reference megapixel at the rate of an + # additional generated one match reference_pixels: case (): return 0 @@ -104,14 +93,14 @@ def _reference_cost(prices: _Flux2MegapixelPrices, image_response: ImageResponse except ValidationError: verbose_logger.warning("Ignoring malformed FLUX.2 reference pixel counts: %r", reported_pixels) return 0.0 - return prices.reference * _billable_reference_megapixels(reference_pixels) + return prices.megapixel * _billable_reference_megapixels(reference_pixels) def _flux2_generated_cost( prices: _Flux2MegapixelPrices, image_response: ImageResponse, requested_pixels: int, n: int | None ) -> float: return sum( - prices.first + prices.additional * (_billable_megapixels(pixels) - 1) + prices.first + prices.megapixel * (_billable_megapixels(pixels) - 1) for pixels in _generated_pixels(image_response, requested_pixels, n) ) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index c9171872734..b51f60b6d30 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -11372,7 +11372,6 @@ }, "azure_ai/flux.2-pro": { "input_cost_per_pixel": 1.430511474609375e-08, - "input_cost_per_reference_pixel": 1.430511474609375e-08, "litellm_provider": "azure_ai", "mode": "image_generation", "output_cost_per_image": 0.03, @@ -11391,7 +11390,6 @@ }, "azure_ai/FLUX.2-flex": { "input_cost_per_pixel": 4.76837158203125e-08, - "input_cost_per_reference_pixel": 4.76837158203125e-08, "litellm_provider": "azure_ai", "max_input_tokens": 32000, "max_tokens": 32000, diff --git a/litellm/types/router.py b/litellm/types/router.py index 6362cea4eec..d545f7ae639 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -789,7 +789,6 @@ class ModelGroupInfo(BaseModel): input_cost_per_token: float | None = None output_cost_per_token: float | None = None input_cost_per_pixel: float | None = None - input_cost_per_reference_pixel: float | None = None mode: ( str | Literal["chat", "embedding", "completion", "image_generation", "audio_transcription", "rerank", "moderations"] diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 857e161599f..cd336c9b989 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -358,7 +358,6 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False): output_cost_per_character_above_128k_tokens: float | None # only for vertex ai models output_cost_per_image: float | None output_cost_per_pixel: ReadOnly[float | None] - input_cost_per_reference_pixel: ReadOnly[float | None] output_cost_per_image_token: float | None output_cost_per_video_token: float | None # for gemini omni models with video output output_vector_size: int | None @@ -3709,7 +3708,6 @@ class CustomPricingLiteLLMParams(MirroredPricingParams): output_cost_per_image_1024: float | None = None output_cost_per_image_1536: float | None = None input_cost_per_pixel: float | None = None - input_cost_per_reference_pixel: float | None = None output_cost_per_pixel: float | None = None # Include all ModelInfoBase fields as optional diff --git a/litellm/utils.py b/litellm/utils.py index 424d4f367f5..be4388802f9 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -6265,7 +6265,6 @@ def _get_model_info_helper( 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), output_cost_per_pixel=_model_info.get("output_cost_per_pixel", None), - input_cost_per_reference_pixel=_model_info.get("input_cost_per_reference_pixel", None), output_cost_per_image_token=_model_info.get("output_cost_per_image_token", None), output_cost_per_video_token=_model_info.get("output_cost_per_video_token", None), output_vector_size=_model_info.get("output_vector_size", None), diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index c9171872734..b51f60b6d30 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -11372,7 +11372,6 @@ }, "azure_ai/flux.2-pro": { "input_cost_per_pixel": 1.430511474609375e-08, - "input_cost_per_reference_pixel": 1.430511474609375e-08, "litellm_provider": "azure_ai", "mode": "image_generation", "output_cost_per_image": 0.03, @@ -11391,7 +11390,6 @@ }, "azure_ai/FLUX.2-flex": { "input_cost_per_pixel": 4.76837158203125e-08, - "input_cost_per_reference_pixel": 4.76837158203125e-08, "litellm_provider": "azure_ai", "max_input_tokens": 32000, "max_tokens": 32000, diff --git a/model_prices_and_context_window.schema.json b/model_prices_and_context_window.schema.json index 0fbf990b671..fa1828c780a 100644 --- a/model_prices_and_context_window.schema.json +++ b/model_prices_and_context_window.schema.json @@ -333,10 +333,6 @@ "type": "number", "minimum": 0 }, - "input_cost_per_reference_pixel": { - "type": "number", - "minimum": 0 - }, "input_cost_per_request": { "type": "number", "minimum": 0 diff --git a/terraform/provider/docs/resources/model.md b/terraform/provider/docs/resources/model.md index 50bcc8e7c21..5bb68bfe918 100644 --- a/terraform/provider/docs/resources/model.md +++ b/terraform/provider/docs/resources/model.md @@ -159,8 +159,6 @@ The following arguments are supported: * `input_cost_per_pixel` - (Optional) float. Cost applied per input pixel for models that charge by image size. -* `input_cost_per_reference_pixel` - (Optional) float. Cost applied per reference image pixel for image-edit models. - * `output_cost_per_pixel` - (Optional) float. Cost applied per output pixel for image-generation models. * `input_cost_per_second` - (Optional) float. Cost applied per input second for audio/transcription models. diff --git a/terraform/provider/litellm/resource_model.go b/terraform/provider/litellm/resource_model.go index 73667418b3f..85cb1d038bf 100644 --- a/terraform/provider/litellm/resource_model.go +++ b/terraform/provider/litellm/resource_model.go @@ -124,10 +124,6 @@ func resourceLiteLLMModel() *schema.Resource { Type: schema.TypeFloat, Optional: true, }, - "input_cost_per_reference_pixel": { - Type: schema.TypeFloat, - Optional: true, - }, "output_cost_per_pixel": { Type: schema.TypeFloat, Optional: true, diff --git a/terraform/provider/litellm/resource_model_crud.go b/terraform/provider/litellm/resource_model_crud.go index d6a3d880568..c7db2a673e5 100644 --- a/terraform/provider/litellm/resource_model_crud.go +++ b/terraform/provider/litellm/resource_model_crud.go @@ -126,10 +126,6 @@ func createOrUpdateModel(d *schema.ResourceData, m interface{}, isUpdate bool) e if inputCostPerPixel := d.Get("input_cost_per_pixel").(float64); inputCostPerPixel > 0 { litellmParams["input_cost_per_pixel"] = inputCostPerPixel } - inputCostPerReferencePixel := d.Get("input_cost_per_reference_pixel").(float64) - if inputCostPerReferencePixel > 0 || (isUpdate && d.HasChange("input_cost_per_reference_pixel")) { - litellmParams["input_cost_per_reference_pixel"] = inputCostPerReferencePixel - } if outputCostPerPixel := d.Get("output_cost_per_pixel").(float64); outputCostPerPixel > 0 { litellmParams["output_cost_per_pixel"] = outputCostPerPixel } diff --git a/terraform/provider/litellm/resource_model_test.go b/terraform/provider/litellm/resource_model_test.go index 82210d6b79d..0be3c39a3c5 100644 --- a/terraform/provider/litellm/resource_model_test.go +++ b/terraform/provider/litellm/resource_model_test.go @@ -242,71 +242,3 @@ func TestResourceLiteLLMModelUpdateSkipsPatchWhenDisplayNameUnchanged(t *testing t.Fatalf("update failed: %v", err) } } - -func TestResourceLiteLLMModelUpdateSendsReferencePixelRate(t *testing.T) { - cases := map[string]struct { - oldRate, newRate string - wantSent bool - wantRate float64 - }{ - "rate cleared to zero is sent": {oldRate: "2e-07", newRate: "0", wantSent: true, wantRate: 0}, - "unchanged rate is sent": {oldRate: "2e-07", newRate: "2e-07", wantSent: true, wantRate: 2e-07}, - "never set rate is omitted": {oldRate: "", newRate: "0", wantSent: false}, - } - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - var updateParams map[string]interface{} - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodPost && r.URL.Path == "/model/update" { - var payload map[string]interface{} - if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { - t.Errorf("failed to decode update payload: %v", err) - } - updateParams = payload["litellm_params"].(map[string]interface{}) - w.Write([]byte(modelInfoBody(""))) - return - } - w.Write([]byte(modelInfoDataEnvelope(""))) - })) - defer srv.Close() - - res := resourceLiteLLMModel() - attrs := map[string]string{ - "model_name": "sonnet-4-5-anthropic", - "custom_llm_provider": "anthropic", - "base_model": "claude-sonnet-4-5", - } - if tc.oldRate != "" { - attrs["input_cost_per_reference_pixel"] = tc.oldRate - } - state := &terraform.InstanceState{ID: "model-123", Attributes: attrs} - config := map[string]interface{}{ - "model_name": "sonnet-4-5-anthropic", - "custom_llm_provider": "anthropic", - "base_model": "claude-sonnet-4-5", - } - if tc.newRate != "0" { - config["input_cost_per_reference_pixel"] = tc.newRate - } - diff, err := res.Diff(context.Background(), state, &terraform.ResourceConfig{Config: config}, nil) - if err != nil { - t.Fatalf("diff failed: %v", err) - } - d, err := schema.InternalMap(res.Schema).Data(state, diff) - if err != nil { - t.Fatalf("data failed: %v", err) - } - - if err := resourceLiteLLMModelUpdate(d, NewClient(srv.URL, "test-key", true)); err != nil { - t.Fatalf("update failed: %v", err) - } - rate, sent := updateParams["input_cost_per_reference_pixel"] - if sent != tc.wantSent { - t.Fatalf("expected input_cost_per_reference_pixel sent=%v, got %v (%v)", tc.wantSent, sent, rate) - } - if sent && rate != tc.wantRate { - t.Errorf("expected input_cost_per_reference_pixel %v, got %v", tc.wantRate, rate) - } - }) - } -} diff --git a/terraform/provider/litellm/types.go b/terraform/provider/litellm/types.go index 0f2cd98fe20..a8784b8a6a9 100644 --- a/terraform/provider/litellm/types.go +++ b/terraform/provider/litellm/types.go @@ -103,7 +103,6 @@ type LiteLLMParams struct { InputCostPerToken float64 `json:"input_cost_per_token,omitempty"` OutputCostPerToken float64 `json:"output_cost_per_token,omitempty"` InputCostPerPixel float64 `json:"input_cost_per_pixel,omitempty"` - InputCostPerReferencePixel float64 `json:"input_cost_per_reference_pixel,omitempty"` OutputCostPerPixel float64 `json:"output_cost_per_pixel,omitempty"` InputCostPerSecond float64 `json:"input_cost_per_second,omitempty"` OutputCostPerSecond float64 `json:"output_cost_per_second,omitempty"` diff --git a/tests/unit/llms/azure_ai/image_edit/test_azure_ai_image_edit_transformation.py b/tests/unit/llms/azure_ai/image_edit/test_azure_ai_image_edit_transformation.py index 4cfcb83918b..ed547b69ab2 100644 --- a/tests/unit/llms/azure_ai/image_edit/test_azure_ai_image_edit_transformation.py +++ b/tests/unit/llms/azure_ai/image_edit/test_azure_ai_image_edit_transformation.py @@ -184,11 +184,9 @@ def test_flux2_image_edit_preserves_controls_and_pixel_cost(dimensions: Mapping[ steps="32", **dimensions, ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() - assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 2048 * 1024 * 2 + reference_rate * 1024 * 1024 - ) + assert response._hidden_params["response_cost"] == pytest.approx(rate * 2048 * 1024 * 2 + rate * 1024 * 1024) def test_flux2_image_edit_accepts_and_drops_openai_only_parameters(): @@ -225,9 +223,8 @@ def _webp(width: int, height: int) -> bytes: ) -def _flex_rates() -> tuple[float, float]: - row: Final = litellm.model_cost["azure_ai/FLUX.2-flex"] - return row["input_cost_per_pixel"], row["input_cost_per_reference_pixel"] +def _flex_megapixel_rate() -> float: + return litellm.model_cost["azure_ai/FLUX.2-flex"]["input_cost_per_pixel"] def _edit_ok(request: httpx.Request) -> httpx.Response: @@ -251,14 +248,12 @@ def test_flux2_image_edit_measures_every_reference_but_bills_each_of_several_as_ client=HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(respond))), size="1024x1024", ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() assert sent["input_image"] == base64.b64encode(references[0]).decode() assert sent["input_image_3"] == base64.b64encode(references[2]).decode() assert response._hidden_params["reference_image_pixels"] == (1024 * 1024, 800 * 600, 640 * 480) - assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 1024 * 1024 + reference_rate * 3 * 1024 * 1024 - ) + assert response._hidden_params["response_cost"] == pytest.approx(rate * 1024 * 1024 + rate * 3 * 1024 * 1024) class _ReadOnlyUpload: @@ -353,12 +348,10 @@ def test_flux2_image_edit_sends_and_bills_every_readable_upload( client=HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(respond))), size="1024x1024", ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() assert sent_images == [base64.b64encode(reference).decode()] - assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 1024 * 1024 + reference_rate * 2 * 1024 * 1024 - ) + assert response._hidden_params["response_cost"] == pytest.approx(rate * 1024 * 1024 + rate * 2 * 1024 * 1024) @pytest.mark.parametrize("upload_kind", ("bytesio", "buffered-reader", "named-temporary-file-at-eof")) @@ -420,12 +413,10 @@ def test_flux2_image_edit_measures_a_stream_reference(): client=HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(_edit_ok))), size="1024x1024", ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() assert response._hidden_params["reference_image_pixels"] == (2048 * 2048,) - assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 1024 * 1024 + reference_rate * 2048 * 2048 - ) + assert response._hidden_params["response_cost"] == pytest.approx(rate * 1024 * 1024 + rate * 2048 * 2048) # Billable megapixels for a lone reference as Azure's FLUX.2-pro request_meta reported them on 2026-09-25 @@ -447,10 +438,10 @@ def test_flux2_image_edit_bills_a_lone_reference_in_whole_megapixels(reference: client=HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(_edit_ok))), size="1024x1024", ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 1024 * 1024 + reference_rate * billed_megapixels * 1024 * 1024 + rate * 1024 * 1024 + rate * billed_megapixels * 1024 * 1024 ) @@ -472,12 +463,10 @@ def test_flux2_image_edit_bills_a_lone_unmeasurable_reference_as_one_megapixel(r client=HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(_edit_ok))), size="1024x1024", ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() assert response._hidden_params["reference_image_pixels"] == (UNMEASURED_REFERENCE_IMAGE_PIXELS,) - assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 1024 * 1024 + reference_rate * 1024 * 1024 - ) + assert response._hidden_params["response_cost"] == pytest.approx(rate * 1024 * 1024 + rate * 1024 * 1024) def test_flux2_image_edit_still_bills_every_reference_when_one_header_reports_zero_pixels(): @@ -490,11 +479,9 @@ def test_flux2_image_edit_still_bills_every_reference_when_one_header_reports_ze client=HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(_edit_ok))), size="1024x1024", ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() - assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 1024 * 1024 + reference_rate * 2 * 1024 * 1024 - ) + assert response._hidden_params["response_cost"] == pytest.approx(rate * 1024 * 1024 + rate * 2 * 1024 * 1024) @pytest.mark.parametrize("stream_position", ("start", "end")) @@ -526,7 +513,7 @@ def test_flux2_image_edit_resends_and_rebills_a_reused_stream(stream_position: s assert [response._hidden_params["reference_image_pixels"] for response in responses] == [(2048 * 1024,)] * 2 -def test_flux2_pro_image_edit_bills_references_on_the_pro_reference_rate(): +def test_flux2_pro_image_edit_bills_references_at_the_pro_megapixel_rate(): pro_row: Final = litellm.model_cost["azure_ai/flux.2-pro"] response: Final = litellm.image_edit( model="azure_ai/flux.2-pro", @@ -538,9 +525,9 @@ def test_flux2_pro_image_edit_bills_references_on_the_pro_reference_rate(): size="1024x1024", ) - assert pro_row["input_cost_per_reference_pixel"] > 0 + assert pro_row["input_cost_per_pixel"] > 0 assert response._hidden_params["response_cost"] == pytest.approx( - pro_row["output_cost_per_image"] + pro_row["input_cost_per_reference_pixel"] * 2 * 1024 * 1024 + pro_row["output_cost_per_image"] + pro_row["input_cost_per_pixel"] * 2 * 1024 * 1024 ) @@ -548,8 +535,7 @@ async def test_flux2_router_image_edit_bills_the_deployment_rates(monkeypatch: p mock_client: Final = AsyncHTTPHandler() mock_client.client = httpx.AsyncClient(transport=httpx.MockTransport(_edit_ok)) monkeypatch.setattr(llm_http_handler_module, "get_async_httpx_client", lambda **_kwargs: mock_client) - generated_rate: Final = 1e-07 - reference_rate: Final = 2e-07 + megapixel_rate: Final = 1e-07 router: Final = litellm.Router( model_list=[ { @@ -558,8 +544,7 @@ async def test_flux2_router_image_edit_bills_the_deployment_rates(monkeypatch: p "model": "azure_ai/FLUX.2-flex", "api_base": "https://example.services.ai.azure.com", "api_key": "test-key", - "input_cost_per_pixel": generated_rate, - "input_cost_per_reference_pixel": reference_rate, + "input_cost_per_pixel": megapixel_rate, }, } ] @@ -573,7 +558,7 @@ async def test_flux2_router_image_edit_bills_the_deployment_rates(monkeypatch: p ) assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 2 * 1024 * 1024 + reference_rate * 2 * 1024 * 1024 + megapixel_rate * 2 * 1024 * 1024 + megapixel_rate * 2 * 1024 * 1024 ) @@ -582,8 +567,8 @@ async def test_flux2_router_image_edit_bills_the_deployment_rates(monkeypatch: p ( ({"output_cost_per_image": 0.5}, 0.5), ( - {"input_cost_per_pixel": 1e-07, "input_cost_per_reference_pixel": 2e-07}, - 1e-07 * 2 * 1024 * 1024 + 2e-07 * 2 * 1024 * 1024, + {"input_cost_per_pixel": 1e-07}, + 1e-07 * 2 * 1024 * 1024 + 1e-07 * 2 * 1024 * 1024, ), ), ids=("flat", "per-pixel"), @@ -663,7 +648,7 @@ def test_flux2_image_edit_bills_the_generated_image_azure_returned(size: str | N assert response._hidden_params["response_cost"] == pytest.approx( pro_row["output_cost_per_image"] + pro_row["input_cost_per_pixel"] * 1024 * 1024 - + pro_row["input_cost_per_reference_pixel"] * 2 * 1024 * 1024 + + pro_row["input_cost_per_pixel"] * 2 * 1024 * 1024 ) @@ -679,9 +664,7 @@ async def test_flux2_aimage_edit_bills_references_like_image_edit(): client=client, size="1024x1024", ) - generated_rate, reference_rate = _flex_rates() + rate: Final = _flex_megapixel_rate() assert response._hidden_params["reference_image_pixels"] == (1024 * 1024, 1024 * 1024) - assert response._hidden_params["response_cost"] == pytest.approx( - generated_rate * 1024 * 1024 + reference_rate * 2 * 1024 * 1024 - ) + assert response._hidden_params["response_cost"] == pytest.approx(rate * 1024 * 1024 + rate * 2 * 1024 * 1024) diff --git a/tests/unit/llms/azure_ai/image_generation/test_azure_ai_flux2_image_generation.py b/tests/unit/llms/azure_ai/image_generation/test_azure_ai_flux2_image_generation.py index 2742fcbab4a..734308c9122 100644 --- a/tests/unit/llms/azure_ai/image_generation/test_azure_ai_flux2_image_generation.py +++ b/tests/unit/llms/azure_ai/image_generation/test_azure_ai_flux2_image_generation.py @@ -35,10 +35,6 @@ def _flex_pixel_rate() -> float: return litellm.model_cost["azure_ai/FLUX.2-flex"]["input_cost_per_pixel"] -def _flex_reference_rate() -> float: - return litellm.model_cost["azure_ai/FLUX.2-flex"]["input_cost_per_reference_pixel"] - - def _flex_edit_cost(response: ImageResponse, model_info: dict | None = None) -> float: return CostCalculatorUtils.route_image_generation_cost_calculator( model="FLUX.2-flex", @@ -58,13 +54,9 @@ def _edit_response(reference_image_pixels: object) -> ImageResponse: ) -def _pro_megapixel_rates() -> tuple[float, float, float]: +def _pro_megapixel_rates() -> tuple[float, float]: row: Final = litellm.model_cost["azure_ai/flux.2-pro"] - return ( - row["output_cost_per_image"], - row["input_cost_per_pixel"] * 1024 * 1024, - row["input_cost_per_reference_pixel"] * 1024 * 1024, - ) + return row["output_cost_per_image"], row["input_cost_per_pixel"] * 1024 * 1024 @pytest.mark.parametrize( @@ -171,13 +163,9 @@ def test_flux2_flex_model_info(): @pytest.mark.parametrize("model", ("flux.2-pro", "FLUX.2-flex")) -def test_flux2_model_info_reports_the_reference_rate_and_the_edit_endpoint(model: str): +def test_flux2_model_info_lists_the_edit_endpoint(model: str): model_info: Final = litellm.get_model_info(model=model, custom_llm_provider="azure_ai") - assert ( - model_info["input_cost_per_reference_pixel"] - == litellm.model_cost[f"azure_ai/{model}"]["input_cost_per_reference_pixel"] - ) assert "/v1/images/edits" in model_info["supported_endpoints"] @@ -269,7 +257,7 @@ def test_flux2_flex_generation_rounds_each_image_up_to_whole_megapixels(): @pytest.mark.parametrize(("size", "megapixels"), (("256x256", 1), ("1024x1280", 2), ("2048x2048", 4))) def test_flux2_pro_generation_bills_the_first_megapixel_then_each_additional_one(size: str, megapixels: int): - first, additional, _reference = _pro_megapixel_rates() + first, additional = _pro_megapixel_rates() cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="flux.2-pro", @@ -305,9 +293,6 @@ def _catalog_image_cost(model: str, megapixels: int) -> float: lambda megapixels: 1e-07 * 1024 * 1024 * megapixels, id="deployment-pixel-rate-prices-every-megapixel", ), - pytest.param( - {"input_cost_per_reference_pixel": 3e-07}, None, id="deployment-reference-rate-keeps-catalog-output" - ), ), ) def test_flux2_generation_bills_each_price_source_as_its_owner_set_it( @@ -333,24 +318,16 @@ def test_flux2_generation_bills_each_price_source_as_its_owner_set_it( @pytest.mark.parametrize( ("deployment_prices", "reference_megapixel_price"), ( - pytest.param(None, None, id="catalog-reference-rate"), + pytest.param(None, None, id="catalog-megapixel-rate"), pytest.param({"output_cost_per_image": 0.07}, 0.0, id="deployment-flat-price-covers-references"), pytest.param( - {"output_cost_per_image": 0.07, "input_cost_per_reference_pixel": 3e-07}, - 3e-07 * 1024 * 1024, - id="deployment-flat-price-with-its-reference-rate", + {"output_cost_per_image": 0.07, "input_cost_per_pixel": 1e-07}, + 0.0, + id="deployment-flat-price-covers-references-despite-its-pixel-rate", ), pytest.param( {"input_cost_per_pixel": 1e-07}, 1e-07 * 1024 * 1024, id="deployment-pixel-rate-prices-references" ), - pytest.param( - {"input_cost_per_pixel": 1e-07, "input_cost_per_reference_pixel": 3e-07}, - 3e-07 * 1024 * 1024, - id="deployment-reference-rate-wins-over-its-pixel-rate", - ), - pytest.param( - {"input_cost_per_reference_pixel": 3e-07}, 3e-07 * 1024 * 1024, id="deployment-reference-rate-alone" - ), ), ) def test_flux2_edit_prices_references_from_the_source_that_priced_the_image( @@ -367,7 +344,7 @@ def test_flux2_edit_prices_references_from_the_source_that_priced_the_image( ) expected_per_megapixel: Final = ( - litellm.model_cost[f"azure_ai/{model}"]["input_cost_per_reference_pixel"] * 1024 * 1024 + litellm.model_cost[f"azure_ai/{model}"]["input_cost_per_pixel"] * 1024 * 1024 if reference_megapixel_price is None else reference_megapixel_price ) @@ -397,7 +374,7 @@ def _png_b64(width: int, height: int) -> str: def test_flux2_pro_bills_each_generated_image_by_its_returned_size( returned_images: tuple[tuple[int, int] | None, ...], requested_size: str, expected_megapixels: tuple[int, ...] ): - first, additional, _reference = _pro_megapixel_rates() + first, additional = _pro_megapixel_rates() response: Final = ImageResponse( data=[ImageObject(b64_json="aW1n" if image is None else _png_b64(*image)) for image in returned_images] ) @@ -415,7 +392,7 @@ def test_flux2_pro_bills_each_generated_image_by_its_returned_size( @pytest.mark.parametrize("size", ("auto", "large", "0x1024", "1024x")) def test_flux2_pro_bills_one_megapixel_for_a_size_it_cannot_measure(size: str): - first, _additional, _reference = _pro_megapixel_rates() + first, _additional = _pro_megapixel_rates() cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="flux.2-pro", @@ -429,7 +406,7 @@ def test_flux2_pro_bills_one_megapixel_for_a_size_it_cannot_measure(size: str): def test_flux2_pro_bills_the_requested_size_when_the_returned_image_is_not_valid_base64(): - first, additional, _reference = _pro_megapixel_rates() + first, additional = _pro_megapixel_rates() cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="flux.2-pro", @@ -444,7 +421,7 @@ def test_flux2_pro_bills_the_requested_size_when_the_returned_image_is_not_valid @pytest.mark.parametrize(("n", "billed_images"), ((2, 2), (None, 0))) def test_flux2_pro_bills_the_requested_image_count_when_the_response_lists_none(n: int | None, billed_images: int): - first, _additional, _reference = _pro_megapixel_rates() + first, _additional = _pro_megapixel_rates() cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="flux.2-pro", @@ -459,7 +436,7 @@ def test_flux2_pro_bills_the_requested_image_count_when_the_response_lists_none( def test_flux2_pro_reads_an_uppercase_size(): - first, additional, _reference = _pro_megapixel_rates() + first, additional = _pro_megapixel_rates() cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="flux.2-pro", @@ -475,22 +452,20 @@ def test_flux2_pro_reads_an_uppercase_size(): def test_flux2_edit_parses_string_prices_registered_in_the_cost_map(monkeypatch: pytest.MonkeyPatch): row: Final = litellm.model_cost["azure_ai/FLUX.2-flex"] monkeypatch.setitem(row, "input_cost_per_pixel", "2e-07") - monkeypatch.setitem(row, "input_cost_per_reference_pixel", "3e-07") litellm.get_model_info.cache_clear() _invalidate_model_cost_lowercase_map() - assert _flex_edit_cost(_edit_response((1024 * 1024,))) == pytest.approx(2e-07 * 1024 * 1024 + 3e-07 * 1024 * 1024) + assert _flex_edit_cost(_edit_response((1024 * 1024,))) == pytest.approx(2e-07 * 1024 * 1024 * 2) @pytest.fixture -def distinct_pro_megapixel_prices(monkeypatch: pytest.MonkeyPatch) -> tuple[float, float, float]: +def distinct_pro_megapixel_prices(monkeypatch: pytest.MonkeyPatch) -> tuple[float, float]: row: Final = litellm.model_cost["azure_ai/flux.2-pro"] monkeypatch.setitem(row, "output_cost_per_image", 0.05) monkeypatch.setitem(row, "input_cost_per_pixel", 2e-08) - monkeypatch.setitem(row, "input_cost_per_reference_pixel", 3e-08) litellm.get_model_info.cache_clear() _invalidate_model_cost_lowercase_map() - return 0.05, 2e-08 * 1024 * 1024, 3e-08 * 1024 * 1024 + return 0.05, 2e-08 * 1024 * 1024 # Billable megapixels as Azure's request_meta reported them for FLUX.2-pro edits on 2026-09-25 @@ -511,9 +486,9 @@ def test_flux2_pro_edit_bills_the_megapixels_azure_meters( reference_pixels: tuple[int, ...], output_megapixels: int, reference_megapixels: int, - distinct_pro_megapixel_prices: tuple[float, float, float], + distinct_pro_megapixel_prices: tuple[float, float], ): - first, additional, reference = distinct_pro_megapixel_prices + first, megapixel = distinct_pro_megapixel_prices cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="flux.2-pro", @@ -523,7 +498,7 @@ def test_flux2_pro_edit_bills_the_megapixels_azure_meters( call_type="image_edit", ) - assert cost == pytest.approx(first + additional * (output_megapixels - 1) + reference * reference_megapixels) + assert cost == pytest.approx(first + megapixel * (output_megapixels - 1 + reference_megapixels)) def test_flux2_flex_edit_bills_reference_pixels_on_top_of_generated_pixels(): @@ -531,10 +506,10 @@ def test_flux2_flex_edit_bills_reference_pixels_on_top_of_generated_pixels(): assert generated_only == pytest.approx(_flex_pixel_rate() * 1024 * 1024) assert _flex_edit_cost(_edit_response((1024 * 1024,))) - generated_only == pytest.approx( - _flex_reference_rate() * 1024 * 1024 + _flex_pixel_rate() * 1024 * 1024 ) assert _flex_edit_cost(_edit_response((3 * 1024 * 1024,))) - generated_only == pytest.approx( - _flex_reference_rate() * 3 * 1024 * 1024 + _flex_pixel_rate() * 3 * 1024 * 1024 ) @@ -546,7 +521,7 @@ def test_flux2_flex_edit_reference_cost_does_not_scale_with_image_count(): ) assert _flex_edit_cost(two_outputs) == pytest.approx( - _flex_pixel_rate() * 1024 * 1024 * 2 + _flex_reference_rate() * 2048 * 1024 + _flex_pixel_rate() * 1024 * 1024 * 2 + _flex_pixel_rate() * 2048 * 1024 ) @@ -582,16 +557,6 @@ def test_flux2_flex_edit_ignores_reference_pixels_when_provider_reports_token_us assert _flex_edit_cost(response, model_info=token_rates) == pytest.approx(50 * 1e-05 + 100 * 2e-05 + 1000 * 4e-05) -def test_flux2_flex_edit_reads_the_reference_rate_from_its_own_catalog_key(monkeypatch: pytest.MonkeyPatch): - monkeypatch.setitem(litellm.model_cost["azure_ai/FLUX.2-flex"], "input_cost_per_reference_pixel", 3e-07) - litellm.get_model_info.cache_clear() - _invalidate_model_cost_lowercase_map() - - assert _flex_edit_cost(_edit_response((1024 * 1024,))) == pytest.approx( - _flex_pixel_rate() * 1024 * 1024 + 3e-07 * 1024 * 1024 - ) - - def test_flux2_flex_edit_prices_output_from_the_request_size_not_the_reference(): large_reference_small_output: Final = ImageResponse( data=[ImageObject(b64_json="aW1n")], @@ -600,7 +565,7 @@ def test_flux2_flex_edit_prices_output_from_the_request_size_not_the_reference() ) assert _flex_edit_cost(large_reference_small_output) == pytest.approx( - _flex_pixel_rate() * 1024 * 1024 + _flex_reference_rate() * 2048 * 2048 + _flex_pixel_rate() * 1024 * 1024 + _flex_pixel_rate() * 2048 * 2048 ) @@ -610,51 +575,30 @@ def test_flux2_flex_edit_with_a_free_deployment_pixel_rate_bills_nothing(): assert cost == 0.0 -def test_flux2_flex_edit_prefers_deployment_reference_rate(): - cost: Final = _flex_edit_cost( - _edit_response((1024 * 1024,)), - model_info={"input_cost_per_pixel": 2e-07, "input_cost_per_reference_pixel": 3e-07}, - ) - - assert cost == pytest.approx(2e-07 * 1024 * 1024 + 3e-07 * 1024 * 1024) - - -def test_flux2_flex_edit_deployment_reference_rate_alone_keeps_catalog_generated_rate(): - cost: Final = _flex_edit_cost(_edit_response((1024 * 1024,)), model_info={"input_cost_per_reference_pixel": 3e-07}) - - assert cost == pytest.approx(_flex_pixel_rate() * 1024 * 1024 + 3e-07 * 1024 * 1024) - - -def test_flux2_flex_edit_honors_explicit_zero_deployment_reference_rate(): - cost: Final = _flex_edit_cost(_edit_response((1024 * 1024,)), model_info={"input_cost_per_reference_pixel": 0.0}) - - assert cost == pytest.approx(_flex_pixel_rate() * 1024 * 1024) - - -def test_custom_named_flux2_deployment_bills_its_own_megapixel_and_reference_rates() -> None: +def test_custom_named_flux2_deployment_bills_its_own_megapixel_rate_for_output_and_references() -> None: cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="my-flux2-prod", completion_response=_edit_response((1024 * 1280,)), custom_llm_provider="azure_ai", size="1024x1280", call_type="image_edit", - model_info={"input_cost_per_pixel": 1e-07, "input_cost_per_reference_pixel": 2e-07}, + model_info={"input_cost_per_pixel": 1e-07}, ) - assert cost == pytest.approx(1e-07 * 1024 * 1024 * 2 + 2e-07 * 1024 * 1024 * 2) + assert cost == pytest.approx(1e-07 * 1024 * 1024 * 2 + 1e-07 * 1024 * 1024 * 2) -def test_custom_named_flux2_deployment_with_only_a_reference_rate_bills_only_its_references() -> None: +def test_custom_named_flux2_deployment_without_an_image_price_bills_nothing() -> None: cost: Final = CostCalculatorUtils.route_image_generation_cost_calculator( model="my-flux2-prod", completion_response=_edit_response((1024 * 1280,)), custom_llm_provider="azure_ai", size="1024x1280", call_type="image_edit", - model_info={"input_cost_per_reference_pixel": 2e-07}, + model_info={"input_cost_per_second": 1.0}, ) - assert cost == pytest.approx(2e-07 * 1024 * 1024 * 2) + assert cost == 0.0 def test_azure_ai_image_cost_calculator_rejects_a_response_that_is_not_an_image_response() -> None: @@ -712,7 +656,7 @@ def test_unlisted_azure_ai_deployment_without_a_generated_image_price_bills_noth custom_llm_provider="azure_ai", size="1024x1024", call_type="image_generation", - model_info={"input_cost_per_reference_pixel": 1e-07}, + model_info={"input_cost_per_second": 1.0}, ) assert cost == 0.0 diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 75234b34bc3..0cdc52c9a93 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -647,7 +647,6 @@ def validate_model_cost_values(model_data, exceptions=None): "output_cost_per_image_2K", "output_cost_per_image_4K", "input_cost_per_pixel", - "input_cost_per_reference_pixel", "output_cost_per_pixel", "input_cost_per_second", "output_cost_per_second", @@ -824,7 +823,6 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "regional_processing_uplift_multiplier_eu": {"type": "number"}, "regional_processing_uplift_multiplier_us": {"type": "number"}, "input_cost_per_pixel": {"type": "number"}, - "input_cost_per_reference_pixel": {"type": "number"}, "input_cost_per_query": {"type": "number"}, "input_cost_per_request": {"type": "number"}, "input_cost_per_second": {"type": "number"}, diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index d48b627c54d..1e0aed46923 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -33053,8 +33053,6 @@ export interface components { input_cost_per_pixel?: number | null; /** Input Cost Per Query */ input_cost_per_query?: number | null; - /** Input Cost Per Reference Pixel */ - input_cost_per_reference_pixel?: number | null; /** Input Cost Per Second */ input_cost_per_second?: number | null; /** Input Cost Per Token */ @@ -35942,8 +35940,6 @@ export interface components { health_status?: string | null; /** Input Cost Per Pixel */ input_cost_per_pixel?: number | null; - /** Input Cost Per Reference Pixel */ - input_cost_per_reference_pixel?: number | null; /** Input Cost Per Token */ input_cost_per_token?: number | null; /** @@ -46850,8 +46846,6 @@ export interface components { input_cost_per_pixel?: number | null; /** Input Cost Per Query */ input_cost_per_query?: number | null; - /** Input Cost Per Reference Pixel */ - input_cost_per_reference_pixel?: number | null; /** Input Cost Per Second */ input_cost_per_second?: number | null; /** Input Cost Per Token */