Commit c504c9af by t0ng7u

💰 feat: Add model billing type (`quota_type`) support across backend & frontend

Summary
• Backend
  1. model/model_meta.go
     – Added `QuotaType` field to `Model` struct (JSON only, gorm `-`).
  2. model/model_groups.go
     – Implemented `GetModelQuotaType(modelName)` leveraging cached pricing map.
  3. controller/model_meta.go
     – Enhanced `fillModelExtra` to populate `QuotaType` using new helper.

• Frontend
  1. web/src/components/table/models/ModelsColumnDefs.js
     – Introduced `renderQuotaType` helper that visualises billing mode with coloured tags (`teal = per-call`, `violet = per-token`).
     – Added “计费类型” column (`quota_type`) to models table.

Why
Providing the billing mode alongside existing pricing/group information gives administrators instant visibility into whether each model is priced per call or per token, aligning UI with new backend metadata.

Notes
No database migration required – `quota_type` is transient, delivered via API. Frontend labels/colours can be adjusted via i18n or theme tokens if necessary.
parent 88168961
......@@ -147,5 +147,7 @@ func fillModelExtra(m *model.Model) {
}
// 填充启用分组
m.EnableGroups = model.GetModelEnableGroups(m.ModelName)
// 填充计费类型
m.QuotaType = model.GetModelQuotaType(m.ModelName)
}
......@@ -10,3 +10,15 @@ func GetModelEnableGroups(modelName string) []string {
}
return make([]string, 0)
}
// GetModelQuotaType 返回指定模型的计费类型(quota_type)。
// 复用缓存的定价映射,避免额外数据库查询。
// 如果未找到对应模型,默认返回 0。
func GetModelQuotaType(modelName string) int {
for _, p := range GetPricing() {
if p.ModelName == modelName {
return p.QuotaType
}
}
return 0
}
......@@ -39,6 +39,7 @@ type Model struct {
BoundChannels []BoundChannel `json:"bound_channels,omitempty" gorm:"-"`
EnableGroups []string `json:"enable_groups,omitempty" gorm:"-"`
QuotaType int `json:"quota_type" gorm:"-"`
}
// Insert 创建新的模型元数据记录
......
......@@ -98,6 +98,25 @@ const renderEndpoints = (text) => {
});
};
// Render quota type
const renderQuotaType = (qt, t) => {
if (qt === 1) {
return (
<Tag color='teal' size='small' shape='circle'>
{t('按次计费')}
</Tag>
);
}
if (qt === 0) {
return (
<Tag color='violet' size='small' shape='circle'>
{t('按量计费')}
</Tag>
);
}
return qt ?? '-';
};
// Render bound channels
const renderBoundChannels = (channels) => {
if (!channels || channels.length === 0) return '-';
......@@ -214,6 +233,11 @@ export const getModelsColumns = ({
render: renderGroups,
},
{
title: t('计费类型'),
dataIndex: 'quota_type',
render: (qt) => renderQuotaType(qt, t),
},
{
title: t('创建时间'),
dataIndex: 'created_time',
render: (text, record, index) => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment