From c58f710227d5c71c606ad78f17f6f1ff19f65606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yolo=C2=B0?= <136311867+yangfan-sys@users.noreply.github.com> Date: Sat, 12 Aug 2023 10:49:30 +0800 Subject: [PATCH 1/2] feat: improve frontend (#387) * fork * fork * chore: update style --------- Co-authored-by: JustSong --- controller/token.go | 11 +++++-- i18n/en.json | 7 +++-- web/src/components/ChannelsTable.js | 4 +-- web/src/components/OtherSetting.js | 2 +- web/src/components/RedemptionsTable.js | 28 ++++++++++++------ web/src/components/SystemSetting.js | 34 +++++++++++++++++++++- web/src/helpers/utils.js | 13 +++++++-- web/src/pages/Channel/EditChannel.js | 8 ++++- web/src/pages/Home/index.js | 9 +++--- web/src/pages/Redemption/EditRedemption.js | 8 ++++- 10 files changed, 98 insertions(+), 26 deletions(-) diff --git a/controller/token.go b/controller/token.go index 5341ea3ab9..b05d820a7e 100644 --- a/controller/token.go +++ b/controller/token.go @@ -109,10 +109,10 @@ func AddToken(c *gin.Context) { }) return } - if len(token.Name) == 0 || len(token.Name) > 20 { + if len(token.Name) == 0 || len(token.Name) > 30 { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "令牌名称长度必须在1-20之间", + "message": "令牌名称过长", }) return } @@ -171,6 +171,13 @@ func UpdateToken(c *gin.Context) { }) return } + if len(token.Name) == 0 || len(token.Name) > 30 { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "令牌名称过长", + }) + return + } cleanToken, err := model.GetTokenByIds(token.Id, userId) if err != nil { c.JSON(http.StatusOK, gin.H{ diff --git a/i18n/en.json b/i18n/en.json index f53aad4c77..78df1acfe5 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -40,7 +40,7 @@ "一次兑换码批量生成的个数不能大于 100": "The number of redemption codes generated in a batch cannot be greater than 100", "通过令牌「%s」使用模型 %s 消耗 %s(模型倍率 %.2f,分组倍率 %.2f)": "Using model %s with token %s consumes %s (model rate %.2f, group rate %.2f)", "当前分组负载已饱和,请稍后再试,或升级账户以提升服务质量。": "The current group load is saturated, please try again later, or upgrade your account to improve service quality.", - "令牌名称长度必须在1-20之间": "The length of the token name must be between 1-20", + "令牌名称过长": "Token name is too long", "令牌已过期,无法启用,请先修改令牌过期时间,或者设置为永不过期": "The token has expired and cannot be enabled. Please modify the expiration time of the token, or set it to never expire.", "令牌可用额度已用尽,无法启用,请先修改令牌剩余额度,或者设置为无限额度": "The available quota of the token has been used up and cannot be enabled. Please modify the remaining quota of the token, or set it to unlimited quota", "管理员关闭了密码登录": "The administrator has turned off password login", @@ -229,7 +229,7 @@ "已是最新版本": "Is the latest version", "检查更新": "Check for updates", "公告": "Announcement", - "在此输入新的公告内容": "Enter new announcement content here", + "在此输入新的公告内容,支持 Markdown & HTML 代码": "Enter the new announcement content here, supports Markdown & HTML code", "保存公告": "Save Announcement", "个性化设置": "Personalization Settings", "系统名称": "System Name", @@ -518,5 +518,6 @@ ",图片演示。": "related image demo.", "令牌创建成功,请在列表页面点击复制获取令牌!": "Token created successfully, please click copy on the list page to get the token!", "代理": "Proxy", - "此项可选,用于通过代理站来进行 API 调用,请输入代理站地址,格式为:https://domain.com": "This is optional, used to make API calls through the proxy site, please enter the proxy site address, the format is: https://domain.com" + "此项可选,用于通过代理站来进行 API 调用,请输入代理站地址,格式为:https://domain.com": "This is optional, used to make API calls through the proxy site, please enter the proxy site address, the format is: https://domain.com", + "取消密码登录将导致所有未绑定其他登录方式的用户(包括管理员)无法通过密码登录,确认取消?": "Canceling password login will cause all users (including administrators) who have not bound other login methods to be unable to log in via password, confirm cancel?" } diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index 0459619a3b..072f5b9079 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -447,8 +447,8 @@ const ChannelsTable = () => { - + {/* */} { { > 复制 - + } + on='click' + flowing + hoverable > - 删除 - + + + + + + } { + return
; +}; +export default HTMLToastContent; export function isAdmin() { let user = localStorage.getItem('user'); if (!user) return false; @@ -107,8 +112,12 @@ export function showInfo(message) { toast.info(message, showInfoOptions); } -export function showNotice(message) { - toast.info(message, showNoticeOptions); +export function showNotice(message, isHTML = false) { + if (isHTML) { + toast(, showNoticeOptions); + } else { + toast.info(message, showNoticeOptions); + } } export function openPage(url) { diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index 4cfec018f4..0d7a4a0153 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { Button, Form, Header, Input, Message, Segment } from 'semantic-ui-react'; -import { useParams } from 'react-router-dom'; +import { useParams, useNavigate } from 'react-router-dom'; import { API, showError, showInfo, showSuccess, verifyJSON } from '../../helpers'; import { CHANNEL_OPTIONS } from '../../constants'; @@ -12,9 +12,14 @@ const MODEL_MAPPING_EXAMPLE = { const EditChannel = () => { const params = useParams(); + const navigate = useNavigate(); const channelId = params.id; const isEdit = channelId !== undefined; const [loading, setLoading] = useState(isEdit); + const handleCancel = () => { + navigate('/channel'); + }; + const originInputs = { name: '', type: 1, @@ -381,6 +386,7 @@ const EditChannel = () => { ) } + diff --git a/web/src/pages/Home/index.js b/web/src/pages/Home/index.js index 20d4210465..c9f4d445e4 100644 --- a/web/src/pages/Home/index.js +++ b/web/src/pages/Home/index.js @@ -14,10 +14,11 @@ const Home = () => { const { success, message, data } = res.data; if (success) { let oldNotice = localStorage.getItem('notice'); - if (data !== oldNotice && data !== '') { - showNotice(data); - localStorage.setItem('notice', data); - } + if (data !== oldNotice && data !== '') { + const htmlNotice = marked(data); + showNotice(htmlNotice, true); + localStorage.setItem('notice', data); + } } else { showError(message); } diff --git a/web/src/pages/Redemption/EditRedemption.js b/web/src/pages/Redemption/EditRedemption.js index df614ab501..7a33f77060 100644 --- a/web/src/pages/Redemption/EditRedemption.js +++ b/web/src/pages/Redemption/EditRedemption.js @@ -1,11 +1,12 @@ import React, { useEffect, useState } from 'react'; import { Button, Form, Header, Segment } from 'semantic-ui-react'; -import { useParams } from 'react-router-dom'; +import { useParams, useNavigate } from 'react-router-dom'; import { API, downloadTextAsFile, showError, showSuccess } from '../../helpers'; import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render'; const EditRedemption = () => { const params = useParams(); + const navigate = useNavigate(); const redemptionId = params.id; const isEdit = redemptionId !== undefined; const [loading, setLoading] = useState(isEdit); @@ -17,6 +18,10 @@ const EditRedemption = () => { const [inputs, setInputs] = useState(originInputs); const { name, quota, count } = inputs; + const handleCancel = () => { + navigate('/redemption'); + }; + const handleInputChange = (e, { name, value }) => { setInputs((inputs) => ({ ...inputs, [name]: value })); }; @@ -113,6 +118,7 @@ const EditRedemption = () => { } + From 476a46ad7e9c25c3d4900a9e2f94ea8717491b37 Mon Sep 17 00:00:00 2001 From: igophper <34326532+igophper@users.noreply.github.com> Date: Sat, 12 Aug 2023 11:04:53 +0800 Subject: [PATCH 2/2] fix: fix finish_reason fileld not fully compatible with OpenAI (close #372, #373) * optimize:unify finish_reason field * refactor: use a global stop finish reason --------- Co-authored-by: JustSong --- controller/relay-ali.go | 5 ++++- controller/relay-baidu.go | 4 +++- controller/relay-claude.go | 5 ++++- controller/relay-palm.go | 2 +- controller/relay-utils.go | 2 ++ controller/relay-xunfei.go | 3 +++ controller/relay-zhipu.go | 3 +-- controller/relay.go | 2 +- 8 files changed, 19 insertions(+), 7 deletions(-) diff --git a/controller/relay-ali.go b/controller/relay-ali.go index e8437c27e2..e94abd6aa5 100644 --- a/controller/relay-ali.go +++ b/controller/relay-ali.go @@ -121,7 +121,10 @@ func responseAli2OpenAI(response *AliChatResponse) *OpenAITextResponse { func streamResponseAli2OpenAI(aliResponse *AliChatResponse) *ChatCompletionsStreamResponse { var choice ChatCompletionsStreamResponseChoice choice.Delta.Content = aliResponse.Output.Text - choice.FinishReason = aliResponse.Output.FinishReason + if aliResponse.Output.FinishReason != "null" { + finishReason := aliResponse.Output.FinishReason + choice.FinishReason = &finishReason + } response := ChatCompletionsStreamResponse{ Id: aliResponse.RequestId, Object: "chat.completion.chunk", diff --git a/controller/relay-baidu.go b/controller/relay-baidu.go index 7960e8ee90..664bbd11a4 100644 --- a/controller/relay-baidu.go +++ b/controller/relay-baidu.go @@ -120,7 +120,9 @@ func responseBaidu2OpenAI(response *BaiduChatResponse) *OpenAITextResponse { func streamResponseBaidu2OpenAI(baiduResponse *BaiduChatStreamResponse) *ChatCompletionsStreamResponse { var choice ChatCompletionsStreamResponseChoice choice.Delta.Content = baiduResponse.Result - choice.FinishReason = "stop" + if baiduResponse.IsEnd { + choice.FinishReason = &stopFinishReason + } response := ChatCompletionsStreamResponse{ Id: baiduResponse.Id, Object: "chat.completion.chunk", diff --git a/controller/relay-claude.go b/controller/relay-claude.go index 1d67fa7b67..052e5605f6 100644 --- a/controller/relay-claude.go +++ b/controller/relay-claude.go @@ -81,7 +81,10 @@ func requestOpenAI2Claude(textRequest GeneralOpenAIRequest) *ClaudeRequest { func streamResponseClaude2OpenAI(claudeResponse *ClaudeResponse) *ChatCompletionsStreamResponse { var choice ChatCompletionsStreamResponseChoice choice.Delta.Content = claudeResponse.Completion - choice.FinishReason = stopReasonClaude2OpenAI(claudeResponse.StopReason) + finishReason := stopReasonClaude2OpenAI(claudeResponse.StopReason) + if finishReason != "null" { + choice.FinishReason = &finishReason + } var response ChatCompletionsStreamResponse response.Object = "chat.completion.chunk" response.Model = claudeResponse.Model diff --git a/controller/relay-palm.go b/controller/relay-palm.go index 74624c7fdb..0053c9b85b 100644 --- a/controller/relay-palm.go +++ b/controller/relay-palm.go @@ -94,7 +94,7 @@ func streamResponsePaLM2OpenAI(palmResponse *PaLMChatResponse) *ChatCompletionsS if len(palmResponse.Candidates) > 0 { choice.Delta.Content = palmResponse.Candidates[0].Content } - choice.FinishReason = "stop" + choice.FinishReason = &stopFinishReason var response ChatCompletionsStreamResponse response.Object = "chat.completion.chunk" response.Model = "palm2" diff --git a/controller/relay-utils.go b/controller/relay-utils.go index 2133d8be7e..3695e11983 100644 --- a/controller/relay-utils.go +++ b/controller/relay-utils.go @@ -6,6 +6,8 @@ import ( "one-api/common" ) +var stopFinishReason = "stop" + var tokenEncoderMap = map[string]*tiktoken.Tiktoken{} func getTokenEncoder(model string) *tiktoken.Tiktoken { diff --git a/controller/relay-xunfei.go b/controller/relay-xunfei.go index 1faf32948a..48472456b4 100644 --- a/controller/relay-xunfei.go +++ b/controller/relay-xunfei.go @@ -138,6 +138,9 @@ func streamResponseXunfei2OpenAI(xunfeiResponse *XunfeiChatResponse) *ChatComple } var choice ChatCompletionsStreamResponseChoice choice.Delta.Content = xunfeiResponse.Payload.Choices.Text[0].Content + if xunfeiResponse.Payload.Choices.Status == 2 { + choice.FinishReason = &stopFinishReason + } response := ChatCompletionsStreamResponse{ Object: "chat.completion.chunk", Created: common.GetTimestamp(), diff --git a/controller/relay-zhipu.go b/controller/relay-zhipu.go index 20a4fa42e9..b125f1e765 100644 --- a/controller/relay-zhipu.go +++ b/controller/relay-zhipu.go @@ -163,7 +163,6 @@ func responseZhipu2OpenAI(response *ZhipuResponse) *OpenAITextResponse { func streamResponseZhipu2OpenAI(zhipuResponse string) *ChatCompletionsStreamResponse { var choice ChatCompletionsStreamResponseChoice choice.Delta.Content = zhipuResponse - choice.FinishReason = "" response := ChatCompletionsStreamResponse{ Object: "chat.completion.chunk", Created: common.GetTimestamp(), @@ -176,7 +175,7 @@ func streamResponseZhipu2OpenAI(zhipuResponse string) *ChatCompletionsStreamResp func streamMetaResponseZhipu2OpenAI(zhipuResponse *ZhipuStreamMetaResponse) (*ChatCompletionsStreamResponse, *Usage) { var choice ChatCompletionsStreamResponseChoice choice.Delta.Content = "" - choice.FinishReason = "stop" + choice.FinishReason = &stopFinishReason response := ChatCompletionsStreamResponse{ Id: zhipuResponse.RequestId, Object: "chat.completion.chunk", diff --git a/controller/relay.go b/controller/relay.go index 617e22b893..030b27f70d 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -124,7 +124,7 @@ type ChatCompletionsStreamResponseChoice struct { Delta struct { Content string `json:"content"` } `json:"delta"` - FinishReason string `json:"finish_reason,omitempty"` + FinishReason *string `json:"finish_reason"` } type ChatCompletionsStreamResponse struct {