feat: add model name matching rules with priority-based lookup

Add flexible model name matching system to support different matching patterns:

Backend changes:
- Add `name_rule` field to Model struct with 4 matching types:
  * 0: Exact match (default)
  * 1: Prefix match
  * 2: Contains match
  * 3: Suffix match
- Implement `FindModelByNameWithRule` function with priority order:
  exact > prefix > suffix > contains
- Add database migration for new `name_rule` column

Frontend changes:
- Add "Match Type" column in models table with colored tags
- Add name rule selector in create/edit modal with validation
- Auto-set exact match and disable selection for preconfigured models
- Add explanatory text showing priority order
- Support i18n for all new UI elements

This enables users to define model patterns once and reuse configurations
across similar models, reducing repetitive setup while maintaining exact
match priority for specific overrides.

Closes: #[issue-number]
This commit is contained in:
t0ng7u
2025-08-04 16:01:56 +08:00
parent 5e70274003
commit fc69f4f757
3 changed files with 100 additions and 0 deletions

View File

@@ -184,6 +184,23 @@ const renderOperations = (text, record, setEditingModel, setShowEdit, manageMode
);
};
// 名称匹配类型渲染
const renderNameRule = (rule, t) => {
const map = {
0: { color: 'green', label: t('精确') },
1: { color: 'blue', label: t('前缀') },
2: { color: 'orange', label: t('包含') },
3: { color: 'purple', label: t('后缀') },
};
const cfg = map[rule];
if (!cfg) return '-';
return (
<Tag color={cfg.color} size="small" shape='circle'>
{cfg.label}
</Tag>
);
};
export const getModelsColumns = ({
t,
manageModel,
@@ -202,6 +219,11 @@ export const getModelsColumns = ({
</Text>
),
},
{
title: t('匹配类型'),
dataIndex: 'name_rule',
render: (val) => renderNameRule(val, t),
},
{
title: t('描述'),
dataIndex: 'description',

View File

@@ -40,6 +40,13 @@ import { API, showError, showSuccess } from '../../../../helpers';
import { useTranslation } from 'react-i18next';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
const nameRuleOptions = [
{ label: '精确名称匹配', value: 0 },
{ label: '前缀名称匹配', value: 1 },
{ label: '包含名称匹配', value: 2 },
{ label: '后缀名称匹配', value: 3 },
];
const endpointOptions = [
{ label: 'OpenAI', value: 'openai' },
{ label: 'Anthropic', value: 'anthropic' },
@@ -111,6 +118,7 @@ const EditModelModal = (props) => {
vendor: '',
vendor_icon: '',
endpoints: [],
name_rule: props.editingModel?.model_name ? 0 : undefined, // 通过未配置模型过来的固定为精确匹配
status: true,
});
@@ -301,6 +309,20 @@ const EditModelModal = (props) => {
showClear
/>
</Col>
<Col span={24}>
<Form.Select
field='name_rule'
label={t('名称匹配类型')}
placeholder={t('请选择名称匹配类型')}
optionList={nameRuleOptions.map(o => ({ label: t(o.label), value: o.value }))}
rules={[{ required: true, message: t('请选择名称匹配类型') }]}
disabled={!!props.editingModel?.model_name} // 通过未配置模型过来的禁用选择
style={{ width: '100%' }}
extraText={t('根据模型名称和匹配规则查找模型元数据,优先级:精确 > 前缀 > 后缀 > 包含')}
/>
</Col>
<Col span={24}>
<Form.TextArea
field='description'