From 0e73418cdfef809fec7c8a2b6bb632a3b207eb88 Mon Sep 17 00:00:00 2001 From: JustSong Date: Sun, 26 Nov 2023 12:05:16 +0800 Subject: [PATCH 1/6] fix: fix log recording & error handling for relay-audio --- controller/relay-audio.go | 81 ++++++++++++++++++++++----------------- controller/relay-utils.go | 17 +++++--- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/controller/relay-audio.go b/controller/relay-audio.go index 89a311a0..5b8898a7 100644 --- a/controller/relay-audio.go +++ b/controller/relay-audio.go @@ -39,41 +39,40 @@ func relayAudioHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode } } - preConsumedTokens := common.PreConsumedQuota modelRatio := common.GetModelRatio(audioModel) groupRatio := common.GetGroupRatio(group) ratio := modelRatio * groupRatio - preConsumedQuota := int(float64(preConsumedTokens) * ratio) + var quota int + var preConsumedQuota int + switch relayMode { + case RelayModeAudioSpeech: + preConsumedQuota = int(float64(len(ttsRequest.Input)) * ratio) + quota = preConsumedQuota + default: + preConsumedQuota = int(float64(common.PreConsumedQuota) * ratio) + } userQuota, err := model.CacheGetUserQuota(userId) if err != nil { return errorWrapper(err, "get_user_quota_failed", http.StatusInternalServerError) } - quota := 0 // Check if user quota is enough - if relayMode == RelayModeAudioSpeech { - quota = int(float64(len(ttsRequest.Input)) * modelRatio * groupRatio) - if quota > userQuota { - return errorWrapper(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) - } - } else { - if userQuota-preConsumedQuota < 0 { - return errorWrapper(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) - } - err = model.CacheDecreaseUserQuota(userId, preConsumedQuota) + if userQuota-preConsumedQuota < 0 { + return errorWrapper(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) + } + err = model.CacheDecreaseUserQuota(userId, preConsumedQuota) + if err != nil { + return errorWrapper(err, "decrease_user_quota_failed", http.StatusInternalServerError) + } + if userQuota > 100*preConsumedQuota { + // in this case, we do not pre-consume quota + // because the user has enough quota + preConsumedQuota = 0 + } + if preConsumedQuota > 0 { + err := model.PreConsumeTokenQuota(tokenId, preConsumedQuota) if err != nil { - return errorWrapper(err, "decrease_user_quota_failed", http.StatusInternalServerError) - } - if userQuota > 100*preConsumedQuota { - // in this case, we do not pre-consume quota - // because the user has enough quota - preConsumedQuota = 0 - } - if preConsumedQuota > 0 { - err := model.PreConsumeTokenQuota(tokenId, preConsumedQuota) - if err != nil { - return errorWrapper(err, "pre_consume_token_quota_failed", http.StatusForbidden) - } + return errorWrapper(err, "pre_consume_token_quota_failed", http.StatusForbidden) } } @@ -141,11 +140,7 @@ func relayAudioHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode return errorWrapper(err, "close_request_body_failed", http.StatusInternalServerError) } - if relayMode == RelayModeAudioSpeech { - defer func(ctx context.Context) { - go postConsumeQuota(ctx, tokenId, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName) - }(c.Request.Context()) - } else { + if relayMode != RelayModeAudioSpeech { responseBody, err := io.ReadAll(resp.Body) if err != nil { return errorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) @@ -159,13 +154,29 @@ func relayAudioHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode if err != nil { return errorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError) } - defer func(ctx context.Context) { - quota := countTokenText(whisperResponse.Text, audioModel) - quotaDelta := quota - preConsumedQuota - go postConsumeQuota(ctx, tokenId, quotaDelta, userId, channelId, modelRatio, groupRatio, audioModel, tokenName) - }(c.Request.Context()) + quota = countTokenText(whisperResponse.Text, audioModel) resp.Body = io.NopCloser(bytes.NewBuffer(responseBody)) } + if resp.StatusCode != http.StatusOK { + if preConsumedQuota > 0 { + // we need to roll back the pre-consumed quota + defer func(ctx context.Context) { + go func() { + // negative means add quota back for token & user + err := model.PostConsumeTokenQuota(tokenId, -preConsumedQuota) + if err != nil { + common.LogError(ctx, fmt.Sprintf("error rollback pre-consumed quota: %s", err.Error())) + } + }() + }(c.Request.Context()) + } + return relayErrorHandler(resp) + } + quotaDelta := quota - preConsumedQuota + defer func(ctx context.Context) { + go postConsumeQuota(ctx, tokenId, quotaDelta, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName) + }(c.Request.Context()) + for k, v := range resp.Header { c.Writer.Header().Set(k, v[0]) } diff --git a/controller/relay-utils.go b/controller/relay-utils.go index c7cd4766..391f28b4 100644 --- a/controller/relay-utils.go +++ b/controller/relay-utils.go @@ -195,8 +195,9 @@ func getFullRequestURL(baseURL string, requestURL string, channelType int) strin return fullRequestURL } -func postConsumeQuota(ctx context.Context, tokenId int, quota int, userId int, channelId int, modelRatio float64, groupRatio float64, modelName string, tokenName string) { - err := model.PostConsumeTokenQuota(tokenId, quota) +func postConsumeQuota(ctx context.Context, tokenId int, quotaDelta int, totalQuota int, userId int, channelId int, modelRatio float64, groupRatio float64, modelName string, tokenName string) { + // quotaDelta is remaining quota to be consumed + err := model.PostConsumeTokenQuota(tokenId, quotaDelta) if err != nil { common.SysError("error consuming token remain quota: " + err.Error()) } @@ -204,10 +205,14 @@ func postConsumeQuota(ctx context.Context, tokenId int, quota int, userId int, c if err != nil { common.SysError("error update user quota cache: " + err.Error()) } - if quota != 0 { + // totalQuota is total quota consumed + if totalQuota != 0 { logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio) - model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent) - model.UpdateUserUsedQuotaAndRequestCount(userId, quota) - model.UpdateChannelUsedQuota(channelId, quota) + model.RecordConsumeLog(ctx, userId, channelId, totalQuota, 0, modelName, tokenName, totalQuota, logContent) + model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota) + model.UpdateChannelUsedQuota(channelId, totalQuota) + } + if totalQuota <= 0 { + common.LogError(ctx, fmt.Sprintf("totalQuota consumed is %d, something is wrong", totalQuota)) } } From b7570d5c772f5e4ba8aac4441f8ab9ec86b7aa55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ShinChven=20=E2=9C=A8?= Date: Sun, 3 Dec 2023 17:34:59 +0800 Subject: [PATCH 2/6] feat: support dalle for Azure (#754) * feat: Add Message-ID to email headers to comply with RFC 5322 - Extract domain from SMTPFrom - Generate a unique Message-ID - Add Message-ID to email headers * chore: check slice length * feat: Add Azure compatibility for relayImageHelper - Handle Azure channel requestURL compatibility - Set api-key header for Azure channel authentication - Handle Azure channel request body fixes: https://github.com/songquanpeng/one-api/issues/751 * refactor: update implementation --------- Co-authored-by: JustSong --- controller/relay-audio.go | 7 +------ controller/relay-image.go | 18 ++++++++++++++++-- controller/relay-text.go | 6 +----- controller/relay-utils.go | 10 +++++++++- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/controller/relay-audio.go b/controller/relay-audio.go index 5b8898a7..9e78dadc 100644 --- a/controller/relay-audio.go +++ b/controller/relay-audio.go @@ -98,12 +98,7 @@ func relayAudioHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode fullRequestURL := getFullRequestURL(baseURL, requestURL, channelType) if relayMode == RelayModeAudioTranscription && channelType == common.ChannelTypeAzure { // https://learn.microsoft.com/en-us/azure/ai-services/openai/whisper-quickstart?tabs=command-line#rest-api - query := c.Request.URL.Query() - apiVersion := query.Get("api-version") - if apiVersion == "" { - apiVersion = c.GetString("api_version") - } - baseURL = c.GetString("base_url") + apiVersion := GetAPIVersion(c) fullRequestURL = fmt.Sprintf("%s/openai/deployments/%s/audio/transcriptions?api-version=%s", baseURL, audioModel, apiVersion) } diff --git a/controller/relay-image.go b/controller/relay-image.go index 0ff18309..b3248fcc 100644 --- a/controller/relay-image.go +++ b/controller/relay-image.go @@ -10,6 +10,7 @@ import ( "net/http" "one-api/common" "one-api/model" + "strings" "github.com/gin-gonic/gin" ) @@ -101,8 +102,15 @@ func relayImageHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode baseURL = c.GetString("base_url") } fullRequestURL := getFullRequestURL(baseURL, requestURL, channelType) + if channelType == common.ChannelTypeAzure && relayMode == RelayModeImagesGenerations { + // https://learn.microsoft.com/en-us/azure/ai-services/openai/dall-e-quickstart?tabs=dalle3%2Ccommand-line&pivots=rest-api + apiVersion := GetAPIVersion(c) + // https://{resource_name}.openai.azure.com/openai/deployments/dall-e-3/images/generations?api-version=2023-06-01-preview + fullRequestURL = fmt.Sprintf("%s/openai/deployments/%s/images/generations?api-version=%s", baseURL, imageModel, apiVersion) + } + var requestBody io.Reader - if isModelMapped { + if isModelMapped || channelType == common.ChannelTypeAzure { // make Azure channel request body jsonStr, err := json.Marshal(imageRequest) if err != nil { return errorWrapper(err, "marshal_text_request_failed", http.StatusInternalServerError) @@ -127,7 +135,13 @@ func relayImageHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode if err != nil { return errorWrapper(err, "new_request_failed", http.StatusInternalServerError) } - req.Header.Set("Authorization", c.Request.Header.Get("Authorization")) + token := c.Request.Header.Get("Authorization") + if channelType == common.ChannelTypeAzure { // Azure authentication + token = strings.TrimPrefix(token, "Bearer ") + req.Header.Set("api-key", token) + } else { + req.Header.Set("Authorization", token) + } req.Header.Set("Content-Type", c.Request.Header.Get("Content-Type")) req.Header.Set("Accept", c.Request.Header.Get("Accept")) diff --git a/controller/relay-text.go b/controller/relay-text.go index dd9e7153..a3e233d3 100644 --- a/controller/relay-text.go +++ b/controller/relay-text.go @@ -129,11 +129,7 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode { case APITypeOpenAI: if channelType == common.ChannelTypeAzure { // https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?pivots=rest-api&tabs=command-line#rest-api - query := c.Request.URL.Query() - apiVersion := query.Get("api-version") - if apiVersion == "" { - apiVersion = c.GetString("api_version") - } + apiVersion := GetAPIVersion(c) requestURL := strings.Split(requestURL, "?")[0] requestURL = fmt.Sprintf("%s?api-version=%s", requestURL, apiVersion) baseURL = c.GetString("base_url") diff --git a/controller/relay-utils.go b/controller/relay-utils.go index 391f28b4..839d6ae5 100644 --- a/controller/relay-utils.go +++ b/controller/relay-utils.go @@ -191,7 +191,6 @@ func getFullRequestURL(baseURL string, requestURL string, channelType int) strin fullRequestURL = fmt.Sprintf("%s%s", baseURL, strings.TrimPrefix(requestURL, "/openai/deployments")) } } - return fullRequestURL } @@ -216,3 +215,12 @@ func postConsumeQuota(ctx context.Context, tokenId int, quotaDelta int, totalQuo common.LogError(ctx, fmt.Sprintf("totalQuota consumed is %d, something is wrong", totalQuota)) } } + +func GetAPIVersion(c *gin.Context) string { + query := c.Request.URL.Query() + apiVersion := query.Get("api-version") + if apiVersion == "" { + apiVersion = c.GetString("api_version") + } + return apiVersion +} From 8f5b83562b1fc7831149a0cc33f49f044f011c42 Mon Sep 17 00:00:00 2001 From: Zhengyi Dong <95915069+simulacraliasing@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:43:30 +0800 Subject: [PATCH 3/6] fix: fix "invalidPayload" error when request Azure dall-e-3 api without optional parameter (#764) * fix: based on #754 add 'omitempty' in ImageRequest to fit official api reference for relay * Revert "fix: based on #754 add 'omitempty' in ImageRequest to fit official api reference for relay" This reverts commit b526006ce07839f29b2d5b8c6a028147401ac295. * fix: add missing omitempty --------- Co-authored-by: JustSong --- controller/relay.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/controller/relay.go b/controller/relay.go index f91ba6da..58ee8381 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -133,12 +133,12 @@ type TextRequest struct { type ImageRequest struct { Model string `json:"model"` Prompt string `json:"prompt" binding:"required"` - N int `json:"n"` - Size string `json:"size"` - Quality string `json:"quality"` - ResponseFormat string `json:"response_format"` - Style string `json:"style"` - User string `json:"user"` + N int `json:"n,omitempty"` + Size string `json:"size,omitempty"` + Quality string `json:"quality,omitempty"` + ResponseFormat string `json:"response_format,omitempty"` + Style string `json:"style,omitempty"` + User string `json:"user,omitempty"` } type WhisperResponse struct { From a3f80a3392a658ea09086f2c2f73f5f1701e9341 Mon Sep 17 00:00:00 2001 From: Tillman Bailee <51190972+YOMIkio@users.noreply.github.com> Date: Sun, 3 Dec 2023 20:10:57 +0800 Subject: [PATCH 4/6] feat: enable channel when test succeed (#771) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加功能: 渠道 - 测试所有通道; 设置 - 运营设置 - 监控设置 - 成功时自动启用通道 * refactor: update implementation --------- Co-authored-by: liyujie <29959257@qq.com> Co-authored-by: JustSong --- common/constants.go | 1 + controller/channel-test.go | 36 +++++++++++++++++++------- controller/relay-utils.go | 13 ++++++++++ i18n/en.json | 2 ++ model/option.go | 3 +++ web/src/components/ChannelsTable.js | 2 +- web/src/components/OperationSetting.js | 7 +++++ 7 files changed, 53 insertions(+), 11 deletions(-) diff --git a/common/constants.go b/common/constants.go index c7d3f222..f6860f67 100644 --- a/common/constants.go +++ b/common/constants.go @@ -78,6 +78,7 @@ var QuotaForInviter = 0 var QuotaForInvitee = 0 var ChannelDisableThreshold = 5.0 var AutomaticDisableChannelEnabled = false +var AutomaticEnableChannelEnabled = false var QuotaRemindThreshold = 1000 var PreConsumedQuota = 500 var ApproximateTokenEnabled = false diff --git a/controller/channel-test.go b/controller/channel-test.go index 1b0b745a..bba9a657 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -81,6 +81,9 @@ func testChannel(channel *model.Channel, request ChatRequest) (err error, openai return fmt.Errorf("Error: %s\nResp body: %s", err, body), nil } if response.Usage.CompletionTokens == 0 { + if response.Error.Message == "" { + response.Error.Message = "补全 tokens 非预期返回 0" + } return errors.New(fmt.Sprintf("type %s, code %v, message %s", response.Error.Type, response.Error.Code, response.Error.Message)), &response.Error } return nil, nil @@ -142,20 +145,32 @@ func TestChannel(c *gin.Context) { var testAllChannelsLock sync.Mutex var testAllChannelsRunning bool = false -// disable & notify -func disableChannel(channelId int, channelName string, reason string) { +func notifyRootUser(subject string, content string) { if common.RootUserEmail == "" { common.RootUserEmail = model.GetRootUserEmail() } - model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled) - subject := fmt.Sprintf("通道「%s」(#%d)已被禁用", channelName, channelId) - content := fmt.Sprintf("通道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason) err := common.SendEmail(subject, common.RootUserEmail, content) if err != nil { common.SysError(fmt.Sprintf("failed to send email: %s", err.Error())) } } +// disable & notify +func disableChannel(channelId int, channelName string, reason string) { + model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled) + subject := fmt.Sprintf("通道「%s」(#%d)已被禁用", channelName, channelId) + content := fmt.Sprintf("通道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason) + notifyRootUser(subject, content) +} + +// enable & notify +func enableChannel(channelId int, channelName string) { + model.UpdateChannelStatusById(channelId, common.ChannelStatusEnabled) + subject := fmt.Sprintf("通道「%s」(#%d)已被启用", channelName, channelId) + content := fmt.Sprintf("通道「%s」(#%d)已被启用", channelName, channelId) + notifyRootUser(subject, content) +} + func testAllChannels(notify bool) error { if common.RootUserEmail == "" { common.RootUserEmail = model.GetRootUserEmail() @@ -178,20 +193,21 @@ func testAllChannels(notify bool) error { } go func() { for _, channel := range channels { - if channel.Status != common.ChannelStatusEnabled { - continue - } + isChannelEnabled := channel.Status == common.ChannelStatusEnabled tik := time.Now() err, openaiErr := testChannel(channel, *testRequest) tok := time.Now() milliseconds := tok.Sub(tik).Milliseconds() - if milliseconds > disableThreshold { + if isChannelEnabled && milliseconds > disableThreshold { err = errors.New(fmt.Sprintf("响应时间 %.2fs 超过阈值 %.2fs", float64(milliseconds)/1000.0, float64(disableThreshold)/1000.0)) disableChannel(channel.Id, channel.Name, err.Error()) } - if shouldDisableChannel(openaiErr, -1) { + if isChannelEnabled && shouldDisableChannel(openaiErr, -1) { disableChannel(channel.Id, channel.Name, err.Error()) } + if !isChannelEnabled && shouldEnableChannel(err, openaiErr) { + enableChannel(channel.Id, channel.Name) + } channel.UpdateResponseTime(milliseconds) time.Sleep(common.RequestInterval) } diff --git a/controller/relay-utils.go b/controller/relay-utils.go index 839d6ae5..38408c7f 100644 --- a/controller/relay-utils.go +++ b/controller/relay-utils.go @@ -145,6 +145,19 @@ func shouldDisableChannel(err *OpenAIError, statusCode int) bool { return false } +func shouldEnableChannel(err error, openAIErr *OpenAIError) bool { + if !common.AutomaticEnableChannelEnabled { + return false + } + if err != nil { + return false + } + if openAIErr != nil { + return false + } + return true +} + func setEventStreamHeaders(c *gin.Context) { c.Writer.Header().Set("Content-Type", "text/event-stream") c.Writer.Header().Set("Cache-Control", "no-cache") diff --git a/i18n/en.json b/i18n/en.json index 9b2ca4c8..b0deb83a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -119,6 +119,7 @@ " 年 ": " y ", "未测试": "Not tested", "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "Channel ${name} test succeeded, time consumed ${time.toFixed(2)} s.", + "已成功开始测试所有通道,请刷新页面查看结果。": "All channels have been successfully tested, please refresh the page to view the results.", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "All enabled channels have been successfully tested, please refresh the page to view the results.", "通道 ${name} 余额更新成功!": "Channel ${name} balance updated successfully!", "已更新完毕所有已启用通道余额!": "The balance of all enabled channels has been updated!", @@ -139,6 +140,7 @@ "启用": "Enable", "编辑": "Edit", "添加新的渠道": "Add a new channel", + "测试所有通道": "Test all channels", "测试所有已启用通道": "Test all enabled channels", "更新所有已启用通道余额": "Update the balance of all enabled channels", "刷新": "Refresh", diff --git a/model/option.go b/model/option.go index 4ef4d260..bb8b709c 100644 --- a/model/option.go +++ b/model/option.go @@ -34,6 +34,7 @@ func InitOptionMap() { common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled) common.OptionMap["RegisterEnabled"] = strconv.FormatBool(common.RegisterEnabled) common.OptionMap["AutomaticDisableChannelEnabled"] = strconv.FormatBool(common.AutomaticDisableChannelEnabled) + common.OptionMap["AutomaticEnableChannelEnabled"] = strconv.FormatBool(common.AutomaticEnableChannelEnabled) common.OptionMap["ApproximateTokenEnabled"] = strconv.FormatBool(common.ApproximateTokenEnabled) common.OptionMap["LogConsumeEnabled"] = strconv.FormatBool(common.LogConsumeEnabled) common.OptionMap["DisplayInCurrencyEnabled"] = strconv.FormatBool(common.DisplayInCurrencyEnabled) @@ -147,6 +148,8 @@ func updateOptionMap(key string, value string) (err error) { common.EmailDomainRestrictionEnabled = boolValue case "AutomaticDisableChannelEnabled": common.AutomaticDisableChannelEnabled = boolValue + case "AutomaticEnableChannelEnabled": + common.AutomaticEnableChannelEnabled = boolValue case "ApproximateTokenEnabled": common.ApproximateTokenEnabled = boolValue case "LogConsumeEnabled": diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index d44ea2d7..5d68e2da 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -234,7 +234,7 @@ const ChannelsTable = () => { const res = await API.get(`/api/channel/test`); const { success, message } = res.data; if (success) { - showInfo('已成功开始测试所有已启用通道,请刷新页面查看结果。'); + showInfo('已成功开始测试所有通道,请刷新页面查看结果。'); } else { showError(message); } diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js index bf8b5ffd..3b52bb27 100644 --- a/web/src/components/OperationSetting.js +++ b/web/src/components/OperationSetting.js @@ -16,6 +16,7 @@ const OperationSetting = () => { ChatLink: '', QuotaPerUnit: 0, AutomaticDisableChannelEnabled: '', + AutomaticEnableChannelEnabled: '', ChannelDisableThreshold: 0, LogConsumeEnabled: '', DisplayInCurrencyEnabled: '', @@ -269,6 +270,12 @@ const OperationSetting = () => { name='AutomaticDisableChannelEnabled' onChange={handleInputChange} /> + { submitConfig('monitor').then(); From 01f7b0186fae589e0e5fb83ab0e6d033ba5339aa Mon Sep 17 00:00:00 2001 From: JustSong Date: Sun, 3 Dec 2023 20:45:11 +0800 Subject: [PATCH 5/6] chore: add routes --- router/relay-router.go | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/router/relay-router.go b/router/relay-router.go index 912f4989..24edc9a9 100644 --- a/router/relay-router.go +++ b/router/relay-router.go @@ -35,12 +35,38 @@ func SetRelayRouter(router *gin.Engine) { relayV1Router.DELETE("/files/:id", controller.RelayNotImplemented) relayV1Router.GET("/files/:id", controller.RelayNotImplemented) relayV1Router.GET("/files/:id/content", controller.RelayNotImplemented) - relayV1Router.POST("/fine-tunes", controller.RelayNotImplemented) - relayV1Router.GET("/fine-tunes", controller.RelayNotImplemented) - relayV1Router.GET("/fine-tunes/:id", controller.RelayNotImplemented) - relayV1Router.POST("/fine-tunes/:id/cancel", controller.RelayNotImplemented) - relayV1Router.GET("/fine-tunes/:id/events", controller.RelayNotImplemented) + relayV1Router.POST("/fine_tuning/jobs", controller.RelayNotImplemented) + relayV1Router.GET("/fine_tuning/jobs", controller.RelayNotImplemented) + relayV1Router.GET("/fine_tuning/jobs/:id", controller.RelayNotImplemented) + relayV1Router.POST("/fine_tuning/jobs/:id/cancel", controller.RelayNotImplemented) + relayV1Router.GET("/fine_tuning/jobs/:id/events", controller.RelayNotImplemented) relayV1Router.DELETE("/models/:model", controller.RelayNotImplemented) relayV1Router.POST("/moderations", controller.Relay) + relayV1Router.POST("/assistants", controller.RelayNotImplemented) + relayV1Router.GET("/assistants/:id", controller.RelayNotImplemented) + relayV1Router.POST("/assistants/:id", controller.RelayNotImplemented) + relayV1Router.DELETE("/assistants/:id", controller.RelayNotImplemented) + relayV1Router.GET("/assistants", controller.RelayNotImplemented) + relayV1Router.POST("/assistants/:id/files", controller.RelayNotImplemented) + relayV1Router.GET("/assistants/:id/files/:fileId", controller.RelayNotImplemented) + relayV1Router.DELETE("/assistants/:id/files/:fileId", controller.RelayNotImplemented) + relayV1Router.GET("/assistants/:id/files", controller.RelayNotImplemented) + relayV1Router.POST("/threads", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id", controller.RelayNotImplemented) + relayV1Router.POST("/threads/:id", controller.RelayNotImplemented) + relayV1Router.DELETE("/threads/:id", controller.RelayNotImplemented) + relayV1Router.POST("/threads/:id/messages", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id/messages/:messageId", controller.RelayNotImplemented) + relayV1Router.POST("/threads/:id/messages/:messageId", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id/messages/:messageId/files/:filesId", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id/messages/:messageId/files", controller.RelayNotImplemented) + relayV1Router.POST("/threads/:id/runs", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id/runs/:runsId", controller.RelayNotImplemented) + relayV1Router.POST("/threads/:id/runs/:runsId", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id/runs", controller.RelayNotImplemented) + relayV1Router.POST("/threads/:id/runs/:runsId/submit_tool_outputs", controller.RelayNotImplemented) + relayV1Router.POST("/threads/:id/runs/:runsId/cancel", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id/runs/:runsId/steps/:stepId", controller.RelayNotImplemented) + relayV1Router.GET("/threads/:id/runs/:runsId/steps", controller.RelayNotImplemented) } } From 88e2e3caad3241f7e15f2d845d4c0b0d6600cc1a Mon Sep 17 00:00:00 2001 From: wood Date: Tue, 5 Dec 2023 01:42:57 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/relay-utils.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/controller/relay-utils.go b/controller/relay-utils.go index 070f459b..3031b084 100644 --- a/controller/relay-utils.go +++ b/controller/relay-utils.go @@ -219,7 +219,9 @@ func postConsumeQuota(ctx context.Context, tokenId int, quotaDelta int, totalQuo } // totalQuota is total quota consumed if totalQuota != 0 { - logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio) + outputPrice := modelRatio * 0.002 + var logContent string + logContent = fmt.Sprintf("单价: $%.6g/1k tokens", outputPrice) model.RecordConsumeLog(ctx, userId, channelId, totalQuota, 0, modelName, tokenName, totalQuota, logContent) model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota) model.UpdateChannelUsedQuota(channelId, totalQuota)