From a57d01ab032d560fc26460f6d675209d957f4b66 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 597e0c6d4d8..bff77e17530 100644 --- a/terraform/provider/CHANGELOG.md +++ b/terraform/provider/CHANGELOG.md @@ -66,6 +66,7 @@ longer signal it. - **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 - **mcp_server, model, key, organization_member**: Requests now accept any 2xx response instead of requiring exactly HTTP 200; the proxy legitimately returns 201 from `POST /v1/mcp/server` (and other create endpoints), so `litellm_mcp_server` create previously succeeded on the proxy but failed in the provider, leaving the resource out of state and risking a duplicate server on retry - **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 - **mcp_server**: A plan right after a clean apply of a minimal litellm_mcp_server no longer shows changes. alias is now computed so the proxy's default (the server name) is kept, spec_version keeps the configured value when the proxy returns none, and an mcp_info block the proxy fills with fields the provider does not model is no longer written to state ### Changed diff --git a/terraform/provider/docs/resources/model.md b/terraform/provider/docs/resources/model.md index 85c495bf1ca..f0cf8aa43e3 100644 --- a/terraform/provider/docs/resources/model.md +++ b/terraform/provider/docs/resources/model.md @@ -139,6 +139,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 e9582ad75fb..1ce85ca9e20 100644 --- a/terraform/provider/litellm/resource_model.go +++ b/terraform/provider/litellm/resource_model.go @@ -128,6 +128,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 4c2d5d27442..881fea73c4a 100644 --- a/terraform/provider/litellm/resource_model_test.go +++ b/terraform/provider/litellm/resource_model_test.go @@ -523,6 +523,25 @@ func TestResourceLiteLLMModelUpdateSkipsPatchWhenDisplayNameUnchanged(t *testing } } +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`) + } +} + func TestResourceLiteLLMModelCreateSendsWriteOnlySecrets(t *testing.T) { var createPayload map[string]interface{} srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {