From 01c2128e2310d06ecfe30d0ca9f8463302c2a598 Mon Sep 17 00:00:00 2001 From: Don Ganesh <59359452+USER-HFC@users.noreply.github.com> Date: Fri, 5 Jun 2026 12:12:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B6=E7=AA=84=20OpenAI=20o=20?= =?UTF-8?q?=E7=B3=BB=E5=88=97=E6=A8=A1=E5=9E=8B=E9=80=82=E9=85=8D=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=20(#5293)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 收窄 OpenAI o 系列模型适配范围 * fix(openai): 限制 gpt-5 适配仅作用于 OpenAI 模型 * fix(openai): narrow o-series reasoning model detection --------- Co-authored-by: Seefs --- controller/channel-test.go | 2 +- dto/openai_request.go | 14 ++++++++++++-- dto/openai_request_zero_value_test.go | 24 ++++++++++++++++++++++++ relay/channel/openai/adaptor.go | 8 +++++--- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/controller/channel-test.go b/controller/channel-test.go index 037b8496..37bf422b 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -814,7 +814,7 @@ func buildTestRequest(model string, endpointType string, channel *model.Channel, testRequest.StreamOptions = &dto.StreamOptions{IncludeUsage: true} } - if strings.HasPrefix(model, "o") { + if dto.IsOpenAIReasoningOModel(model) { testRequest.MaxCompletionTokens = lo.ToPtr(uint(16)) } else if strings.Contains(model, "thinking") { if !strings.Contains(model, "claude") { diff --git a/dto/openai_request.go b/dto/openai_request.go index 8c104ddd..fd0bed0e 100644 --- a/dto/openai_request.go +++ b/dto/openai_request.go @@ -213,12 +213,22 @@ func (r *GeneralOpenAIRequest) ToMap() map[string]any { return result } +func IsOpenAIReasoningOModel(modelName string) bool { + return strings.HasPrefix(modelName, "o1") || + strings.HasPrefix(modelName, "o3") || + strings.HasPrefix(modelName, "o4") +} + +func IsOpenAIGPT5Model(modelName string) bool { + return strings.HasPrefix(modelName, "gpt-5") +} + func (r *GeneralOpenAIRequest) GetSystemRoleName() string { - if strings.HasPrefix(r.Model, "o") { + if IsOpenAIReasoningOModel(r.Model) { if !strings.HasPrefix(r.Model, "o1-mini") && !strings.HasPrefix(r.Model, "o1-preview") { return "developer" } - } else if strings.HasPrefix(r.Model, "gpt-5") { + } else if IsOpenAIGPT5Model(r.Model) { return "developer" } return "system" diff --git a/dto/openai_request_zero_value_test.go b/dto/openai_request_zero_value_test.go index 4b0dbd7c..e9d9e30e 100644 --- a/dto/openai_request_zero_value_test.go +++ b/dto/openai_request_zero_value_test.go @@ -71,3 +71,27 @@ func TestOpenAIResponsesRequestPreserveExplicitZeroValues(t *testing.T) { require.True(t, gjson.GetBytes(encoded, "stream").Exists()) require.True(t, gjson.GetBytes(encoded, "top_p").Exists()) } + +func TestGeneralOpenAIRequestGetSystemRoleName(t *testing.T) { + tests := []struct { + name string + model string + want string + }{ + {name: "o1 uses developer", model: "o1", want: "developer"}, + {name: "o3 family uses developer", model: "o3-mini-high", want: "developer"}, + {name: "o4 family uses developer", model: "o4-mini", want: "developer"}, + {name: "o1 mini stays system", model: "o1-mini", want: "system"}, + {name: "o1 preview stays system", model: "o1-preview", want: "system"}, + {name: "gpt 5 uses developer", model: "gpt-5", want: "developer"}, + {name: "omni is not o series", model: "omni-moderation-latest", want: "system"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := GeneralOpenAIRequest{Model: tt.model} + + require.Equal(t, tt.want, req.GetSystemRoleName()) + }) + } +} diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go index 581ae196..26fac8e7 100644 --- a/relay/channel/openai/adaptor.go +++ b/relay/channel/openai/adaptor.go @@ -310,18 +310,20 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn } } - if strings.HasPrefix(info.UpstreamModelName, "o") || strings.HasPrefix(info.UpstreamModelName, "gpt-5") { + isOModel := dto.IsOpenAIReasoningOModel(info.UpstreamModelName) + isGPT5Model := dto.IsOpenAIGPT5Model(info.UpstreamModelName) + if isOModel || isGPT5Model { if lo.FromPtrOr(request.MaxCompletionTokens, uint(0)) == 0 && lo.FromPtrOr(request.MaxTokens, uint(0)) != 0 { request.MaxCompletionTokens = request.MaxTokens request.MaxTokens = nil } - if strings.HasPrefix(info.UpstreamModelName, "o") { + if isOModel { request.Temperature = nil } // gpt-5系列模型适配 归零不再支持的参数 - if strings.HasPrefix(info.UpstreamModelName, "gpt-5") { + if isGPT5Model { request.Temperature = nil request.TopP = nil request.LogProbs = nil