mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-12-14 12:15:56 +08:00
Compare commits
14 Commits
v0.4.7-alp
...
v0.4.8-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f05128368 | ||
|
|
9b178a28a3 | ||
|
|
4a6a7f4635 | ||
|
|
6b1a24d650 | ||
|
|
94ba3dd024 | ||
|
|
f6eb4e5628 | ||
|
|
57bd907f83 | ||
|
|
dd8e8d5ee8 | ||
|
|
1ca1aa0cdc | ||
|
|
f2ba0c0300 | ||
|
|
f5c1fcd3c3 | ||
|
|
5fdf670a19 | ||
|
|
3ce982d8ee | ||
|
|
a515f9284e |
@@ -32,6 +32,9 @@ func GetSubscription(c *gin.Context) {
|
|||||||
if common.DisplayInCurrencyEnabled {
|
if common.DisplayInCurrencyEnabled {
|
||||||
amount /= common.QuotaPerUnit
|
amount /= common.QuotaPerUnit
|
||||||
}
|
}
|
||||||
|
if token != nil && token.UnlimitedQuota {
|
||||||
|
amount = 100000000
|
||||||
|
}
|
||||||
subscription := OpenAISubscriptionResponse{
|
subscription := OpenAISubscriptionResponse{
|
||||||
Object: "billing_subscription",
|
Object: "billing_subscription",
|
||||||
HasPaymentMethod: true,
|
HasPaymentMethod: true,
|
||||||
@@ -71,7 +74,7 @@ func GetUsage(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
usage := OpenAIUsageResponse{
|
usage := OpenAIUsageResponse{
|
||||||
Object: "list",
|
Object: "list",
|
||||||
TotalUsage: amount,
|
TotalUsage: amount * 100,
|
||||||
}
|
}
|
||||||
c.JSON(200, usage)
|
c.JSON(200, usage)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ func GetAllLogs(c *gin.Context) {
|
|||||||
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
|
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
|
||||||
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
|
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
|
||||||
username := c.Query("username")
|
username := c.Query("username")
|
||||||
|
tokenName := c.Query("token_name")
|
||||||
modelName := c.Query("model_name")
|
modelName := c.Query("model_name")
|
||||||
logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, p*common.ItemsPerPage, common.ItemsPerPage)
|
logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*common.ItemsPerPage, common.ItemsPerPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
@@ -97,9 +98,10 @@ func GetLogsStat(c *gin.Context) {
|
|||||||
logType, _ := strconv.Atoi(c.Query("type"))
|
logType, _ := strconv.Atoi(c.Query("type"))
|
||||||
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
|
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
|
||||||
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
|
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
|
||||||
|
tokenName := c.Query("token_name")
|
||||||
username := c.Query("username")
|
username := c.Query("username")
|
||||||
modelName := c.Query("model_name")
|
modelName := c.Query("model_name")
|
||||||
quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, "")
|
quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName)
|
||||||
//tokenNum := model.SumUsedToken(logType, startTimestamp, endTimestamp, modelName, username, "")
|
//tokenNum := model.SumUsedToken(logType, startTimestamp, endTimestamp, modelName, username, "")
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"success": true,
|
"success": true,
|
||||||
|
|||||||
@@ -224,6 +224,24 @@ func init() {
|
|||||||
Root: "text-moderation-stable",
|
Root: "text-moderation-stable",
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Id: "text-davinci-edit-001",
|
||||||
|
Object: "model",
|
||||||
|
Created: 1677649963,
|
||||||
|
OwnedBy: "openai",
|
||||||
|
Permission: permission,
|
||||||
|
Root: "text-davinci-edit-001",
|
||||||
|
Parent: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: "code-davinci-edit-001",
|
||||||
|
Object: "model",
|
||||||
|
Created: 1677649963,
|
||||||
|
OwnedBy: "openai",
|
||||||
|
Permission: permission,
|
||||||
|
Root: "code-davinci-edit-001",
|
||||||
|
Parent: nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
openAIModelsMap = make(map[string]OpenAIModels)
|
openAIModelsMap = make(map[string]OpenAIModels)
|
||||||
for _, model := range openAIModels {
|
for _, model := range openAIModels {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"io"
|
"io"
|
||||||
@@ -26,9 +27,32 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
return errorWrapper(err, "bind_request_body_failed", http.StatusBadRequest)
|
return errorWrapper(err, "bind_request_body_failed", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if relayMode == RelayModeModeration && textRequest.Model == "" {
|
if relayMode == RelayModeModerations && textRequest.Model == "" {
|
||||||
textRequest.Model = "text-moderation-latest"
|
textRequest.Model = "text-moderation-latest"
|
||||||
}
|
}
|
||||||
|
// request validation
|
||||||
|
if textRequest.Model == "" {
|
||||||
|
return errorWrapper(errors.New("model is required"), "required_field_missing", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
switch relayMode {
|
||||||
|
case RelayModeCompletions:
|
||||||
|
if textRequest.Prompt == "" {
|
||||||
|
return errorWrapper(errors.New("field prompt is required"), "required_field_missing", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
case RelayModeChatCompletions:
|
||||||
|
if textRequest.Messages == nil || len(textRequest.Messages) == 0 {
|
||||||
|
return errorWrapper(errors.New("field messages is required"), "required_field_missing", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
case RelayModeEmbeddings:
|
||||||
|
case RelayModeModerations:
|
||||||
|
if textRequest.Input == "" {
|
||||||
|
return errorWrapper(errors.New("field input is required"), "required_field_missing", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
case RelayModeEdits:
|
||||||
|
if textRequest.Instruction == "" {
|
||||||
|
return errorWrapper(errors.New("field instruction is required"), "required_field_missing", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
baseURL := common.ChannelBaseURLs[channelType]
|
baseURL := common.ChannelBaseURLs[channelType]
|
||||||
requestURL := c.Request.URL.String()
|
requestURL := c.Request.URL.String()
|
||||||
if c.GetString("base_url") != "" {
|
if c.GetString("base_url") != "" {
|
||||||
@@ -64,7 +88,7 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
promptTokens = countTokenMessages(textRequest.Messages, textRequest.Model)
|
promptTokens = countTokenMessages(textRequest.Messages, textRequest.Model)
|
||||||
case RelayModeCompletions:
|
case RelayModeCompletions:
|
||||||
promptTokens = countTokenInput(textRequest.Prompt, textRequest.Model)
|
promptTokens = countTokenInput(textRequest.Prompt, textRequest.Model)
|
||||||
case RelayModeModeration:
|
case RelayModeModerations:
|
||||||
promptTokens = countTokenInput(textRequest.Input, textRequest.Model)
|
promptTokens = countTokenInput(textRequest.Input, textRequest.Model)
|
||||||
}
|
}
|
||||||
preConsumedTokens := common.PreConsumedQuota
|
preConsumedTokens := common.PreConsumedQuota
|
||||||
@@ -124,7 +148,10 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
defer func() {
|
defer func() {
|
||||||
if consumeQuota {
|
if consumeQuota {
|
||||||
quota := 0
|
quota := 0
|
||||||
completionRatio := 1.333333 // default for gpt-3
|
completionRatio := 1.0
|
||||||
|
if strings.HasPrefix(textRequest.Model, "gpt-3.5") {
|
||||||
|
completionRatio = 1.333333
|
||||||
|
}
|
||||||
if strings.HasPrefix(textRequest.Model, "gpt-4") {
|
if strings.HasPrefix(textRequest.Model, "gpt-4") {
|
||||||
completionRatio = 2
|
completionRatio = 2
|
||||||
}
|
}
|
||||||
@@ -139,18 +166,26 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
if ratio != 0 && quota <= 0 {
|
if ratio != 0 && quota <= 0 {
|
||||||
quota = 1
|
quota = 1
|
||||||
}
|
}
|
||||||
|
totalTokens := promptTokens + completionTokens
|
||||||
|
if totalTokens == 0 {
|
||||||
|
// in this case, must be some error happened
|
||||||
|
// we cannot just return, because we may have to return the pre-consumed quota
|
||||||
|
quota = 0
|
||||||
|
}
|
||||||
quotaDelta := quota - preConsumedQuota
|
quotaDelta := quota - preConsumedQuota
|
||||||
err := model.PostConsumeTokenQuota(tokenId, quotaDelta)
|
err := model.PostConsumeTokenQuota(tokenId, quotaDelta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.SysError("error consuming token remain quota: " + err.Error())
|
common.SysError("error consuming token remain quota: " + err.Error())
|
||||||
}
|
}
|
||||||
|
if quota != 0 {
|
||||||
tokenName := c.GetString("token_name")
|
tokenName := c.GetString("token_name")
|
||||||
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
|
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f", modelRatio, groupRatio, completionRatio)
|
||||||
model.RecordConsumeLog(userId, promptTokens, completionTokens, textRequest.Model, tokenName, quota, logContent)
|
model.RecordConsumeLog(userId, promptTokens, completionTokens, textRequest.Model, tokenName, quota, logContent)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
model.UpdateChannelUsedQuota(channelId, quota)
|
model.UpdateChannelUsedQuota(channelId, quota)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if isStream {
|
if isStream {
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ const (
|
|||||||
RelayModeChatCompletions
|
RelayModeChatCompletions
|
||||||
RelayModeCompletions
|
RelayModeCompletions
|
||||||
RelayModeEmbeddings
|
RelayModeEmbeddings
|
||||||
RelayModeModeration
|
RelayModeModerations
|
||||||
RelayModeImagesGenerations
|
RelayModeImagesGenerations
|
||||||
|
RelayModeEdits
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://platform.openai.com/docs/api-reference/chat
|
// https://platform.openai.com/docs/api-reference/chat
|
||||||
@@ -35,6 +36,7 @@ type GeneralOpenAIRequest struct {
|
|||||||
TopP float64 `json:"top_p"`
|
TopP float64 `json:"top_p"`
|
||||||
N int `json:"n"`
|
N int `json:"n"`
|
||||||
Input any `json:"input"`
|
Input any `json:"input"`
|
||||||
|
Instruction string `json:"instruction"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatRequest struct {
|
type ChatRequest struct {
|
||||||
@@ -99,9 +101,11 @@ func Relay(c *gin.Context) {
|
|||||||
} else if strings.HasPrefix(c.Request.URL.Path, "/v1/embeddings") {
|
} else if strings.HasPrefix(c.Request.URL.Path, "/v1/embeddings") {
|
||||||
relayMode = RelayModeEmbeddings
|
relayMode = RelayModeEmbeddings
|
||||||
} else if strings.HasPrefix(c.Request.URL.Path, "/v1/moderations") {
|
} else if strings.HasPrefix(c.Request.URL.Path, "/v1/moderations") {
|
||||||
relayMode = RelayModeModeration
|
relayMode = RelayModeModerations
|
||||||
} else if strings.HasPrefix(c.Request.URL.Path, "/v1/images/generations") {
|
} else if strings.HasPrefix(c.Request.URL.Path, "/v1/images/generations") {
|
||||||
relayMode = RelayModeImagesGenerations
|
relayMode = RelayModeImagesGenerations
|
||||||
|
} else if strings.HasPrefix(c.Request.URL.Path, "/v1/edits") {
|
||||||
|
relayMode = RelayModeEdits
|
||||||
}
|
}
|
||||||
var err *OpenAIErrorWithStatusCode
|
var err *OpenAIErrorWithStatusCode
|
||||||
switch relayMode {
|
switch relayMode {
|
||||||
|
|||||||
@@ -453,7 +453,8 @@
|
|||||||
"起始时间": "Start Time",
|
"起始时间": "Start Time",
|
||||||
"结束时间": "End Time",
|
"结束时间": "End Time",
|
||||||
"查询": "Query",
|
"查询": "Query",
|
||||||
"提示令牌": "Prompt Token",
|
"提示": "Prompt",
|
||||||
"补全令牌": "Completion Token",
|
"补全": "Completion",
|
||||||
"消耗额度": "Used Quota"
|
"消耗额度": "Used Quota",
|
||||||
|
"可选值": "Optional Values"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func RecordConsumeLog(userId int, promptTokens int, completionTokens int, modelN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, startIdx int, num int) (logs []*Log, err error) {
|
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int) (logs []*Log, err error) {
|
||||||
var tx *gorm.DB
|
var tx *gorm.DB
|
||||||
if logType == LogTypeUnknown {
|
if logType == LogTypeUnknown {
|
||||||
tx = DB
|
tx = DB
|
||||||
@@ -79,6 +79,9 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName
|
|||||||
if username != "" {
|
if username != "" {
|
||||||
tx = tx.Where("username = ?", username)
|
tx = tx.Where("username = ?", username)
|
||||||
}
|
}
|
||||||
|
if tokenName != "" {
|
||||||
|
tx = tx.Where("token_name = ?", tokenName)
|
||||||
|
}
|
||||||
if startTimestamp != 0 {
|
if startTimestamp != 0 {
|
||||||
tx = tx.Where("created_at >= ?", startTimestamp)
|
tx = tx.Where("created_at >= ?", startTimestamp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func SetRelayRouter(router *gin.Engine) {
|
|||||||
{
|
{
|
||||||
relayV1Router.POST("/completions", controller.Relay)
|
relayV1Router.POST("/completions", controller.Relay)
|
||||||
relayV1Router.POST("/chat/completions", controller.Relay)
|
relayV1Router.POST("/chat/completions", controller.Relay)
|
||||||
relayV1Router.POST("/edits", controller.RelayNotImplemented)
|
relayV1Router.POST("/edits", controller.Relay)
|
||||||
relayV1Router.POST("/images/generations", controller.RelayNotImplemented)
|
relayV1Router.POST("/images/generations", controller.RelayNotImplemented)
|
||||||
relayV1Router.POST("/images/edits", controller.RelayNotImplemented)
|
relayV1Router.POST("/images/edits", controller.RelayNotImplemented)
|
||||||
relayV1Router.POST("/images/variations", controller.RelayNotImplemented)
|
relayV1Router.POST("/images/variations", controller.RelayNotImplemented)
|
||||||
|
|||||||
@@ -51,12 +51,13 @@ const LogsTable = () => {
|
|||||||
const isAdminUser = isAdmin();
|
const isAdminUser = isAdmin();
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
const [inputs, setInputs] = useState({
|
const [inputs, setInputs] = useState({
|
||||||
name: '',
|
username: '',
|
||||||
|
token_name: '',
|
||||||
model_name: '',
|
model_name: '',
|
||||||
start_timestamp: timestamp2string(0),
|
start_timestamp: timestamp2string(0),
|
||||||
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600)
|
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600)
|
||||||
});
|
});
|
||||||
const { name, model_name, start_timestamp, end_timestamp } = inputs;
|
const { username, token_name, model_name, start_timestamp, end_timestamp } = inputs;
|
||||||
|
|
||||||
const [stat, setStat] = useState({
|
const [stat, setStat] = useState({
|
||||||
quota: 0,
|
quota: 0,
|
||||||
@@ -70,7 +71,7 @@ const LogsTable = () => {
|
|||||||
const getLogSelfStat = async () => {
|
const getLogSelfStat = async () => {
|
||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
||||||
let res = await API.get(`/api/log/self/stat?type=${logType}&token_name=${name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`);
|
let res = await API.get(`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setStat(data);
|
setStat(data);
|
||||||
@@ -82,7 +83,7 @@ const LogsTable = () => {
|
|||||||
const getLogStat = async () => {
|
const getLogStat = async () => {
|
||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
||||||
let res = await API.get(`/api/log/stat?type=${logType}&username=${name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`);
|
let res = await API.get(`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setStat(data);
|
setStat(data);
|
||||||
@@ -96,9 +97,9 @@ const LogsTable = () => {
|
|||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
||||||
if (isAdminUser) {
|
if (isAdminUser) {
|
||||||
url = `/api/log/?p=${startIdx}&type=${logType}&username=${name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
url = `/api/log/?p=${startIdx}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
||||||
} else {
|
} else {
|
||||||
url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
||||||
}
|
}
|
||||||
const res = await API.get(url);
|
const res = await API.get(url);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
@@ -106,7 +107,7 @@ const LogsTable = () => {
|
|||||||
if (startIdx === 0) {
|
if (startIdx === 0) {
|
||||||
setLogs(data);
|
setLogs(data);
|
||||||
} else {
|
} else {
|
||||||
let newLogs = logs;
|
let newLogs = [...logs];
|
||||||
newLogs.push(...data);
|
newLogs.push(...data);
|
||||||
setLogs(newLogs);
|
setLogs(newLogs);
|
||||||
}
|
}
|
||||||
@@ -128,6 +129,7 @@ const LogsTable = () => {
|
|||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
setActivePage(1)
|
||||||
await loadLogs(0);
|
await loadLogs(0);
|
||||||
if (isAdminUser) {
|
if (isAdminUser) {
|
||||||
getLogStat().then();
|
getLogStat().then();
|
||||||
@@ -167,9 +169,17 @@ const LogsTable = () => {
|
|||||||
if (logs.length === 0) return;
|
if (logs.length === 0) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let sortedLogs = [...logs];
|
let sortedLogs = [...logs];
|
||||||
|
if (typeof sortedLogs[0][key] === 'string'){
|
||||||
sortedLogs.sort((a, b) => {
|
sortedLogs.sort((a, b) => {
|
||||||
return ('' + a[key]).localeCompare(b[key]);
|
return ('' + a[key]).localeCompare(b[key]);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sortedLogs.sort((a, b) => {
|
||||||
|
if (a[key] === b[key]) return 0;
|
||||||
|
if (a[key] > b[key]) return -1;
|
||||||
|
if (a[key] < b[key]) return 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
if (sortedLogs[0].id === logs[0].id) {
|
if (sortedLogs[0].id === logs[0].id) {
|
||||||
sortedLogs.reverse();
|
sortedLogs.reverse();
|
||||||
}
|
}
|
||||||
@@ -183,10 +193,17 @@ const LogsTable = () => {
|
|||||||
<Header as='h3'>使用明细(总消耗额度:{renderQuota(stat.quota)})</Header>
|
<Header as='h3'>使用明细(总消耗额度:{renderQuota(stat.quota)})</Header>
|
||||||
<Form>
|
<Form>
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Input fluid label={isAdminUser ? '用户名称' : '令牌名称'} width={3} value={name}
|
{
|
||||||
placeholder={isAdminUser ? '留空则查询全部用户' : '留空则查询全部令牌'} name='name'
|
isAdminUser && (
|
||||||
|
<Form.Input fluid label={'用户名称'} width={2} value={username}
|
||||||
|
placeholder={'可选值'} name='username'
|
||||||
onChange={handleInputChange} />
|
onChange={handleInputChange} />
|
||||||
<Form.Input fluid label='模型名称' width={3} value={model_name} placeholder='留空则查询全部模型' name='model_name'
|
)
|
||||||
|
}
|
||||||
|
<Form.Input fluid label={'令牌名称'} width={isAdminUser ? 2 : 3} value={token_name}
|
||||||
|
placeholder={'可选值'} name='token_name' onChange={handleInputChange} />
|
||||||
|
<Form.Input fluid label='模型名称' width={isAdminUser ? 2 : 3} value={model_name} placeholder='可选值'
|
||||||
|
name='model_name'
|
||||||
onChange={handleInputChange} />
|
onChange={handleInputChange} />
|
||||||
<Form.Input fluid label='起始时间' width={4} value={start_timestamp} type='datetime-local'
|
<Form.Input fluid label='起始时间' width={4} value={start_timestamp} type='datetime-local'
|
||||||
name='start_timestamp'
|
name='start_timestamp'
|
||||||
@@ -209,18 +226,32 @@ const LogsTable = () => {
|
|||||||
>
|
>
|
||||||
时间
|
时间
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
{
|
||||||
|
isAdminUser && <Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
sortLog('username');
|
||||||
|
}}
|
||||||
width={1}
|
width={1}
|
||||||
>
|
>
|
||||||
{isAdminUser ? '用户' : '令牌'}
|
用户
|
||||||
|
</Table.HeaderCell>
|
||||||
|
}
|
||||||
|
<Table.HeaderCell
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
sortLog('token_name');
|
||||||
|
}}
|
||||||
|
width={1}
|
||||||
|
>
|
||||||
|
令牌
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
sortLog('type');
|
sortLog('type');
|
||||||
}}
|
}}
|
||||||
width={2}
|
width={1}
|
||||||
>
|
>
|
||||||
类型
|
类型
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
@@ -240,7 +271,7 @@ const LogsTable = () => {
|
|||||||
}}
|
}}
|
||||||
width={1}
|
width={1}
|
||||||
>
|
>
|
||||||
提示令牌
|
提示
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@@ -249,7 +280,7 @@ const LogsTable = () => {
|
|||||||
}}
|
}}
|
||||||
width={1}
|
width={1}
|
||||||
>
|
>
|
||||||
补全令牌
|
补全
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@@ -265,7 +296,7 @@ const LogsTable = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
sortLog('content');
|
sortLog('content');
|
||||||
}}
|
}}
|
||||||
width={4}
|
width={isAdminUser ? 4 : 5}
|
||||||
>
|
>
|
||||||
详情
|
详情
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
@@ -288,15 +319,11 @@ const LogsTable = () => {
|
|||||||
<Table.Cell>{log.username ? <Label>{log.username}</Label> : ''}</Table.Cell>
|
<Table.Cell>{log.username ? <Label>{log.username}</Label> : ''}</Table.Cell>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
<Table.Cell>{log.token_name ? <Label basic>{log.token_name}</Label> : ''}</Table.Cell>
|
||||||
!isAdminUser && (
|
|
||||||
<Table.Cell>{log.token_name ? <Label>{log.token_name}</Label> : ''}</Table.Cell>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<Table.Cell>{renderType(log.type)}</Table.Cell>
|
<Table.Cell>{renderType(log.type)}</Table.Cell>
|
||||||
<Table.Cell>{log.model_name ? <Label basic>{log.model_name}</Label> : ''}</Table.Cell>
|
<Table.Cell>{log.model_name ? <Label basic>{log.model_name}</Label> : ''}</Table.Cell>
|
||||||
<Table.Cell>{log.prompt_tokens ? log.prompt_tokens: ''}</Table.Cell>
|
<Table.Cell>{log.prompt_tokens ? log.prompt_tokens : ''}</Table.Cell>
|
||||||
<Table.Cell>{log.completion_tokens ? log.completion_tokens: ''}</Table.Cell>
|
<Table.Cell>{log.completion_tokens ? log.completion_tokens : ''}</Table.Cell>
|
||||||
<Table.Cell>{log.quota ? renderQuota(log.quota, 6) : ''}</Table.Cell>
|
<Table.Cell>{log.quota ? renderQuota(log.quota, 6) : ''}</Table.Cell>
|
||||||
<Table.Cell>{log.content}</Table.Cell>
|
<Table.Cell>{log.content}</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
@@ -306,7 +333,7 @@ const LogsTable = () => {
|
|||||||
|
|
||||||
<Table.Footer>
|
<Table.Footer>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.HeaderCell colSpan={'8'}>
|
<Table.HeaderCell colSpan={'9'}>
|
||||||
<Select
|
<Select
|
||||||
placeholder='选择明细分类'
|
placeholder='选择明细分类'
|
||||||
options={LOG_OPTIONS}
|
options={LOG_OPTIONS}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import { Button, Form, Header, Segment } from 'semantic-ui-react';
|
import { Button, Form, Header, Segment } from 'semantic-ui-react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { API, showError, showSuccess } from '../../helpers';
|
import { API, showError, showSuccess } from '../../helpers';
|
||||||
|
import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render';
|
||||||
|
|
||||||
const EditUser = () => {
|
const EditUser = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -134,7 +135,7 @@ const EditUser = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='剩余额度'
|
label={`剩余额度${renderQuotaWithPrompt(quota)}`}
|
||||||
name='quota'
|
name='quota'
|
||||||
placeholder={'请输入新的剩余额度'}
|
placeholder={'请输入新的剩余额度'}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
|
|||||||
Reference in New Issue
Block a user