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()