@@ -13,6 +13,7 @@ import (
1313 "github.com/goccy/go-yaml"
1414
1515 "github.com/docker/docker-agent/pkg/config/types"
16+ "github.com/docker/docker-agent/pkg/effort"
1617)
1718
1819const Version = "7"
@@ -396,13 +397,10 @@ type ModelConfig struct {
396397 // ProviderOpts allows provider-specific options.
397398 ProviderOpts map [string ]any `json:"provider_opts,omitempty"`
398399 TrackUsage * bool `json:"track_usage,omitempty"`
399- // ThinkingBudget controls reasoning effort/budget:
400- // - For OpenAI: accepts string levels "minimal", "low", "medium", "high", "xhigh"
401- // - For Anthropic: accepts integer token budget (1024-32000), "adaptive",
402- // or string levels "low", "medium", "high", "max" (uses adaptive thinking with effort)
403- // - For Bedrock Claude: accepts integer token budget or string levels
404- // "minimal", "low", "medium", "high" (mapped to token budgets via EffortTokens)
405- // - For other providers: may be ignored
400+ // ThinkingBudget controls reasoning effort/budget.
401+ // Accepts an integer token count or a string effort level.
402+ // See [effort.ValidNames] for the full list of accepted strings.
403+ // Provider-specific mappings are in the effort package.
406404 ThinkingBudget * ThinkingBudget `json:"thinking_budget,omitempty"`
407405 // Routing defines rules for routing requests to different models.
408406 // When routing is configured, this model becomes a rule-based router:
@@ -672,42 +670,15 @@ func (d DeferConfig) MarshalYAML() (any, error) {
672670}
673671
674672// ThinkingBudget represents reasoning budget configuration.
675- // It accepts either a string effort level or an integer token budget:
676- // - String: "minimal", "low", "medium", "high", "xhigh" (for OpenAI)
677- // - String: "adaptive" (Anthropic adaptive thinking with high effort by default)
678- // - String: "adaptive/<effort>" where effort is low/medium/high/max (Anthropic adaptive with specified effort)
679- // - Integer: token count (for Anthropic, range 1024-32768)
673+ // It accepts either a string effort level (see [effort.ValidNames]) or an
674+ // integer token budget.
680675type ThinkingBudget struct {
681676 // Effort stores string-based reasoning effort levels
682677 Effort string `json:"effort,omitempty"`
683678 // Tokens stores integer-based token budgets
684679 Tokens int `json:"tokens,omitempty"`
685680}
686681
687- // validThinkingEfforts lists all accepted string values for thinking_budget.
688- const validThinkingEfforts = "none, minimal, low, medium, high, xhigh, max, adaptive, adaptive/<effort>"
689-
690- // validAdaptiveEfforts lists the accepted effort levels for adaptive thinking.
691- var validAdaptiveEfforts = map [string ]bool {
692- "low" : true , "medium" : true , "high" : true , "max" : true ,
693- }
694-
695- // isValidThinkingEffort reports whether s (case-insensitive, trimmed) is a
696- // recognised thinking_budget effort level.
697- func isValidThinkingEffort (s string ) bool {
698- norm := strings .ToLower (strings .TrimSpace (s ))
699- switch norm {
700- case "none" , "minimal" , "low" , "medium" , "high" , "xhigh" , "max" , "adaptive" :
701- return true
702- default :
703- // Support "adaptive/<effort>" format (e.g. "adaptive/high")
704- if after , ok := strings .CutPrefix (norm , "adaptive/" ); ok {
705- return validAdaptiveEfforts [after ]
706- }
707- return false
708- }
709- }
710-
711682func (t * ThinkingBudget ) UnmarshalYAML (unmarshal func (any ) error ) error {
712683 // Try integer tokens first
713684 var n int
@@ -719,8 +690,8 @@ func (t *ThinkingBudget) UnmarshalYAML(unmarshal func(any) error) error {
719690 // Try string level
720691 var s string
721692 if err := unmarshal (& s ); err == nil {
722- if ! isValidThinkingEffort (s ) {
723- return fmt .Errorf ("invalid thinking_budget effort %q: must be one of %s" , s , validThinkingEfforts )
693+ if ! effort . IsValid (s ) {
694+ return fmt .Errorf ("invalid thinking_budget effort %q: must be one of %s" , s , effort . ValidNames () )
724695 }
725696 * t = ThinkingBudget {Effort : s }
726697 return nil
@@ -772,6 +743,16 @@ func (t *ThinkingBudget) IsAdaptive() bool {
772743 return norm == "adaptive" || strings .HasPrefix (norm , "adaptive/" )
773744}
774745
746+ // EffortLevel parses the Effort field into an [effort.Level].
747+ // Returns ("", false) when the budget is nil, uses token counts, or has an
748+ // unrecognised effort string.
749+ func (t * ThinkingBudget ) EffortLevel () (effort.Level , bool ) {
750+ if t == nil {
751+ return "" , false
752+ }
753+ return effort .Parse (t .Effort )
754+ }
755+
775756// AdaptiveEffort returns the effort level for adaptive thinking.
776757// For "adaptive" it returns the default ("high").
777758// For "adaptive/<effort>" it returns the specified effort.
@@ -789,28 +770,16 @@ func (t *ThinkingBudget) AdaptiveEffort() (string, bool) {
789770
790771// EffortTokens maps a string effort level to a token budget for providers
791772// that only support token-based thinking (e.g. Bedrock Claude).
792- //
793- // The Anthropic direct API uses adaptive thinking + output_config.effort
794- // for string levels instead; see anthropicEffort in the anthropic package.
773+ // Delegates to [effort.BedrockTokens].
795774//
796775// Returns (tokens, true) when a mapping exists, or (0, false) when
797776// the budget uses an explicit token count or an unrecognised effort string.
798777func (t * ThinkingBudget ) EffortTokens () (int , bool ) {
799- if t == nil || t .Effort == "" {
800- return 0 , false
801- }
802- switch strings .ToLower (strings .TrimSpace (t .Effort )) {
803- case "minimal" :
804- return 1024 , true
805- case "low" :
806- return 2048 , true
807- case "medium" :
808- return 8192 , true
809- case "high" :
810- return 16384 , true
811- default :
778+ l , ok := t .EffortLevel ()
779+ if ! ok {
812780 return 0 , false
813781 }
782+ return effort .BedrockTokens (l )
814783}
815784
816785// MarshalJSON implements custom marshaling to output simple string or int format
@@ -838,8 +807,8 @@ func (t *ThinkingBudget) UnmarshalJSON(data []byte) error {
838807 // Try string level
839808 var s string
840809 if err := json .Unmarshal (data , & s ); err == nil {
841- if ! isValidThinkingEffort (s ) {
842- return fmt .Errorf ("invalid thinking_budget effort %q: must be one of %s" , s , validThinkingEfforts )
810+ if ! effort . IsValid (s ) {
811+ return fmt .Errorf ("invalid thinking_budget effort %q: must be one of %s" , s , effort . ValidNames () )
843812 }
844813 * t = ThinkingBudget {Effort : s }
845814 return nil
0 commit comments