Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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,
}
}
12 changes: 11 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,15 @@ 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))
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