2023-06-19 02:28:55 +00:00
|
|
|
package controller
|
|
|
|
|
|
|
|
import (
|
2024-02-17 16:15:31 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2023-06-19 02:28:55 +00:00
|
|
|
"fmt"
|
2024-07-06 05:19:41 +00:00
|
|
|
"io"
|
|
|
|
"net/http"
|
2024-10-01 02:56:07 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"context"
|
2024-07-06 05:19:41 +00:00
|
|
|
|
2024-01-14 11:21:03 +00:00
|
|
|
"github.com/gin-gonic/gin"
|
2024-01-28 11:38:58 +00:00
|
|
|
"github.com/songquanpeng/one-api/common/logger"
|
2024-04-05 17:50:12 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay"
|
2024-07-22 14:38:50 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay/adaptor"
|
2024-04-05 17:36:48 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
2024-04-05 16:44:33 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay/apitype"
|
2024-04-05 17:02:35 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay/billing"
|
2024-04-05 17:26:48 +00:00
|
|
|
billingratio "github.com/songquanpeng/one-api/relay/billing/ratio"
|
2024-04-05 16:44:33 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
2024-04-05 17:31:44 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay/meta"
|
2024-02-17 16:15:31 +00:00
|
|
|
"github.com/songquanpeng/one-api/relay/model"
|
2023-06-19 02:28:55 +00:00
|
|
|
)
|
|
|
|
|
2024-02-17 16:15:31 +00:00
|
|
|
func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
2024-01-21 15:21:42 +00:00
|
|
|
ctx := c.Request.Context()
|
2024-04-05 17:31:44 +00:00
|
|
|
meta := meta.GetByContext(c)
|
2024-10-01 02:56:07 +00:00
|
|
|
|
|
|
|
// Read the original request body
|
|
|
|
bodyBytes, err := ioutil.ReadAll(c.Request.Body)
|
|
|
|
if err != nil {
|
|
|
|
logger.Errorf(ctx, "Failed to read request body: %s", err.Error())
|
|
|
|
return openai.ErrorWrapper(err, "invalid_request_body", http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore the request body for `getAndValidateTextRequest`
|
|
|
|
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
|
|
|
|
|
|
|
// Call `getAndValidateTextRequest`
|
|
|
|
textRequest, err := getAndValidateTextRequest(c, meta.Mode)
|
|
|
|
if err != nil {
|
|
|
|
logger.Errorf(ctx, "getAndValidateTextRequest failed: %s", err.Error())
|
|
|
|
return openai.ErrorWrapper(err, "invalid_text_request", http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
meta.IsStream = textRequest.Stream
|
|
|
|
|
|
|
|
// Parse the request body into a map
|
|
|
|
var rawRequest map[string]interface{}
|
|
|
|
if err := json.Unmarshal(bodyBytes, &rawRequest); err != nil {
|
|
|
|
logger.Errorf(ctx, "Failed to parse request body into map: %s", err.Error())
|
|
|
|
return openai.ErrorWrapper(err, "invalid_json", http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply parameter overrides
|
|
|
|
applyParameterOverrides(ctx, meta, textRequest, rawRequest)
|
2024-02-17 16:15:31 +00:00
|
|
|
|
2024-01-28 11:13:11 +00:00
|
|
|
// map model name
|
2024-02-17 16:15:31 +00:00
|
|
|
meta.OriginModelName = textRequest.Model
|
2024-07-22 14:38:50 +00:00
|
|
|
textRequest.Model, _ = getMappedModelName(textRequest.Model, meta.ModelMapping)
|
2024-02-17 16:15:31 +00:00
|
|
|
meta.ActualModelName = textRequest.Model
|
2024-01-28 11:13:11 +00:00
|
|
|
// get model ratio & group ratio
|
2024-07-06 05:19:41 +00:00
|
|
|
modelRatio := billingratio.GetModelRatio(textRequest.Model, meta.ChannelType)
|
2024-04-05 17:26:48 +00:00
|
|
|
groupRatio := billingratio.GetGroupRatio(meta.Group)
|
2023-06-19 02:28:55 +00:00
|
|
|
ratio := modelRatio * groupRatio
|
2024-01-28 11:13:11 +00:00
|
|
|
// pre-consume quota
|
|
|
|
promptTokens := getPromptTokens(textRequest, meta.Mode)
|
2024-02-21 14:19:42 +00:00
|
|
|
meta.PromptTokens = promptTokens
|
2024-01-28 11:13:11 +00:00
|
|
|
preConsumedQuota, bizErr := preConsumeQuota(ctx, textRequest, promptTokens, ratio, meta)
|
|
|
|
if bizErr != nil {
|
|
|
|
logger.Warnf(ctx, "preConsumeQuota failed: %+v", *bizErr)
|
|
|
|
return bizErr
|
2023-08-16 15:40:24 +00:00
|
|
|
}
|
2024-01-28 11:13:11 +00:00
|
|
|
|
2024-04-05 17:50:12 +00:00
|
|
|
adaptor := relay.GetAdaptor(meta.APIType)
|
2024-02-17 16:15:31 +00:00
|
|
|
if adaptor == nil {
|
|
|
|
return openai.ErrorWrapper(fmt.Errorf("invalid api type: %d", meta.APIType), "invalid_api_type", http.StatusBadRequest)
|
2023-07-22 08:18:03 +00:00
|
|
|
}
|
2024-04-26 15:05:48 +00:00
|
|
|
adaptor.Init(meta)
|
2024-02-17 16:15:31 +00:00
|
|
|
|
|
|
|
// get request body
|
2024-07-22 14:38:50 +00:00
|
|
|
requestBody, err := getRequestBody(c, meta, textRequest, adaptor)
|
|
|
|
if err != nil {
|
|
|
|
return openai.ErrorWrapper(err, "convert_request_failed", http.StatusInternalServerError)
|
2024-02-17 16:15:31 +00:00
|
|
|
}
|
2023-07-29 13:55:57 +00:00
|
|
|
|
2024-02-17 16:15:31 +00:00
|
|
|
// do request
|
|
|
|
resp, err := adaptor.DoRequest(c, meta, requestBody)
|
|
|
|
if err != nil {
|
|
|
|
logger.Errorf(ctx, "DoRequest failed: %s", err.Error())
|
|
|
|
return openai.ErrorWrapper(err, "do_request_failed", http.StatusInternalServerError)
|
2023-08-19 09:58:45 +00:00
|
|
|
}
|
2024-04-27 05:37:22 +00:00
|
|
|
if isErrorHappened(meta, resp) {
|
|
|
|
billing.ReturnPreConsumedQuota(ctx, preConsumedQuota, meta.TokenId)
|
|
|
|
return RelayErrorHandler(resp)
|
2024-02-17 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
2024-01-28 11:13:11 +00:00
|
|
|
// do response
|
2024-02-17 16:15:31 +00:00
|
|
|
usage, respErr := adaptor.DoResponse(c, resp, meta)
|
2024-01-21 15:21:42 +00:00
|
|
|
if respErr != nil {
|
2024-01-28 11:13:11 +00:00
|
|
|
logger.Errorf(ctx, "respErr is not nil: %+v", respErr)
|
2024-04-05 17:26:48 +00:00
|
|
|
billing.ReturnPreConsumedQuota(ctx, preConsumedQuota, meta.TokenId)
|
2024-01-21 15:21:42 +00:00
|
|
|
return respErr
|
2023-06-19 02:28:55 +00:00
|
|
|
}
|
2024-01-28 11:13:11 +00:00
|
|
|
// post-consume quota
|
|
|
|
go postConsumeQuota(ctx, usage, meta, textRequest, ratio, preConsumedQuota, modelRatio, groupRatio)
|
2024-01-21 15:21:42 +00:00
|
|
|
return nil
|
2023-06-19 02:28:55 +00:00
|
|
|
}
|
2024-07-22 14:38:50 +00:00
|
|
|
|
|
|
|
func getRequestBody(c *gin.Context, meta *meta.Meta, textRequest *model.GeneralOpenAIRequest, adaptor adaptor.Adaptor) (io.Reader, error) {
|
|
|
|
if meta.APIType == apitype.OpenAI && meta.OriginModelName == meta.ActualModelName && meta.ChannelType != channeltype.Baichuan {
|
|
|
|
// no need to convert request for openai
|
|
|
|
return c.Request.Body, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// get request body
|
|
|
|
var requestBody io.Reader
|
|
|
|
convertedRequest, err := adaptor.ConvertRequest(c, meta.Mode, textRequest)
|
|
|
|
if err != nil {
|
|
|
|
logger.Debugf(c.Request.Context(), "converted request failed: %s\n", err.Error())
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
jsonData, err := json.Marshal(convertedRequest)
|
|
|
|
if err != nil {
|
|
|
|
logger.Debugf(c.Request.Context(), "converted request json_marshal_failed: %s\n", err.Error())
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
logger.Debugf(c.Request.Context(), "converted request: \n%s", string(jsonData))
|
|
|
|
requestBody = bytes.NewBuffer(jsonData)
|
|
|
|
return requestBody, nil
|
|
|
|
}
|
2024-10-01 02:56:07 +00:00
|
|
|
|
|
|
|
func applyParameterOverrides(ctx context.Context, meta *meta.Meta, textRequest *model.GeneralOpenAIRequest, rawRequest map[string]interface{}) {
|
|
|
|
if meta.ParamsOverride != nil {
|
|
|
|
modelName := meta.OriginModelName
|
|
|
|
if overrideParams, exists := meta.ParamsOverride[modelName]; exists {
|
|
|
|
logger.Infof(ctx, "Applying parameter overrides for model %s on channel %d", modelName, meta.ChannelId)
|
|
|
|
for key, value := range overrideParams {
|
|
|
|
if _, userSpecified := rawRequest[key]; !userSpecified {
|
|
|
|
// Apply the override since the user didn't specify this parameter
|
|
|
|
switch key {
|
|
|
|
case "temperature":
|
|
|
|
if v, ok := value.(float64); ok {
|
|
|
|
textRequest.Temperature = v
|
|
|
|
} else if v, ok := value.(int); ok {
|
|
|
|
textRequest.Temperature = float64(v)
|
|
|
|
}
|
|
|
|
case "max_tokens":
|
|
|
|
if v, ok := value.(float64); ok {
|
|
|
|
textRequest.MaxTokens = int(v)
|
|
|
|
} else if v, ok := value.(int); ok {
|
|
|
|
textRequest.MaxTokens = v
|
|
|
|
}
|
|
|
|
case "top_p":
|
|
|
|
if v, ok := value.(float64); ok {
|
|
|
|
textRequest.TopP = v
|
|
|
|
} else if v, ok := value.(int); ok {
|
|
|
|
textRequest.TopP = float64(v)
|
|
|
|
}
|
|
|
|
case "frequency_penalty":
|
|
|
|
if v, ok := value.(float64); ok {
|
|
|
|
textRequest.FrequencyPenalty = v
|
|
|
|
} else if v, ok := value.(int); ok {
|
|
|
|
textRequest.FrequencyPenalty = float64(v)
|
|
|
|
}
|
|
|
|
case "presence_penalty":
|
|
|
|
if v, ok := value.(float64); ok {
|
|
|
|
textRequest.PresencePenalty = v
|
|
|
|
} else if v, ok := value.(int); ok {
|
|
|
|
textRequest.PresencePenalty = float64(v)
|
|
|
|
}
|
|
|
|
case "stop":
|
|
|
|
textRequest.Stop = value
|
|
|
|
case "n":
|
|
|
|
if v, ok := value.(float64); ok {
|
|
|
|
textRequest.N = int(v)
|
|
|
|
} else if v, ok := value.(int); ok {
|
|
|
|
textRequest.N = v
|
|
|
|
}
|
|
|
|
case "stream":
|
|
|
|
if v, ok := value.(bool); ok {
|
|
|
|
textRequest.Stream = v
|
|
|
|
}
|
|
|
|
case "num_ctx":
|
|
|
|
if v, ok := value.(float64); ok {
|
|
|
|
textRequest.NumCtx = int(v)
|
|
|
|
} else if v, ok := value.(int); ok {
|
|
|
|
textRequest.NumCtx = v
|
|
|
|
}
|
|
|
|
// Handle other parameters as needed
|
|
|
|
default:
|
|
|
|
logger.Warnf(ctx, "Unknown parameter override key: %s", key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|