🔍 feat: Show matched model names & counts for non-exact model rules
Summary
-------
1. **Backend**
• `model/model_meta.go`
– Add `MatchedModels []string` and `MatchedCount int` (ignored by GORM) to expose matching details in API responses.
• `controller/model_meta.go`
– When processing prefix/suffix/contains rules in `fillModelExtra`, collect every matched model name, fill `MatchedModels`, and calculate `MatchedCount`.
2. **Frontend**
• `web/src/components/table/models/ModelsColumnDefs.js`
– Import `Tooltip`.
– Enhance `renderNameRule` to:
– Display tag text like “前缀 5个模型” for non-exact rules.
– Show a tooltip listing all matched model names on hover.
Impact
------
Users now see the total number of concrete models aggregated under each prefix/suffix/contains rule and can inspect the exact list via tooltip, improving transparency in model management.
This commit is contained in:
@@ -183,6 +183,9 @@ func fillModelExtra(m *model.Model) {
|
|||||||
// 非精确匹配:计算并集
|
// 非精确匹配:计算并集
|
||||||
pricings := model.GetPricing()
|
pricings := model.GetPricing()
|
||||||
|
|
||||||
|
// 匹配到的模型名称集合
|
||||||
|
matchedNames := make([]string, 0)
|
||||||
|
|
||||||
// 端点去重集合
|
// 端点去重集合
|
||||||
endpointSet := make(map[constant.EndpointType]struct{})
|
endpointSet := make(map[constant.EndpointType]struct{})
|
||||||
// 已绑定渠道去重集合
|
// 已绑定渠道去重集合
|
||||||
@@ -206,6 +209,9 @@ func fillModelExtra(m *model.Model) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录匹配到的模型名称
|
||||||
|
matchedNames = append(matchedNames, p.ModelName)
|
||||||
|
|
||||||
// 收集端点
|
// 收集端点
|
||||||
for _, et := range p.SupportedEndpointTypes {
|
for _, et := range p.SupportedEndpointTypes {
|
||||||
endpointSet[et] = struct{}{}
|
endpointSet[et] = struct{}{}
|
||||||
@@ -265,4 +271,8 @@ func fillModelExtra(m *model.Model) {
|
|||||||
} else {
|
} else {
|
||||||
m.QuotaType = -1
|
m.QuotaType = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置匹配信息
|
||||||
|
m.MatchedModels = matchedNames
|
||||||
|
m.MatchedCount = len(matchedNames)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ type Model struct {
|
|||||||
EnableGroups []string `json:"enable_groups,omitempty" gorm:"-"`
|
EnableGroups []string `json:"enable_groups,omitempty" gorm:"-"`
|
||||||
QuotaType int `json:"quota_type" gorm:"-"`
|
QuotaType int `json:"quota_type" gorm:"-"`
|
||||||
NameRule int `json:"name_rule" gorm:"default:0"`
|
NameRule int `json:"name_rule" gorm:"default:0"`
|
||||||
|
|
||||||
|
MatchedModels []string `json:"matched_models,omitempty" gorm:"-"`
|
||||||
|
MatchedCount int `json:"matched_count,omitempty" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert 创建新的模型元数据记录
|
// Insert 创建新的模型元数据记录
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button, Space, Tag, Typography, Modal } from '@douyinfe/semi-ui';
|
import { Button, Space, Tag, Typography, Modal, Tooltip } from '@douyinfe/semi-ui';
|
||||||
import {
|
import {
|
||||||
timestamp2string,
|
timestamp2string,
|
||||||
getLobeHubIcon,
|
getLobeHubIcon,
|
||||||
@@ -208,8 +208,8 @@ const renderOperations = (text, record, setEditingModel, setShowEdit, manageMode
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 名称匹配类型渲染
|
// 名称匹配类型渲染(带匹配数量 Tooltip)
|
||||||
const renderNameRule = (rule, t) => {
|
const renderNameRule = (rule, record, t) => {
|
||||||
const map = {
|
const map = {
|
||||||
0: { color: 'green', label: t('精确') },
|
0: { color: 'green', label: t('精确') },
|
||||||
1: { color: 'blue', label: t('前缀') },
|
1: { color: 'blue', label: t('前缀') },
|
||||||
@@ -218,11 +218,27 @@ const renderNameRule = (rule, t) => {
|
|||||||
};
|
};
|
||||||
const cfg = map[rule];
|
const cfg = map[rule];
|
||||||
if (!cfg) return '-';
|
if (!cfg) return '-';
|
||||||
return (
|
|
||||||
|
let label = cfg.label;
|
||||||
|
if (rule !== 0 && record.matched_count) {
|
||||||
|
label = `${cfg.label} ${record.matched_count}${t('个模型')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagElement = (
|
||||||
<Tag color={cfg.color} size="small" shape='circle'>
|
<Tag color={cfg.color} size="small" shape='circle'>
|
||||||
{cfg.label}
|
{label}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (rule === 0 || !record.matched_models || record.matched_models.length === 0) {
|
||||||
|
return tagElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip content={record.matched_models.join(', ')} showArrow>
|
||||||
|
{tagElement}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getModelsColumns = ({
|
export const getModelsColumns = ({
|
||||||
@@ -253,7 +269,7 @@ export const getModelsColumns = ({
|
|||||||
{
|
{
|
||||||
title: t('匹配类型'),
|
title: t('匹配类型'),
|
||||||
dataIndex: 'name_rule',
|
dataIndex: 'name_rule',
|
||||||
render: (val) => renderNameRule(val, t),
|
render: (val, record) => renderNameRule(val, record, t),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('描述'),
|
title: t('描述'),
|
||||||
|
|||||||
Reference in New Issue
Block a user