mirror of
https://github.com/linux-do/new-api.git
synced 2025-12-26 16:45:57 +08:00
feat: pricing page support multi groups #487
This commit is contained in:
@@ -7,18 +7,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetPricing(c *gin.Context) {
|
func GetPricing(c *gin.Context) {
|
||||||
userId := c.GetInt("id")
|
pricing := model.GetPricing()
|
||||||
// if no login, get default group ratio
|
|
||||||
groupRatio := common.GetGroupRatio("default")
|
|
||||||
group, err := model.CacheGetUserGroup(userId)
|
|
||||||
if err == nil {
|
|
||||||
groupRatio = common.GetGroupRatio(group)
|
|
||||||
}
|
|
||||||
pricing := model.GetPricing(group)
|
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"success": true,
|
"success": true,
|
||||||
"data": pricing,
|
"data": pricing,
|
||||||
"group_ratio": groupRatio,
|
"group_ratio": common.GroupRatio,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ func GetEnabledModels() []string {
|
|||||||
return models
|
return models
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAllEnableAbilities() []Ability {
|
||||||
|
var abilities []Ability
|
||||||
|
DB.Find(&abilities, "enabled = ?", true)
|
||||||
|
return abilities
|
||||||
|
}
|
||||||
|
|
||||||
func getPriority(group string, model string, retry int) (int, error) {
|
func getPriority(group string, model string, retry int) (int, error) {
|
||||||
groupCol := "`group`"
|
groupCol := "`group`"
|
||||||
trueVal := "1"
|
trueVal := "1"
|
||||||
|
|||||||
@@ -7,14 +7,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Pricing struct {
|
type Pricing struct {
|
||||||
Available bool `json:"available"`
|
|
||||||
ModelName string `json:"model_name"`
|
ModelName string `json:"model_name"`
|
||||||
QuotaType int `json:"quota_type"`
|
QuotaType int `json:"quota_type"`
|
||||||
ModelRatio float64 `json:"model_ratio"`
|
ModelRatio float64 `json:"model_ratio"`
|
||||||
ModelPrice float64 `json:"model_price"`
|
ModelPrice float64 `json:"model_price"`
|
||||||
OwnerBy string `json:"owner_by"`
|
OwnerBy string `json:"owner_by"`
|
||||||
CompletionRatio float64 `json:"completion_ratio"`
|
CompletionRatio float64 `json:"completion_ratio"`
|
||||||
EnableGroup []string `json:"enable_group,omitempty"`
|
EnableGroup []string `json:"enable_groups,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -23,40 +22,47 @@ var (
|
|||||||
updatePricingLock sync.Mutex
|
updatePricingLock sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPricing(group string) []Pricing {
|
func GetPricing() []Pricing {
|
||||||
updatePricingLock.Lock()
|
updatePricingLock.Lock()
|
||||||
defer updatePricingLock.Unlock()
|
defer updatePricingLock.Unlock()
|
||||||
|
|
||||||
if time.Since(lastGetPricingTime) > time.Minute*1 || len(pricingMap) == 0 {
|
if time.Since(lastGetPricingTime) > time.Minute*1 || len(pricingMap) == 0 {
|
||||||
updatePricing()
|
updatePricing()
|
||||||
}
|
}
|
||||||
if group != "" {
|
//if group != "" {
|
||||||
userPricingMap := make([]Pricing, 0)
|
// userPricingMap := make([]Pricing, 0)
|
||||||
models := GetGroupModels(group)
|
// models := GetGroupModels(group)
|
||||||
for _, pricing := range pricingMap {
|
// for _, pricing := range pricingMap {
|
||||||
if !common.StringsContains(models, pricing.ModelName) {
|
// if !common.StringsContains(models, pricing.ModelName) {
|
||||||
pricing.Available = false
|
// pricing.Available = false
|
||||||
}
|
// }
|
||||||
userPricingMap = append(userPricingMap, pricing)
|
// userPricingMap = append(userPricingMap, pricing)
|
||||||
}
|
// }
|
||||||
return userPricingMap
|
// return userPricingMap
|
||||||
}
|
//}
|
||||||
return pricingMap
|
return pricingMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePricing() {
|
func updatePricing() {
|
||||||
//modelRatios := common.GetModelRatios()
|
//modelRatios := common.GetModelRatios()
|
||||||
enabledModels := GetEnabledModels()
|
enableAbilities := GetAllEnableAbilities()
|
||||||
allModels := make(map[string]int)
|
modelGroupsMap := make(map[string][]string)
|
||||||
for i, model := range enabledModels {
|
for _, ability := range enableAbilities {
|
||||||
allModels[model] = i
|
groups := modelGroupsMap[ability.Model]
|
||||||
|
if groups == nil {
|
||||||
|
groups = make([]string, 0)
|
||||||
|
}
|
||||||
|
if !common.StringsContains(groups, ability.Group) {
|
||||||
|
groups = append(groups, ability.Group)
|
||||||
|
}
|
||||||
|
modelGroupsMap[ability.Model] = groups
|
||||||
}
|
}
|
||||||
|
|
||||||
pricingMap = make([]Pricing, 0)
|
pricingMap = make([]Pricing, 0)
|
||||||
for model, _ := range allModels {
|
for model, groups := range modelGroupsMap {
|
||||||
pricing := Pricing{
|
pricing := Pricing{
|
||||||
Available: true,
|
|
||||||
ModelName: model,
|
ModelName: model,
|
||||||
|
EnableGroup: groups,
|
||||||
}
|
}
|
||||||
modelPrice, findPrice := common.GetModelPrice(model, false)
|
modelPrice, findPrice := common.GetModelPrice(model, false)
|
||||||
if findPrice {
|
if findPrice {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ let buttons = [
|
|||||||
text: '首页',
|
text: '首页',
|
||||||
itemKey: 'home',
|
itemKey: 'home',
|
||||||
to: '/',
|
to: '/',
|
||||||
icon: <IconHomeStroked />,
|
// icon: <IconHomeStroked />,
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// text: '模型价格',
|
// text: '模型价格',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useContext, useEffect, useRef, useMemo, useState } from 'react';
|
import React, { useContext, useEffect, useRef, useMemo, useState } from 'react';
|
||||||
import { API, copy, showError, showSuccess } from '../helpers';
|
import { API, copy, showError, showInfo, showSuccess } from '../helpers';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Banner,
|
Banner,
|
||||||
@@ -87,6 +87,7 @@ const ModelPricing = () => {
|
|||||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
const [modalImageUrl, setModalImageUrl] = useState('');
|
const [modalImageUrl, setModalImageUrl] = useState('');
|
||||||
const [isModalOpenurl, setIsModalOpenurl] = useState(false);
|
const [isModalOpenurl, setIsModalOpenurl] = useState(false);
|
||||||
|
const [selectedGroup, setSelectedGroup] = useState('default');
|
||||||
|
|
||||||
const rowSelection = useMemo(
|
const rowSelection = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -120,7 +121,8 @@ const ModelPricing = () => {
|
|||||||
title: '可用性',
|
title: '可用性',
|
||||||
dataIndex: 'available',
|
dataIndex: 'available',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return renderAvailable(text);
|
// if record.enable_groups contains selectedGroup, then available is true
|
||||||
|
return renderAvailable(record.enable_groups.includes(selectedGroup));
|
||||||
},
|
},
|
||||||
sorter: (a, b) => a.available - b.available,
|
sorter: (a, b) => a.available - b.available,
|
||||||
},
|
},
|
||||||
@@ -166,6 +168,43 @@ const ModelPricing = () => {
|
|||||||
},
|
},
|
||||||
sorter: (a, b) => a.quota_type - b.quota_type,
|
sorter: (a, b) => a.quota_type - b.quota_type,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '可用分组',
|
||||||
|
dataIndex: 'enable_groups',
|
||||||
|
render: (text, record, index) => {
|
||||||
|
// enable_groups is a string array
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{text.map((group) => {
|
||||||
|
if (group === selectedGroup) {
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
color='blue'
|
||||||
|
size='large'
|
||||||
|
prefixIcon={<IconVerify />}
|
||||||
|
>
|
||||||
|
{group}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
color='blue'
|
||||||
|
size='large'
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedGroup(group);
|
||||||
|
showInfo('当前查看的分组为:' + group + ',倍率为:' + groupRatio[group]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{group}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: () => (
|
title: () => (
|
||||||
<span style={{'display':'flex','alignItems':'center'}}>
|
<span style={{'display':'flex','alignItems':'center'}}>
|
||||||
@@ -201,6 +240,8 @@ const ModelPricing = () => {
|
|||||||
<Text>模型:{record.quota_type === 0 ? text : '无'}</Text>
|
<Text>模型:{record.quota_type === 0 ? text : '无'}</Text>
|
||||||
<br />
|
<br />
|
||||||
<Text>补全:{record.quota_type === 0 ? completionRatio : '无'}</Text>
|
<Text>补全:{record.quota_type === 0 ? completionRatio : '无'}</Text>
|
||||||
|
<br />
|
||||||
|
<Text>分组:{groupRatio[selectedGroup]}</Text>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
return <div>{content}</div>;
|
return <div>{content}</div>;
|
||||||
@@ -213,11 +254,11 @@ const ModelPricing = () => {
|
|||||||
let content = text;
|
let content = text;
|
||||||
if (record.quota_type === 0) {
|
if (record.quota_type === 0) {
|
||||||
// 这里的 *2 是因为 1倍率=0.002刀,请勿删除
|
// 这里的 *2 是因为 1倍率=0.002刀,请勿删除
|
||||||
let inputRatioPrice = record.model_ratio * 2 * record.group_ratio;
|
let inputRatioPrice = record.model_ratio * 2 * groupRatio[selectedGroup];
|
||||||
let completionRatioPrice =
|
let completionRatioPrice =
|
||||||
record.model_ratio *
|
record.model_ratio *
|
||||||
record.completion_ratio * 2 *
|
record.completion_ratio * 2 *
|
||||||
record.group_ratio;
|
groupRatio[selectedGroup];
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
<Text>提示 ${inputRatioPrice} / 1M tokens</Text>
|
<Text>提示 ${inputRatioPrice} / 1M tokens</Text>
|
||||||
@@ -226,7 +267,7 @@ const ModelPricing = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let price = parseFloat(text) * record.group_ratio;
|
let price = parseFloat(text) * groupRatio[selectedGroup];
|
||||||
content = <>模型价格:${price}</>;
|
content = <>模型价格:${price}</>;
|
||||||
}
|
}
|
||||||
return <div>{content}</div>;
|
return <div>{content}</div>;
|
||||||
@@ -237,12 +278,12 @@ const ModelPricing = () => {
|
|||||||
const [models, setModels] = useState([]);
|
const [models, setModels] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [userState, userDispatch] = useContext(UserContext);
|
const [userState, userDispatch] = useContext(UserContext);
|
||||||
const [groupRatio, setGroupRatio] = useState(1);
|
const [groupRatio, setGroupRatio] = useState({});
|
||||||
|
|
||||||
const setModelsFormat = (models, groupRatio) => {
|
const setModelsFormat = (models, groupRatio) => {
|
||||||
for (let i = 0; i < models.length; i++) {
|
for (let i = 0; i < models.length; i++) {
|
||||||
models[i].key = models[i].model_name;
|
models[i].key = models[i].model_name;
|
||||||
models[i].group_ratio = groupRatio;
|
models[i].group_ratio = groupRatio[models[i].model_name];
|
||||||
}
|
}
|
||||||
// sort by quota_type
|
// sort by quota_type
|
||||||
models.sort((a, b) => {
|
models.sort((a, b) => {
|
||||||
@@ -275,6 +316,7 @@ const ModelPricing = () => {
|
|||||||
const { success, message, data, group_ratio } = res.data;
|
const { success, message, data, group_ratio } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setGroupRatio(group_ratio);
|
setGroupRatio(group_ratio);
|
||||||
|
setSelectedGroup(userState.user ? userState.user.group : 'default')
|
||||||
setModelsFormat(data, group_ratio);
|
setModelsFormat(data, group_ratio);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@@ -307,14 +349,14 @@ const ModelPricing = () => {
|
|||||||
type="success"
|
type="success"
|
||||||
fullMode={false}
|
fullMode={false}
|
||||||
closeIcon="null"
|
closeIcon="null"
|
||||||
description={`您的分组为:${userState.user.group},分组倍率为:${groupRatio}`}
|
description={`您的默认分组为:${userState.user.group},分组倍率为:${groupRatio[userState.user.group]}`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Banner
|
<Banner
|
||||||
type='warning'
|
type='warning'
|
||||||
fullMode={false}
|
fullMode={false}
|
||||||
closeIcon="null"
|
closeIcon="null"
|
||||||
description={`您还未登陆,显示的价格为默认分组倍率: ${groupRatio}`}
|
description={`您还未登陆,显示的价格为默认分组倍率: ${groupRatio['default']}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<br/>
|
<br/>
|
||||||
|
|||||||
Reference in New Issue
Block a user