Compare commits

...

12 Commits

Author SHA1 Message Date
1808837298@qq.com
9b421478c1 fix: fix typo 2024-02-24 14:01:29 +08:00
1808837298@qq.com
569751f5db fix: 修复测试渠道指定模型时实际使用gpt-3.5-turbo的问题 (close #67) 2024-02-23 17:40:49 +08:00
1808837298@qq.com
608c945ae6 feat: 未配置在线支付时,限制用户输入金额 (close #74) 2024-02-23 17:28:00 +08:00
1808837298@qq.com
05beade3ad feat: 在线支付限制输入金额 2024-02-23 17:18:50 +08:00
1808837298@qq.com
81167c4322 feat: 记录更多的错误信息 2024-02-22 19:42:33 +08:00
1808837298@qq.com
6fa1837c24 feat: support logprobs 2024-02-22 17:52:03 +08:00
1808837298@qq.com
ba0c5bb4d9 feat: 优化令牌无效提示 2024-02-22 01:48:35 +08:00
1808837298@qq.com
5482fff62d fix: 修复redis缓存用户额度不过期的问题 2024-02-20 00:23:31 +08:00
CaIon
1cde58c089 fix: 修正 Unicode 转义序列解析问题,close #63 2024-02-02 20:02:40 +08:00
CaIon
a0c4e9f10e feat: add model gpt-3.5-turbo-0125 2024-02-02 12:46:12 +08:00
CaIon
d6d91e4340 feat: Happy New Year 2024-02-01 21:47:28 +08:00
CaIon
f19b11be8c fix: fix test channel 2024-02-01 19:27:00 +08:00
19 changed files with 110 additions and 29 deletions

View File

@@ -34,8 +34,9 @@ var ModelRatio = map[string]float64{
"gpt-3.5-turbo-16k-0613": 1.5,
"gpt-3.5-turbo-instruct": 0.75, // $0.0015 / 1K tokens
"gpt-3.5-turbo-1106": 0.5, // $0.001 / 1K tokens
"babbage-002": 0.2, // $0.0004 / 1K tokens
"davinci-002": 1, // $0.002 / 1K tokens
"gpt-3.5-turbo-0125": 0.25,
"babbage-002": 0.2, // $0.0004 / 1K tokens
"davinci-002": 1, // $0.002 / 1K tokens
"text-ada-001": 0.2,
"text-babbage-001": 0.25,
"text-curie-001": 1,
@@ -148,6 +149,9 @@ func GetModelRatio(name string) float64 {
func GetCompletionRatio(name string) float64 {
if strings.HasPrefix(name, "gpt-3.5") {
if strings.HasSuffix(name, "0125") {
return 3
}
if strings.HasSuffix(name, "1106") {
return 2
}

View File

@@ -73,6 +73,35 @@ func RedisDel(key string) error {
}
func RedisDecrease(key string, value int64) error {
ctx := context.Background()
return RDB.DecrBy(ctx, key, value).Err()
// 检查键的剩余生存时间
ttlCmd := RDB.TTL(context.Background(), key)
ttl, err := ttlCmd.Result()
if err != nil {
// 失败则尝试直接减少
return RDB.DecrBy(context.Background(), key, value).Err()
}
// 如果剩余生存时间大于0则进行减少操作
if ttl > 0 {
ctx := context.Background()
// 开始一个Redis事务
txn := RDB.TxPipeline()
// 减少余额
decrCmd := txn.DecrBy(ctx, key, value)
if err := decrCmd.Err(); err != nil {
return err // 如果减少失败,则直接返回错误
}
// 重新设置过期时间,使用原来的过期时间
txn.Expire(ctx, key, ttl)
// 执行事务
_, err = txn.Exec(ctx)
return err
} else {
_ = RedisDel(key)
}
return nil
}

View File

@@ -16,6 +16,7 @@ import (
)
func testChannel(channel *model.Channel, request ChatRequest) (err error, openaiErr *OpenAIError) {
common.SysLog(fmt.Sprintf("testing channel %d with model %s", channel.Id, request.Model))
switch channel.Type {
case common.ChannelTypePaLM:
fallthrough
@@ -47,7 +48,11 @@ func testChannel(channel *model.Channel, request ChatRequest) (err error, openai
request.Model = "gpt-3.5-turbo"
}
}
requestURL := getFullRequestURL(channel.GetBaseURL(), "/v1/chat/completions", channel.Type)
baseUrl := common.ChannelBaseURLs[channel.Type]
if channel.GetBaseURL() != "" {
baseUrl = channel.GetBaseURL()
}
requestURL := getFullRequestURL(baseUrl, "/v1/chat/completions", channel.Type)
if channel.Type == common.ChannelTypeAzure {
requestURL = getFullRequestURL(channel.GetBaseURL(), fmt.Sprintf("/openai/deployments/%s/chat/completions?api-version=2023-03-15-preview", request.Model), channel.Type)
@@ -109,7 +114,7 @@ func TestChannel(c *gin.Context) {
})
return
}
testModel := c.Param("model")
testModel := c.Query("model")
channel, err := model.GetChannelById(id, true)
if err != nil {
c.JSON(http.StatusOK, gin.H{

View File

@@ -38,6 +38,7 @@ func GetStatus(c *gin.Context) {
"enable_drawing": common.DrawingEnabled,
"enable_data_export": common.DataExportEnabled,
"data_export_default_time": common.DataExportDefaultTime,
"enable_online_topup": common.PayAddress != "" && common.EpayId != "" && common.EpayKey != "",
},
})
return

View File

@@ -180,6 +180,15 @@ func init() {
Root: "gpt-3.5-turbo-1106",
Parent: nil,
},
{
Id: "gpt-3.5-turbo-0125",
Object: "model",
Created: 1706232090,
OwnedBy: "openai",
Permission: permission,
Root: "gpt-3.5-turbo-0125",
Parent: nil,
},
{
Id: "gpt-3.5-turbo-instruct",
Object: "model",

View File

@@ -88,17 +88,17 @@ func requestOpenAI2Ali(request GeneralOpenAIRequest) *AliChatRequest {
message := request.Messages[i]
if message.Role == "system" {
messages = append(messages, AliMessage{
User: string(message.Content),
User: message.StringContent(),
Bot: "Okay",
})
continue
} else {
if i == len(request.Messages)-1 {
prompt = string(message.Content)
prompt = message.StringContent()
break
}
messages = append(messages, AliMessage{
User: string(message.Content),
User: message.StringContent(),
Bot: string(request.Messages[i+1].Content),
})
i++

View File

@@ -89,7 +89,7 @@ func requestOpenAI2Baidu(request GeneralOpenAIRequest) *BaiduChatRequest {
if message.Role == "system" {
messages = append(messages, BaiduMessage{
Role: "user",
Content: string(message.Content),
Content: message.StringContent(),
})
messages = append(messages, BaiduMessage{
Role: "assistant",
@@ -98,7 +98,7 @@ func requestOpenAI2Baidu(request GeneralOpenAIRequest) *BaiduChatRequest {
} else {
messages = append(messages, BaiduMessage{
Role: message.Role,
Content: string(message.Content),
Content: message.StringContent(),
})
}
}

View File

@@ -97,7 +97,7 @@ func requestOpenAI2Gemini(textRequest GeneralOpenAIRequest) *GeminiChatRequest {
Role: message.Role,
Parts: []GeminiPart{
{
Text: string(message.Content),
Text: message.StringContent(),
},
},
}

View File

@@ -59,7 +59,7 @@ func requestOpenAI2PaLM(textRequest GeneralOpenAIRequest) *PaLMChatRequest {
}
for _, message := range textRequest.Messages {
palmMessage := PaLMChatMessage{
Content: string(message.Content),
Content: message.StringContent(),
}
if message.Role == "user" {
palmMessage.Author = "0"

View File

@@ -84,7 +84,7 @@ func requestOpenAI2Tencent(request GeneralOpenAIRequest) *TencentChatRequest {
if message.Role == "system" {
messages = append(messages, TencentMessage{
Role: "user",
Content: string(message.Content),
Content: message.StringContent(),
})
messages = append(messages, TencentMessage{
Role: "assistant",
@@ -93,7 +93,7 @@ func requestOpenAI2Tencent(request GeneralOpenAIRequest) *TencentChatRequest {
continue
}
messages = append(messages, TencentMessage{
Content: string(message.Content),
Content: message.StringContent(),
Role: message.Role,
})
}

View File

@@ -81,7 +81,7 @@ func requestOpenAI2Xunfei(request GeneralOpenAIRequest, xunfeiAppId string, doma
if message.Role == "system" {
messages = append(messages, XunfeiMessage{
Role: "user",
Content: string(message.Content),
Content: message.StringContent(),
})
messages = append(messages, XunfeiMessage{
Role: "assistant",
@@ -90,7 +90,7 @@ func requestOpenAI2Xunfei(request GeneralOpenAIRequest, xunfeiAppId string, doma
} else {
messages = append(messages, XunfeiMessage{
Role: message.Role,
Content: string(message.Content),
Content: message.StringContent(),
})
}
}

View File

@@ -114,7 +114,7 @@ func requestOpenAI2Zhipu(request GeneralOpenAIRequest) *ZhipuRequest {
if message.Role == "system" {
messages = append(messages, ZhipuMessage{
Role: "system",
Content: string(message.Content),
Content: message.StringContent(),
})
messages = append(messages, ZhipuMessage{
Role: "user",
@@ -123,7 +123,7 @@ func requestOpenAI2Zhipu(request GeneralOpenAIRequest) *ZhipuRequest {
} else {
messages = append(messages, ZhipuMessage{
Role: message.Role,
Content: string(message.Content),
Content: message.StringContent(),
})
}
}

View File

@@ -36,6 +36,14 @@ const (
ContentTypeImageURL = "image_url"
)
func (m Message) StringContent() string {
var stringContent string
if err := json.Unmarshal(m.Content, &stringContent); err == nil {
return stringContent
}
return string(m.Content)
}
func (m Message) ParseContent() []MediaMessage {
var contentList []MediaMessage
var stringContent string
@@ -132,6 +140,8 @@ type GeneralOpenAIRequest struct {
Tools any `json:"tools,omitempty"`
ToolChoice any `json:"tool_choice,omitempty"`
User string `json:"user,omitempty"`
LogProbs bool `json:"logprobs,omitempty"`
TopLogProbs int `json:"top_logprobs,omitempty"`
}
func (r GeneralOpenAIRequest) ParseInput() []string {
@@ -399,12 +409,13 @@ func RelayMidjourney(c *gin.Context) {
if err.Code == 30 {
err.Result = "当前分组负载已饱和,请稍后再试,或升级账户以提升服务质量。"
}
c.JSON(400, gin.H{
"error": err.Description + " " + err.Result,
c.JSON(429, gin.H{
"error": fmt.Sprintf("%s %s", err.Description, err.Result),
"type": "upstream_error",
})
}
channelId := c.GetInt("channel_id")
common.SysError(fmt.Sprintf("relay error (channel #%d): %s", channelId, err.Result))
common.SysError(fmt.Sprintf("relay error (channel #%d): %s", channelId, fmt.Sprintf("%s %s", err.Description, err.Result)))
//if shouldDisableChannel(&err.OpenAIError) {
// channelId := c.GetInt("channel_id")
// channelName := c.GetString("channel_name")

View File

@@ -48,7 +48,7 @@ func Distribute() func(c *gin.Context) {
err = common.UnmarshalBodyReusable(c, &modelRequest)
}
if err != nil {
abortWithMessage(c, http.StatusBadRequest, "无效的请求: "+err.Error())
abortWithMessage(c, http.StatusBadRequest, "无效的请求, "+err.Error())
return
}
if strings.HasPrefix(c.Request.URL.Path, "/v1/moderations") {

View File

@@ -47,7 +47,9 @@ func ValidateUserToken(key string) (token *Token, err error) {
token, err = CacheGetTokenByKey(key)
if err == nil {
if token.Status == common.TokenStatusExhausted {
return nil, errors.New("该令牌额度已用尽 token.Status == common.TokenStatusExhausted " + key)
keyPrefix := key[:3]
keySuffix := key[len(key)-3:]
return nil, errors.New("该令牌额度已用尽 TokenStatusExhausted[sk-" + keyPrefix + "***" + keySuffix + "]")
} else if token.Status == common.TokenStatusExpired {
return nil, errors.New("该令牌已过期")
}
@@ -73,7 +75,9 @@ func ValidateUserToken(key string) (token *Token, err error) {
common.SysError("failed to update token status" + err.Error())
}
}
return nil, errors.New(fmt.Sprintf("%s 该令牌额度已用尽 !token.UnlimitedQuota && token.RemainQuota = %d", token.Key, token.RemainQuota))
keyPrefix := key[:3]
keySuffix := key[len(key)-3:]
return nil, errors.New(fmt.Sprintf("[sk-%s***%s] 该令牌额度已用尽 !token.UnlimitedQuota && token.RemainQuota = %d", keyPrefix, keySuffix, token.RemainQuota))
}
return token, nil
}

View File

@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@douyinfe/semi-ui": "^2.46.1",
"@douyinfe/semi-icons": "^2.46.1",
"@visactor/vchart": "~1.8.8",
"@visactor/react-vchart": "~1.8.8",
"@visactor/vchart-semi-theme": "~1.8.8",

View File

@@ -463,11 +463,8 @@ const ChannelsTable = () => {
const res = await API.get(`/api/channel/test/${record.id}?model=${model}`);
const {success, message, time} = res.data;
if (success) {
let newChannels = [...channels];
record.response_time = time * 1000;
record.test_time = Date.now() / 1000;
setChannelFormat(newChannels)
showInfo(`通道 ${record.name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
} else {
showError(message);

View File

@@ -43,7 +43,8 @@ const HeaderBar = () => {
const logo = getLogo();
var themeMode = localStorage.getItem('theme-mode');
const currentDate = new Date();
const isNewYear = currentDate.getMonth() === 0 && currentDate.getDate() === 1;
// enable fireworks on new year(1.1 and 2.9-2.24)
const isNewYear = (currentDate.getMonth() === 0 && currentDate.getDate() === 1) || (currentDate.getMonth() === 1 && currentDate.getDate() >= 9 && currentDate.getDate() <= 24);
async function logout() {
setShowSidebar(false);

View File

@@ -8,8 +8,10 @@ const TopUp = () => {
const [redemptionCode, setRedemptionCode] = useState('');
const [topUpCode, setTopUpCode] = useState('');
const [topUpCount, setTopUpCount] = useState(10);
const [minTopupCount, setMinTopUpCount] = useState(1);
const [amount, setAmount] = useState(0.0);
const [topUpLink, setTopUpLink] = useState('');
const [enableOnlineTopUp, setEnableOnlineTopUp] = useState(false);
const [userQuota, setUserQuota] = useState(0);
const [isSubmitting, setIsSubmitting] = useState(false);
const [open, setOpen] = useState(false);
@@ -52,6 +54,10 @@ const TopUp = () => {
};
const preTopUp = async (payment) => {
if (!enableOnlineTopUp) {
showError('管理员未开启在线充值!');
return;
}
if (amount === 0) {
await getAmount();
}
@@ -126,6 +132,9 @@ const TopUp = () => {
if (status.top_up_link) {
setTopUpLink(status.top_up_link);
}
if (status.enable_online_topup) {
setEnableOnlineTopUp(status.enable_online_topup);
}
}
getUserQuota().then();
}, []);
@@ -227,13 +236,23 @@ const TopUp = () => {
</Divider>
<Form>
<Form.Input
disabled={!enableOnlineTopUp}
field={'redemptionCount'}
label={'实付金额:' + renderAmount()}
placeholder='充值数量'
name='redemptionCount'
type={'number'}
value={topUpCount}
suffix={'$'}
min={1}
max={100000}
onChange={async (value) => {
if (value < 1) {
value = 1;
}
if (value > 100000) {
value = 100000;
}
setTopUpCount(value);
await getAmount(value);
}}