mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-09-17 17:16:38 +08:00
feat: show stream & elapsed time in log detail
This commit is contained in:
parent
ea0721d525
commit
dc470ce82e
@ -13,3 +13,8 @@ func GetTimeString() string {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
return fmt.Sprintf("%s%d", now.Format("20060102150405"), now.UnixNano()%1e9)
|
return fmt.Sprintf("%s%d", now.Format("20060102150405"), now.UnixNano()%1e9)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalcElapsedTime return the elapsed time in milliseconds (ms)
|
||||||
|
func CalcElapsedTime(start time.Time) int64 {
|
||||||
|
return time.Now().Sub(start).Milliseconds()
|
||||||
|
}
|
||||||
|
23
model/log.go
23
model/log.go
@ -25,7 +25,10 @@ type Log struct {
|
|||||||
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
|
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
|
||||||
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
|
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
|
||||||
ChannelId int `json:"channel" gorm:"index"`
|
ChannelId int `json:"channel" gorm:"index"`
|
||||||
RequestId string `json:"request_id"`
|
RequestId string `json:"request_id" gorm:"default:''"`
|
||||||
|
ElapsedTime int64 `json:"elapsed_time" gorm:"default:0"` // unit is ms
|
||||||
|
IsStream bool `json:"is_stream" gorm:"default:false"`
|
||||||
|
SystemPromptReset bool `json:"system_prompt_reset" gorm:"default:false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -73,23 +76,13 @@ func RecordTopupLog(ctx context.Context, userId int, content string, quota int)
|
|||||||
recordLogHelper(ctx, log)
|
recordLogHelper(ctx, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int64, content string) {
|
func RecordConsumeLog(ctx context.Context, log *Log) {
|
||||||
if !config.LogConsumeEnabled {
|
if !config.LogConsumeEnabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log := &Log{
|
log.Username = GetUsernameById(log.UserId)
|
||||||
UserId: userId,
|
log.CreatedAt = helper.GetTimestamp()
|
||||||
Username: GetUsernameById(userId),
|
log.Type = LogTypeConsume
|
||||||
CreatedAt: helper.GetTimestamp(),
|
|
||||||
Type: LogTypeConsume,
|
|
||||||
Content: content,
|
|
||||||
PromptTokens: promptTokens,
|
|
||||||
CompletionTokens: completionTokens,
|
|
||||||
TokenName: tokenName,
|
|
||||||
ModelName: modelName,
|
|
||||||
Quota: int(quota),
|
|
||||||
ChannelId: channelId,
|
|
||||||
}
|
|
||||||
recordLogHelper(ctx, log)
|
recordLogHelper(ctx, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,16 @@ func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int64, totalQ
|
|||||||
// totalQuota is total quota consumed
|
// totalQuota is total quota consumed
|
||||||
if totalQuota != 0 {
|
if totalQuota != 0 {
|
||||||
logContent := fmt.Sprintf("%.2f × %.2f", modelRatio, groupRatio)
|
logContent := fmt.Sprintf("%.2f × %.2f", modelRatio, groupRatio)
|
||||||
model.RecordConsumeLog(ctx, userId, channelId, int(totalQuota), 0, modelName, tokenName, totalQuota, logContent)
|
model.RecordConsumeLog(ctx, &model.Log{
|
||||||
|
UserId: userId,
|
||||||
|
ChannelId: channelId,
|
||||||
|
PromptTokens: int(totalQuota),
|
||||||
|
CompletionTokens: 0,
|
||||||
|
ModelName: modelName,
|
||||||
|
TokenName: tokenName,
|
||||||
|
Quota: int(totalQuota),
|
||||||
|
Content: logContent,
|
||||||
|
})
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota)
|
||||||
model.UpdateChannelUsedQuota(channelId, totalQuota)
|
model.UpdateChannelUsedQuota(channelId, totalQuota)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
"github.com/songquanpeng/one-api/relay/constant/role"
|
"github.com/songquanpeng/one-api/relay/constant/role"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -121,12 +122,20 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *meta.M
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(ctx, "error update user quota cache: "+err.Error())
|
logger.Error(ctx, "error update user quota cache: "+err.Error())
|
||||||
}
|
}
|
||||||
var extraLog string
|
logContent := fmt.Sprintf("%.2f × %.2f × %.2f", modelRatio, groupRatio, completionRatio)
|
||||||
if systemPromptReset {
|
model.RecordConsumeLog(ctx, &model.Log{
|
||||||
extraLog = " (注意系统提示词已被重置)"
|
UserId: meta.UserId,
|
||||||
}
|
ChannelId: meta.ChannelId,
|
||||||
logContent := fmt.Sprintf("%.2f × %.2f × %.2f%s", modelRatio, groupRatio, completionRatio, extraLog)
|
PromptTokens: promptTokens,
|
||||||
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent)
|
CompletionTokens: completionTokens,
|
||||||
|
ModelName: textRequest.Model,
|
||||||
|
TokenName: meta.TokenName,
|
||||||
|
Quota: int(quota),
|
||||||
|
Content: logContent,
|
||||||
|
IsStream: meta.IsStream,
|
||||||
|
ElapsedTime: helper.CalcElapsedTime(meta.StartTime),
|
||||||
|
SystemPromptReset: systemPromptReset,
|
||||||
|
})
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
||||||
model.UpdateChannelUsedQuota(meta.ChannelId, quota)
|
model.UpdateChannelUsedQuota(meta.ChannelId, quota)
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,16 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
if quota != 0 {
|
if quota != 0 {
|
||||||
tokenName := c.GetString(ctxkey.TokenName)
|
tokenName := c.GetString(ctxkey.TokenName)
|
||||||
logContent := fmt.Sprintf("%.2f × %.2f", modelRatio, groupRatio)
|
logContent := fmt.Sprintf("%.2f × %.2f", modelRatio, groupRatio)
|
||||||
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, 0, 0, imageRequest.Model, tokenName, quota, logContent)
|
model.RecordConsumeLog(ctx, &model.Log{
|
||||||
|
UserId: meta.UserId,
|
||||||
|
ChannelId: meta.ChannelId,
|
||||||
|
PromptTokens: 0,
|
||||||
|
CompletionTokens: 0,
|
||||||
|
ModelName: imageRequest.Model,
|
||||||
|
TokenName: tokenName,
|
||||||
|
Quota: int(quota),
|
||||||
|
Content: logContent,
|
||||||
|
})
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
||||||
channelId := c.GetInt(ctxkey.ChannelId)
|
channelId := c.GetInt(ctxkey.ChannelId)
|
||||||
model.UpdateChannelUsedQuota(channelId, quota)
|
model.UpdateChannelUsedQuota(channelId, quota)
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"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/relay"
|
"github.com/songquanpeng/one-api/relay"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor"
|
"github.com/songquanpeng/one-api/relay/adaptor"
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/common/ctxkey"
|
"github.com/songquanpeng/one-api/common/ctxkey"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
@ -31,6 +34,7 @@ type Meta struct {
|
|||||||
RequestURLPath string
|
RequestURLPath string
|
||||||
PromptTokens int // only for DoResponse
|
PromptTokens int // only for DoResponse
|
||||||
SystemPrompt string
|
SystemPrompt string
|
||||||
|
StartTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetByContext(c *gin.Context) *Meta {
|
func GetByContext(c *gin.Context) *Meta {
|
||||||
@ -48,6 +52,7 @@ func GetByContext(c *gin.Context) *Meta {
|
|||||||
APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
|
APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
|
||||||
RequestURLPath: c.Request.URL.String(),
|
RequestURLPath: c.Request.URL.String(),
|
||||||
SystemPrompt: c.GetString(ctxkey.SystemPrompt),
|
SystemPrompt: c.GetString(ctxkey.SystemPrompt),
|
||||||
|
StartTime: time.Now(),
|
||||||
}
|
}
|
||||||
cfg, ok := c.Get(ctxkey.Config)
|
cfg, ok := c.Get(ctxkey.Config)
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Button, Form, Header, Label, Pagination, Segment, Select, Table } from 'semantic-ui-react';
|
import {
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
Header,
|
||||||
|
Label,
|
||||||
|
Pagination,
|
||||||
|
Segment,
|
||||||
|
Select,
|
||||||
|
Table,
|
||||||
|
} from 'semantic-ui-react';
|
||||||
import { API, isAdmin, showError, timestamp2string } from '../helpers';
|
import { API, isAdmin, showError, timestamp2string } from '../helpers';
|
||||||
|
|
||||||
import { ITEMS_PER_PAGE } from '../constants';
|
import { ITEMS_PER_PAGE } from '../constants';
|
||||||
import { renderQuota } from '../helpers/render';
|
import { renderQuota } from '../helpers/render';
|
||||||
|
|
||||||
function renderTimestamp(timestamp) {
|
function renderTimestamp(timestamp) {
|
||||||
return (
|
return <>{timestamp2string(timestamp)}</>;
|
||||||
<>
|
|
||||||
{timestamp2string(timestamp)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MODE_OPTIONS = [
|
const MODE_OPTIONS = [
|
||||||
{ key: 'all', text: '全部用户', value: 'all' },
|
{ key: 'all', text: '全部用户', value: 'all' },
|
||||||
{ key: 'self', text: '当前用户', value: 'self' }
|
{ key: 'self', text: '当前用户', value: 'self' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const LOG_OPTIONS = [
|
const LOG_OPTIONS = [
|
||||||
@ -23,24 +28,87 @@ const LOG_OPTIONS = [
|
|||||||
{ key: '1', text: '充值', value: 1 },
|
{ key: '1', text: '充值', value: 1 },
|
||||||
{ key: '2', text: '消费', value: 2 },
|
{ key: '2', text: '消费', value: 2 },
|
||||||
{ key: '3', text: '管理', value: 3 },
|
{ key: '3', text: '管理', value: 3 },
|
||||||
{ key: '4', text: '系统', value: 4 }
|
{ key: '4', text: '系统', value: 4 },
|
||||||
];
|
];
|
||||||
|
|
||||||
function renderType(type) {
|
function renderType(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1:
|
case 1:
|
||||||
return <Label basic color='green'> 充值 </Label>;
|
return (
|
||||||
|
<Label basic color='green'>
|
||||||
|
充值
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return <Label basic color='olive'> 消费 </Label>;
|
return (
|
||||||
|
<Label basic color='olive'>
|
||||||
|
消费
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return <Label basic color='orange'> 管理 </Label>;
|
return (
|
||||||
|
<Label basic color='orange'>
|
||||||
|
管理
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
case 4:
|
case 4:
|
||||||
return <Label basic color='purple'> 系统 </Label>;
|
return (
|
||||||
|
<Label basic color='purple'>
|
||||||
|
系统
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return <Label basic color='black'> 未知 </Label>;
|
return (
|
||||||
|
<Label basic color='black'>
|
||||||
|
未知
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getColorByElapsedTime(elapsedTime) {
|
||||||
|
if (elapsedTime === undefined || 0) return 'black';
|
||||||
|
if (elapsedTime < 1000) return 'green';
|
||||||
|
if (elapsedTime < 3000) return 'olive';
|
||||||
|
if (elapsedTime < 5000) return 'yellow';
|
||||||
|
if (elapsedTime < 10000) return 'orange';
|
||||||
|
return 'red';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDetail(log) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
倍率:{log.content}
|
||||||
|
<br />
|
||||||
|
{log.elapsed_time && (
|
||||||
|
<Label
|
||||||
|
basic
|
||||||
|
size={'mini'}
|
||||||
|
color={getColorByElapsedTime(log.elapsed_time)}
|
||||||
|
>
|
||||||
|
{log.elapsed_time} ms
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
{log.is_stream && (
|
||||||
|
<>
|
||||||
|
<Label size={'mini'} color='pink'>
|
||||||
|
Stream
|
||||||
|
</Label>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{log.system_prompt_reset && (
|
||||||
|
<>
|
||||||
|
<Label basic size={'mini'} color='red'>
|
||||||
|
System Prompt Reset
|
||||||
|
</Label>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<br />
|
||||||
|
<code>{log.request_id}</code>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const LogsTable = () => {
|
const LogsTable = () => {
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState([]);
|
||||||
const [showStat, setShowStat] = useState(false);
|
const [showStat, setShowStat] = useState(false);
|
||||||
@ -57,13 +125,20 @@ const LogsTable = () => {
|
|||||||
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),
|
||||||
channel: ''
|
channel: '',
|
||||||
});
|
});
|
||||||
const { username, token_name, model_name, start_timestamp, end_timestamp, channel } = inputs;
|
const {
|
||||||
|
username,
|
||||||
|
token_name,
|
||||||
|
model_name,
|
||||||
|
start_timestamp,
|
||||||
|
end_timestamp,
|
||||||
|
channel,
|
||||||
|
} = inputs;
|
||||||
|
|
||||||
const [stat, setStat] = useState({
|
const [stat, setStat] = useState({
|
||||||
quota: 0,
|
quota: 0,
|
||||||
token: 0
|
token: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleInputChange = (e, { name, value }) => {
|
const handleInputChange = (e, { name, value }) => {
|
||||||
@ -73,7 +148,9 @@ 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=${token_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);
|
||||||
@ -85,7 +162,9 @@ 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=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`);
|
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}&channel=${channel}`
|
||||||
|
);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setStat(data);
|
setStat(data);
|
||||||
@ -201,37 +280,82 @@ const LogsTable = () => {
|
|||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
使用明细(总消耗额度:
|
使用明细(总消耗额度:
|
||||||
{showStat && renderQuota(stat.quota)}
|
{showStat && renderQuota(stat.quota)}
|
||||||
{!showStat && <span onClick={handleEyeClick} style={{ cursor: 'pointer', color: 'gray' }}>点击查看</span>}
|
{!showStat && (
|
||||||
|
<span
|
||||||
|
onClick={handleEyeClick}
|
||||||
|
style={{ cursor: 'pointer', color: 'gray' }}
|
||||||
|
>
|
||||||
|
点击查看
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
)
|
)
|
||||||
</Header>
|
</Header>
|
||||||
<Form>
|
<Form>
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Input fluid label={'令牌名称'} width={3} value={token_name}
|
<Form.Input
|
||||||
placeholder={'可选值'} name='token_name' onChange={handleInputChange} />
|
fluid
|
||||||
<Form.Input fluid label='模型名称' width={3} value={model_name} placeholder='可选值'
|
label={'令牌名称'}
|
||||||
|
width={3}
|
||||||
|
value={token_name}
|
||||||
|
placeholder={'可选值'}
|
||||||
|
name='token_name'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
fluid
|
||||||
|
label='模型名称'
|
||||||
|
width={3}
|
||||||
|
value={model_name}
|
||||||
|
placeholder='可选值'
|
||||||
name='model_name'
|
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'
|
||||||
onChange={handleInputChange} />
|
onChange={handleInputChange}
|
||||||
<Form.Input fluid label='结束时间' width={4} value={end_timestamp} type='datetime-local'
|
/>
|
||||||
|
<Form.Input
|
||||||
|
fluid
|
||||||
|
label='结束时间'
|
||||||
|
width={4}
|
||||||
|
value={end_timestamp}
|
||||||
|
type='datetime-local'
|
||||||
name='end_timestamp'
|
name='end_timestamp'
|
||||||
onChange={handleInputChange} />
|
onChange={handleInputChange}
|
||||||
<Form.Button fluid label='操作' width={2} onClick={refresh}>查询</Form.Button>
|
/>
|
||||||
|
<Form.Button fluid label='操作' width={2} onClick={refresh}>
|
||||||
|
查询
|
||||||
|
</Form.Button>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
{
|
{isAdminUser && (
|
||||||
isAdminUser && <>
|
<>
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Input fluid label={'渠道 ID'} width={3} value={channel}
|
<Form.Input
|
||||||
placeholder='可选值' name='channel'
|
fluid
|
||||||
onChange={handleInputChange} />
|
label={'渠道 ID'}
|
||||||
<Form.Input fluid label={'用户名称'} width={3} value={username}
|
width={3}
|
||||||
placeholder={'可选值'} name='username'
|
value={channel}
|
||||||
onChange={handleInputChange} />
|
placeholder='可选值'
|
||||||
|
name='channel'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
fluid
|
||||||
|
label={'用户名称'}
|
||||||
|
width={3}
|
||||||
|
value={username}
|
||||||
|
placeholder={'可选值'}
|
||||||
|
name='username'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
<Table basic compact size='small'>
|
<Table basic compact size='small'>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
@ -245,8 +369,8 @@ const LogsTable = () => {
|
|||||||
>
|
>
|
||||||
时间
|
时间
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
{
|
{isAdminUser && (
|
||||||
isAdminUser && <Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
sortLog('channel');
|
sortLog('channel');
|
||||||
@ -255,9 +379,9 @@ const LogsTable = () => {
|
|||||||
>
|
>
|
||||||
渠道
|
渠道
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
}
|
)}
|
||||||
{
|
{isAdminUser && (
|
||||||
isAdminUser && <Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
sortLog('username');
|
sortLog('username');
|
||||||
@ -266,7 +390,7 @@ const LogsTable = () => {
|
|||||||
>
|
>
|
||||||
用户
|
用户
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
}
|
)}
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -328,7 +452,7 @@ const LogsTable = () => {
|
|||||||
}}
|
}}
|
||||||
width={isAdminUser ? 4 : 6}
|
width={isAdminUser ? 4 : 6}
|
||||||
>
|
>
|
||||||
详情(模型倍率 × 分组倍率 × 补全倍率)
|
详情
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
@ -344,26 +468,41 @@ const LogsTable = () => {
|
|||||||
return (
|
return (
|
||||||
<Table.Row key={log.id}>
|
<Table.Row key={log.id}>
|
||||||
<Table.Cell>{renderTimestamp(log.created_at)}</Table.Cell>
|
<Table.Cell>{renderTimestamp(log.created_at)}</Table.Cell>
|
||||||
{
|
{isAdminUser && (
|
||||||
isAdminUser && (
|
<Table.Cell>
|
||||||
<Table.Cell>{log.channel ? <Label basic>{log.channel}</Label> : ''}</Table.Cell>
|
{log.channel ? <Label basic>{log.channel}</Label> : ''}
|
||||||
)
|
</Table.Cell>
|
||||||
}
|
)}
|
||||||
{
|
{isAdminUser && (
|
||||||
isAdminUser && (
|
<Table.Cell>
|
||||||
<Table.Cell>{log.username ? <Label>{log.username}</Label> : ''}</Table.Cell>
|
{log.username ? <Label>{log.username}</Label> : ''}
|
||||||
)
|
</Table.Cell>
|
||||||
}
|
)}
|
||||||
<Table.Cell>{log.token_name ? <Label basic>{log.token_name}</Label> : ''}</Table.Cell>
|
<Table.Cell>
|
||||||
|
{log.token_name ? (
|
||||||
|
<Label basic>{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>
|
||||||
<Table.Cell>{log.prompt_tokens ? log.prompt_tokens : ''}</Table.Cell>
|
{log.model_name ? (
|
||||||
<Table.Cell>{log.completion_tokens ? log.completion_tokens : ''}</Table.Cell>
|
<Label basic>{log.model_name}</Label>
|
||||||
<Table.Cell>{log.quota ? renderQuota(log.quota, 6) : ''}</Table.Cell>
|
) : (
|
||||||
<Table.Cell>{log.content}{<>
|
''
|
||||||
<br/>
|
)}
|
||||||
<code>{log.request_id}</code>
|
</Table.Cell>
|
||||||
</>}</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.quota ? renderQuota(log.quota, 6) : ''}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>{renderDetail(log)}</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -382,7 +521,9 @@ const LogsTable = () => {
|
|||||||
setLogType(value);
|
setLogType(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button size='small' onClick={refresh} loading={loading}>刷新</Button>
|
<Button size='small' onClick={refresh} loading={loading}>
|
||||||
|
刷新
|
||||||
|
</Button>
|
||||||
<Pagination
|
<Pagination
|
||||||
floated='right'
|
floated='right'
|
||||||
activePage={activePage}
|
activePage={activePage}
|
||||||
|
Loading…
Reference in New Issue
Block a user