From 630156dc0a937396e81a3be92ae64a73f235240e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=9F=8E=E9=93=AD?=
Date: Fri, 16 Jun 2023 14:20:25 +0800
Subject: [PATCH 1/6] fix: the prompt field can be array type now (close #166,
#167)
* fix: the prompt field can be array type now (close #166)
* fix: fix prompt type
---------
Co-authored-by: JustSong
---
controller/relay.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/controller/relay.go b/controller/relay.go
index aeb36da7..3462409f 100644
--- a/controller/relay.go
+++ b/controller/relay.go
@@ -32,7 +32,7 @@ const (
type GeneralOpenAIRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
- Prompt string `json:"prompt"`
+ Prompt any `json:"prompt"`
Stream bool `json:"stream"`
MaxTokens int `json:"max_tokens"`
Temperature float64 `json:"temperature"`
@@ -188,7 +188,7 @@ func relayHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
case RelayModeChatCompletions:
promptTokens = countTokenMessages(textRequest.Messages, textRequest.Model)
case RelayModeCompletions:
- promptTokens = countTokenText(textRequest.Prompt, textRequest.Model)
+ promptTokens = countTokenInput(textRequest.Prompt, textRequest.Model)
case RelayModeModeration:
promptTokens = countTokenInput(textRequest.Input, textRequest.Model)
}
From 58fb18aaceccfdcbaf7aa9d247958a382f4c6f47 Mon Sep 17 00:00:00 2001
From: JustSong
Date: Fri, 16 Jun 2023 14:24:16 +0800
Subject: [PATCH 2/6] fix: do not record completion ratio anymore
---
controller/relay.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/controller/relay.go b/controller/relay.go
index 3462409f..2033476f 100644
--- a/controller/relay.go
+++ b/controller/relay.go
@@ -260,7 +260,7 @@ func relayHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
common.SysError("Error consuming token remain quota: " + err.Error())
}
userId := c.GetInt("id")
- model.RecordLog(userId, model.LogTypeConsume, fmt.Sprintf("使用模型 %s 消耗 %d 点额度(模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f)", textRequest.Model, quota, modelRatio, groupRatio, completionRatio))
+ model.RecordLog(userId, model.LogTypeConsume, fmt.Sprintf("使用模型 %s 消耗 %d 点额度(模型倍率 %.2f,分组倍率 %.2f)", textRequest.Model, quota, modelRatio, groupRatio))
}
}()
From 760183a97026a9b40aa4288da702c016bb554e9d Mon Sep 17 00:00:00 2001
From: JustSong
Date: Fri, 16 Jun 2023 15:20:06 +0800
Subject: [PATCH 3/6] feat: record used quota & request count (close #102,
#165)
---
controller/relay.go | 1 +
model/user.go | 14 ++++++++++++++
web/src/components/UsersTable.js | 10 +++++++---
web/src/helpers/render.js | 30 +++++++++++++++++++++---------
4 files changed, 43 insertions(+), 12 deletions(-)
diff --git a/controller/relay.go b/controller/relay.go
index 2033476f..bb519258 100644
--- a/controller/relay.go
+++ b/controller/relay.go
@@ -261,6 +261,7 @@ func relayHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
}
userId := c.GetInt("id")
model.RecordLog(userId, model.LogTypeConsume, fmt.Sprintf("使用模型 %s 消耗 %d 点额度(模型倍率 %.2f,分组倍率 %.2f)", textRequest.Model, quota, modelRatio, groupRatio))
+ model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
}
}()
diff --git a/model/user.go b/model/user.go
index 60278a07..a8fb7842 100644
--- a/model/user.go
+++ b/model/user.go
@@ -23,6 +23,8 @@ type User struct {
VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database!
AccessToken string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
Quota int `json:"quota" gorm:"type:int;default:0"`
+ UsedQuota int `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
+ RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number
Group string `json:"group" gorm:"type:varchar(32);default:'default'"`
}
@@ -262,3 +264,15 @@ func GetRootUserEmail() (email string) {
DB.Model(&User{}).Where("role = ?", common.RoleRootUser).Select("email").Find(&email)
return email
}
+
+func UpdateUserUsedQuotaAndRequestCount(id int, quota int) {
+ err := DB.Model(&User{}).Where("id = ?", id).Updates(
+ map[string]interface{}{
+ "used_quota": gorm.Expr("used_quota + ?", quota),
+ "request_count": gorm.Expr("request_count + ?", 1),
+ },
+ ).Error
+ if err != nil {
+ common.SysError("Failed to update user used quota and request count: " + err.Error())
+ }
+}
diff --git a/web/src/components/UsersTable.js b/web/src/components/UsersTable.js
index f2e8521e..d64c69af 100644
--- a/web/src/components/UsersTable.js
+++ b/web/src/components/UsersTable.js
@@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';
import { API, showError, showSuccess } from '../helpers';
import { ITEMS_PER_PAGE } from '../constants';
-import { renderGroup, renderText } from '../helpers/render';
+import { renderGroup, renderNumber, renderText } from '../helpers/render';
function renderRole(role) {
switch (role) {
@@ -197,7 +197,7 @@ const UsersTable = () => {
sortUser('quota');
}}
>
- 剩余额度
+ 统计信息
{
{renderGroup(user.group)}
{user.email ? renderText(user.email, 30) : '无'}
- {user.quota}
+
+ {renderNumber(user.quota)}} />
+ {renderNumber(user.used_quota)}} />
+ {renderNumber(user.request_count)}} />
+
{renderRole(user.role)}
{renderStatus(user.status)}
diff --git a/web/src/helpers/render.js b/web/src/helpers/render.js
index c09a79ad..11096194 100644
--- a/web/src/helpers/render.js
+++ b/web/src/helpers/render.js
@@ -8,19 +8,31 @@ export function renderText(text, limit) {
}
export function renderGroup(group) {
- if (group === "") {
- return
+ if (group === '') {
+ return ;
}
- let groups = group.split(",");
+ let groups = group.split(',');
groups.sort();
return <>
{groups.map((group) => {
- if (group === "vip" || group === "pro") {
- return
- } else if (group === "svip" || group === "premium") {
- return
+ if (group === 'vip' || group === 'pro') {
+ return ;
+ } else if (group === 'svip' || group === 'premium') {
+ return ;
}
- return
+ return ;
})}
- >
+ >;
+}
+
+export function renderNumber(num) {
+ if (num >= 1000000000) {
+ return (num / 1000000000).toFixed(1) + 'B';
+ } else if (num >= 1000000) {
+ return (num / 1000000).toFixed(1) + 'M';
+ } else if (num >= 10000) {
+ return (num / 1000).toFixed(1) + 'k';
+ } else {
+ return num;
+ }
}
\ No newline at end of file
From 0cdab80a6e05441d265646d297f22d22da4a4aeb Mon Sep 17 00:00:00 2001
From: JustSong
Date: Fri, 16 Jun 2023 16:02:00 +0800
Subject: [PATCH 4/6] feat: record channel's used quota (close #137)
---
controller/relay.go | 2 ++
model/channel.go | 9 +++++++++
2 files changed, 11 insertions(+)
diff --git a/controller/relay.go b/controller/relay.go
index bb519258..8e7073f7 100644
--- a/controller/relay.go
+++ b/controller/relay.go
@@ -262,6 +262,8 @@ func relayHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
userId := c.GetInt("id")
model.RecordLog(userId, model.LogTypeConsume, fmt.Sprintf("使用模型 %s 消耗 %d 点额度(模型倍率 %.2f,分组倍率 %.2f)", textRequest.Model, quota, modelRatio, groupRatio))
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
+ channelId := c.GetInt("channel_id")
+ model.UpdateChannelUsedQuota(channelId, quota)
}
}()
diff --git a/model/channel.go b/model/channel.go
index fdc89eb9..e53efc20 100644
--- a/model/channel.go
+++ b/model/channel.go
@@ -1,6 +1,7 @@
package model
import (
+ "gorm.io/gorm"
"one-api/common"
)
@@ -20,6 +21,7 @@ type Channel struct {
BalanceUpdatedTime int64 `json:"balance_updated_time" gorm:"bigint"`
Models string `json:"models"`
Group string `json:"group" gorm:"type:varchar(32);default:'default'"`
+ UsedQuota int64 `json:"used_quota" gorm:"bigint;default:0"`
}
func GetAllChannels(startIdx int, num int, selectAll bool) ([]*Channel, error) {
@@ -136,3 +138,10 @@ func UpdateChannelStatusById(id int, status int) {
common.SysError("failed to update channel status: " + err.Error())
}
}
+
+func UpdateChannelUsedQuota(id int, quota int) {
+ err := DB.Model(&Channel{}).Where("id = ?", id).Update("used_quota", gorm.Expr("used_quota + ?", quota)).Error
+ if err != nil {
+ common.SysError("failed to update channel used quota: " + err.Error())
+ }
+}
From 549e944b95a1ac0aba088d8fa5d8dedbe4c3b8cc Mon Sep 17 00:00:00 2001
From: JustSong
Date: Fri, 16 Jun 2023 16:40:07 +0800
Subject: [PATCH 5/6] docs: update README
---
README.md | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 821f0a41..1c055b88 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,8 @@ _✨ All in one 的 OpenAI 接口,整合各种 API 访问方式,开箱即用
·
部署教程
·
+ 使用方法
+ ·
意见反馈
·
截图展示
@@ -158,11 +160,24 @@ sudo service nginx restart
等到系统启动后,使用 `root` 用户登录系统并做进一步的配置。
-## 使用方式
-在`渠道`页面中添加你的 API Key,之后在`令牌`页面中新增一个访问令牌。
+## 使用方法
+在`渠道`页面中添加你的 API Key,之后在`令牌`页面中新增访问令牌。
之后就可以使用你的令牌访问 One API 了,使用方式与 [OpenAI API](https://platform.openai.com/docs/api-reference/introduction) 一致。
+你需要在各种用到 OpenAI API 的地方设置 API Base 为你的 One API 的部署地址,例如:`https://openai.justsong.cn`,API Key 则为你在 One API 中生成的令牌。
+
+注意,具体的 API Base 的格式取决于你所使用的客户端。
+
+```mermaid
+graph LR
+ A(用户)
+ A --->|请求| B(One API)
+ B -->|中继请求| C(OpenAI)
+ B -->|中继请求| D(Azure)
+ B -->|中继请求| E(其他下游渠道)
+```
+
可以通过在令牌后面添加渠道 ID 的方式指定使用哪一个渠道处理本次请求,例如:`Authorization: Bearer ONE_API_KEY-CHANNEL_ID`。
注意,需要是管理员用户创建的令牌才能指定渠道 ID。
From 57b213a0359bb2bd29755219e3d8241b03f7ffd2 Mon Sep 17 00:00:00 2001
From: JustSong
Date: Fri, 16 Jun 2023 16:40:54 +0800
Subject: [PATCH 6/6] docs: update README
---
README.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/README.md b/README.md
index 1c055b88..8eeb0673 100644
--- a/README.md
+++ b/README.md
@@ -29,8 +29,6 @@ _✨ All in one 的 OpenAI 接口,整合各种 API 访问方式,开箱即用
- 程序下载
- ·
部署教程
·
使用方法