fix: translate error messages and comments to English for consistency

This commit is contained in:
Laisky.Cai 2025-01-27 03:34:27 +00:00
parent 59dba5bef3
commit d5fa98f2e0
25 changed files with 197 additions and 186 deletions

View File

@ -53,7 +53,7 @@ func getLarkUserInfoByCode(code string) (*LarkUser, error) {
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { if err != nil {
logger.SysLog(err.Error()) logger.SysLog(err.Error())
return nil, errors.New("None法连接至飞书服务器请稍后Retry") return nil, errors.New("Unable to connect to Lark server, please try again later!")
} }
defer res.Body.Close() defer res.Body.Close()
var oAuthResponse LarkOAuthResponse var oAuthResponse LarkOAuthResponse
@ -69,7 +69,7 @@ func getLarkUserInfoByCode(code string) (*LarkUser, error) {
res2, err := client.Do(req) res2, err := client.Do(req)
if err != nil { if err != nil {
logger.SysLog(err.Error()) logger.SysLog(err.Error())
return nil, errors.New("None法连接至飞书服务器请稍后Retry") return nil, errors.New("Unable to connect to Lark server, please try again later!")
} }
var larkUser LarkUser var larkUser LarkUser
err = json.NewDecoder(res2.Body).Decode(&larkUser) err = json.NewDecoder(res2.Body).Decode(&larkUser)
@ -168,7 +168,7 @@ func LarkBind(c *gin.Context) {
if model.IsLarkIdAlreadyTaken(user.LarkId) { if model.IsLarkIdAlreadyTaken(user.LarkId) {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "该飞书账户已被Bind", "message": "This Lark account has already been bound",
}) })
return return
} }

View File

@ -5,15 +5,16 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http"
"strconv"
"time"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common/config" "github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/logger" "github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/controller" "github.com/songquanpeng/one-api/controller"
"github.com/songquanpeng/one-api/model" "github.com/songquanpeng/one-api/model"
"net/http"
"strconv"
"time"
) )
type OidcResponse struct { type OidcResponse struct {
@ -60,7 +61,7 @@ func getOidcUserInfoByCode(code string) (*OidcUser, error) {
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { if err != nil {
logger.SysLog(err.Error()) logger.SysLog(err.Error())
return nil, errors.New("None法连接至 OIDC 服务器请稍后Retry") return nil, errors.New("Unable to connect to the OIDC server, please try again later!")
} }
defer res.Body.Close() defer res.Body.Close()
var oidcResponse OidcResponse var oidcResponse OidcResponse
@ -76,7 +77,7 @@ func getOidcUserInfoByCode(code string) (*OidcUser, error) {
res2, err := client.Do(req) res2, err := client.Do(req)
if err != nil { if err != nil {
logger.SysLog(err.Error()) logger.SysLog(err.Error())
return nil, errors.New("None法连接至 OIDC 服务器请稍后Retry") return nil, errors.New("Unable to connect to the OIDC server, please try again later!")
} }
var oidcUser OidcUser var oidcUser OidcUser
err = json.NewDecoder(res2.Body).Decode(&oidcUser) err = json.NewDecoder(res2.Body).Decode(&oidcUser)
@ -104,7 +105,7 @@ func OidcAuth(c *gin.Context) {
if !config.OidcEnabled { if !config.OidcEnabled {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "Administrator未开启通过 OIDC Log in以及Sign up", "message": "Administrator has not enabled OIDC Log in and Sign up",
}) })
return return
} }
@ -173,7 +174,7 @@ func OidcBind(c *gin.Context) {
if !config.OidcEnabled { if !config.OidcEnabled {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "Administrator未开启通过 OIDC Log in以及Sign up", "message": "The administrator has turned off new user registration",
}) })
return return
} }
@ -192,7 +193,7 @@ func OidcBind(c *gin.Context) {
if model.IsOidcIdAlreadyTaken(user.OidcId) { if model.IsOidcIdAlreadyTaken(user.OidcId) {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "该 OIDC 账户已被Bind", "message": "This OIDC account has already been bound",
}) })
return return
} }

View File

@ -3,14 +3,14 @@ package controller
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/message"
"github.com/songquanpeng/one-api/model"
"net/http" "net/http"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/message"
"github.com/songquanpeng/one-api/model"
) )
func GetStatus(c *gin.Context) { func GetStatus(c *gin.Context) {
@ -100,7 +100,7 @@ func SendEmailVerification(c *gin.Context) {
if !allowed { if !allowed {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "AdministratorEnable了邮箱域名白名单您的Email address的域名不在白名单中", "message": "Administrator has enabled email domain whitelist, your email domain is not in the whitelist",
}) })
return return
} }
@ -154,8 +154,8 @@ func SendPasswordResetEmail(c *gin.Context) {
link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", config.ServerAddress, email, code) link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", config.ServerAddress, email, code)
subject := fmt.Sprintf("%s Password reset", config.SystemName) subject := fmt.Sprintf("%s Password reset", config.SystemName)
content := fmt.Sprintf("<p>Hello, you are resetting %s password.</p>"+ content := fmt.Sprintf("<p>Hello, you are resetting %s password.</p>"+
"<p>点击 <a href='%s'>此处</a> 进行Password reset。</p>"+ "<p>Click <a href='%s'>here</a> to reset your password.</p>"+
"<p>如果链接None法点击请尝试点击下面的链接或将其Copy到浏览器中打开<br> %s </p>"+ "<p>If the link cannot be clicked, please try clicking the link below or copy it to your browser to open:<br> %s </p>"+
"<p>The reset link is valid within %d minutes. If it is not your operation, please ignore it.</p>", config.SystemName, link, link, common.VerificationValidMinutes) "<p>The reset link is valid within %d minutes. If it is not your operation, please ignore it.</p>", config.SystemName, link, link, common.VerificationValidMinutes)
err := message.SendEmail(subject, email, content) err := message.SendEmail(subject, email, content)
if err != nil { if err != nil {

View File

@ -2,13 +2,13 @@ package controller
import ( import (
"encoding/json" "encoding/json"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/helper"
"github.com/songquanpeng/one-api/model"
"net/http" "net/http"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/helper"
"github.com/songquanpeng/one-api/model"
) )
func GetOptions(c *gin.Context) { func GetOptions(c *gin.Context) {
@ -47,7 +47,7 @@ func UpdateOption(c *gin.Context) {
if !config.ValidThemes[option.Value] { if !config.ValidThemes[option.Value] {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "None效的主题", "message": "invalid theme",
}) })
return return
} }
@ -55,7 +55,7 @@ func UpdateOption(c *gin.Context) {
if option.Value == "true" && config.GitHubClientId == "" { if option.Value == "true" && config.GitHubClientId == "" {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "None法Enable GitHub OAuth请先填入 GitHub Client Id 以及 GitHub Client Secret", "message": "Unable to enable GitHub OAuth, please fill in the GitHub Client Id and GitHub Client Secret first!",
}) })
return return
} }
@ -63,7 +63,7 @@ func UpdateOption(c *gin.Context) {
if option.Value == "true" && len(config.EmailDomainWhitelist) == 0 { if option.Value == "true" && len(config.EmailDomainWhitelist) == 0 {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "None法Enable邮箱域名限制请先填入限制的邮箱域名", "message": "Unable to enable email domain restriction, please fill in the restricted email domains first!",
}) })
return return
} }

View File

@ -162,7 +162,7 @@ func AddToken(c *gin.Context) {
if err != nil { if err != nil {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": fmt.Sprintf("参数Error:%s", err.Error()), "message": fmt.Sprintf("invalid token: %s", err.Error()),
}) })
return return
} }
@ -336,7 +336,7 @@ func UpdateToken(c *gin.Context) {
if err != nil { if err != nil {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": fmt.Sprintf("参数Error:%s", err.Error()), "message": fmt.Sprintf("invalid token: %s", err.Error()),
}) })
return return
} }

View File

@ -271,7 +271,7 @@ func GetUserDashboard(c *gin.Context) {
if err != nil { if err != nil {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "None法获取Statistics", "message": "Failed to get user dashboard data: " + err.Error(),
"data": nil, "data": nil,
}) })
return return
@ -415,7 +415,7 @@ func UpdateUser(c *gin.Context) {
if myRole <= updatedUser.Role && myRole != model.RoleRootUser { if myRole <= updatedUser.Role && myRole != model.RoleRootUser {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "None权将其他Users权限等级Promote到大于Equals自己的权限等级", "message": "No permission to promote other users to a permission level greater than or equal to your own",
}) })
return return
} }
@ -529,7 +529,7 @@ func DeleteSelf(c *gin.Context) {
if user.Role == model.RoleRootUser { if user.Role == model.RoleRootUser {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "不能DeleteSuper administrator账户", "message": "Cannot delete super administrator account",
}) })
return return
} }
@ -813,7 +813,7 @@ func AdminTopUp(c *gin.Context) {
return return
} }
if req.Remark == "" { if req.Remark == "" {
req.Remark = fmt.Sprintf("通过 API Recharge %s", common.LogQuota(int64(req.Quota))) req.Remark = fmt.Sprintf("Recharged via API %s", common.LogQuota(int64(req.Quota)))
} }
model.RecordTopupLog(req.UserId, req.Remark, req.Quota) model.RecordTopupLog(req.UserId, req.Remark, req.Quota)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{

View File

@ -107,7 +107,7 @@ func TokenAuth() func(c *gin.Context) {
} }
if token.Subnet != nil && *token.Subnet != "" { if token.Subnet != nil && *token.Subnet != "" {
if !network.IsIpInSubnets(ctx, c.ClientIP(), *token.Subnet) { if !network.IsIpInSubnets(ctx, c.ClientIP(), *token.Subnet) {
abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("该API Keys只能在指定网段使用%s当前 ip%s", *token.Subnet, c.ClientIP())) abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("This API key can only be used in the specified subnet: %s, current IP: %s", *token.Subnet, c.ClientIP()))
return return
} }
} }
@ -129,7 +129,7 @@ func TokenAuth() func(c *gin.Context) {
if token.Models != nil && *token.Models != "" { if token.Models != nil && *token.Models != "" {
c.Set(ctxkey.AvailableModels, *token.Models) c.Set(ctxkey.AvailableModels, *token.Models)
if requestModel != "" && !isModelInList(requestModel, *token.Models) { if requestModel != "" && !isModelInList(requestModel, *token.Models) {
abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("该API KeysNone权使用Model%s", requestModel)) abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("This API key does not have permission to use the model: %s", requestModel))
return return
} }
} }

View File

@ -30,12 +30,12 @@ func Distribute() func(c *gin.Context) {
if ok { if ok {
id, err := strconv.Atoi(channelId.(string)) id, err := strconv.Atoi(channelId.(string))
if err != nil { if err != nil {
abortWithMessage(c, http.StatusBadRequest, "None效的Channel Id") abortWithMessage(c, http.StatusBadRequest, "Invalid Channel Id")
return return
} }
channel, err = model.GetChannelById(id, true) channel, err = model.GetChannelById(id, true)
if err != nil { if err != nil {
abortWithMessage(c, http.StatusBadRequest, "None效的Channel Id") abortWithMessage(c, http.StatusBadRequest, "Invalid Channel Id")
return return
} }
if channel.Status != model.ChannelStatusEnabled { if channel.Status != model.ChannelStatusEnabled {
@ -47,7 +47,7 @@ func Distribute() func(c *gin.Context) {
var err error var err error
channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, requestModel, false) channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, requestModel, false)
if err != nil { if err != nil {
message := fmt.Sprintf("当前Group %s 下对于Model %s No available channels", userGroup, requestModel) message := fmt.Sprintf("No available channels for Model %s under Group %s", requestModel, userGroup)
if channel != nil { if channel != nil {
logger.SysError(fmt.Sprintf("Channel does not exist: %d", channel.Id)) logger.SysError(fmt.Sprintf("Channel does not exist: %d", channel.Id))
message = "Database consistency has been broken, please contact the administrator" message = "Database consistency has been broken, please contact the administrator"

View File

@ -2,6 +2,7 @@ package model
import ( import (
"fmt" "fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/songquanpeng/one-api/common" "github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/helper" "github.com/songquanpeng/one-api/common/helper"
@ -80,7 +81,7 @@ func Redeem(key string, userId int) (quota int64, err error) {
return err return err
}) })
if err != nil { if err != nil {
return 0, errors.New("Redeem失败," + err.Error()) return 0, errors.New("Redeem failed, " + err.Error())
} }
RecordLog(userId, LogTypeTopup, fmt.Sprintf("Recharge %s through redemption code", common.LogQuota(redemption.Quota))) RecordLog(userId, LogTypeTopup, fmt.Sprintf("Recharge %s through redemption code", common.LogQuota(redemption.Quota)))
return redemption.Quota, nil return redemption.Quota, nil

View File

@ -72,7 +72,7 @@ func ValidateUserToken(key string) (token *Token, err error) {
return nil, errors.Wrap(err, "failed to get token by key") return nil, errors.Wrap(err, "failed to get token by key")
} }
if token.Status == TokenStatusExhausted { if token.Status == TokenStatusExhausted {
return nil, fmt.Errorf("API Keys %s#%dQuota已用尽", token.Name, token.Id) return nil, fmt.Errorf("API Key %s (#%d) quota has been exhausted", token.Name, token.Id)
} else if token.Status == TokenStatusExpired { } else if token.Status == TokenStatusExpired {
return nil, errors.New("The token has expired") return nil, errors.New("The token has expired")
} }

View File

@ -2,6 +2,7 @@ package monitor
import ( import (
"fmt" "fmt"
"github.com/songquanpeng/one-api/common/config" "github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/logger" "github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/common/message" "github.com/songquanpeng/one-api/common/message"
@ -38,9 +39,10 @@ func DisableChannel(channelId int, channelName string, reason string) {
func MetricDisableChannel(channelId int, successRate float64) { func MetricDisableChannel(channelId int, successRate float64) {
model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled) model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled)
logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100)) logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100))
subject := fmt.Sprintf("Channel #%d 已被Disable", channelId) subject := fmt.Sprintf("Channel #%d has been disabled", channelId)
content := fmt.Sprintf("该Channel#%d在最近 %d 次调用中成功率为 %.2f%%,低于阈值 %.2f%%因此被System自动Disable。", content := fmt.Sprintf("The channel (#%d) has been automatically disabled by the system due to "+
channelId, config.MetricQueueSize, successRate*100, config.MetricSuccessRateThreshold*100) "a success rate of %.2f%% over the last %d calls, which is below the threshold of %.2f%%.",
channelId, successRate*100, config.MetricQueueSize, config.MetricSuccessRateThreshold*100)
notifyRootUser(subject, content) notifyRootUser(subject, content)
} }
@ -48,7 +50,7 @@ func MetricDisableChannel(channelId int, successRate float64) {
func EnableChannel(channelId int, channelName string) { func EnableChannel(channelId int, channelName string) {
model.UpdateChannelStatusById(channelId, model.ChannelStatusEnabled) model.UpdateChannelStatusById(channelId, model.ChannelStatusEnabled)
logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId)) logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId))
subject := fmt.Sprintf("Channel「%s」#%d已被Enable", channelName, channelId) subject := fmt.Sprintf("Channel %s (#%d) has been enabled", channelName, channelId)
content := fmt.Sprintf("Channel「%s」#%d已被Enable", channelName, channelId) content := fmt.Sprintf("Channel %s (#%d) has been enabled", channelName, channelId)
notifyRootUser(subject, content) notifyRootUser(subject, content)
} }

View File

@ -35,7 +35,7 @@ func ShouldDisableChannel(err *model.Error, statusCode int) bool {
strings.Contains(lowerMessage, "balance") || strings.Contains(lowerMessage, "balance") ||
strings.Contains(lowerMessage, "permission denied") || strings.Contains(lowerMessage, "permission denied") ||
strings.Contains(lowerMessage, "organization has been restricted") || // groq strings.Contains(lowerMessage, "organization has been restricted") || // groq
strings.Contains(lowerMessage, "已欠费") { strings.Contains(lowerMessage, "overdue payment") {
return true return true
} }
return false return false

View File

@ -38,7 +38,7 @@ func aiProxyDocuments2Markdown(documents []LibraryDocument) string {
if len(documents) == 0 { if len(documents) == 0 {
return "" return ""
} }
content := "\n\n参考文档:\n" content := "\n\nReference Documents:\n"
for i, document := range documents { for i, document := range documents {
content += fmt.Sprintf("%d. [%s](%s)\n", i+1, document.Title, document.URL) content += fmt.Sprintf("%d. [%s](%s)\n", i+1, document.Title, document.URL)
} }

View File

@ -148,18 +148,18 @@ func responseAli2OpenAIImage(response *TaskResponse, responseFormat string) *ope
for _, data := range response.Output.Results { for _, data := range response.Output.Results {
var b64Json string var b64Json string
if responseFormat == "b64_json" { if responseFormat == "b64_json" {
// 读取 data.Url 的图片数据并转存到 b64Json // Read the image data from data.Url and store it in b64Json
imageData, err := getImageData(data.Url) imageData, err := getImageData(data.Url)
if err != nil { if err != nil {
// 处理获取图片数据失败的情况 // Handle the case where getting image data fails
logger.SysError("getImageData Error getting image data: " + err.Error()) logger.SysError("getImageData Error getting image data: " + err.Error())
continue continue
} }
// 将图片数据转为 Base64 编码的字符串 // Convert the image data to a Base64 encoded string
b64Json = Base64Encode(imageData) b64Json = Base64Encode(imageData)
} else { } else {
// 如果 responseFormat 不是 "b64_json",则直接使用 data.B64Image // If responseFormat is not "b64_json", use data.B64Image directly
b64Json = data.B64Image b64Json = data.B64Image
} }

View File

@ -2,23 +2,23 @@ package cohere
type Request struct { type Request struct {
Message string `json:"message" required:"true"` Message string `json:"message" required:"true"`
Model string `json:"model,omitempty"` // Default值为"command-r" Model string `json:"model,omitempty"` // default to "command-r"
Stream bool `json:"stream,omitempty"` // Default值为false Stream bool `json:"stream,omitempty"` // default to false
Preamble string `json:"preamble,omitempty"` Preamble string `json:"preamble,omitempty"`
ChatHistory []ChatMessage `json:"chat_history,omitempty"` ChatHistory []ChatMessage `json:"chat_history,omitempty"`
ConversationID string `json:"conversation_id,omitempty"` ConversationID string `json:"conversation_id,omitempty"`
PromptTruncation string `json:"prompt_truncation,omitempty"` // Default值为"AUTO" PromptTruncation string `json:"prompt_truncation,omitempty"` // default to "AUTO"
Connectors []Connector `json:"connectors,omitempty"` Connectors []Connector `json:"connectors,omitempty"`
Documents []Document `json:"documents,omitempty"` Documents []Document `json:"documents,omitempty"`
Temperature *float64 `json:"temperature,omitempty"` // Default值为0.3 Temperature *float64 `json:"temperature,omitempty"` // default to 0.3
MaxTokens int `json:"max_tokens,omitempty"` MaxTokens int `json:"max_tokens,omitempty"`
MaxInputTokens int `json:"max_input_tokens,omitempty"` MaxInputTokens int `json:"max_input_tokens,omitempty"`
K int `json:"k,omitempty"` // Default值为0 K int `json:"k,omitempty"` // default to 0
P *float64 `json:"p,omitempty"` // Default值为0.75 P *float64 `json:"p,omitempty"` // default to 0.75
Seed int `json:"seed,omitempty"` Seed int `json:"seed,omitempty"`
StopSequences []string `json:"stop_sequences,omitempty"` StopSequences []string `json:"stop_sequences,omitempty"`
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"` // Default值为0.0 FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"` // default to 0.0
PresencePenalty *float64 `json:"presence_penalty,omitempty"` // Default值为0.0 PresencePenalty *float64 `json:"presence_penalty,omitempty"` // default to 0.0
Tools []Tool `json:"tools,omitempty"` Tools []Tool `json:"tools,omitempty"`
ToolResults []ToolResult `json:"tool_results,omitempty"` ToolResults []ToolResult `json:"tool_results,omitempty"`
} }

View File

@ -6,40 +6,46 @@ type Message struct {
} }
type ChatRequest struct { type ChatRequest struct {
// Model nameOptional values包括 hunyuan-lite、hunyuan-standard、hunyuan-standard-256K、hunyuan-pro。 // Model name, optional values include hunyuan-lite, hunyuan-standard, hunyuan-standard-256K, hunyuan-pro.
// 各Model介绍请阅读 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 中的说明。 // For descriptions of each model, please read the [Product Overview](https://cloud.tencent.com/document/product/1729/104753).
// //
// Note // Note:
// 不同的Model计费不同请根据 [购买指南](https://cloud.tencent.com/document/product/1729/97731) 按需调用。 // Different models have different pricing. Please refer to the [Purchase Guide](https://cloud.tencent.com/document/product/1729/97731) for details.
Model *string `json:"Model"` Model *string `json:"Model"`
// Chat上下文信息。 // Chat context information.
// 说明: // Description:
// 1. 长度最多为 40按对话Time从旧到新在数Group中排列。 // 1. The maximum length is 40, arranged in the array in chronological order from oldest to newest.
// 2. Message.Role Optional valuessystem、user、assistant。 // 2. Message.Role optional values: system, user, assistant.
// 其中system 角色可选如存在则必须位于列表的最开始。user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[system可选 user assistant user assistant user ...]。 // Among them, the system role is optional. If it exists, it must be at the beginning of the list.
// 3. Messages 中 Content 总长度不能超过ModelEnter长度上限可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。 // User and assistant must alternate (one question and one answer), starting and ending with user,
// and Content cannot be empty. The order of roles is as follows: [system (optional) user assistant user assistant user ...].
// 3. The total length of Content in Messages cannot exceed the model's length limit
// (refer to the [Product Overview](https://cloud.tencent.com/document/product/1729/104753) document).
// If it exceeds, the earliest content will be truncated, leaving only the latest content.
Messages []*Message `json:"Messages"` Messages []*Message `json:"Messages"`
// 流式调用开关。 // Stream call switch.
// 说明: // Description:
// 1. 未传值时Default为非流式调用false // 1. If not provided, the default is non-streaming call (false).
// 2. 流式调用时以 SSE 协议增量返回结果(返回值取 Choices[n].Delta 中的值,需要拼接增量数据才能获得完整结果)。 // 2. In streaming calls, results are returned incrementally using the SSE protocol
// 3. 非流式调用时: // (the return value is taken from Choices[n].Delta, and incremental data needs to be concatenated to obtain the complete result).
// 调用方式与普通 HTTP 请求None异。 // 3. In non-streaming calls:
// 接口响应耗时较长,**如需更低时延建议Settings为 true**。 // The call method is the same as a regular HTTP request.
// 只返回一次最终结果(返回值取 Choices[n].Message 中的值)。 // The interface response time is relatively long. **If lower latency is required, it is recommended to set this to true**.
// Only the final result is returned once (the return value is taken from Choices[n].Message).
// //
// Note // Note:
// 通过 SDK 调用时,流式和非流式调用需用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。 // When calling through the SDK, different methods are required to obtain return values for streaming and non-streaming calls.
// Refer to the comments or examples in the SDK (in the examples/hunyuan/v20230901/ directory of each language SDK code repository).
Stream *bool `json:"Stream"` Stream *bool `json:"Stream"`
// 说明: // Description:
// 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。 // 1. Affects the diversity of the output text. The larger the value, the more diverse the generated text.
// 2. 取值区间为 [0.0, 1.0]未传值时使用各Model推荐值。 // 2. The value range is [0.0, 1.0]. If not provided, the recommended value for each model is used.
// 3. 非必要不建议使用,不合理的取值会影响效果。 // 3. It is not recommended to use this unless necessary, as unreasonable values can affect the results.
TopP *float64 `json:"TopP"` TopP *float64 `json:"TopP"`
// 说明: // Description:
// 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 // 1. Higher values make the output more random, while lower values make it more focused and deterministic.
// 2. 取值区间为 [0.0, 2.0]未传值时使用各Model推荐值。 // 2. The value range is [0.0, 2.0]. If not provided, the recommended value for each model is used.
// 3. 非必要不建议使用,不合理的取值会影响效果。 // 3. It is not recommended to use this unless necessary, as unreasonable values can affect the results.
Temperature *float64 `json:"Temperature"` Temperature *float64 `json:"Temperature"`
} }
@ -55,19 +61,19 @@ type Usage struct {
} }
type ResponseChoices struct { type ResponseChoices struct {
FinishReason string `json:"FinishReason,omitempty"` // 流式结束标志位,为 stop 则表示尾包 FinishReason string `json:"FinishReason,omitempty"` // Stream end flag, "stop" indicates the end packet
Messages Message `json:"Message,omitempty"` // 内容,同步模式返回内容,流模式为 null 输出 content 内容总数最多支持 1024token。 Messages Message `json:"Message,omitempty"` // Content, returned in synchronous mode, null in stream mode. The total content supports up to 1024 tokens.
Delta Message `json:"Delta,omitempty"` // 内容,流模式返回内容,同步模式为 null 输出 content 内容总数最多支持 1024token。 Delta Message `json:"Delta,omitempty"` // Content, returned in stream mode, null in synchronous mode. The total content supports up to 1024 tokens.
} }
type ChatResponse struct { type ChatResponse struct {
Choices []ResponseChoices `json:"Choices,omitempty"` // 结果 Choices []ResponseChoices `json:"Choices,omitempty"` // Results
Created int64 `json:"Created,omitempty"` // unix Time戳的字符串 Created int64 `json:"Created,omitempty"` // Unix timestamp string
Id string `json:"Id,omitempty"` // 会话 id Id string `json:"Id,omitempty"` // Session ID
Usage Usage `json:"Usage,omitempty"` // token 数量 Usage Usage `json:"Usage,omitempty"` // Token count
Error Error `json:"Error,omitempty"` // 错误信息 Note此字段可能返回 null表示取不到有效值 Error Error `json:"Error,omitempty"` // Error information. Note: This field may return null, indicating that no valid value was found.
Note string `json:"Note,omitempty"` // 注释 Note string `json:"Note,omitempty"` // Note
ReqID string `json:"Req_id,omitempty"` // 唯一请求 Id每次请求都会返回。用于反馈接口入参 ReqID string `json:"Req_id,omitempty"` // Unique request ID, returned with each request. Used for feedback on interface input parameters.
} }
type ChatResponseP struct { type ChatResponseP struct {

View File

@ -385,8 +385,9 @@ func GetAudioCompletionRatio(actualModelName string) float64 {
// AudioTokensPerSecond is the number of audio tokens per second for each model. // AudioTokensPerSecond is the number of audio tokens per second for each model.
var AudioPromptTokensPerSecond = map[string]float64{ var AudioPromptTokensPerSecond = map[string]float64{
// whisper 的 API 价格是 $0.0001/sec。one-api 的历史倍率为 15对应 $0.03/kilo_tokens。 // Whisper API price is $0.0001/sec. One-api's historical ratio is 15,
// 那么换算后可得,每秒的 tokens 应该为 0.0001/0.03*1000 = 3.3333 // corresponding to $0.03/kilo_tokens.
// After conversion, tokens per second should be 0.0001/0.03*1000 = 3.3333.
"whisper-1": 0.0001 / 0.03 * 1000, "whisper-1": 0.0001 / 0.03 * 1000,
// gpt-4o-audio series processes 10 tokens per second // gpt-4o-audio series processes 10 tokens per second
"gpt-4o-audio-preview": 10, "gpt-4o-audio-preview": 10,

View File

@ -125,7 +125,7 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *meta.M
} }
var extraLog string var extraLog string
if systemPromptReset { if systemPromptReset {
extraLog = " NoteSystemPrompt词已被重置" extraLog = " (Note: System prompt has been reset)"
} }
logContent := fmt.Sprintf("model rate %.2f, group rate %.2f, completion rate %.2f%s", modelRatio, groupRatio, completionRatio, extraLog) logContent := fmt.Sprintf("model rate %.2f, group rate %.2f, completion rate %.2f%s", modelRatio, groupRatio, completionRatio, extraLog)
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent) model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent)

View File

@ -203,7 +203,7 @@ const ChannelsTable = () => {
trigger={<Label basic color='red'> trigger={<Label basic color='red'>
Disabled Disabled
</Label>} </Label>}
content='本Channel被手动Disable' content='This channel has been manually disabled'
basic basic
/> />
); );
@ -213,7 +213,7 @@ const ChannelsTable = () => {
trigger={<Label basic color='yellow'> trigger={<Label basic color='yellow'>
Disabled Disabled
</Label>} </Label>}
content='本Channel被程序自动Disable' content='This channel has been automatically disabled by the program'
basic basic
/> />
); );
@ -277,7 +277,7 @@ const ChannelsTable = () => {
newChannels[realIdx].response_time = time * 1000; newChannels[realIdx].response_time = time * 1000;
newChannels[realIdx].test_time = Date.now() / 1000; newChannels[realIdx].test_time = Date.now() / 1000;
setChannels(newChannels); setChannels(newChannels);
showInfo(`Channel ${name} Test成功Model ${model},耗时 ${time.toFixed(2)}s。`); showInfo(`Channel ${name} tested successfully with model ${model}, taking ${time.toFixed(2)} seconds.`);
} else { } else {
showError(message); showError(message);
} }
@ -292,7 +292,7 @@ const ChannelsTable = () => {
const res = await API.get(`/api/channel/test?scope=${scope}`); const res = await API.get(`/api/channel/test?scope=${scope}`);
const { success, message } = res.data; const { success, message } = res.data;
if (success) { if (success) {
showInfo('已成功开始TestChannel请Refresh页面查看结果。'); showInfo('Successfully started testing channels, please refresh the page to see the results.');
} else { } else {
showError(message); showError(message);
} }
@ -302,7 +302,7 @@ const ChannelsTable = () => {
const res = await API.delete(`/api/channel/disabled`); const res = await API.delete(`/api/channel/disabled`);
const { success, message, data } = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
showSuccess(`已Delete所有DisableChannel共计 ${data}`); showSuccess(`Successfully deleted all disabled channels, total ${data} channels`);
await refresh(); await refresh();
} else { } else {
showError(message); showError(message);
@ -504,26 +504,26 @@ const ChannelsTable = () => {
idx, idx,
event.target.value event.target.value
); );
}}> }}>
<input style={{ maxWidth: '60px' }} /> <input style={{ maxWidth: '60px' }} />
</Input>} </Input>}
content='Channel priority - higher value means higher priority' content='Channel priority - higher value means higher priority'
/> />
</Table.Cell> </Table.Cell>
<Table.Cell hidden={!showDetail}> <Table.Cell hidden={!showDetail}>
<Dropdown <Dropdown
placeholder='请选择TestModel' placeholder='Please select TestModel'
selection selection
options={channel.model_options} options={channel.model_options}
defaultValue={channel.test_model} defaultValue={channel.test_model}
onChange={(event, data) => { onChange={(event, data) => {
switchTestModel(idx, data.value); switchTestModel(idx, data.value);
}} }}
/> />
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
<div> <div>
<Button <Button
size={'small'} size={'small'}
positive positive
onClick={() => { onClick={() => {
@ -597,14 +597,14 @@ const ChannelsTable = () => {
Test all channels Test all channels
</Button> </Button>
<Button size='small' loading={loading} onClick={()=>{testChannels("disabled")}}> <Button size='small' loading={loading} onClick={()=>{testChannels("disabled")}}>
TestDisableChannel Test disabled channels
</Button> </Button>
{/*<Button size='small' onClick={updateAllChannelsBalance}*/} {/*<Button size='small' onClick={updateAllChannelsBalance}*/}
{/* loading={loading || updatingBalance}>Update the balance of enabled channels</Button>*/} {/* loading={loading || updatingBalance}>Update the balance of enabled channels</Button>*/}
<Popup <Popup
trigger={ trigger={
<Button size='small' loading={loading}> <Button size='small' loading={loading}>
DeleteDisableChannel Delete disabled channels
</Button> </Button>
} }
on='click' on='click'
@ -627,7 +627,7 @@ const ChannelsTable = () => {
} }
/> />
<Button size='small' onClick={refresh} loading={loading}>Refresh</Button> <Button size='small' onClick={refresh} loading={loading}>Refresh</Button>
<Button size='small' onClick={toggleShowDetail}>{showDetail ? "隐藏Details" : "Details"}</Button> <Button size='small' onClick={toggleShowDetail}>{showDetail ? "Hide Details" : "Details"}</Button>
</Table.HeaderCell> </Table.HeaderCell>
</Table.Row> </Table.Row>
</Table.Footer> </Table.Footer>

View File

@ -247,24 +247,24 @@ const OperationSetting = () => {
</Form.Group> </Form.Group>
<Form.Button onClick={() => { <Form.Button onClick={() => {
deleteHistoryLogs().then(); deleteHistoryLogs().then();
}}>Clear History Logs</Form.Button> }}>Clear History Logs</Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3'>
Monitoring Settings Monitoring Settings
</Header> </Header>
<Form.Group widths={3}> <Form.Group widths={3}>
<Form.Input <Form.Input
label='Longest Response Time' label='Maximum Response Time'
name='ChannelDisableThreshold' name='ChannelDisableThreshold'
onChange={handleInputChange} onChange={handleInputChange}
autoComplete='new-password' autoComplete='new-password'
value={inputs.ChannelDisableThreshold} value={inputs.ChannelDisableThreshold}
type='number' type='number'
min='0' min='0'
placeholder='Unit in secondsWhen all operating channels are testedChannels will be automatically disabled if this time is exceeded' placeholder='Unit in seconds. When all operating channels are tested, channels will be automatically disabled if this time is exceeded'
/> />
<Form.Input <Form.Input
label='Quota reminder threshold' label='Quota Reminder Threshold'
name='QuotaRemindThreshold' name='QuotaRemindThreshold'
onChange={handleInputChange} onChange={handleInputChange}
autoComplete='new-password' autoComplete='new-password'
@ -273,8 +273,8 @@ const OperationSetting = () => {
min='0' min='0'
placeholder='Email will be sent to remind users when the quota is below this' placeholder='Email will be sent to remind users when the quota is below this'
/> />
</Form.Group> </Form.Group>
<Form.Group inline> <Form.Group inline>
<Form.Checkbox <Form.Checkbox
checked={inputs.AutomaticDisableChannelEnabled === 'true'} checked={inputs.AutomaticDisableChannelEnabled === 'true'}
label='Automatically disable the channel when it fails' label='Automatically disable the channel when it fails'
@ -283,12 +283,12 @@ const OperationSetting = () => {
/> />
<Form.Checkbox <Form.Checkbox
checked={inputs.AutomaticEnableChannelEnabled === 'true'} checked={inputs.AutomaticEnableChannelEnabled === 'true'}
label='成功时自动EnableChannel' label='Automatically enable the channel when it succeeds'
name='AutomaticEnableChannelEnabled' name='AutomaticEnableChannelEnabled'
onChange={handleInputChange} onChange={handleInputChange}
/> />
</Form.Group> </Form.Group>
<Form.Button onClick={() => { <Form.Button onClick={() => {
submitConfig('monitor').then(); submitConfig('monitor').then();
}}>Save Monitoring Settings</Form.Button> }}>Save Monitoring Settings</Form.Button>
<Divider /> <Divider />

View File

@ -249,7 +249,7 @@ const PersonalSetting = () => {
} }
{ {
status.lark_client_id && ( status.lark_client_id && (
<Button onClick={()=>{onLarkOAuthClicked(status.lark_client_id)}}>Bind飞书账号</Button> <Button onClick={()=>{onLarkOAuthClicked(status.lark_client_id)}}>Bind Lark Account</Button>
) )
} }
<Button <Button

View File

@ -264,7 +264,7 @@ const SystemSetting = () => {
<Form.Group widths='equal'> <Form.Group widths='equal'>
<Form.Input <Form.Input
label='Server Address' label='Server Address'
placeholder='For examplehttps://yourdomain.com' placeholder='For example: https://yourdomain.com'
value={inputs.ServerAddress} value={inputs.ServerAddress}
name='ServerAddress' name='ServerAddress'
onChange={handleInputChange} onChange={handleInputChange}
@ -290,7 +290,7 @@ const SystemSetting = () => {
size={'tiny'} size={'tiny'}
style={{ maxWidth: '450px' }} style={{ maxWidth: '450px' }}
> >
<Modal.Header>警告</Modal.Header> <Modal.Header>Warning</Modal.Header>
<Modal.Content> <Modal.Content>
<p>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?</p> <p>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?</p>
</Modal.Content> </Modal.Content>
@ -303,7 +303,7 @@ const SystemSetting = () => {
await updateOption('PasswordLoginEnabled', 'false'); await updateOption('PasswordLoginEnabled', 'false');
}} }}
> >
确定 Confirm
</Button> </Button>
</Modal.Actions> </Modal.Actions>
</Modal> </Modal>
@ -336,7 +336,7 @@ const SystemSetting = () => {
<Form.Group inline> <Form.Group inline>
<Form.Checkbox <Form.Checkbox
checked={inputs.RegisterEnabled === 'true'} checked={inputs.RegisterEnabled === 'true'}
label='Allow new user registration (if this option is off, new users will not be able to register in any way' label='Allow new user registration (if this option is off, new users will not be able to register in any way)'
name='RegisterEnabled' name='RegisterEnabled'
onChange={handleInputChange} onChange={handleInputChange}
/> />
@ -349,12 +349,12 @@ const SystemSetting = () => {
</Form.Group> </Form.Group>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3'>
配置邮箱域名白名单 Configure Email Domain Whitelist
<Header.Subheader>用以防止恶意Users利用临时邮箱批量Sign up</Header.Subheader> <Header.Subheader>To prevent malicious users from using temporary emails to sign up in bulk</Header.Subheader>
</Header> </Header>
<Form.Group widths={3}> <Form.Group widths={3}>
<Form.Checkbox <Form.Checkbox
label='Enable邮箱域名白名单' label='Enable Email Domain Whitelist'
name='EmailDomainRestrictionEnabled' name='EmailDomainRestrictionEnabled'
onChange={handleInputChange} onChange={handleInputChange}
checked={inputs.EmailDomainRestrictionEnabled === 'true'} checked={inputs.EmailDomainRestrictionEnabled === 'true'}
@ -362,8 +362,8 @@ const SystemSetting = () => {
</Form.Group> </Form.Group>
<Form.Group widths={2}> <Form.Group widths={2}>
<Form.Dropdown <Form.Dropdown
label='允许的邮箱域名' label='Allowed Email Domains'
placeholder='允许的邮箱域名' placeholder='Allowed Email Domains'
name='EmailDomainWhitelist' name='EmailDomainWhitelist'
required required
fluid fluid
@ -375,11 +375,11 @@ const SystemSetting = () => {
options={EmailDomainWhitelist} options={EmailDomainWhitelist}
/> />
<Form.Input <Form.Input
label='添加新的允许的邮箱域名' label='Add New Allowed Email Domain'
action={ action={
<Button type='button' onClick={() => { <Button type='button' onClick={() => {
submitNewRestrictedDomain(); submitNewRestrictedDomain();
}}>填入</Button> }}>Add</Button>
} }
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
@ -387,14 +387,14 @@ const SystemSetting = () => {
} }
}} }}
autoComplete='new-password' autoComplete='new-password'
placeholder='Enter新的允许的邮箱域名' placeholder='Enter new allowed email domain'
value={restrictedDomainInput} value={restrictedDomainInput}
onChange={(e, { value }) => { onChange={(e, { value }) => {
setRestrictedDomainInput(value); setRestrictedDomainInput(value);
}} }}
/> />
</Form.Group> </Form.Group>
<Form.Button onClick={submitEmailDomainWhitelist}>保存邮箱域名白名单Settings</Form.Button> <Form.Button onClick={submitEmailDomainWhitelist}>Save Email Domain Whitelist Settings</Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3'>
Configure SMTP Configure SMTP
@ -428,7 +428,7 @@ const SystemSetting = () => {
</Form.Group> </Form.Group>
<Form.Group widths={3}> <Form.Group widths={3}>
<Form.Input <Form.Input
label='SMTP Sender email' label='SMTP Sender Email'
name='SMTPFrom' name='SMTPFrom'
onChange={handleInputChange} onChange={handleInputChange}
autoComplete='new-password' autoComplete='new-password'
@ -450,7 +450,7 @@ const SystemSetting = () => {
<Header as='h3'> <Header as='h3'>
Configure GitHub OAuth App Configure GitHub OAuth App
<Header.Subheader> <Header.Subheader>
To support login & registration via GitHub To support login & registration via GitHub,
<a href='https://github.com/settings/developers' target='_blank'> <a href='https://github.com/settings/developers' target='_blank'>
Click here Click here
</a> </a>
@ -459,7 +459,7 @@ const SystemSetting = () => {
</Header> </Header>
<Message> <Message>
Fill in the Homepage URL <code>{inputs.ServerAddress}</code> Fill in the Homepage URL <code>{inputs.ServerAddress}</code>
Fill in the Authorization callback URL{' '} , Fill in the Authorization callback URL{' '}
<code>{`${inputs.ServerAddress}/oauth/github`}</code> <code>{`${inputs.ServerAddress}/oauth/github`}</code>
</Message> </Message>
<Form.Group widths={3}> <Form.Group widths={3}>
@ -486,18 +486,18 @@ const SystemSetting = () => {
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3'>
配置飞书授权Log in Configure Lark OAuth
<Header.Subheader> <Header.Subheader>
用以支持通过飞书进行Log inSign up To support login & registration via Lark,
<a href='https://open.feishu.cn/app' target='_blank'> <a href='https://open.feishu.cn/app' target='_blank'>
Click here Click here
</a> </a>
Management你的飞书应用 Manage your Lark App
</Header.Subheader> </Header.Subheader>
</Header> </Header>
<Message> <Message>
主页链接填 <code>{inputs.ServerAddress}</code> Fill in the Homepage URL <code>{inputs.ServerAddress}</code>
重定向 URL {' '} , Fill in the Redirect URL{' '}
<code>{`${inputs.ServerAddress}/oauth/lark`}</code> <code>{`${inputs.ServerAddress}/oauth/lark`}</code>
</Message> </Message>
<Form.Group widths={3}> <Form.Group widths={3}>
@ -520,13 +520,13 @@ const SystemSetting = () => {
/> />
</Form.Group> </Form.Group>
<Form.Button onClick={submitLarkOAuth}> <Form.Button onClick={submitLarkOAuth}>
保存飞书 OAuth Settings Save Lark OAuth Settings
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3'>
Configure WeChat Server Configure WeChat Server
<Header.Subheader> <Header.Subheader>
To support login & registration via WeChat To support login & registration via WeChat,
<a <a
href='https://github.com/songquanpeng/wechat-server' href='https://github.com/songquanpeng/wechat-server'
target='_blank' target='_blank'
@ -538,9 +538,9 @@ const SystemSetting = () => {
</Header> </Header>
<Form.Group widths={3}> <Form.Group widths={3}>
<Form.Input <Form.Input
label='WeChat Server Server Address' label='WeChat Server Address'
name='WeChatServerAddress' name='WeChatServerAddress'
placeholder='For examplehttps://yourdomain.com' placeholder='For example: https://yourdomain.com'
onChange={handleInputChange} onChange={handleInputChange}
autoComplete='new-password' autoComplete='new-password'
value={inputs.WeChatServerAddress} value={inputs.WeChatServerAddress}
@ -568,29 +568,29 @@ const SystemSetting = () => {
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3'>
配置 Message Pusher Configure Message Pusher
<Header.Subheader> <Header.Subheader>
用以推送报警信息 To push alert messages,
<a <a
href='https://github.com/songquanpeng/message-pusher' href='https://github.com/songquanpeng/message-pusher'
target='_blank' target='_blank'
> >
Click here Click here
</a> </a>
了解 Message Pusher Learn about Message Pusher
</Header.Subheader> </Header.Subheader>
</Header> </Header>
<Form.Group widths={3}> <Form.Group widths={3}>
<Form.Input <Form.Input
label='Message Pusher 推送地址' label='Message Pusher Address'
name='MessagePusherAddress' name='MessagePusherAddress'
placeholder='For examplehttps://msgpusher.com/push/your_username' placeholder='For example: https://msgpusher.com/push/your_username'
onChange={handleInputChange} onChange={handleInputChange}
autoComplete='new-password' autoComplete='new-password'
value={inputs.MessagePusherAddress} value={inputs.MessagePusherAddress}
/> />
<Form.Input <Form.Input
label='Message Pusher 访问凭证' label='Message Pusher Access Credential'
name='MessagePusherToken' name='MessagePusherToken'
type='password' type='password'
onChange={handleInputChange} onChange={handleInputChange}
@ -600,13 +600,13 @@ const SystemSetting = () => {
/> />
</Form.Group> </Form.Group>
<Form.Button onClick={submitMessagePusher}> <Form.Button onClick={submitMessagePusher}>
保存 Message Pusher Settings Save Message Pusher Settings
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3'>
Configure Turnstile Configure Turnstile
<Header.Subheader> <Header.Subheader>
To support user verification To support user verification,
<a href='https://dash.cloudflare.com/' target='_blank'> <a href='https://dash.cloudflare.com/' target='_blank'>
Click here Click here
</a> </a>

View File

@ -422,12 +422,12 @@ const TokensTable = () => {
</Button> </Button>
<Button size='small' onClick={refresh} loading={loading}>Refresh</Button> <Button size='small' onClick={refresh} loading={loading}>Refresh</Button>
<Dropdown <Dropdown
placeholder='排序方式' placeholder='Sort By'
selection selection
options={[ options={[
{ key: '', text: 'Default排序', value: '' }, { key: '', text: 'Default Order', value: '' },
{ key: 'remain_quota', text: '按Remaining quota排序', value: 'remain_quota' }, { key: 'remain_quota', text: 'Sort by Remaining Quota', value: 'remain_quota' },
{ key: 'used_quota', text: '按Used quota排序', value: 'used_quota' }, { key: 'used_quota', text: 'Sort by Used Quota', value: 'used_quota' },
]} ]}
value={orderBy} value={orderBy}
onChange={handleOrderByChange} onChange={handleOrderByChange}

View File

@ -330,13 +330,13 @@ const UsersTable = () => {
Add New User Add New User
</Button> </Button>
<Dropdown <Dropdown
placeholder='排序方式' placeholder='Sort By'
selection selection
options={[ options={[
{ key: '', text: 'Default排序', value: '' }, { key: '', text: 'Default Order', value: '' },
{ key: 'quota', text: '按Remaining quota排序', value: 'quota' }, { key: 'quota', text: 'Sort by Remaining Quota', value: 'quota' },
{ key: 'used_quota', text: '按Used quota排序', value: 'used_quota' }, { key: 'used_quota', text: 'Sort by Used Quota', value: 'used_quota' },
{ key: 'request_count', text: '按Number of Requests排序', value: 'request_count' }, { key: 'request_count', text: 'Sort by Number of Requests', value: 'request_count' },
]} ]}
value={orderBy} value={orderBy}
onChange={handleOrderByChange} onChange={handleOrderByChange}

View File

@ -138,8 +138,8 @@ const EditToken = () => {
</Form.Field> </Form.Field>
<Form.Field> <Form.Field>
<Form.Dropdown <Form.Dropdown
label='Model范围' label='Model Range'
placeholder={'请选择允许使用的Model留空则不进行限制'} placeholder={'Please select the allowed models, leave blank for no restriction'}
name='models' name='models'
fluid fluid
multiple multiple
@ -156,9 +156,9 @@ const EditToken = () => {
</Form.Field> </Form.Field>
<Form.Field> <Form.Field>
<Form.Input <Form.Input
label='IP 限制' label='IP Restriction'
name='subnet' name='subnet'
placeholder={'请Enter允许访问的网段For example192.168.0.0/24请使用英文逗号分隔多个网段'} placeholder={'Please enter the allowed subnet, e.g., 192.168.0.0/24, use commas to separate multiple subnets'}
onChange={handleInputChange} onChange={handleInputChange}
value={inputs.subnet} value={inputs.subnet}
autoComplete='new-password' autoComplete='new-password'
@ -166,9 +166,9 @@ const EditToken = () => {
</Form.Field> </Form.Field>
<Form.Field> <Form.Field>
<Form.Input <Form.Input
label='Expiration time' label='Expiration Time'
name='expired_time' name='expired_time'
placeholder={'Please enter the expiration time, the format is yyyy-MM-dd HH:mm:ss, -1 means unlimited'} placeholder={'Please enter the expiration time, format: yyyy-MM-dd HH:mm:ss, -1 means unlimited'}
onChange={handleInputChange} onChange={handleInputChange}
value={expired_time} value={expired_time}
autoComplete='new-password' autoComplete='new-password'
@ -192,7 +192,7 @@ const EditToken = () => {
setExpiredTime(0, 0, 0, 1); setExpiredTime(0, 0, 0, 1);
}}>Expires after one minute</Button> }}>Expires after one minute</Button>
</div> </div>
<Message>Note that the quota of the token is only used to limit the maximum quota usage of the token itself, and the actual usage is limited by the remaining quota of the account.</Message> <Message>Note that the token's quota is only used to limit the maximum usage of the token itself, and the actual usage is limited by the remaining quota of the account.</Message>
<Form.Field> <Form.Field>
<Form.Input <Form.Input
label={`Quota${renderQuotaWithPrompt(remain_quota)}`} label={`Quota${renderQuotaWithPrompt(remain_quota)}`}