Files
new-api-custom/relay/helper/reasoning_suffix_test.go
T

610 lines
21 KiB
Go

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 TestApplyReasoningModelSuffixClaudeEffortTailKeptWhenAdapterDisabled(t *testing.T) {
settings := model_setting.GetClaudeSettings()
original := settings.ThinkingAdapterEnabled
t.Cleanup(func() { settings.ThinkingAdapterEnabled = original })
settings.ThinkingAdapterEnabled = false
req := &dto.ClaudeRequest{Model: "claude-opus-5-5-high"}
info := &relaycommon.RelayInfo{
OriginModelName: "claude-opus-5-5-high",
Request: req,
ChannelMeta: &relaycommon.ChannelMeta{
UpstreamModelName: "claude-opus-5-5-high",
},
}
mustApplyReasoningModelSuffix(t, info, req)
assert.Equal(t, "claude-opus-5-5-high", info.UpstreamModelName)
assert.Equal(t, "claude-opus-5-5-high", req.Model)
assert.Nil(t, info.ReasoningConversion)
}
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"))
}