Commit f6107734 by CaIon

feat: enhance request handling with pass-through options and system prompt support

parent 1adc83e4
package dto package dto
type ChannelSettings struct { type ChannelSettings struct {
ForceFormat bool `json:"force_format,omitempty"` ForceFormat bool `json:"force_format,omitempty"`
ThinkingToContent bool `json:"thinking_to_content,omitempty"` ThinkingToContent bool `json:"thinking_to_content,omitempty"`
Proxy string `json:"proxy"` Proxy string `json:"proxy"`
PassThroughBodyEnabled bool `json:"pass_through_body_enabled,omitempty"`
SystemPrompt string `json:"system_prompt,omitempty"`
} }
...@@ -73,6 +73,15 @@ func (r *GeneralOpenAIRequest) ToMap() map[string]any { ...@@ -73,6 +73,15 @@ func (r *GeneralOpenAIRequest) ToMap() map[string]any {
return result return result
} }
func (r *GeneralOpenAIRequest) GetSystemRoleName() string {
if strings.HasPrefix(r.Model, "o") {
if !strings.HasPrefix(r.Model, "o1-mini") && !strings.HasPrefix(r.Model, "o1-preview") {
return "developer"
}
}
return "system"
}
type ToolCallRequest struct { type ToolCallRequest struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Type string `json:"type"` Type string `json:"type"`
......
...@@ -585,6 +585,20 @@ ...@@ -585,6 +585,20 @@
"渠道权重": "渠道权重", "渠道权重": "渠道权重",
"渠道额外设置": "渠道额外设置", "渠道额外设置": "渠道额外设置",
"此项可选,用于配置渠道特定设置,为一个 JSON 字符串,例如:": "此项可选,用于配置渠道特定设置,为一个 JSON 字符串,例如:", "此项可选,用于配置渠道特定设置,为一个 JSON 字符串,例如:": "此项可选,用于配置渠道特定设置,为一个 JSON 字符串,例如:",
"强制格式化": "强制格式化",
"强制格式化(只适用于OpenAI渠道类型)": "强制格式化(只适用于OpenAI渠道类型)",
"强制将响应格式化为 OpenAI 标准格式": "强制将响应格式化为 OpenAI 标准格式",
"思考内容转换": "思考内容转换",
"将 reasoning_content 转换为 <think> 标签拼接到内容中": "将 reasoning_content 转换为 <think> 标签拼接到内容中",
"透传请求体": "透传请求体",
"启用请求体透传功能": "启用请求体透传功能",
"代理地址": "代理地址",
"例如: socks5://user:pass@host:port": "例如: socks5://user:pass@host:port",
"用于配置网络代理": "用于配置网络代理",
"用于配置网络代理,支持 socks5 协议": "用于配置网络代理,支持 socks5 协议",
"系统提示词": "系统提示词",
"输入系统提示词,用户的系统提示词将优先于此设置": "输入系统提示词,用户的系统提示词将优先于此设置",
"用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置": "用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置",
"参数覆盖": "参数覆盖", "参数覆盖": "参数覆盖",
"此项可选,用于覆盖请求参数。不支持覆盖 stream 参数。为一个 JSON 字符串,例如:": "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数。为一个 JSON 字符串,例如:", "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数。为一个 JSON 字符串,例如:": "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数。为一个 JSON 字符串,例如:",
"请输入组织org-xxx": "请输入组织org-xxx", "请输入组织org-xxx": "请输入组织org-xxx",
......
...@@ -80,7 +80,6 @@ func ClaudeHelper(c *gin.Context) (newAPIError *types.NewAPIError) { ...@@ -80,7 +80,6 @@ func ClaudeHelper(c *gin.Context) (newAPIError *types.NewAPIError) {
return types.NewError(fmt.Errorf("invalid api type: %d", relayInfo.ApiType), types.ErrorCodeInvalidApiType) return types.NewError(fmt.Errorf("invalid api type: %d", relayInfo.ApiType), types.ErrorCodeInvalidApiType)
} }
adaptor.Init(relayInfo) adaptor.Init(relayInfo)
var requestBody io.Reader
if textRequest.MaxTokens == 0 { if textRequest.MaxTokens == 0 {
textRequest.MaxTokens = uint(model_setting.GetClaudeSettings().GetDefaultMaxTokens(textRequest.Model)) textRequest.MaxTokens = uint(model_setting.GetClaudeSettings().GetDefaultMaxTokens(textRequest.Model))
...@@ -108,18 +107,41 @@ func ClaudeHelper(c *gin.Context) (newAPIError *types.NewAPIError) { ...@@ -108,18 +107,41 @@ func ClaudeHelper(c *gin.Context) (newAPIError *types.NewAPIError) {
relayInfo.UpstreamModelName = textRequest.Model relayInfo.UpstreamModelName = textRequest.Model
} }
convertedRequest, err := adaptor.ConvertClaudeRequest(c, relayInfo, textRequest) var requestBody io.Reader
if err != nil { if model_setting.GetGlobalSettings().PassThroughRequestEnabled || relayInfo.ChannelSetting.PassThroughBodyEnabled {
return types.NewError(err, types.ErrorCodeConvertRequestFailed) body, err := common.GetRequestBody(c)
} if err != nil {
jsonData, err := common.Marshal(convertedRequest) return types.NewErrorWithStatusCode(err, types.ErrorCodeReadRequestBodyFailed, http.StatusBadRequest)
if common.DebugEnabled { }
println("requestBody: ", string(jsonData)) requestBody = bytes.NewBuffer(body)
} } else {
if err != nil { convertedRequest, err := adaptor.ConvertClaudeRequest(c, relayInfo, textRequest)
return types.NewError(err, types.ErrorCodeConvertRequestFailed) if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed)
}
jsonData, err := common.Marshal(convertedRequest)
if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed)
}
// apply param override
if len(relayInfo.ParamOverride) > 0 {
reqMap := make(map[string]interface{})
_ = common.Unmarshal(jsonData, &reqMap)
for key, value := range relayInfo.ParamOverride {
reqMap[key] = value
}
jsonData, err = common.Marshal(reqMap)
if err != nil {
return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid)
}
}
if common.DebugEnabled {
println("requestBody: ", string(jsonData))
}
requestBody = bytes.NewBuffer(jsonData)
} }
requestBody = bytes.NewBuffer(jsonData)
statusCodeMappingStr := c.GetString("status_code_mapping") statusCodeMappingStr := c.GetString("status_code_mapping")
var httpResp *http.Response var httpResp *http.Response
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"net/http" "net/http"
"one-api/common" "one-api/common"
"one-api/dto" "one-api/dto"
...@@ -194,16 +195,39 @@ func GeminiHelper(c *gin.Context) (newAPIError *types.NewAPIError) { ...@@ -194,16 +195,39 @@ func GeminiHelper(c *gin.Context) (newAPIError *types.NewAPIError) {
} }
} }
requestBody, err := json.Marshal(req) var requestBody io.Reader
if err != nil { if model_setting.GetGlobalSettings().PassThroughRequestEnabled || relayInfo.ChannelSetting.PassThroughBodyEnabled {
return types.NewError(err, types.ErrorCodeConvertRequestFailed) body, err := common.GetRequestBody(c)
} if err != nil {
return types.NewErrorWithStatusCode(err, types.ErrorCodeReadRequestBodyFailed, http.StatusBadRequest)
}
requestBody = bytes.NewReader(body)
} else {
jsonData, err := json.Marshal(req)
if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed)
}
// apply param override
if len(relayInfo.ParamOverride) > 0 {
reqMap := make(map[string]interface{})
_ = common.Unmarshal(jsonData, &reqMap)
for key, value := range relayInfo.ParamOverride {
reqMap[key] = value
}
jsonData, err = common.Marshal(reqMap)
if err != nil {
return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid)
}
}
if common.DebugEnabled { if common.DebugEnabled {
println("Gemini request body: %s", string(requestBody)) println("Gemini request body: %s", string(jsonData))
}
requestBody = bytes.NewReader(jsonData)
} }
resp, err := adaptor.DoRequest(c, relayInfo, bytes.NewReader(requestBody)) resp, err := adaptor.DoRequest(c, relayInfo, requestBody)
if err != nil { if err != nil {
common.LogError(c, "Do gemini request failed: "+err.Error()) common.LogError(c, "Do gemini request failed: "+err.Error())
return types.NewError(err, types.ErrorCodeDoRequestFailed) return types.NewError(err, types.ErrorCodeDoRequestFailed)
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
"one-api/relay/helper" "one-api/relay/helper"
"one-api/service" "one-api/service"
"one-api/setting" "one-api/setting"
"one-api/setting/model_setting"
"one-api/types" "one-api/types"
"strings" "strings"
...@@ -187,22 +188,43 @@ func ImageHelper(c *gin.Context) (newAPIError *types.NewAPIError) { ...@@ -187,22 +188,43 @@ func ImageHelper(c *gin.Context) (newAPIError *types.NewAPIError) {
var requestBody io.Reader var requestBody io.Reader
convertedRequest, err := adaptor.ConvertImageRequest(c, relayInfo, *imageRequest) if model_setting.GetGlobalSettings().PassThroughRequestEnabled || relayInfo.ChannelSetting.PassThroughBodyEnabled {
if err != nil { body, err := common.GetRequestBody(c)
return types.NewError(err, types.ErrorCodeConvertRequestFailed) if err != nil {
} return types.NewErrorWithStatusCode(err, types.ErrorCodeReadRequestBodyFailed, http.StatusBadRequest)
if relayInfo.RelayMode == relayconstant.RelayModeImagesEdits { }
requestBody = convertedRequest.(io.Reader) requestBody = bytes.NewBuffer(body)
} else { } else {
jsonData, err := json.Marshal(convertedRequest) convertedRequest, err := adaptor.ConvertImageRequest(c, relayInfo, *imageRequest)
if err != nil { if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed) return types.NewError(err, types.ErrorCodeConvertRequestFailed)
} }
requestBody = bytes.NewBuffer(jsonData) if relayInfo.RelayMode == relayconstant.RelayModeImagesEdits {
} requestBody = convertedRequest.(io.Reader)
} else {
jsonData, err := json.Marshal(convertedRequest)
if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed)
}
if common.DebugEnabled { // apply param override
println(fmt.Sprintf("image request body: %s", requestBody)) if len(relayInfo.ParamOverride) > 0 {
reqMap := make(map[string]interface{})
_ = common.Unmarshal(jsonData, &reqMap)
for key, value := range relayInfo.ParamOverride {
reqMap[key] = value
}
jsonData, err = common.Marshal(reqMap)
if err != nil {
return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid)
}
}
if common.DebugEnabled {
println(fmt.Sprintf("image request body: %s", string(jsonData)))
}
requestBody = bytes.NewBuffer(jsonData)
}
} }
statusCodeMappingStr := c.GetString("status_code_mapping") statusCodeMappingStr := c.GetString("status_code_mapping")
......
...@@ -2,7 +2,6 @@ package relay ...@@ -2,7 +2,6 @@ package relay
import ( import (
"bytes" "bytes"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
...@@ -171,7 +170,7 @@ func TextHelper(c *gin.Context) (newAPIError *types.NewAPIError) { ...@@ -171,7 +170,7 @@ func TextHelper(c *gin.Context) (newAPIError *types.NewAPIError) {
adaptor.Init(relayInfo) adaptor.Init(relayInfo)
var requestBody io.Reader var requestBody io.Reader
if model_setting.GetGlobalSettings().PassThroughRequestEnabled { if model_setting.GetGlobalSettings().PassThroughRequestEnabled || relayInfo.ChannelSetting.PassThroughBodyEnabled {
body, err := common.GetRequestBody(c) body, err := common.GetRequestBody(c)
if err != nil { if err != nil {
return types.NewErrorWithStatusCode(err, types.ErrorCodeReadRequestBodyFailed, http.StatusBadRequest) return types.NewErrorWithStatusCode(err, types.ErrorCodeReadRequestBodyFailed, http.StatusBadRequest)
...@@ -182,7 +181,28 @@ func TextHelper(c *gin.Context) (newAPIError *types.NewAPIError) { ...@@ -182,7 +181,28 @@ func TextHelper(c *gin.Context) (newAPIError *types.NewAPIError) {
if err != nil { if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed) return types.NewError(err, types.ErrorCodeConvertRequestFailed)
} }
jsonData, err := json.Marshal(convertedRequest)
if relayInfo.ChannelSetting.SystemPrompt != "" {
// 如果有系统提示,则将其添加到请求中
request := convertedRequest.(*dto.GeneralOpenAIRequest)
containSystemPrompt := false
for _, message := range request.Messages {
if message.Role == request.GetSystemRoleName() {
containSystemPrompt = true
break
}
}
if !containSystemPrompt {
// 如果没有系统提示,则添加系统提示
systemMessage := dto.Message{
Role: request.GetSystemRoleName(),
Content: relayInfo.ChannelSetting.SystemPrompt,
}
request.Messages = append([]dto.Message{systemMessage}, request.Messages...)
}
}
jsonData, err := common.Marshal(convertedRequest)
if err != nil { if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed) return types.NewError(err, types.ErrorCodeConvertRequestFailed)
} }
......
...@@ -3,12 +3,14 @@ package relay ...@@ -3,12 +3,14 @@ package relay
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"net/http" "net/http"
"one-api/common" "one-api/common"
"one-api/dto" "one-api/dto"
relaycommon "one-api/relay/common" relaycommon "one-api/relay/common"
"one-api/relay/helper" "one-api/relay/helper"
"one-api/service" "one-api/service"
"one-api/setting/model_setting"
"one-api/types" "one-api/types"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
...@@ -70,18 +72,42 @@ func RerankHelper(c *gin.Context, relayMode int) (newAPIError *types.NewAPIError ...@@ -70,18 +72,42 @@ func RerankHelper(c *gin.Context, relayMode int) (newAPIError *types.NewAPIError
} }
adaptor.Init(relayInfo) adaptor.Init(relayInfo)
convertedRequest, err := adaptor.ConvertRerankRequest(c, relayInfo.RelayMode, *rerankRequest) var requestBody io.Reader
if err != nil { if model_setting.GetGlobalSettings().PassThroughRequestEnabled || relayInfo.ChannelSetting.PassThroughBodyEnabled {
return types.NewError(err, types.ErrorCodeConvertRequestFailed) body, err := common.GetRequestBody(c)
} if err != nil {
jsonData, err := common.Marshal(convertedRequest) return types.NewErrorWithStatusCode(err, types.ErrorCodeReadRequestBodyFailed, http.StatusBadRequest)
if err != nil { }
return types.NewError(err, types.ErrorCodeConvertRequestFailed) requestBody = bytes.NewBuffer(body)
} } else {
requestBody := bytes.NewBuffer(jsonData) convertedRequest, err := adaptor.ConvertRerankRequest(c, relayInfo.RelayMode, *rerankRequest)
if common.DebugEnabled { if err != nil {
println(fmt.Sprintf("Rerank request body: %s", requestBody.String())) return types.NewError(err, types.ErrorCodeConvertRequestFailed)
}
jsonData, err := common.Marshal(convertedRequest)
if err != nil {
return types.NewError(err, types.ErrorCodeConvertRequestFailed)
}
// apply param override
if len(relayInfo.ParamOverride) > 0 {
reqMap := make(map[string]interface{})
_ = common.Unmarshal(jsonData, &reqMap)
for key, value := range relayInfo.ParamOverride {
reqMap[key] = value
}
jsonData, err = common.Marshal(reqMap)
if err != nil {
return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid)
}
}
if common.DebugEnabled {
println(fmt.Sprintf("Rerank request body: %s", string(jsonData)))
}
requestBody = bytes.NewBuffer(jsonData)
} }
resp, err := adaptor.DoRequest(c, relayInfo, requestBody) resp, err := adaptor.DoRequest(c, relayInfo, requestBody)
if err != nil { if err != nil {
return types.NewOpenAIError(err, types.ErrorCodeDoRequestFailed, http.StatusInternalServerError) return types.NewOpenAIError(err, types.ErrorCodeDoRequestFailed, http.StatusInternalServerError)
......
...@@ -1330,6 +1330,19 @@ ...@@ -1330,6 +1330,19 @@
"API地址": "Base URL", "API地址": "Base URL",
"对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "For official channels, the new-api has a built-in address. Unless it is a third-party proxy site or a special Azure access address, there is no need to fill it in", "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "For official channels, the new-api has a built-in address. Unless it is a third-party proxy site or a special Azure access address, there is no need to fill it in",
"渠道额外设置": "Channel extra settings", "渠道额外设置": "Channel extra settings",
"强制格式化": "Force format",
"强制格式化(只适用于OpenAI渠道类型)": "Force format (Only for OpenAI channel types)",
"强制将响应格式化为 OpenAI 标准格式": "Force format responses to OpenAI standard format",
"思考内容转换": "Thinking content conversion",
"将 reasoning_content 转换为 <think> 标签拼接到内容中": "Convert reasoning_content to <think> tags and append to content",
"透传请求体": "Pass through body",
"启用请求体透传功能": "Enable request body pass-through functionality",
"代理地址": "Proxy address",
"例如: socks5://user:pass@host:port": "e.g.: socks5://user:pass@host:port",
"用于配置网络代理,支持 socks5 协议": "Used to configure network proxy, supports socks5 protocol",
"系统提示词": "System Prompt",
"输入系统提示词,用户的系统提示词将优先于此设置": "Enter system prompt, user's system prompt will take priority over this setting",
"用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置": "User priority: If the user specifies a system prompt in the request, the user's setting will be used first",
"参数覆盖": "Parameters override", "参数覆盖": "Parameters override",
"模型请求速率限制": "Model request rate limit", "模型请求速率限制": "Model request rate limit",
"启用用户模型请求速率限制(可能会影响高并发性能)": "Enable user model request rate limit (may affect high concurrency performance)", "启用用户模型请求速率限制(可能会影响高并发性能)": "Enable user model request rate limit (may affect high concurrency performance)",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment