From 4f6b1ab4125b0fa8936deba70414ee9a3e82be89 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 17 Mar 2026 09:25:13 +0100 Subject: [PATCH] Reject unknown thinking_budget effort levels at parse time Previously, a typo like 'thinking_budget: adaptative' was silently accepted but no provider recognized it, so thinking was never enabled. Now the YAML and JSON unmarshalers validate the effort string against the set of accepted values (none, minimal, low, medium, high, max, adaptive) and return a clear error on unrecognized input. Assisted-By: docker-agent --- pkg/config/latest/types.go | 20 ++++++++++++++++++++ pkg/config/latest/types_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/pkg/config/latest/types.go b/pkg/config/latest/types.go index 679a73a70..642d01211 100644 --- a/pkg/config/latest/types.go +++ b/pkg/config/latest/types.go @@ -683,6 +683,20 @@ type ThinkingBudget struct { Tokens int `json:"tokens,omitempty"` } +// validThinkingEfforts lists all accepted string values for thinking_budget. +const validThinkingEfforts = "none, minimal, low, medium, high, max, adaptive" + +// isValidThinkingEffort reports whether s (case-insensitive, trimmed) is a +// recognised thinking_budget effort level. +func isValidThinkingEffort(s string) bool { + switch strings.ToLower(strings.TrimSpace(s)) { + case "none", "minimal", "low", "medium", "high", "max", "adaptive": + return true + default: + return false + } +} + func (t *ThinkingBudget) UnmarshalYAML(unmarshal func(any) error) error { // Try integer tokens first var n int @@ -694,6 +708,9 @@ func (t *ThinkingBudget) UnmarshalYAML(unmarshal func(any) error) error { // Try string level var s string if err := unmarshal(&s); err == nil { + if !isValidThinkingEffort(s) { + return fmt.Errorf("invalid thinking_budget effort %q: must be one of %s", s, validThinkingEfforts) + } *t = ThinkingBudget{Effort: s} return nil } @@ -793,6 +810,9 @@ func (t *ThinkingBudget) UnmarshalJSON(data []byte) error { // Try string level var s string if err := json.Unmarshal(data, &s); err == nil { + if !isValidThinkingEffort(s) { + return fmt.Errorf("invalid thinking_budget effort %q: must be one of %s", s, validThinkingEfforts) + } *t = ThinkingBudget{Effort: s} return nil } diff --git a/pkg/config/latest/types_test.go b/pkg/config/latest/types_test.go index 6aede16cb..c69e87100 100644 --- a/pkg/config/latest/types_test.go +++ b/pkg/config/latest/types_test.go @@ -1,6 +1,7 @@ package latest import ( + "encoding/json" "testing" "github.com/goccy/go-yaml" @@ -144,6 +145,32 @@ func TestThinkingBudget_IsDisabled(t *testing.T) { } } +func TestThinkingBudget_UnmarshalYAML_InvalidEffort(t *testing.T) { + t.Parallel() + + input := []byte(`thinking_budget: adaptative`) + var config struct { + ThinkingBudget *ThinkingBudget `yaml:"thinking_budget"` + } + + err := yaml.Unmarshal(input, &config) + require.Error(t, err) + require.Contains(t, err.Error(), `invalid thinking_budget effort "adaptative"`) +} + +func TestThinkingBudget_UnmarshalJSON_InvalidEffort(t *testing.T) { + t.Parallel() + + data := []byte(`{"thinking_budget": "adaptative"}`) + var config struct { + ThinkingBudget *ThinkingBudget `json:"thinking_budget"` + } + + err := json.Unmarshal(data, &config) + require.Error(t, err) + require.Contains(t, err.Error(), `invalid thinking_budget effort "adaptative"`) +} + func TestThinkingBudget_IsAdaptive(t *testing.T) { t.Parallel()