Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion relay/channel/aws/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package aws
import "strings"

var awsModelIDMap = map[string]string{
"claude-3-sonnet-20240229": "anthropic.claude-3-sonnet-20240229-v1:0",
"claude-3-sonnet-20240229": "anthropic.claude-3-sonnet-20240229-v1:0",
"claude-3-opus-20240229": "anthropic.claude-3-opus-20240229-v1:0",
"claude-3-haiku-20240307": "anthropic.claude-3-haiku-20240307-v1:0",
"claude-3-5-sonnet-20240620": "anthropic.claude-3-5-sonnet-20240620-v1:0",
Expand All @@ -16,6 +16,7 @@ var awsModelIDMap = map[string]string{
"claude-sonnet-4-5-20250929": "anthropic.claude-sonnet-4-5-20250929-v1:0",
"claude-haiku-4-5-20251001": "anthropic.claude-haiku-4-5-20251001-v1:0",
"claude-opus-4-5-20251101": "anthropic.claude-opus-4-5-20251101-v1:0",
"claude-opus-4-6": "anthropic.claude-opus-4-6-v1",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

AWS Bedrock claude-opus-4-6 model ID format anthropic

💡 Result:

AWS Bedrock Anthropic Claude model IDs use this pattern:

  • Base model ID: anthropic.claude-opus-<major>-<minor>-<YYYYMMDD>-v1:0 (e.g., anthropic.claude-opus-4-1-20250805-v1:0, anthropic.claude-opus-4-5-20251101-v1:0) [2]
  • Cross‑region inference profile prefixes (when available): global. / us. / eu. / jp. / apac. + the base model ID (as shown in Anthropic’s Bedrock table) [2]

For Claude Opus 4.6, AWS announced it became available in Bedrock on Feb 5, 2026, but the exact Bedrock model ID string (including the <YYYYMMDD> snapshot) isn’t shown in the public tables I found yet [1][2]. The reliable way to get the exact ID in your region is to list models from Bedrock:

aws bedrock list-foundation-models --by-provider anthropic --query "modelSummaries[*].modelId" --region <your-region>

AWS Bedrock model ID is malformed — missing both snapshot date and version suffix.

According to AWS Bedrock documentation, Anthropic Claude model IDs follow the pattern anthropic.claude-opus-<major>-<minor>-<YYYYMMDD>-v1:0. The entry "anthropic.claude-opus-4-6-v1" is missing both the snapshot date (<YYYYMMDD>) and the :0 version suffix. All other Claude models in this map include these components (e.g., anthropic.claude-opus-4-1-20250805-v1:0). The malformed ID will fail API calls to AWS Bedrock. Update to the correct format with the appropriate snapshot date for Claude Opus 4.6.

🤖 Prompt for AI Agents
In `@relay/channel/aws/constants.go` at line 19, The map entry for the key
"claude-opus-4-6" in relay/channel/aws/constants.go uses a malformed Bedrock
model ID ("anthropic.claude-opus-4-6-v1") missing the snapshot date and the ":0"
suffix; update the value to the full Bedrock ID format
anthropic.claude-opus-4-6-<YYYYMMDD>-v1:0 (replace <YYYYMMDD> with the correct
Claude Opus 4.6 snapshot date) so it matches the pattern used by other entries
(e.g., anthropic.claude-opus-4-1-20250805-v1:0).

// Nova models
"nova-micro-v1:0": "amazon.nova-micro-v1:0",
"nova-lite-v1:0": "amazon.nova-lite-v1:0",
Expand Down Expand Up @@ -79,6 +80,11 @@ var awsModelCanCrossRegionMap = map[string]map[string]bool{
"ap": true,
"eu": true,
},
"anthropic.claude-opus-4-6-v1": {
"us": true,
"ap": true,
"eu": true,
},
"anthropic.claude-haiku-4-5-20251001-v1:0": {
"us": true,
"ap": true,
Expand Down
1 change: 1 addition & 0 deletions relay/channel/aws/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type AwsClaudeRequest struct {
Tools any `json:"tools,omitempty"`
ToolChoice any `json:"tool_choice,omitempty"`
Thinking *dto.Thinking `json:"thinking,omitempty"`
OutputConfig json.RawMessage `json:"output_config,omitempty"`
}

func formatRequest(requestBody io.Reader, requestHeader http.Header) (*AwsClaudeRequest, error) {
Expand Down
5 changes: 5 additions & 0 deletions relay/channel/claude/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ var ModelList = []string{
"claude-sonnet-4-5-20250929-thinking",
"claude-opus-4-5-20251101",
"claude-opus-4-5-20251101-thinking",
"claude-opus-4-6",
"claude-opus-4-6-max",
"claude-opus-4-6-high",
"claude-opus-4-6-medium",
"claude-opus-4-6-low",
}

var ChannelName = "claude"
12 changes: 11 additions & 1 deletion relay/channel/claude/relay-claude.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/QuantumNous/new-api/relay/reasonmap"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/setting/reasoning"
"github.com/QuantumNous/new-api/types"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -141,7 +142,16 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe
claudeRequest.MaxTokens = uint(model_setting.GetClaudeSettings().GetDefaultMaxTokens(textRequest.Model))
}

if model_setting.GetClaudeSettings().ThinkingAdapterEnabled &&
if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(textRequest.Model); ok && effortLevel != "" &&
strings.HasPrefix(textRequest.Model, "claude-opus-4-6") {
claudeRequest.Model = baseModel
claudeRequest.Thinking = &dto.Thinking{
Type: "adaptive",
}
claudeRequest.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel))
claudeRequest.TopP = 0
claudeRequest.Temperature = common.GetPointer[float64](1.0)
} else if model_setting.GetClaudeSettings().ThinkingAdapterEnabled &&
strings.HasSuffix(textRequest.Model, "-thinking") {

// 因为BudgetTokens 必须大于1024
Expand Down
1 change: 1 addition & 0 deletions relay/channel/vertex/adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var claudeModelMap = map[string]string{
"claude-sonnet-4-5-20250929": "claude-sonnet-4-5@20250929",
"claude-haiku-4-5-20251001": "claude-haiku-4-5@20251001",
"claude-opus-4-5-20251101": "claude-opus-4-5@20251101",
"claude-opus-4-6": "claude-opus-4-6",
}

const anthropicVersion = "vertex-2023-10-16"
Expand Down
4 changes: 4 additions & 0 deletions relay/channel/vertex/dto.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package vertex

import (
"encoding/json"

"github.com/QuantumNous/new-api/dto"
)

Expand All @@ -17,6 +19,7 @@ type VertexAIClaudeRequest struct {
Tools any `json:"tools,omitempty"`
ToolChoice any `json:"tool_choice,omitempty"`
Thinking *dto.Thinking `json:"thinking,omitempty"`
OutputConfig json.RawMessage `json:"output_config,omitempty"`
}

func copyRequest(req *dto.ClaudeRequest, version string) *VertexAIClaudeRequest {
Expand All @@ -33,5 +36,6 @@ func copyRequest(req *dto.ClaudeRequest, version string) *VertexAIClaudeRequest
Tools: req.Tools,
ToolChoice: req.ToolChoice,
Thinking: req.Thinking,
OutputConfig: req.OutputConfig,
}
}
14 changes: 13 additions & 1 deletion relay/claude_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package relay

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/QuantumNous/new-api/relay/helper"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/setting/reasoning"
"github.com/QuantumNous/new-api/types"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -49,7 +51,17 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ
request.MaxTokens = uint(model_setting.GetClaudeSettings().GetDefaultMaxTokens(request.Model))
}

if model_setting.GetClaudeSettings().ThinkingAdapterEnabled &&
if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(request.Model); ok && effortLevel != "" &&
strings.HasPrefix(request.Model, "claude-opus-4-6") {
request.Model = baseModel
request.Thinking = &dto.Thinking{
Type: "adaptive",
}
request.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel))
request.TopP = 0
request.Temperature = common.GetPointer[float64](1.0)
info.UpstreamModelName = request.Model
Comment on lines +54 to +63

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bug: Missing TopP and Temperature reset for adaptive thinking.

The parallel code in relay/channel/claude/relay-claude.go (lines 152–153) resets TopP = 0 and Temperature = 1.0 for the adaptive thinking branch, matching the existing -thinking branch in this file (lines 77–78). This branch omits those resets, which can cause Claude API errors if the incoming request carries non-default values.

🐛 Proposed fix
 	if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(request.Model); ok && effortLevel != "" &&
 		strings.HasPrefix(request.Model, "claude-opus-4-6") {
 		request.Model = baseModel
 		request.Thinking = &dto.Thinking{
 			Type: "adaptive",
 		}
 		request.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel))
+		request.TopP = 0
+		request.Temperature = common.GetPointer[float64](1.0)
 		info.UpstreamModelName = request.Model
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(request.Model); ok && effortLevel != "" &&
strings.HasPrefix(request.Model, "claude-opus-4-6") {
request.Model = baseModel
request.Thinking = &dto.Thinking{
Type: "adaptive",
}
request.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel))
info.UpstreamModelName = request.Model
if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(request.Model); ok && effortLevel != "" &&
strings.HasPrefix(request.Model, "claude-opus-4-6") {
request.Model = baseModel
request.Thinking = &dto.Thinking{
Type: "adaptive",
}
request.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel))
request.TopP = 0
request.Temperature = common.GetPointer[float64](1.0)
info.UpstreamModelName = request.Model
🤖 Prompt for AI Agents
In `@relay/claude_handler.go` around lines 54 - 61, The adaptive-thinking branch
that trims the effort suffix (when reasoning.TrimEffortSuffix returns ok and
strings.HasPrefix(request.Model, "claude-opus-4-6")) fails to reset request.TopP
and request.Temperature, which can propagate non-default values and cause Claude
API errors; update that branch in claude_handler.go (the block that sets
request.Model, request.Thinking, request.OutputConfig and
info.UpstreamModelName) to explicitly set request.TopP = 0 and
request.Temperature = 1.0 alongside the existing changes so adaptive requests
use the expected defaults.

} else if model_setting.GetClaudeSettings().ThinkingAdapterEnabled &&
strings.HasSuffix(request.Model, "-thinking") {
if request.Thinking == nil {
// 因为BudgetTokens 必须大于1024
Expand Down
12 changes: 12 additions & 0 deletions setting/ratio_setting/cache_ratio.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ var defaultCacheRatio = map[string]float64{
"claude-sonnet-4-5-20250929-thinking": 0.1,
"claude-opus-4-5-20251101": 0.1,
"claude-opus-4-5-20251101-thinking": 0.1,
"claude-opus-4-6": 0.1,
"claude-opus-4-6-thinking": 0.1,
"claude-opus-4-6-max": 0.1,
"claude-opus-4-6-high": 0.1,
"claude-opus-4-6-medium": 0.1,
"claude-opus-4-6-low": 0.1,
}

var defaultCreateCacheRatio = map[string]float64{
Expand All @@ -82,6 +88,12 @@ var defaultCreateCacheRatio = map[string]float64{
"claude-sonnet-4-5-20250929-thinking": 1.25,
"claude-opus-4-5-20251101": 1.25,
"claude-opus-4-5-20251101-thinking": 1.25,
"claude-opus-4-6": 1.25,
"claude-opus-4-6-thinking": 1.25,
"claude-opus-4-6-max": 1.25,
"claude-opus-4-6-high": 1.25,
"claude-opus-4-6-medium": 1.25,
"claude-opus-4-6-low": 1.25,
}

//var defaultCreateCacheRatio = map[string]float64{}
Expand Down
5 changes: 5 additions & 0 deletions setting/ratio_setting/model_ratio.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ var defaultModelRatio = map[string]float64{
"claude-sonnet-4-20250514": 1.5,
"claude-sonnet-4-5-20250929": 1.5,
"claude-opus-4-5-20251101": 2.5,
"claude-opus-4-6": 2.5,
"claude-opus-4-6-max": 2.5,
"claude-opus-4-6-high": 2.5,
"claude-opus-4-6-medium": 2.5,
"claude-opus-4-6-low": 2.5,
"claude-3-opus-20240229": 7.5, // $15 / 1M tokens
"claude-opus-4-20250514": 7.5,
"claude-opus-4-1-20250805": 7.5,
Expand Down
2 changes: 1 addition & 1 deletion setting/reasoning/suffix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/samber/lo"
)

var EffortSuffixes = []string{"-high", "-medium", "-low", "-minimal"}
var EffortSuffixes = []string{"-max", "-high", "-medium", "-low", "-minimal"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n --type=go 'TrimEffortSuffix' -B 5 -A 5

Repository: QuantumNous/new-api

Length of output: 5151


Add model-prefix guards to three additional callers of TrimEffortSuffix that lack them.

Adding -max to EffortSuffixes is correct for Claude Opus 4.6, which has four effort levels. However, both Claude handler call sites properly guard with strings.HasPrefix(model, "claude-opus-4-6"), while three other callers do not:

  • relay/channel/vertex/adaptor.go:219 (Vertex)
  • relay/channel/gemini/relay-gemini.go:187 (Gemini relay)
  • relay/channel/gemini/adaptor.go:141 (Gemini adaptor)

These unguarded sites will now strip -max from any model name ending in -max, risking incorrect behavior for other providers (e.g., hypothetical gpt-4o-max in Gemini). Add model-provider checks similar to the Claude guards.

🤖 Prompt for AI Agents
In `@setting/reasoning/suffix.go` at line 9, The three callers that invoke
TrimEffortSuffix need the same model-prefix guard used in the Claude handlers:
before calling TrimEffortSuffix, add a check like strings.HasPrefix(model,
"claude-opus-4-6") (or the appropriate provider-specific prefix) so you only
strip the new "-max" suffix for Claude Opus 4.6; update the call sites
referenced (the Vertex adaptor call, the Gemini relay call, and the Gemini
adaptor call) to wrap their TrimEffortSuffix(model) invocation in a conditional
that first verifies the model string prefix, using the existing pattern from the
Claude handlers to avoid stripping "-max" from unrelated models.


// TrimEffortSuffix -> modelName level(low) exists
func TrimEffortSuffix(modelName string) (string, string, bool) {
Expand Down