on main help sign in
fix(bedrock): honor reasoning_effort for Anthropic-on-Bedrock models (#2887)

Bedrock routes through Fantasy's Anthropic implementation with a different display name, so provider options must still be filed under anthropic.Name for the language model to pick them up. Previously the bedrock provider type was missing from getProviderOptions entirely, so reasoning_effort and think were silently dropped. Co-authored-by: JJ Bot <john.jansen+bot@me.com>

commit0490a8a6b48f7bd956a9a017f87549a0540c68a3 authorJohn Jansen <john.jansen@me.com> dateWed 13 May 2026, 08:31 (5 months ago) parent295a62d
2 files changed+36−1
internal/agent/coordinator.go+1−1 internal/agent/coordinator_test.go+35
internal/agent/coordinator.go+1−1
@@ -286,7 +286,7 @@ func getProviderOptions(model Model, providerCfg config.ProviderConfig) fantasy.
286286 options[openai.Name] = parsed
287287 }
288288 }
289− case anthropic.Name:
289+ case anthropic.Name, bedrock.Name:
290290 var (
291291 _, hasEffort = mergedOptions["effort"]
292292 _, hasThink = mergedOptions["thinking"]
internal/agent/coordinator_test.go+35
@@ -7,6 +7,8 @@ import (
77
88 "charm.land/catwalk/pkg/catwalk"
99 "charm.land/fantasy"
10+ "charm.land/fantasy/providers/anthropic"
11+ "charm.land/fantasy/providers/bedrock"
1012 "github.com/charmbracelet/crush/internal/config"
1113 "github.com/stretchr/testify/assert"
1214 "github.com/stretchr/testify/require"
@@ -383,3 +385,36 @@ func TestUpdateParentSessionCost(t *testing.T) {
383385 assert.InDelta(t, 0.0, updated.Cost, 1e-9)
384386 })
385387}
388+
389+func TestGetProviderOptionsReasoningEffort(t *testing.T) {
390+ // Bedrock is Fantasy's Anthropic under a different provider name; options
391+ // must land under anthropic.Name so the Anthropic language model picks them up.
392+ tests := []struct {
393+ name string
394+ providerType catwalk.Type
395+ }{
396+ {"anthropic honors reasoning_effort", catwalk.Type(anthropic.Name)},
397+ {"bedrock honors reasoning_effort", catwalk.Type(bedrock.Name)},
398+ }
399+ for _, tc := range tests {
400+ t.Run(tc.name, func(t *testing.T) {
401+ model := Model{
402+ CatwalkCfg: catwalk.Model{ID: "claude-opus-4-7"},
403+ ModelCfg: config.SelectedModel{
404+ Provider: "test",
405+ ReasoningEffort: "max",
406+ },
407+ }
408+ providerCfg := config.ProviderConfig{ID: "test", Type: tc.providerType}
409+
410+ opts := getProviderOptions(model, providerCfg)
411+
412+ raw, ok := opts[anthropic.Name]
413+ require.True(t, ok, "options should be keyed under anthropic.Name for type %q", tc.providerType)
414+ parsed, ok := raw.(*anthropic.ProviderOptions)
415+ require.True(t, ok)
416+ require.NotNil(t, parsed.Effort)
417+ assert.Equal(t, anthropic.Effort("max"), *parsed.Effort)
418+ })
419+ }
420+}