From d1886d290810b249b3e6a48d2efc34781c4f6977 Mon Sep 17 00:00:00 2001 From: Nick Silva Date: Tue, 29 Sep 2026 15:07:21 -0700 Subject: [PATCH] fix(terraform): allow responses, realtime and other in-use modes on litellm_model The mode enum on litellm_model only allowed completion, embedding, image_generation, chat, moderation, audio_transcription, audio_speech and rerank, rejecting responses, realtime, image_edit, ocr, search, video_generation and evaluation even though model_prices_and_context_window.json already assigns these to shipped models. guardrail and vector_store are left out since those are modeled as their own litellm_guardrail and litellm_vector_store resources. --- terraform/provider/CHANGELOG.md | 1 + terraform/provider/docs/resources/model.md | 7 +++++++ terraform/provider/litellm/resource_model.go | 7 +++++++ .../provider/litellm/resource_model_test.go | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/terraform/provider/CHANGELOG.md b/terraform/provider/CHANGELOG.md index d8c53e1432c..f23ba4e7ff9 100644 --- a/terraform/provider/CHANGELOG.md +++ b/terraform/provider/CHANGELOG.md @@ -61,6 +61,7 @@ longer signal it. - **key**: A config-supplied `key` value (write-only) is now forwarded to `/key/generate`; previously it was silently dropped and the proxy generated a random key instead - **security**: The `litellm_key` data source and `litellm_key_block` resource normalize raw `sk-` keys to their SHA-256 token hash before building request URLs and resource IDs, so plaintext keys no longer land in reverse-proxy access logs, Terraform plan output, or state IDs - **unified_access_group**: create now accepts any 2xx response instead of requiring exactly HTTP 200; `POST /v1/unified_access_group` legitimately returns 201, so creation previously succeeded on the proxy but failed in the provider, leaving the group out of state and forcing a `terraform import` to recover on the next apply's 409. Same fix and shape as the one already applied to `mcp_server`/`model`/`key`/`organization_member`; `handleResponse` (shared by several other resources) was the one status-check helper that fix didn't reach. The legacy `litellm_access_group` resource calls the unrelated `/access_group/new` endpoint, which already returns 200, so it was never affected +- **model**: `mode` on `litellm_model` now accepts `responses`, `realtime`, `image_edit`, `ocr`, `search`, `video_generation` and `evaluation`, matching the modes `model_prices_and_context_window.json` already assigns to shipped models (for example `responses` on `azure/codex-mini` and `realtime` on `amazon.nova-2-sonic-v1:0`); previously the enum only allowed `completion`, `embedding`, `image_generation`, `chat`, `moderation`, `audio_transcription`, `audio_speech` and `rerank`, so setting a model's `mode` to any of the missing values failed at plan time with `expected mode to be one of [...]`. `guardrail` and `vector_store` are deliberately left out, since those are modeled as the dedicated `litellm_guardrail` and `litellm_vector_store` resources rather than as a `litellm_model` mode ### Changed diff --git a/terraform/provider/docs/resources/model.md b/terraform/provider/docs/resources/model.md index 5bb68bfe918..c5c4f79e967 100644 --- a/terraform/provider/docs/resources/model.md +++ b/terraform/provider/docs/resources/model.md @@ -137,6 +137,13 @@ The following arguments are supported: * `audio_transcription` * `audio_speech` * `rerank` + * `responses` + * `realtime` + * `image_edit` + * `ocr` + * `search` + * `video_generation` + * `evaluation` * `tpm` - (Optional) integer. Tokens per minute limit for this model. diff --git a/terraform/provider/litellm/resource_model.go b/terraform/provider/litellm/resource_model.go index 85cb1d038bf..83ccb2fc78e 100644 --- a/terraform/provider/litellm/resource_model.go +++ b/terraform/provider/litellm/resource_model.go @@ -110,6 +110,13 @@ func resourceLiteLLMModel() *schema.Resource { "audio_transcription", "audio_speech", "rerank", + "responses", + "realtime", + "image_edit", + "ocr", + "search", + "video_generation", + "evaluation", }, false), }, "input_cost_per_million_tokens": { diff --git a/terraform/provider/litellm/resource_model_test.go b/terraform/provider/litellm/resource_model_test.go index 0be3c39a3c5..1eef0e213d6 100644 --- a/terraform/provider/litellm/resource_model_test.go +++ b/terraform/provider/litellm/resource_model_test.go @@ -242,3 +242,22 @@ func TestResourceLiteLLMModelUpdateSkipsPatchWhenDisplayNameUnchanged(t *testing t.Fatalf("update failed: %v", err) } } + +func TestResourceLiteLLMModel_ModeValidation(t *testing.T) { + validateMode := resourceLiteLLMModel().Schema["mode"].ValidateFunc + + valid := []string{ + "completion", "embedding", "image_generation", "chat", "moderation", + "audio_transcription", "audio_speech", "rerank", + "responses", "realtime", "image_edit", "ocr", "search", "video_generation", "evaluation", + } + for _, mode := range valid { + if _, errs := validateMode(mode, "mode"); len(errs) != 0 { + t.Errorf("mode %q: expected no validation error, got %v", mode, errs) + } + } + + if _, errs := validateMode("not_a_real_mode", "mode"); len(errs) == 0 { + t.Error(`mode "not_a_real_mode": expected a validation error, got none`) + } +}