feat: publish v1.0.0-rc.36-custom.1
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
This commit is contained in:
commit
bb7968b612
2381 files changed
+551494
No files matched your search
@@ -0,0 +1,588 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
kitreasoning "github.com/QuantumNous/new-api/relaykit/relayconvert/reasoning"
|
||||
"github.com/QuantumNous/new-api/relaykit/types"
|
||||
"github.com/QuantumNous/new-api/setting/model_setting"
|
||||
hostreasoning "github.com/QuantumNous/new-api/setting/reasoning"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func mustApplyReasoningModelSuffix(t *testing.T, info *relaycommon.RelayInfo, outbound ...dto.Request) {
|
||||
t.Helper()
|
||||
require.NoError(t, ApplyReasoningModelSuffix(nil, info, outbound...))
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixTrimsUpstreamAndAttachesState(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "claude-3-7-sonnet-thinking",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "claude-3-7-sonnet-thinking",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "claude-3-7-sonnet", info.UpstreamModelName)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "enabled", info.ReasoningConversion.Mode)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixRetryKeepsEquivalentState(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "claude-opus-4-8-high",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "claude-opus-4-8-high",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
firstMode := info.ReasoningConversion.Mode
|
||||
firstEffort := info.ReasoningConversion.Effort
|
||||
|
||||
info.UpstreamModelName = info.OriginModelName
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, firstMode, info.ReasoningConversion.Mode)
|
||||
assert.Equal(t, firstEffort, info.ReasoningConversion.Effort)
|
||||
assert.Equal(t, "claude-opus-4-8", info.UpstreamModelName)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixRetryClearsStateWhenNewChannelHasNoSuffix(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
req := &dto.ClaudeRequest{Model: "claude-3-7-sonnet"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "claude-3-7-sonnet",
|
||||
Request: req,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "claude-3-7-sonnet-thinking",
|
||||
IsModelMapped: true,
|
||||
},
|
||||
}
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
require.NotNil(t, info.ReasoningState())
|
||||
|
||||
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
ctx.Request = httptest.NewRequest("POST", "/v1/messages", nil)
|
||||
common.SetContextKey(ctx, constant.ContextKeyOriginalModel, "claude-3-7-sonnet")
|
||||
common.SetContextKey(ctx, constant.ContextKeyChannelType, constant.ChannelTypeAnthropic)
|
||||
info.InitChannelMeta(ctx)
|
||||
assert.Nil(t, info.ReasoningState())
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Nil(t, info.ReasoningState())
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixPassThroughDoesNotTrim(t *testing.T) {
|
||||
settings := model_setting.GetGlobalSettings()
|
||||
original := settings.PassThroughRequestEnabled
|
||||
t.Cleanup(func() { settings.PassThroughRequestEnabled = original })
|
||||
settings.PassThroughRequestEnabled = true
|
||||
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "claude-3-7-sonnet-thinking",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "claude-3-7-sonnet-thinking",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "claude-3-7-sonnet-thinking", info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixBlacklistDoesNotTrim(t *testing.T) {
|
||||
settings := model_setting.GetGlobalSettings()
|
||||
original := append([]string(nil), settings.ThinkingModelBlacklist...)
|
||||
t.Cleanup(func() { settings.ThinkingModelBlacklist = original })
|
||||
settings.ThinkingModelBlacklist = append(settings.ThinkingModelBlacklist, "claude-3-7-sonnet-thinking")
|
||||
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "claude-3-7-sonnet-thinking",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "claude-3-7-sonnet-thinking",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "claude-3-7-sonnet-thinking", info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixModifierOverridesExplicitConflict(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "claude-3-7-sonnet-thinking",
|
||||
Request: &dto.ClaudeRequest{
|
||||
Model: "claude-3-7-sonnet-thinking",
|
||||
Thinking: &dto.Thinking{Type: "disabled"},
|
||||
},
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "claude-3-7-sonnet-thinking",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, info.Request)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "enabled", info.ReasoningConversion.Mode)
|
||||
assert.Nil(t, info.Request.(*dto.ClaudeRequest).Thinking)
|
||||
diagnostics := info.ConversionDiagnostics()
|
||||
require.NotEmpty(t, diagnostics)
|
||||
assert.Equal(t, "model_modifier_overrode_request", diagnostics[0].Code)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixGeminiNoThinkingWhenAdapterEnabled(t *testing.T) {
|
||||
settings := model_setting.GetGeminiSettings()
|
||||
original := settings.ThinkingAdapterEnabled
|
||||
t.Cleanup(func() { settings.ThinkingAdapterEnabled = original })
|
||||
settings.ThinkingAdapterEnabled = true
|
||||
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "gemini-2.5-flash-nothinking",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "gemini-2.5-flash-nothinking",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "gemini-2.5-flash", info.UpstreamModelName)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "disabled", info.ReasoningConversion.Mode)
|
||||
assert.Equal(t, "none", info.ReasoningConversion.Effort)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixPreservesEffortTailModelID(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "qwen-max",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "qwen-max",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "qwen-max", info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixLeavesDeepSeekV4SuffixForAdaptor(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "deepseek-v4-chat-max",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
ChannelType: constant.ChannelTypeDeepSeek,
|
||||
UpstreamModelName: "deepseek-v4-chat-max",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "deepseek-v4-chat-max", info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixLeavesVolcengineDeepSeekThinkingForAdaptor(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "deepseek-r1-thinking",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
ChannelType: constant.ChannelTypeVolcEngine,
|
||||
UpstreamModelName: "deepseek-r1-thinking",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "deepseek-r1-thinking", info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixStillParsesOpenAIEffortTail(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "gpt-5.1-high",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
ChannelType: constant.ChannelTypeOpenAI,
|
||||
UpstreamModelName: "gpt-5.1-high",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "gpt-5.1", info.UpstreamModelName)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "enabled", info.ReasoningConversion.Mode)
|
||||
assert.Equal(t, "high", info.ReasoningConversion.Effort)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixLeavesUnknownOpenRouterThinkingModel(t *testing.T) {
|
||||
openRouter := &relaycommon.RelayInfo{
|
||||
OriginModelName: "some-model-thinking",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
ChannelType: constant.ChannelTypeOpenRouter,
|
||||
UpstreamModelName: "some-model-thinking",
|
||||
},
|
||||
}
|
||||
mustApplyReasoningModelSuffix(t, openRouter)
|
||||
assert.Equal(t, "some-model-thinking", openRouter.UpstreamModelName)
|
||||
assert.Nil(t, openRouter.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixLeavesVersionedQwenMaxUntouched(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "qwen3.8-max",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "qwen3.8-max",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "qwen3.8-max", info.UpstreamModelName)
|
||||
assert.Empty(t, info.BillingModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixTreatsCloudflareAtAsModelName(t *testing.T) {
|
||||
const model = "@cf/meta/llama-3.1-8b-instruct"
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: model,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: model},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, model, info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixAppliesExplicitModifierChain(t *testing.T) {
|
||||
temperature := 0.9
|
||||
topP := 1.0
|
||||
request := &dto.GeneralOpenAIRequest{
|
||||
Model: "qwen3.8-max@thinking:on@effort:high@temperature:0.2@topp:0.8",
|
||||
ReasoningEffort: "low",
|
||||
Temperature: &temperature,
|
||||
TopP: &topP,
|
||||
}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, "qwen3.8-max", info.UpstreamModelName)
|
||||
assert.Empty(t, info.BillingModelName)
|
||||
assert.Equal(t, "qwen3.8-max", request.Model)
|
||||
assert.Equal(t, 0.2, *request.Temperature)
|
||||
assert.Equal(t, 0.8, *request.TopP)
|
||||
assert.Equal(t, "high", request.ReasoningEffort)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "enabled", info.ReasoningConversion.Mode)
|
||||
assert.Equal(t, "high", info.ReasoningConversion.Effort)
|
||||
assert.Contains(t, info.ConversionDiagnostics(), types.ConversionDiagnostic{
|
||||
Code: "model_modifier_overrode_request",
|
||||
Path: "model.@temperature",
|
||||
Message: "model temperature modifier overrides request temperature 0.9",
|
||||
Severity: types.ConversionDiagnosticWarning,
|
||||
})
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixRejectsUnknownModifierKey(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "m@thinkin:on"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
err := ApplyReasoningModelSuffix(nil, info, request)
|
||||
require.Error(t, err)
|
||||
assert.True(t, kitreasoning.IsClientError(err))
|
||||
assert.Contains(t, err.Error(), `unsupported model modifier "thinkin"`)
|
||||
assert.Contains(t, err.Error(), "Models that skip thinking suffix processing")
|
||||
assert.Contains(t, err.Error(), "re:")
|
||||
assert.Equal(t, "m@thinkin:on", info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixThinkingOnOnly(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "qwen3.8-max@thinking:on"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, "qwen3.8-max", info.UpstreamModelName)
|
||||
assert.Equal(t, "", request.ReasoningEffort)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "enabled", info.ReasoningConversion.Mode)
|
||||
assert.Equal(t, "", info.ReasoningConversion.Effort)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixThinkingOff(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "qwen3.8-max@thinking:off"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, "none", request.ReasoningEffort)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "disabled", info.ReasoningConversion.Mode)
|
||||
assert.Equal(t, "none", info.ReasoningConversion.Effort)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixThinkingLegacyValuesRejected(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "qwen3.8-max@thinking:enabled"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
err := ApplyReasoningModelSuffix(nil, info, request)
|
||||
require.Error(t, err)
|
||||
assert.True(t, kitreasoning.IsClientError(err))
|
||||
assert.Contains(t, err.Error(), `invalid thinking modifier value "enabled"`)
|
||||
assert.Contains(t, err.Error(), "Models that skip thinking suffix processing")
|
||||
assert.Equal(t, "qwen3.8-max@thinking:enabled", info.UpstreamModelName)
|
||||
assert.Equal(t, "qwen3.8-max@thinking:enabled", request.Model)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
assert.Empty(t, info.ConversionDiagnostics())
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixDuplicateModifierLastWins(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "qwen3.8-max@thinking:on@thinking:off"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, "qwen3.8-max", info.UpstreamModelName)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "disabled", info.ReasoningConversion.Mode)
|
||||
assert.Contains(t, info.ConversionDiagnostics(), types.ConversionDiagnostic{
|
||||
Code: "duplicate_model_modifier",
|
||||
Path: "model.@thinking",
|
||||
Message: "model modifier \"thinking\" is repeated; the rightmost value is used",
|
||||
Severity: types.ConversionDiagnosticWarning,
|
||||
})
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixExactExemptionKeepsOpaqueName(t *testing.T) {
|
||||
const model = "opaque@sha256:abc"
|
||||
settings := model_setting.GetGlobalSettings()
|
||||
original := append([]string(nil), settings.ThinkingModelBlacklist...)
|
||||
t.Cleanup(func() { settings.ThinkingModelBlacklist = original })
|
||||
settings.ThinkingModelBlacklist = append(original, model)
|
||||
|
||||
request := &dto.GeneralOpenAIRequest{Model: model}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, model, info.UpstreamModelName)
|
||||
assert.Equal(t, model, request.Model)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
assert.Empty(t, info.ConversionDiagnostics())
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixRegexExemptionKeepsOpaqueName(t *testing.T) {
|
||||
const model = "m@sha256:abc"
|
||||
settings := model_setting.GetGlobalSettings()
|
||||
original := append([]string(nil), settings.ThinkingModelBlacklist...)
|
||||
t.Cleanup(func() { settings.ThinkingModelBlacklist = original })
|
||||
settings.ThinkingModelBlacklist = append(original, "re:.*@sha256:.*")
|
||||
|
||||
request := &dto.GeneralOpenAIRequest{Model: model}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, model, info.UpstreamModelName)
|
||||
assert.Equal(t, model, request.Model)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
assert.Empty(t, info.ConversionDiagnostics())
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixEffortNoneDisables(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "qwen3.8-max@effort:none"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, "none", request.ReasoningEffort)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "disabled", info.ReasoningConversion.Mode)
|
||||
assert.Equal(t, "none", info.ReasoningConversion.Effort)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixPassThroughKeepsModifierBodyVerbatim(t *testing.T) {
|
||||
settings := model_setting.GetGlobalSettings()
|
||||
original := settings.PassThroughRequestEnabled
|
||||
t.Cleanup(func() { settings.PassThroughRequestEnabled = original })
|
||||
settings.PassThroughRequestEnabled = true
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
const model = "qwen3.8-max@thinking:on@effort:high@temperature:0.2@topp:0.8"
|
||||
body := `{"model":"` + model + `","messages":[],"vendor_extension":{"keep":true}}`
|
||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
c.Request = httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(body))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
request := &dto.GeneralOpenAIRequest{Model: model}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: model,
|
||||
Request: request,
|
||||
RelayFormat: types.RelayFormatOpenAI,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: model,
|
||||
},
|
||||
}
|
||||
|
||||
require.NoError(t, ApplyReasoningModelSuffix(c, info, request))
|
||||
assert.Equal(t, model, info.UpstreamModelName)
|
||||
assert.Equal(t, model, request.Model)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
assert.Empty(t, info.ConversionDiagnostics())
|
||||
storage, err := common.GetBodyStorage(c)
|
||||
require.NoError(t, err)
|
||||
got, err := storage.Bytes()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, body, string(got))
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixChannelPassThroughKeepsModifierBodyVerbatim(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
const model = "qwen3.8-max@thinking:on"
|
||||
body := `{"model":"` + model + `","messages":[]}`
|
||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
c.Request = httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(body))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
request := &dto.GeneralOpenAIRequest{Model: model}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: model,
|
||||
ChannelSetting: dto.ChannelSettings{PassThroughBodyEnabled: true},
|
||||
},
|
||||
}
|
||||
|
||||
require.NoError(t, ApplyReasoningModelSuffix(c, info, request))
|
||||
assert.Equal(t, model, info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
storage, err := common.GetBodyStorage(c)
|
||||
require.NoError(t, err)
|
||||
got, err := storage.Bytes()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, body, string(got))
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixPassThroughAllowsUnknownModifier(t *testing.T) {
|
||||
settings := model_setting.GetGlobalSettings()
|
||||
original := settings.PassThroughRequestEnabled
|
||||
t.Cleanup(func() { settings.PassThroughRequestEnabled = original })
|
||||
settings.PassThroughRequestEnabled = true
|
||||
|
||||
const model = "m@sha256:abc"
|
||||
request := &dto.GeneralOpenAIRequest{Model: model}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, model, info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixAppliesModifiersWhenPassThroughOff(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "qwen3.8-max@thinking:on@effort:high"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
assert.Equal(t, "qwen3.8-max", info.UpstreamModelName)
|
||||
assert.Equal(t, "high", request.ReasoningEffort)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "enabled", info.ReasoningConversion.Mode)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixThinkingMinusOnePassthrough(t *testing.T) {
|
||||
request := &dto.GeneralOpenAIRequest{Model: "m@thinking:-1"}
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: request.Model,
|
||||
Request: request,
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: request.Model,
|
||||
},
|
||||
}
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
mustApplyReasoningModelSuffix(t, info, request)
|
||||
})
|
||||
assert.Equal(t, "m", info.UpstreamModelName)
|
||||
require.NotNil(t, info.ReasoningConversion)
|
||||
require.NotNil(t, info.ReasoningConversion.BudgetTokens)
|
||||
assert.Equal(t, -1, *info.ReasoningConversion.BudgetTokens)
|
||||
require.NotEmpty(t, request.Reasoning)
|
||||
assert.Equal(t, float64(-1), gjson.GetBytes(request.Reasoning, "max_tokens").Num)
|
||||
}
|
||||
|
||||
func TestApplyReasoningModelSuffixPreservesGpt51CodexMax(t *testing.T) {
|
||||
info := &relaycommon.RelayInfo{
|
||||
OriginModelName: "gpt-5.1-codex-max",
|
||||
ChannelMeta: &relaycommon.ChannelMeta{
|
||||
UpstreamModelName: "gpt-5.1-codex-max",
|
||||
},
|
||||
}
|
||||
|
||||
mustApplyReasoningModelSuffix(t, info)
|
||||
assert.Equal(t, "gpt-5.1-codex-max", info.UpstreamModelName)
|
||||
assert.Nil(t, info.ReasoningConversion)
|
||||
assert.Equal(t, "gpt-5.1-codex-max", hostreasoning.BaseModelName("gpt-5.1-codex-max"))
|
||||
}
|
||||
Reference in new issue
Block a user