diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index 594afd4..d4b4fbe 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -163,7 +163,7 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe // BudgetTokens 为 max_tokens 的 80% claudeRequest.Thinking = &dto.Thinking{ - Type: "enabled", + Type: "adaptive", BudgetTokens: common.GetPointer[int](int(float64(claudeRequest.MaxTokens) * model_setting.GetClaudeSettings().ThinkingAdapterBudgetTokensPercentage)), } // TODO: 临时处理 @@ -179,17 +179,17 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe switch textRequest.ReasoningEffort { case "low": claudeRequest.Thinking = &dto.Thinking{ - Type: "enabled", + Type: "adaptive", BudgetTokens: common.GetPointer[int](1280), } case "medium": claudeRequest.Thinking = &dto.Thinking{ - Type: "enabled", + Type: "adaptive", BudgetTokens: common.GetPointer[int](2048), } case "high": claudeRequest.Thinking = &dto.Thinking{ - Type: "enabled", + Type: "adaptive", BudgetTokens: common.GetPointer[int](4096), } } @@ -205,7 +205,7 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe budgetTokens := reasoning.MaxTokens if budgetTokens > 0 { claudeRequest.Thinking = &dto.Thinking{ - Type: "enabled", + Type: "adaptive", BudgetTokens: &budgetTokens, } } diff --git a/relay/claude_handler.go b/relay/claude_handler.go index d74fa57..3b951c6 100644 --- a/relay/claude_handler.go +++ b/relay/claude_handler.go @@ -71,7 +71,7 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ // BudgetTokens 为 max_tokens 的 80% request.Thinking = &dto.Thinking{ - Type: "enabled", + Type: "adaptive", BudgetTokens: common.GetPointer[int](int(float64(request.MaxTokens) * model_setting.GetClaudeSettings().ThinkingAdapterBudgetTokensPercentage)), } // TODO: 临时处理 @@ -127,13 +127,30 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ return nil } + // Fix thinking.type for models that only support "adaptive" (e.g. claude-sonnet-4-6, claude-opus-4-6). + // Anthropic returns 400 if thinking.type="enabled" is sent to these models. + if request.Thinking != nil && request.Thinking.Type == "enabled" { + request.Thinking.Type = "adaptive" + } + var requestBody io.Reader if model_setting.GetGlobalSettings().PassThroughRequestEnabled || info.ChannelSetting.PassThroughBodyEnabled { storage, err := common.GetBodyStorage(c) if err != nil { return types.NewErrorWithStatusCode(err, types.ErrorCodeReadRequestBodyFailed, http.StatusBadRequest, types.ErrOptionWithSkipRetry()) } - requestBody = common.ReaderOnly(storage) + // In passthrough mode, fix thinking.type in the raw JSON body. + if request.Thinking != nil && request.Thinking.Type == "adaptive" { + bodyBytes, readErr := io.ReadAll(storage) + if readErr == nil { + bodyBytes = bytes.Replace(bodyBytes, []byte(`"thinking":{"type":"enabled"`), []byte(`"thinking":{"type":"adaptive"`), 1) + requestBody = bytes.NewBuffer(bodyBytes) + } else { + requestBody = bytes.NewBuffer(nil) + } + } else { + requestBody = common.ReaderOnly(storage) + } } else { convertedRequest, err := adaptor.ConvertClaudeRequest(c, info, request) if err != nil {