2024-02-29 01:08:18 +08:00
package relay
import (
"bytes"
"fmt"
"io"
"net/http"
"strings"
2025-10-11 15:30:09 +08:00
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/logger"
relaycommon "github.com/QuantumNous/new-api/relay/common"
2026-01-11 21:38:07 +08:00
relayconstant "github.com/QuantumNous/new-api/relay/constant"
2025-10-11 15:30:09 +08:00
"github.com/QuantumNous/new-api/relay/helper"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/setting/model_setting"
2025-12-31 21:29:10 +08:00
"github.com/QuantumNous/new-api/setting/ratio_setting"
2025-10-11 15:30:09 +08:00
"github.com/QuantumNous/new-api/types"
2026-03-01 15:47:03 +08:00
"github.com/samber/lo"
2025-10-11 15:30:09 +08:00
2024-02-29 01:08:18 +08:00
"github.com/gin-gonic/gin"
)
2025-08-14 20:05:06 +08:00
func TextHelper ( c * gin . Context , info * relaycommon . RelayInfo ) ( newAPIError * types . NewAPIError ) {
info . InitChannelMeta ( c )
2024-02-29 01:08:18 +08:00
2025-08-23 13:12:15 +08:00
textReq , ok := info . Request . ( * dto . GeneralOpenAIRequest )
2025-08-14 20:05:06 +08:00
if ! ok {
2025-08-23 13:34:56 +08:00
return types . NewErrorWithStatusCode ( fmt . Errorf ( "invalid request type, expected dto.GeneralOpenAIRequest, got %T" , info . Request ) , types . ErrorCodeInvalidRequest , http . StatusBadRequest , types . ErrOptionWithSkipRetry ( ) )
2024-02-29 01:08:18 +08:00
}
2025-08-23 13:12:15 +08:00
request , err := common . DeepCopy ( textReq )
if err != nil {
return types . NewError ( fmt . Errorf ( "failed to copy request to GeneralOpenAIRequest: %w" , err ) , types . ErrorCodeInvalidRequest , types . ErrOptionWithSkipRetry ( ) )
}
if request . WebSearchOptions != nil {
c . Set ( "chat_completion_web_search_context_size" , request . WebSearchOptions . SearchContextSize )
2025-06-18 00:37:22 +08:00
}
2025-08-23 13:12:15 +08:00
err = helper . ModelMappedHelper ( c , info , request )
2025-02-20 16:41:46 +08:00
if err != nil {
2025-07-30 22:35:31 +08:00
return types . NewError ( err , types . ErrorCodeChannelModelMappedError , types . ErrOptionWithSkipRetry ( ) )
2025-02-20 16:41:46 +08:00
}
2025-08-14 21:10:04 +08:00
includeUsage := true
// 判断用户是否需要返回使用情况
2025-08-23 13:12:15 +08:00
if request . StreamOptions != nil {
includeUsage = request . StreamOptions . IncludeUsage
2025-08-14 21:10:04 +08:00
}
// 如果不支持StreamOptions, 将StreamOptions设置为nil
2026-03-01 15:47:03 +08:00
if ! info . SupportStreamOptions || ! lo . FromPtrOr ( request . Stream , false ) {
2025-08-23 13:12:15 +08:00
request . StreamOptions = nil
2025-08-14 21:10:04 +08:00
} else {
// 如果支持StreamOptions, 且请求中没有设置StreamOptions, 根据配置文件设置StreamOptions
if constant . ForceStreamOption {
2025-08-23 13:12:15 +08:00
request . StreamOptions = & dto . StreamOptions {
2025-08-14 21:10:04 +08:00
IncludeUsage : true ,
}
}
}
info . ShouldIncludeUsage = includeUsage
2025-08-14 20:05:06 +08:00
adaptor := GetAdaptor ( info . ApiType )
2024-02-29 01:08:18 +08:00
if adaptor == nil {
2025-08-14 20:05:06 +08:00
return types . NewError ( fmt . Errorf ( "invalid api type: %d" , info . ApiType ) , types . ErrorCodeInvalidApiType , types . ErrOptionWithSkipRetry ( ) )
2024-02-29 01:08:18 +08:00
}
2025-08-14 20:05:06 +08:00
adaptor . Init ( info )
2026-01-11 21:38:07 +08:00
2026-01-14 22:29:43 +08:00
passThroughGlobal := model_setting . GetGlobalSettings ( ) . PassThroughRequestEnabled
2026-01-11 21:38:07 +08:00
if info . RelayMode == relayconstant . RelayModeChatCompletions &&
2026-01-14 22:29:43 +08:00
! passThroughGlobal &&
2026-01-11 21:38:07 +08:00
! info . ChannelSetting . PassThroughBodyEnabled &&
2026-01-26 19:57:41 +08:00
service . ShouldChatCompletionsUseResponsesGlobal ( info . ChannelId , info . ChannelType , info . OriginModelName ) {
2026-01-11 21:38:07 +08:00
applySystemPromptIfNeeded ( c , info , request )
usage , newApiErr := chatCompletionsViaResponses ( c , info , adaptor , request )
if newApiErr != nil {
return newApiErr
}
var containAudioTokens = usage . CompletionTokenDetails . AudioTokens > 0 || usage . PromptTokensDetails . AudioTokens > 0
var containsAudioRatios = ratio_setting . ContainsAudioRatio ( info . OriginModelName ) || ratio_setting . ContainsAudioCompletionRatio ( info . OriginModelName )
if containAudioTokens && containsAudioRatios {
service . PostAudioConsumeQuota ( c , info , usage , "" )
} else {
2026-03-23 14:22:12 +08:00
service . PostTextConsumeQuota ( c , info , usage , nil )
2026-01-11 21:38:07 +08:00
}
return nil
}
2024-02-29 01:08:18 +08:00
var requestBody io . Reader
2024-07-08 01:27:57 +08:00
2026-01-14 22:29:43 +08:00
if passThroughGlobal || info . ChannelSetting . PassThroughBodyEnabled {
2026-02-12 01:51:17 +08:00
storage , err := common . GetBodyStorage ( c )
2025-03-11 17:02:35 +08:00
if err != nil {
2025-07-30 22:35:31 +08:00
return types . NewErrorWithStatusCode ( err , types . ErrorCodeReadRequestBodyFailed , http . StatusBadRequest , types . ErrOptionWithSkipRetry ( ) )
2025-03-11 17:02:35 +08:00
}
2025-07-26 12:11:20 +08:00
if common . DebugEnabled {
2026-02-12 01:51:17 +08:00
if debugBytes , bErr := storage . Bytes ( ) ; bErr == nil {
println ( "requestBody: " , string ( debugBytes ) )
}
2025-07-26 12:11:20 +08:00
}
2026-02-12 01:51:17 +08:00
requestBody = common . ReaderOnly ( storage )
2025-03-11 17:02:35 +08:00
} else {
2025-08-23 13:12:15 +08:00
convertedRequest , err := adaptor . ConvertOpenAIRequest ( c , info , request )
2025-03-11 17:02:35 +08:00
if err != nil {
2025-07-30 22:35:31 +08:00
return types . NewError ( err , types . ErrorCodeConvertRequestFailed , types . ErrOptionWithSkipRetry ( ) )
2025-03-11 17:02:35 +08:00
}
2026-01-20 23:43:29 +08:00
relaycommon . AppendRequestConversionFromRequest ( info , convertedRequest )
2025-07-26 11:39:09 +08:00
2025-08-14 20:05:06 +08:00
if info . ChannelSetting . SystemPrompt != "" {
2025-07-26 11:39:09 +08:00
// 如果有系统提示,则将其添加到请求中
2025-09-15 19:38:31 +08:00
request , ok := convertedRequest . ( * dto . GeneralOpenAIRequest )
if ok {
containSystemPrompt := false
for _ , message := range request . Messages {
2025-08-09 12:53:06 +08:00
if message . Role == request . GetSystemRoleName ( ) {
2025-09-15 19:38:31 +08:00
containSystemPrompt = true
2025-08-09 12:53:06 +08:00
break
}
}
2025-09-15 19:38:31 +08:00
if ! containSystemPrompt {
// 如果没有系统提示,则添加系统提示
systemMessage := dto . Message {
Role : request . GetSystemRoleName ( ) ,
Content : info . ChannelSetting . SystemPrompt ,
}
request . Messages = append ( [ ] dto . Message { systemMessage } , request . Messages ... )
} else if info . ChannelSetting . SystemPromptOverride {
common . SetContextKey ( c , constant . ContextKeySystemPromptOverride , true )
// 如果有系统提示,且允许覆盖,则拼接到前面
for i , message := range request . Messages {
if message . Role == request . GetSystemRoleName ( ) {
if message . IsStringContent ( ) {
request . Messages [ i ] . SetStringContent ( info . ChannelSetting . SystemPrompt + "\n" + message . StringContent ( ) )
} else {
contents := message . ParseContent ( )
contents = append ( [ ] dto . MediaContent {
{
Type : dto . ContentTypeText ,
Text : info . ChannelSetting . SystemPrompt ,
} ,
} , contents ... )
request . Messages [ i ] . Content = contents
}
break
}
}
}
2025-07-26 11:39:09 +08:00
}
}
jsonData , err := common . Marshal ( convertedRequest )
2025-03-11 17:02:35 +08:00
if err != nil {
2025-08-26 16:26:56 +08:00
return types . NewError ( err , types . ErrorCodeJsonMarshalFailed , types . ErrOptionWithSkipRetry ( ) )
2025-03-11 17:02:35 +08:00
}
2025-03-29 14:39:39 +08:00
2025-10-02 00:14:35 +08:00
// remove disabled fields for OpenAI API
2026-02-19 15:09:13 +08:00
jsonData , err = relaycommon . RemoveDisabledFields ( jsonData , info . ChannelOtherSettings , info . ChannelSetting . PassThroughBodyEnabled )
2025-10-02 00:14:35 +08:00
if err != nil {
return types . NewError ( err , types . ErrorCodeConvertRequestFailed , types . ErrOptionWithSkipRetry ( ) )
}
2025-03-29 14:39:39 +08:00
// apply param override
2025-08-14 20:05:06 +08:00
if len ( info . ParamOverride ) > 0 {
2026-02-22 00:45:49 +08:00
jsonData , err = relaycommon . ApplyParamOverrideWithRelayInfo ( jsonData , info )
2025-03-29 14:39:39 +08:00
if err != nil {
2026-02-22 00:10:49 +08:00
return newAPIErrorFromParamOverride ( err )
2025-03-29 14:39:39 +08:00
}
}
2025-08-14 20:05:06 +08:00
logger . LogDebug ( c , fmt . Sprintf ( "text request body: %s" , string ( jsonData ) ) )
2025-03-11 17:02:35 +08:00
requestBody = bytes . NewBuffer ( jsonData )
2024-02-29 01:08:18 +08:00
}
2024-10-04 16:08:18 +08:00
var httpResp * http . Response
2025-08-14 20:05:06 +08:00
resp , err := adaptor . DoRequest ( c , info , requestBody )
2024-03-06 17:41:55 +08:00
if err != nil {
2025-07-19 11:28:18 +08:00
return types . NewOpenAIError ( err , types . ErrorCodeDoRequestFailed , http . StatusInternalServerError )
2024-03-06 17:41:55 +08:00
}
2024-02-29 01:08:18 +08:00
2025-03-11 17:02:35 +08:00
statusCodeMappingStr := c . GetString ( "status_code_mapping" )
2024-04-23 11:44:40 +08:00
if resp != nil {
2024-10-04 16:08:18 +08:00
httpResp = resp . ( * http . Response )
2025-08-14 20:05:06 +08:00
info . IsStream = info . IsStream || strings . HasPrefix ( httpResp . Header . Get ( "Content-Type" ) , "text/event-stream" )
2024-10-04 16:08:18 +08:00
if httpResp . StatusCode != http . StatusOK {
2025-09-10 15:30:23 +08:00
newApiErr := service . RelayErrorHandler ( c . Request . Context ( ) , httpResp , false )
2024-04-23 11:44:40 +08:00
// reset status code 重置状态码
2025-07-10 15:02:40 +08:00
service . ResetStatusCode ( newApiErr , statusCodeMappingStr )
return newApiErr
2024-04-23 11:44:40 +08:00
}
2024-03-06 17:41:55 +08:00
}
2025-08-14 20:05:06 +08:00
usage , newApiErr := adaptor . DoResponse ( c , httpResp , info )
2025-07-10 15:02:40 +08:00
if newApiErr != nil {
2024-04-20 21:05:23 +08:00
// reset status code 重置状态码
2025-07-10 15:02:40 +08:00
service . ResetStatusCode ( newApiErr , statusCodeMappingStr )
return newApiErr
2024-02-29 01:08:18 +08:00
}
2024-11-07 16:12:09 +08:00
2025-12-31 21:29:10 +08:00
var containAudioTokens = usage . ( * dto . Usage ) . CompletionTokenDetails . AudioTokens > 0 || usage . ( * dto . Usage ) . PromptTokensDetails . AudioTokens > 0
var containsAudioRatios = ratio_setting . ContainsAudioRatio ( info . OriginModelName ) || ratio_setting . ContainsAudioCompletionRatio ( info . OriginModelName )
if containAudioTokens && containsAudioRatios {
2025-08-14 20:05:06 +08:00
service . PostAudioConsumeQuota ( c , info , usage . ( * dto . Usage ) , "" )
2024-11-07 16:12:09 +08:00
} else {
2026-03-23 14:22:12 +08:00
service . PostTextConsumeQuota ( c , info , usage . ( * dto . Usage ) , nil )
2024-11-07 16:12:09 +08:00
}
2024-02-29 01:08:18 +08:00
return nil
}