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
14 changes: 14 additions & 0 deletions dto/claude.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ type ClaudeRequest struct {
ServiceTier string `json:"service_tier,omitempty"`
}

// OutputConfigForEffort just for extract effort
type OutputConfigForEffort struct {
Effort string `json:"effort,omitempty"`
}

// createClaudeFileSource 根据数据内容创建正确类型的 FileSource
func createClaudeFileSource(data string) *types.FileSource {
if strings.HasPrefix(data, "http://") || strings.HasPrefix(data, "https://") {
Expand Down Expand Up @@ -409,6 +414,15 @@ func (c *ClaudeRequest) GetTools() []any {
}
}

func (c *ClaudeRequest) GetEfforts() string {
var OutputConfig OutputConfigForEffort
if err := json.Unmarshal(c.OutputConfig, &OutputConfig); err == nil {
effort := OutputConfig.Effort
return effort
}
return ""
Comment on lines +417 to +423

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:

# Read the target file to see the actual code at lines 417-423
cd /dev/shm && find . -type f -name "claude.go" -path "*/dto/*" 2>/dev/null | head -5

Repository: QuantumNous/new-api

Length of output: 121


🏁 Script executed:

# List files in dto directory
ls -la dto/ 2>/dev/null | head -20

Repository: QuantumNous/new-api

Length of output: 1351


🏁 Script executed:

# Check what files exist in dto
git ls-files dto/ | head -20

Repository: QuantumNous/new-api

Length of output: 484


🏁 Script executed:

# Search for GetEfforts in the codebase
rg -n "GetEfforts" --type go

Repository: QuantumNous/new-api

Length of output: 207


🏁 Script executed:

# Read the GetEfforts method from dto/claude.go with context
sed -n '410,430p' dto/claude.go

Repository: QuantumNous/new-api

Length of output: 507


🏁 Script executed:

# Check imports at the top of dto/claude.go
head -50 dto/claude.go

Repository: QuantumNous/new-api

Length of output: 1534


🏁 Script executed:

# Check what JSON wrapper functions are available in common/json.go
cat common/json.go

Repository: QuantumNous/new-api

Length of output: 817


Use common.Unmarshal in GetEfforts instead of json.Unmarshal.

Line 419 directly calls json.Unmarshal in business code. This violates the project guideline requiring all JSON marshal/unmarshal operations to use wrapper functions from common/json.go.

Proposed fix
 func (c *ClaudeRequest) GetEfforts() string {
-	var OutputConfig OutputConfigForEffort
-	if err := json.Unmarshal(c.OutputConfig, &OutputConfig); err == nil {
-		effort := OutputConfig.Effort
-		return effort
-	}
-	return ""
+	if len(c.OutputConfig) == 0 {
+		return ""
+	}
+	var outputConfig OutputConfigForEffort
+	if err := common.Unmarshal(c.OutputConfig, &outputConfig); err != nil {
+		return ""
+	}
+	return outputConfig.Effort
 }
📝 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
func (c *ClaudeRequest) GetEfforts() string {
var OutputConfig OutputConfigForEffort
if err := json.Unmarshal(c.OutputConfig, &OutputConfig); err == nil {
effort := OutputConfig.Effort
return effort
}
return ""
func (c *ClaudeRequest) GetEfforts() string {
if len(c.OutputConfig) == 0 {
return ""
}
var outputConfig OutputConfigForEffort
if err := common.Unmarshal(c.OutputConfig, &outputConfig); err != nil {
return ""
}
return outputConfig.Effort
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dto/claude.go` around lines 417 - 423, Replace the direct call to
json.Unmarshal in GetEfforts with the project's wrapper common.Unmarshal: call
common.Unmarshal(c.OutputConfig, &OutputConfig) inside ClaudeRequest.GetEfforts
(which uses the OutputConfigForEffort struct) and preserve the current behavior
of returning OutputConfig.Effort on success and "" on error; ensure the function
handles the returned error from common.Unmarshal the same way the current code
did (i.e., only return effort when err == nil).

}

// ProcessTools 处理工具列表,支持类型断言
func ProcessTools(tools []any) ([]*Tool, []*ClaudeWebSearchTool) {
var normalTools []*Tool
Expand Down
1 change: 1 addition & 0 deletions relay/channel/openai/adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
}

reasoning := openrouter.RequestReasoning{
Enabled: true,
MaxTokens: *thinking.BudgetTokens,
}

Expand Down
1 change: 1 addition & 0 deletions relay/channel/openrouter/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package openrouter
import "encoding/json"

type RequestReasoning struct {
Enabled bool `json:"enabled"`
// One of the following (not both):
Effort string `json:"effort,omitempty"` // Can be "high", "medium", or "low" (OpenAI-style)
MaxTokens int `json:"max_tokens,omitempty"` // Specific token limit (Anthropic-style)
Expand Down
32 changes: 22 additions & 10 deletions service/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,34 @@ func ClaudeToOpenAIRequest(claudeRequest dto.ClaudeRequest, info *relaycommon.Re

isOpenRouter := info.ChannelType == constant.ChannelTypeOpenRouter

if claudeRequest.Thinking != nil && claudeRequest.Thinking.Type == "enabled" {
if isOpenRouter {
reasoning := openrouter.RequestReasoning{
MaxTokens: claudeRequest.Thinking.GetBudgetTokens(),
if isOpenRouter {
if effort := claudeRequest.GetEfforts(); effort != "" {
effortBytes, _ := json.Marshal(effort)
openAIRequest.Verbosity = effortBytes
}
if claudeRequest.Thinking != nil {
var reasoning openrouter.RequestReasoning
if claudeRequest.Thinking.Type == "enabled" {
reasoning = openrouter.RequestReasoning{
Enabled: true,
MaxTokens: claudeRequest.Thinking.GetBudgetTokens(),
}
} else if claudeRequest.Thinking.Type == "adaptive" {
reasoning = openrouter.RequestReasoning{
Enabled: true,
}
}
reasoningJSON, err := json.Marshal(reasoning)
Comment on lines +42 to 54

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 | 🟡 Minor

Guard unsupported thinking types before marshaling reasoning.

If Thinking is non-nil but Type is neither "enabled" nor "adaptive", reasoning remains zero-value and still gets marshaled/assigned. Prefer explicit validation (or skip assignment) to avoid emitting ambiguous {} reasoning payloads.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@service/convert.go` around lines 42 - 54, claudeRequest.Thinking may be
non-nil with an unsupported Type, leaving reasoning as a zero-value struct that
still gets marshaled; update the block around claudeRequest.Thinking to
explicitly validate Type (e.g., switch on claudeRequest.Thinking.Type) and only
construct and marshal openrouter.RequestReasoning when Type is "enabled" or
"adaptive"; for other types either skip setting reasoning (do not call
json.Marshal) or return/propagate an error, ensuring you reference
claudeRequest.Thinking, openrouter.RequestReasoning, and the
reasoningJSON/json.Marshal lines so the marshal is only called when a valid
reasoning value was created.

if err != nil {
return nil, fmt.Errorf("failed to marshal reasoning: %w", err)
}
openAIRequest.Reasoning = reasoningJSON
} else {
thinkingSuffix := "-thinking"
if strings.HasSuffix(info.OriginModelName, thinkingSuffix) &&
!strings.HasSuffix(openAIRequest.Model, thinkingSuffix) {
openAIRequest.Model = openAIRequest.Model + thinkingSuffix
}
}
} else {
thinkingSuffix := "-thinking"
if strings.HasSuffix(info.OriginModelName, thinkingSuffix) &&
!strings.HasSuffix(openAIRequest.Model, thinkingSuffix) {
openAIRequest.Model = openAIRequest.Model + thinkingSuffix
}
}

Expand Down