179 lines
6.0 KiB
Go
179 lines
6.0 KiB
Go
package common
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/QuantumNous/new-api/relaykit/dto"
|
|
"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
|
|
"github.com/QuantumNous/new-api/relaykit/types"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatPrefersExplicitFinal(t *testing.T) {
|
|
info := &RelayInfo{
|
|
RelayFormat: types.RelayFormatOpenAI,
|
|
RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatClaude},
|
|
FinalRequestRelayFormat: types.RelayFormatOpenAIResponses,
|
|
}
|
|
|
|
require.Equal(t, types.RelayFormat(types.RelayFormatOpenAIResponses), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatFallsBackToConversionChain(t *testing.T) {
|
|
info := &RelayInfo{
|
|
RelayFormat: types.RelayFormatOpenAI,
|
|
RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatClaude},
|
|
}
|
|
|
|
require.Equal(t, types.RelayFormat(types.RelayFormatClaude), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatFallsBackToRelayFormat(t *testing.T) {
|
|
info := &RelayInfo{
|
|
RelayFormat: types.RelayFormatGemini,
|
|
}
|
|
|
|
require.Equal(t, types.RelayFormat(types.RelayFormatGemini), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatNilReceiver(t *testing.T) {
|
|
var info *RelayInfo
|
|
require.Equal(t, types.RelayFormat(""), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestRelayInfoMetaTypedNilReceiver(t *testing.T) {
|
|
var info *RelayInfo
|
|
var meta convmeta.Meta = info
|
|
|
|
assert.Empty(t, meta.GetOriginModelName())
|
|
assert.Empty(t, meta.GetUpstreamModelName())
|
|
assert.False(t, meta.HasChannelMeta())
|
|
assert.Zero(t, meta.GetChannelID())
|
|
assert.Zero(t, meta.GetChannelType())
|
|
assert.False(t, meta.GetIsStream())
|
|
assert.Empty(t, meta.GetReasoningEffort())
|
|
assert.Zero(t, meta.GetEstimatePromptTokens())
|
|
assert.Zero(t, meta.GetSendResponseCount())
|
|
|
|
assert.NotPanics(t, func() {
|
|
meta.SetReasoningEffort("high")
|
|
meta.IncrSendResponseCount()
|
|
meta.AppendRequestConversion(types.RelayFormatClaude)
|
|
})
|
|
|
|
firstState := meta.EnsureClaudeConvertInfo()
|
|
secondState := meta.EnsureClaudeConvertInfo()
|
|
require.NotNil(t, firstState)
|
|
require.NotNil(t, secondState)
|
|
assert.Equal(t, convmeta.LastMessageTypeNone, firstState.LastMessagesType)
|
|
assert.NotSame(t, firstState, secondState)
|
|
|
|
firstOptions := meta.ConvOptions()
|
|
secondOptions := meta.ConvOptions()
|
|
require.NotNil(t, firstOptions)
|
|
require.NotNil(t, secondOptions)
|
|
assert.NotSame(t, firstOptions, secondOptions)
|
|
assert.NotNil(t, firstOptions.Claude.DefaultMaxTokens)
|
|
assert.NotNil(t, firstOptions.Gemini.SupportsImagine)
|
|
assert.NotNil(t, firstOptions.Gemini.SafetySetting)
|
|
assert.NotNil(t, firstOptions.PreserveThinkingSuffix)
|
|
}
|
|
|
|
func TestGenRelayInfoCapturesRequestReasoningEffort(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
tests := []struct {
|
|
name string
|
|
path string
|
|
relayFormat types.RelayFormat
|
|
request dto.Request
|
|
expected string
|
|
}{
|
|
{
|
|
name: "OpenAI chat top-level effort",
|
|
path: "/v1/chat/completions",
|
|
relayFormat: types.RelayFormatOpenAI,
|
|
request: &dto.GeneralOpenAIRequest{Model: "gpt-5.6-sol", ReasoningEffort: " high "},
|
|
expected: "high",
|
|
},
|
|
{
|
|
name: "OpenRouter nested chat effort",
|
|
path: "/v1/chat/completions",
|
|
relayFormat: types.RelayFormatOpenAI,
|
|
request: &dto.GeneralOpenAIRequest{Model: "anthropic/claude", Reasoning: json.RawMessage(`{"effort":"xhigh"}`)},
|
|
expected: "xhigh",
|
|
},
|
|
{
|
|
name: "OpenAI Responses effort",
|
|
path: "/v1/responses",
|
|
relayFormat: types.RelayFormatOpenAIResponses,
|
|
request: &dto.OpenAIResponsesRequest{Model: "gpt-5.6-sol", Reasoning: &dto.Reasoning{Effort: "max"}},
|
|
expected: "max",
|
|
},
|
|
{
|
|
name: "explicit none is preserved",
|
|
path: "/v1/responses",
|
|
relayFormat: types.RelayFormatOpenAIResponses,
|
|
request: &dto.OpenAIResponsesRequest{Model: "gpt-5.6-sol", Reasoning: &dto.Reasoning{Effort: "none"}},
|
|
expected: "none",
|
|
},
|
|
{
|
|
name: "non-string nested effort is ignored",
|
|
path: "/v1/chat/completions",
|
|
relayFormat: types.RelayFormatOpenAI,
|
|
request: &dto.GeneralOpenAIRequest{Model: "anthropic/claude", Reasoning: json.RawMessage(`{"effort":42}`)},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "Claude output config effort",
|
|
path: "/v1/messages",
|
|
relayFormat: types.RelayFormatClaude,
|
|
request: &dto.ClaudeRequest{Model: "claude-opus-4-7", OutputConfig: json.RawMessage(`{"effort":"medium"}`)},
|
|
expected: "medium",
|
|
},
|
|
{
|
|
name: "Gemini thinking level",
|
|
path: "/v1beta/models/gemini-3-pro:generateContent",
|
|
relayFormat: types.RelayFormatGemini,
|
|
request: &dto.GeminiChatRequest{GenerationConfig: dto.GeminiChatGenerationConfig{
|
|
ThinkingConfig: &dto.GeminiThinkingConfig{ThinkingLevel: "low"},
|
|
}},
|
|
expected: "low",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
|
ctx.Request = httptest.NewRequest("POST", tt.path, nil)
|
|
|
|
info, err := GenRelayInfo(ctx, tt.relayFormat, tt.request, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.expected, info.ReasoningEffort)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInitChannelMetaRestoresRequestReasoningEffortForRetry(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
|
ctx.Request = httptest.NewRequest("POST", "/v1/responses", nil)
|
|
request := &dto.OpenAIResponsesRequest{
|
|
Model: "gpt-5.6-sol",
|
|
Reasoning: &dto.Reasoning{Effort: "max"},
|
|
}
|
|
info, err := GenRelayInfo(ctx, types.RelayFormatOpenAIResponses, request, nil)
|
|
require.NoError(t, err)
|
|
|
|
info.SetReasoningEffort("high")
|
|
info.InitChannelMeta(ctx)
|
|
assert.Equal(t, "max", info.ReasoningEffort)
|
|
|
|
info.SetReasoningEffort("low")
|
|
info.InitChannelMeta(ctx)
|
|
assert.Equal(t, "max", info.ReasoningEffort)
|
|
}
|