diff --git a/common/constants.go b/common/constants.go
index 99c78ac2..cb58391c 100644
--- a/common/constants.go
+++ b/common/constants.go
@@ -208,6 +208,8 @@ const (
ChannelTypeLingYiWanWu = 31
ChannelTypeAws = 33
ChannelTypeCohere = 34
+
+ ChannelTypeDummy // this one is only for count, do not add any channel after this
)
var ChannelBaseURLs = []string{
diff --git a/common/model-ratio.go b/common/model-ratio.go
index 9d6c4462..f4703173 100644
--- a/common/model-ratio.go
+++ b/common/model-ratio.go
@@ -28,6 +28,7 @@ var DefaultModelRatio = map[string]float64{
"gpt-4-vision-preview": 5, // $0.01 / 1K tokens
"gpt-4-1106-vision-preview": 5, // $0.01 / 1K tokens
"gpt-4-turbo": 5, // $0.01 / 1K tokens
+ "gpt-4-turbo-2024-04-09": 5, // $0.01 / 1K tokens
"gpt-3.5-turbo": 0.25, // $0.0015 / 1K tokens
//"gpt-3.5-turbo-0301": 0.75, //deprecated
"gpt-3.5-turbo-0613": 0.75,
@@ -111,6 +112,8 @@ var DefaultModelRatio = map[string]float64{
"command-light-nightly": 0.5,
"command-r": 0.25,
"command-r-plus ": 1.5,
+ "deepseek-chat": 0.07,
+ "deepseek-coder": 0.07,
}
var DefaultModelPrice = map[string]float64{
@@ -135,6 +138,12 @@ var DefaultModelPrice = map[string]float64{
var modelPrice map[string]float64 = nil
var modelRatio map[string]float64 = nil
+var CompletionRatio map[string]float64 = nil
+var DefaultCompletionRatio = map[string]float64{
+ "gpt-4-gizmo-*": 2,
+ "gpt-4-all": 2,
+}
+
func ModelPrice2JSONString() string {
if modelPrice == nil {
modelPrice = DefaultModelPrice
@@ -199,6 +208,22 @@ func GetModelRatio(name string) float64 {
return ratio
}
+func CompletionRatio2JSONString() string {
+ if CompletionRatio == nil {
+ CompletionRatio = DefaultCompletionRatio
+ }
+ jsonBytes, err := json.Marshal(CompletionRatio)
+ if err != nil {
+ SysError("error marshalling completion ratio: " + err.Error())
+ }
+ return string(jsonBytes)
+}
+
+func UpdateCompletionRatioByJSONString(jsonStr string) error {
+ CompletionRatio = make(map[string]float64)
+ return json.Unmarshal([]byte(jsonStr), &CompletionRatio)
+}
+
func GetCompletionRatio(name string) float64 {
if strings.HasPrefix(name, "gpt-3.5") {
if name == "gpt-3.5-turbo" || strings.HasSuffix(name, "0125") {
@@ -211,7 +236,7 @@ func GetCompletionRatio(name string) float64 {
}
return 4.0 / 3.0
}
- if strings.HasPrefix(name, "gpt-4") {
+ if strings.HasPrefix(name, "gpt-4") && name != "gpt-4-all" && !strings.HasPrefix(name, "gpt-4-gizmo") {
if strings.HasPrefix(name, "gpt-4-turbo") || strings.HasSuffix(name, "preview") {
return 3
}
@@ -240,9 +265,19 @@ func GetCompletionRatio(name string) float64 {
return 2
}
}
+ if strings.HasPrefix(name, "deepseek") {
+ return 2
+ }
switch name {
case "llama2-70b-4096":
- return 0.8 / 0.7
+ return 0.8 / 0.64
+ case "llama3-8b-8192":
+ return 2
+ case "llama3-70b-8192":
+ return 0.79 / 0.59
+ }
+ if ratio, ok := CompletionRatio[name]; ok {
+ return ratio
}
return 1
}
diff --git a/common/utils.go b/common/utils.go
index d540c2e7..657ffd49 100644
--- a/common/utils.go
+++ b/common/utils.go
@@ -1,6 +1,7 @@
package common
import (
+ "encoding/json"
"fmt"
"github.com/google/uuid"
"html/template"
@@ -241,3 +242,11 @@ func RandomSleep() {
// Sleep for 0-3000 ms
time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)
}
+
+func MapToJsonStr(m map[string]interface{}) string {
+ bytes, err := json.Marshal(m)
+ if err != nil {
+ return ""
+ }
+ return string(bytes)
+}
diff --git a/controller/channel-test.go b/controller/channel-test.go
index f66e0d66..f37f309f 100644
--- a/controller/channel-test.go
+++ b/controller/channel-test.go
@@ -208,7 +208,7 @@ func testAllChannels(notify bool) error {
if isChannelEnabled && service.ShouldDisableChannel(openaiErr, -1) && ban {
service.DisableChannel(channel.Id, channel.Name, err.Error())
}
- if !isChannelEnabled && service.ShouldEnableChannel(err, openaiErr) {
+ if !isChannelEnabled && service.ShouldEnableChannel(err, openaiErr, channel.Status) {
service.EnableChannel(channel.Id, channel.Name)
}
channel.UpdateResponseTime(milliseconds)
diff --git a/controller/misc.go b/controller/misc.go
index 273a618e..8c59952b 100644
--- a/controller/misc.go
+++ b/controller/misc.go
@@ -147,7 +147,7 @@ func SendEmailVerification(c *gin.Context) {
}
}
if common.EmailAliasRestrictionEnabled {
- containsSpecialSymbols := strings.Contains(localPart, "+") || strings.Count(localPart, ".") > 1
+ containsSpecialSymbols := strings.Contains(localPart, "+") || strings.Contains(localPart, ".")
if containsSpecialSymbols {
c.JSON(http.StatusOK, gin.H{
"success": false,
diff --git a/controller/model.go b/controller/model.go
index 1e78b4c5..890b2603 100644
--- a/controller/model.go
+++ b/controller/model.go
@@ -4,13 +4,15 @@ import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
+ "one-api/common"
"one-api/constant"
"one-api/dto"
"one-api/model"
"one-api/relay"
"one-api/relay/channel/ai360"
- "one-api/relay/channel/moonshot"
"one-api/relay/channel/lingyiwanwu"
+ "one-api/relay/channel/moonshot"
+ relaycommon "one-api/relay/common"
relayconstant "one-api/relay/constant"
)
@@ -43,8 +45,9 @@ type OpenAIModels struct {
var openAIModels []OpenAIModels
var openAIModelsMap map[string]OpenAIModels
+var channelId2Models map[int][]string
-func init() {
+func getPermission() []OpenAIModelPermission {
var permission []OpenAIModelPermission
permission = append(permission, OpenAIModelPermission{
Id: "modelperm-LwHkVFn8AcMItP432fKKDIKJ",
@@ -60,7 +63,12 @@ func init() {
Group: nil,
IsBlocking: false,
})
+ return permission
+}
+
+func init() {
// https://platform.openai.com/docs/models/model-endpoint-compatibility
+ permission := getPermission()
for i := 0; i < relayconstant.APITypeDummy; i++ {
if i == relayconstant.APITypeAIProxyLibrary {
continue
@@ -85,7 +93,7 @@ func init() {
Id: modelName,
Object: "model",
Created: 1626777600,
- OwnedBy: "360",
+ OwnedBy: ai360.ChannelName,
Permission: permission,
Root: modelName,
Parent: nil,
@@ -128,6 +136,17 @@ func init() {
for _, model := range openAIModels {
openAIModelsMap[model.Id] = model
}
+ channelId2Models = make(map[int][]string)
+ for i := 1; i <= common.ChannelTypeDummy; i++ {
+ apiType := relayconstant.ChannelType2APIType(i)
+ if apiType == -1 || apiType == relayconstant.APITypeAIProxyLibrary {
+ continue
+ }
+ meta := &relaycommon.RelayInfo{ChannelType: i}
+ adaptor := relay.GetAdaptor(apiType)
+ adaptor.Init(meta, dto.GeneralOpenAIRequest{})
+ channelId2Models[i] = adaptor.GetModelList()
+ }
}
func ListModels(c *gin.Context) {
@@ -142,21 +161,39 @@ func ListModels(c *gin.Context) {
}
models := model.GetGroupModels(user.Group)
userOpenAiModels := make([]OpenAIModels, 0)
+ permission := getPermission()
for _, s := range models {
if _, ok := openAIModelsMap[s]; ok {
userOpenAiModels = append(userOpenAiModels, openAIModelsMap[s])
+ } else {
+ userOpenAiModels = append(userOpenAiModels, OpenAIModels{
+ Id: s,
+ Object: "model",
+ Created: 1626777600,
+ OwnedBy: "openai",
+ Permission: permission,
+ Root: s,
+ Parent: nil,
+ })
}
}
c.JSON(200, gin.H{
- "object": "list",
- "data": userOpenAiModels,
+ "success": true,
+ "data": userOpenAiModels,
})
}
func ChannelListModels(c *gin.Context) {
c.JSON(200, gin.H{
- "object": "list",
- "data": openAIModels,
+ "success": true,
+ "data": openAIModels,
+ })
+}
+
+func DashboardListModels(c *gin.Context) {
+ c.JSON(200, gin.H{
+ "success": true,
+ "data": channelId2Models,
})
}
diff --git a/model/log.go b/model/log.go
index 2740c5a2..57c64d0e 100644
--- a/model/log.go
+++ b/model/log.go
@@ -24,6 +24,7 @@ type Log struct {
IsStream bool `json:"is_stream" gorm:"default:false"`
ChannelId int `json:"channel" gorm:"index"`
TokenId int `json:"token_id" gorm:"default:0;index"`
+ Other string `json:"other"`
}
const (
@@ -57,12 +58,13 @@ func RecordLog(userId int, logType int, content string) {
}
}
-func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int, content string, tokenId int, userQuota int, useTimeSeconds int, isStream bool) {
+func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int, content string, tokenId int, userQuota int, useTimeSeconds int, isStream bool, other map[string]interface{}) {
common.LogInfo(ctx, fmt.Sprintf("record consume log: userId=%d, 用户调用前余额=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, userQuota, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content))
if !common.LogConsumeEnabled {
return
}
username, _ := CacheGetUsername(userId)
+ otherStr := common.MapToJsonStr(other)
log := &Log{
UserId: userId,
Username: username,
@@ -78,6 +80,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
TokenId: tokenId,
UseTime: useTimeSeconds,
IsStream: isStream,
+ Other: otherStr,
}
err := DB.Create(log).Error
if err != nil {
diff --git a/model/option.go b/model/option.go
index 1adc84cb..bfa7ddc9 100644
--- a/model/option.go
+++ b/model/option.go
@@ -83,6 +83,7 @@ func InitOptionMap() {
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
common.OptionMap["ModelPrice"] = common.ModelPrice2JSONString()
common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString()
+ common.OptionMap["CompletionRatio"] = common.CompletionRatio2JSONString()
common.OptionMap["TopUpLink"] = common.TopUpLink
common.OptionMap["ChatLink"] = common.ChatLink
common.OptionMap["ChatLink2"] = common.ChatLink2
@@ -290,6 +291,8 @@ func updateOptionMap(key string, value string) (err error) {
err = common.UpdateModelRatioByJSONString(value)
case "GroupRatio":
err = common.UpdateGroupRatioByJSONString(value)
+ case "CompletionRatio":
+ err = common.UpdateCompletionRatioByJSONString(value)
case "ModelPrice":
err = common.UpdateModelPriceByJSONString(value)
case "TopUpLink":
diff --git a/model/user.go b/model/user.go
index 3e7169ae..eab8ced6 100644
--- a/model/user.go
+++ b/model/user.go
@@ -253,14 +253,17 @@ func (user *User) Edit(updatePassword bool) error {
}
}
newUser := *user
- DB.First(&user, user.Id)
- err = DB.Model(user).Updates(map[string]interface{}{
+ updates := map[string]interface{}{
"username": newUser.Username,
- "password": newUser.Password,
"display_name": newUser.DisplayName,
"group": newUser.Group,
"quota": newUser.Quota,
- }).Error
+ }
+ if updatePassword {
+ updates["password"] = newUser.Password
+ }
+ DB.First(&user, user.Id)
+ err = DB.Model(user).Updates(updates).Error
if err == nil {
if common.RedisEnabled {
_ = common.RedisSet(fmt.Sprintf("user_group:%d", user.Id), user.Group, time.Duration(UserId2GroupCacheSeconds)*time.Second)
diff --git a/relay/channel/ai360/constants.go b/relay/channel/ai360/constants.go
index cfc3cb28..82698fa8 100644
--- a/relay/channel/ai360/constants.go
+++ b/relay/channel/ai360/constants.go
@@ -6,3 +6,5 @@ var ModelList = []string{
"embedding_s1_v1",
"semantic_similarity_s1_v1",
}
+
+var ChannelName = "ai360"
diff --git a/relay/channel/ollama/constants.go b/relay/channel/ollama/constants.go
index 970e9777..682626a2 100644
--- a/relay/channel/ollama/constants.go
+++ b/relay/channel/ollama/constants.go
@@ -1,5 +1,7 @@
package ollama
-var ModelList []string
+var ModelList = []string{
+ "llama3-7b",
+}
var ChannelName = "ollama"
diff --git a/relay/channel/openai/constant.go b/relay/channel/openai/constant.go
index 91f4e510..8c560c7d 100644
--- a/relay/channel/openai/constant.go
+++ b/relay/channel/openai/constant.go
@@ -6,7 +6,7 @@ var ModelList = []string{
"gpt-3.5-turbo-instruct",
"gpt-4", "gpt-4-0314", "gpt-4-0613", "gpt-4-1106-preview", "gpt-4-0125-preview",
"gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613",
- "gpt-4-turbo-preview",
+ "gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09",
"gpt-4-vision-preview",
"text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large",
"text-curie-001", "text-babbage-001", "text-ada-001", "text-davinci-002", "text-davinci-003",
diff --git a/relay/constant/api_type.go b/relay/constant/api_type.go
index 7f11ae22..1bc8b47f 100644
--- a/relay/constant/api_type.go
+++ b/relay/constant/api_type.go
@@ -25,8 +25,18 @@ const (
)
func ChannelType2APIType(channelType int) int {
- apiType := APITypeOpenAI
+ apiType := -1
switch channelType {
+ case common.ChannelTypeOpenAI:
+ apiType = APITypeOpenAI
+ case common.ChannelTypeAzure:
+ apiType = APITypeOpenAI
+ case common.ChannelTypeMoonshot:
+ apiType = APITypeOpenAI
+ case common.ChannelTypeLingYiWanWu:
+ apiType = APITypeOpenAI
+ case common.ChannelType360:
+ apiType = APITypeOpenAI
case common.ChannelTypeAnthropic:
apiType = APITypeAnthropic
case common.ChannelTypeBaidu:
diff --git a/relay/relay-audio.go b/relay/relay-audio.go
index 09ac2a0c..ef89597d 100644
--- a/relay/relay-audio.go
+++ b/relay/relay-audio.go
@@ -196,7 +196,10 @@ func AudioHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode {
if quota != 0 {
tokenName := c.GetString("token_name")
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
- model.RecordConsumeLog(ctx, userId, channelId, promptTokens, 0, audioRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false)
+ other := make(map[string]interface{})
+ other["model_ratio"] = modelRatio
+ other["group_ratio"] = groupRatio
+ model.RecordConsumeLog(ctx, userId, channelId, promptTokens, 0, audioRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false, other)
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
channelId := c.GetInt("channel_id")
model.UpdateChannelUsedQuota(channelId, quota)
diff --git a/relay/relay-image.go b/relay/relay-image.go
index ce072d16..7f8cd9e6 100644
--- a/relay/relay-image.go
+++ b/relay/relay-image.go
@@ -191,7 +191,10 @@ func RelayImageHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusC
quality = "hd"
}
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f, 大小 %s, 品质 %s", modelRatio, groupRatio, imageRequest.Size, quality)
- model.RecordConsumeLog(ctx, userId, channelId, 0, 0, imageRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false)
+ other := make(map[string]interface{})
+ other["model_ratio"] = modelRatio
+ other["group_ratio"] = groupRatio
+ model.RecordConsumeLog(ctx, userId, channelId, 0, 0, imageRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false, other)
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
channelId := c.GetInt("channel_id")
model.UpdateChannelUsedQuota(channelId, quota)
diff --git a/relay/relay-mj.go b/relay/relay-mj.go
index 27b4c6d3..16ad4121 100644
--- a/relay/relay-mj.go
+++ b/relay/relay-mj.go
@@ -202,7 +202,10 @@ func RelaySwapFace(c *gin.Context) *dto.MidjourneyResponse {
if quota != 0 {
tokenName := c.GetString("token_name")
logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s", modelPrice, groupRatio, constant.MjActionSwapFace)
- model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false)
+ other := make(map[string]interface{})
+ other["model_price"] = modelPrice
+ other["group_ratio"] = groupRatio
+ model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false, other)
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
channelId := c.GetInt("channel_id")
model.UpdateChannelUsedQuota(channelId, quota)
@@ -498,7 +501,10 @@ func RelayMidjourneySubmit(c *gin.Context, relayMode int) *dto.MidjourneyRespons
if quota != 0 {
tokenName := c.GetString("token_name")
logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s", modelPrice, groupRatio, midjRequest.Action)
- model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false)
+ other := make(map[string]interface{})
+ other["model_price"] = modelPrice
+ other["group_ratio"] = groupRatio
+ model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false, other)
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
channelId := c.GetInt("channel_id")
model.UpdateChannelUsedQuota(channelId, quota)
diff --git a/relay/relay-text.go b/relay/relay-text.go
index 7c2720ec..9010381d 100644
--- a/relay/relay-text.go
+++ b/relay/relay-text.go
@@ -315,7 +315,12 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, textRe
logModel = "gpt-4-gizmo-*"
logContent += fmt.Sprintf(",模型 %s", textRequest.Model)
}
- model.RecordConsumeLog(ctx, relayInfo.UserId, relayInfo.ChannelId, promptTokens, completionTokens, logModel, tokenName, quota, logContent, relayInfo.TokenId, userQuota, int(useTimeSeconds), relayInfo.IsStream)
+ other := make(map[string]interface{})
+ other["model_ratio"] = modelRatio
+ other["group_ratio"] = groupRatio
+ other["completion_ratio"] = completionRatio
+ other["model_price"] = modelPrice
+ model.RecordConsumeLog(ctx, relayInfo.UserId, relayInfo.ChannelId, promptTokens, completionTokens, logModel, tokenName, quota, logContent, relayInfo.TokenId, userQuota, int(useTimeSeconds), relayInfo.IsStream, other)
//if quota != 0 {
//
diff --git a/router/api-router.go b/router/api-router.go
index 85474543..8c0ae30c 100644
--- a/router/api-router.go
+++ b/router/api-router.go
@@ -14,6 +14,7 @@ func SetApiRouter(router *gin.Engine) {
apiRouter.Use(middleware.GlobalAPIRateLimit())
{
apiRouter.GET("/status", controller.GetStatus)
+ apiRouter.GET("/models", middleware.UserAuth(), controller.DashboardListModels)
apiRouter.GET("/status/test", middleware.AdminAuth(), controller.TestStatus)
apiRouter.GET("/notice", controller.GetNotice)
apiRouter.GET("/about", controller.GetAbout)
diff --git a/service/channel.go b/service/channel.go
index 82ffd771..41ca6b77 100644
--- a/service/channel.go
+++ b/service/channel.go
@@ -63,7 +63,7 @@ func ShouldDisableChannel(err *relaymodel.OpenAIError, statusCode int) bool {
return false
}
-func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError) bool {
+func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError, status int) bool {
if !common.AutomaticEnableChannelEnabled {
return false
}
@@ -73,5 +73,8 @@ func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError) bool {
if openAIErr != nil {
return false
}
+ if status != common.ChannelStatusAutoDisabled {
+ return false
+ }
return true
}
diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js
index ebfcf4df..452309c3 100644
--- a/web/src/components/ChannelsTable.js
+++ b/web/src/components/ChannelsTable.js
@@ -31,6 +31,7 @@ import {
} from '@douyinfe/semi-ui';
import EditChannel from '../pages/Channel/EditChannel';
import { IconTreeTriangleDown } from '@douyinfe/semi-icons';
+import { loadChannelModels } from './utils.js';
function renderTimestamp(timestamp) {
return <>{timestamp2string(timestamp)}>;
@@ -354,27 +355,29 @@ const ChannelsTable = () => {
};
const copySelectedChannel = async (id) => {
- const channelToCopy = channels.find(channel => String(channel.id) === String(id));
- console.log(channelToCopy)
+ const channelToCopy = channels.find(
+ (channel) => String(channel.id) === String(id),
+ );
+ console.log(channelToCopy);
channelToCopy.name += '_复制';
channelToCopy.created_time = null;
channelToCopy.balance = 0;
channelToCopy.used_quota = 0;
if (!channelToCopy) {
- showError("渠道未找到,请刷新页面后重试。");
- return;
+ showError('渠道未找到,请刷新页面后重试。');
+ return;
}
try {
- const newChannel = {...channelToCopy, id: undefined};
- const response = await API.post('/api/channel/', newChannel);
- if (response.data.success) {
- showSuccess("渠道复制成功");
- await refresh();
- } else {
- showError(response.data.message);
- }
+ const newChannel = { ...channelToCopy, id: undefined };
+ const response = await API.post('/api/channel/', newChannel);
+ if (response.data.success) {
+ showSuccess('渠道复制成功');
+ await refresh();
+ } else {
+ showError(response.data.message);
+ }
} catch (error) {
- showError("渠道复制失败: " + error.message);
+ showError('渠道复制失败: ' + error.message);
}
};
@@ -395,6 +398,7 @@ const ChannelsTable = () => {
showError(reason);
});
fetchGroups().then();
+ loadChannelModels().then();
}, []);
const manageChannel = async (id, action, record, value) => {
diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js
index 9331fa7b..0de36325 100644
--- a/web/src/components/LogsTable.js
+++ b/web/src/components/LogsTable.js
@@ -19,9 +19,15 @@ import {
Spin,
Table,
Tag,
+ Tooltip,
} from '@douyinfe/semi-ui';
import { ITEMS_PER_PAGE } from '../constants';
-import { renderNumber, renderQuota, stringToColor } from '../helpers/render';
+import {
+ renderModelPrice,
+ renderNumber,
+ renderQuota,
+ stringToColor,
+} from '../helpers/render';
import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph';
const { Header } = Layout;
@@ -292,16 +298,44 @@ const LogsTable = () => {
title: '详情',
dataIndex: 'content',
render: (text, record, index) => {
+ if (record.other === '') {
+ return (
+