diff --git a/terraform/provider/docs/resources/model.md b/terraform/provider/docs/resources/model.md index 8068cfad5ec..0e1bfea6744 100644 --- a/terraform/provider/docs/resources/model.md +++ b/terraform/provider/docs/resources/model.md @@ -159,7 +159,7 @@ 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 billable pixel of the reference images sent with an image edit, for models that meter reference images separately. Azure AI FLUX.2 counts reference images in whole 1024x1024 megapixels. +* `input_cost_per_reference_pixel` - (Optional) float. Cost applied per billable pixel of the reference images sent with an image edit, for models that meter reference images separately. Azure AI FLUX.2 counts reference images in whole 1024x1024 megapixels. Changing it to 0 or removing it on an existing model sets the rate to 0, so reference images are then free. * `output_cost_per_pixel` - (Optional) float. Cost applied per output pixel for image-generation models. diff --git a/terraform/provider/litellm/resource_model_crud.go b/terraform/provider/litellm/resource_model_crud.go index 94da6fe76ad..d6a3d880568 100644 --- a/terraform/provider/litellm/resource_model_crud.go +++ b/terraform/provider/litellm/resource_model_crud.go @@ -126,7 +126,8 @@ 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 } - if inputCostPerReferencePixel := d.Get("input_cost_per_reference_pixel").(float64); inputCostPerReferencePixel > 0 { + 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 { diff --git a/terraform/provider/litellm/resource_model_test.go b/terraform/provider/litellm/resource_model_test.go index 0be3c39a3c5..82210d6b79d 100644 --- a/terraform/provider/litellm/resource_model_test.go +++ b/terraform/provider/litellm/resource_model_test.go @@ -242,3 +242,71 @@ 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) + } + }) + } +}