diff --git a/common/model.go b/common/model.go index 4ebc7b532d74..2de153328d33 100644 --- a/common/model.go +++ b/common/model.go @@ -12,7 +12,7 @@ var ( ImageGenerationModels = []string{ "dall-e-3", "dall-e-2", - "gpt-image-1", + "gpt-image-", "prefix:imagen-", "flux-", "flux.1-", diff --git a/common/model_test.go b/common/model_test.go new file mode 100644 index 000000000000..b6ea43185779 --- /dev/null +++ b/common/model_test.go @@ -0,0 +1,39 @@ +package common + +import ( + "testing" + + "github.com/QuantumNous/new-api/constant" + "github.com/stretchr/testify/assert" +) + +func TestIsImageGenerationModelRecognizesGPTImageFamily(t *testing.T) { + tests := []struct { + name string + modelName string + want bool + }{ + {name: "gpt image 1", modelName: "gpt-image-1", want: true}, + {name: "gpt image 1 mini", modelName: "gpt-image-1-mini", want: true}, + {name: "gpt image 1.5", modelName: "gpt-image-1.5", want: true}, + {name: "gpt image 2", modelName: "gpt-image-2", want: true}, + {name: "provider-prefixed gpt image 2", modelName: "openai/gpt-image-2", want: true}, + {name: "uppercase gpt image 2", modelName: "GPT-IMAGE-2", want: true}, + {name: "chat model", modelName: "gpt-5", want: false}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, test.want, IsImageGenerationModel(test.modelName)) + }) + } +} + +func TestGetEndpointTypesByChannelTypePrioritizesGPTImageGeneration(t *testing.T) { + endpointTypes := GetEndpointTypesByChannelType(constant.ChannelTypeOpenAI, "gpt-image-2") + + assert.Equal(t, []constant.EndpointType{ + constant.EndpointTypeImageGeneration, + constant.EndpointTypeOpenAI, + }, endpointTypes) +} diff --git a/controller/channel-test.go b/controller/channel-test.go index 4ba3698bd54c..c69ae60c036a 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -46,6 +46,9 @@ func normalizeChannelTestEndpoint(channel *model.Channel, modelName, endpointTyp if normalized != "" { return normalized } + if common.IsImageGenerationModel(modelName) { + return string(constant.EndpointTypeImageGeneration) + } if strings.HasSuffix(modelName, ratio_setting.CompactModelSuffix) { return string(constant.EndpointTypeOpenAIResponseCompact) } diff --git a/controller/channel_test_internal_test.go b/controller/channel_test_internal_test.go index 56810fdc0ba5..7a1f191e5c7e 100644 --- a/controller/channel_test_internal_test.go +++ b/controller/channel_test_internal_test.go @@ -211,6 +211,30 @@ func TestResolveChannelTestUserIDUsesRequestUser(t *testing.T) { require.Equal(t, 2, userID) } +func TestNormalizeChannelTestEndpointDetectsGPTImageGeneration(t *testing.T) { + endpointType := normalizeChannelTestEndpoint( + &model.Channel{Type: constant.ChannelTypeOpenAI}, + "gpt-image-2", + "", + ) + + assert.Equal(t, string(constant.EndpointTypeImageGeneration), endpointType) + request := buildTestRequest("gpt-image-2", endpointType, nil, false) + imageRequest, ok := request.(*dto.ImageRequest) + require.True(t, ok) + assert.Equal(t, "gpt-image-2", imageRequest.Model) +} + +func TestNormalizeChannelTestEndpointPreservesExplicitEndpoint(t *testing.T) { + endpointType := normalizeChannelTestEndpoint( + &model.Channel{Type: constant.ChannelTypeOpenAI}, + "gpt-image-2", + string(constant.EndpointTypeOpenAI), + ) + + assert.Equal(t, string(constant.EndpointTypeOpenAI), endpointType) +} + func TestSelectChannelsForAutomaticTestPassiveRecoveryOnlyUsesAutoDisabled(t *testing.T) { channels := []*model.Channel{ {Id: 1, Status: common.ChannelStatusEnabled}, diff --git a/controller/relay.go b/controller/relay.go index 6e91ccb60506..3c31215a3f2e 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -355,7 +355,8 @@ func shouldRetry(c *gin.Context, openaiErr *types.NewAPIError, retryTimes int) b } func processChannelError(c *gin.Context, channelError types.ChannelError, err *types.NewAPIError) { - logger.LogError(c, fmt.Sprintf("channel error (channel #%d, status code: %d): %s", channelError.ChannelId, err.StatusCode, common.LocalLogPreview(err.Error()))) + logger.LogError(c, fmt.Sprintf("channel error (channel #%d,channelName:%s,modelName:%s, status code: %d): %s", + channelError.ChannelId, channelError.ChannelName, c.GetString("original_model"), err.StatusCode, common.LocalLogPreview(err.Error()))) // 不要使用context获取渠道信息,异步处理时可能会出现渠道信息不一致的情况 // do not use context to get channel info, there may be inconsistent channel info when processing asynchronously if service.ShouldDisableChannel(err) && channelError.AutoBan { diff --git a/model/log.go b/model/log.go index 401d53c435a5..7cdb3eb0aa1b 100644 --- a/model/log.go +++ b/model/log.go @@ -281,7 +281,7 @@ func RecordTopupLog(userId int, content string, callerIp string, paymentMethod s func RecordErrorLog(c *gin.Context, userId int, channelId int, modelName string, tokenName string, content string, tokenId int, useTimeSeconds int, isStream bool, group string, other map[string]interface{}) { - logger.LogInfo(c, fmt.Sprintf("record error log: userId=%d, channelId=%d, modelName=%s, tokenName=%s, content=%s", userId, channelId, modelName, tokenName, common.LocalLogPreview(content))) + logger.LogError(c, fmt.Sprintf("record error log: userId=%d,userName=%s, channelId=%d, modelName=%s, tokenName=%s, content=%s", userId, c.GetString("username"), channelId, modelName, tokenName, common.LocalLogPreview(content))) username := c.GetString("username") requestId := c.GetString(common.RequestIdKey) upstreamRequestId := c.GetString(common.UpstreamRequestIdKey)