fix(relay): stop injecting empty tools into Claude requests

This commit is contained in:
zcxads666
2026-08-15 13:55:18 +08:00
committed by GitHub
parent e90a7c48e5
commit 4442bb3028
2 changed files with 80 additions and 1 deletions
@@ -7,6 +7,7 @@ import (
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
sharedclaude "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/shared/claude"
kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -104,6 +105,82 @@ func TestClaudeThinkingAdapterSatisfiesMaxTokensWithoutCallback(t *testing.T) {
assert.Equal(t, uint(1280), *got.MaxTokens)
}
func TestOpenAIChatRequestToClaudeMessagesOmitsEmptyTools(t *testing.T) {
maxTokens := uint(16)
tests := []struct {
name string
request dto.GeneralOpenAIRequest
wantTools bool
}{
{
name: "omitted tools",
request: dto.GeneralOpenAIRequest{
Model: "claude-test",
MaxTokens: &maxTokens,
Messages: []dto.Message{{Role: "user", Content: "hi"}},
},
},
{
name: "explicit empty tools",
request: dto.GeneralOpenAIRequest{
Model: "claude-test",
MaxTokens: &maxTokens,
Messages: []dto.Message{{Role: "user", Content: "hi"}},
Tools: []dto.ToolCallRequest{},
},
},
{
name: "function tool",
request: dto.GeneralOpenAIRequest{
Model: "claude-test",
MaxTokens: &maxTokens,
Messages: []dto.Message{{Role: "user", Content: "hi"}},
Tools: []dto.ToolCallRequest{{
Type: "function",
Function: dto.FunctionRequest{
Name: "get_weather",
Description: "Get weather by city",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{"city": map[string]any{"type": "string"}},
"required": []any{"city"},
},
},
}},
},
wantTools: true,
},
{
name: "web search only",
request: dto.GeneralOpenAIRequest{
Model: "claude-test",
MaxTokens: &maxTokens,
Messages: []dto.Message{{Role: "user", Content: "hi"}},
WebSearchOptions: &dto.WebSearchOptions{SearchContextSize: "low"},
},
wantTools: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got, err := OpenAIChatRequestToClaudeMessages(context.Background(), &convmeta.Values{}, test.request)
require.NoError(t, err)
body, err := kitutil.Marshal(got)
require.NoError(t, err)
if test.wantTools {
assert.NotNil(t, got.Tools)
assert.Contains(t, string(body), `"tools":`)
return
}
assert.Nil(t, got.Tools)
assert.NotContains(t, string(body), `"tools":`)
})
}
}
func claudeDefaultsMeta(defaultMaxTokens func(string) int) convmeta.Meta {
return &convmeta.Values{Options: &convmeta.Options{
Claude: convmeta.ClaudeOptions{DefaultMaxTokens: defaultMaxTokens},
@@ -101,7 +101,9 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te
Model: textRequest.Model,
StopSequences: nil,
Temperature: textRequest.Temperature,
Tools: claudeTools,
}
if len(claudeTools) > 0 {
claudeRequest.Tools = claudeTools
}
if maxTokens := textRequest.GetMaxTokens(); maxTokens > 0 {
claudeRequest.MaxTokens = kitutil.GetPointer(maxTokens)