fix(model-catalog): refine summary capability filters

This commit is contained in:
2026-05-24 23:38:31 +08:00
parent 355d8cad74
commit 3d23918542
4 changed files with 251 additions and 55 deletions
+35 -13
View File
@@ -60,9 +60,15 @@ export function ModelsPage(props: { data: ConsoleData }) {
<main className="modelsContent">
<div className="modelsToolbar">
<div className="searchField modelHeaderSearch">
<div className="searchField modelHeaderSearch" role="search">
<Search size={16} />
<Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="模型名称、能力或厂商" />
<Input
aria-label="搜索模型"
size="sm"
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="模型名称、能力或厂商"
/>
</div>
</div>
@@ -235,21 +241,37 @@ function ModelIcon(props: { iconPath?: string; label: string }) {
function modelMatchesCapability(model: ModelCatalogItem, capability: string) {
if (model.modelType.some((type) => modelTypeMatchesCapability(type, capability))) return true;
if (capability === 'tools') return model.capabilityTags.includes('工具调用');
if (capability === 'omni') return model.capabilityTags.includes('全模态');
if (model.capabilityTags.some((tag) => capabilityValueForTag(tag) === capability)) return true;
return false;
}
function modelTypeMatchesCapability(value: string, capability: string) {
const type = value.trim().toLowerCase();
if (capability === 'chat') return type === 'text_generate' || type === 'chat' || type === 'responses' || type.includes('text');
if (capability === 'image') return type.includes('image') && !type.includes('video');
if (capability === 'video') return type.includes('video');
if (capability === 'audio') return type.includes('audio') || type.includes('speech');
if (capability === 'embedding') return type === 'text_embedding' || type === 'embedding';
if (capability === 'tools') return type === 'tools_call';
if (capability === 'omni') return type === 'omni' || type === 'omni_video';
return type === capability;
return canonicalCapabilityValue(value) === capability;
}
function canonicalCapabilityValue(value: string) {
const type = value.trim().toLowerCase().replaceAll('-', '_');
if (type === 'embedding') return 'text_embedding';
if (type === 'model') return 'model_3d';
return type;
}
function capabilityValueForTag(tag: string) {
const values: Record<string, string> = {
: 'tools_call',
: 'omni',
: 'omni_video',
: 'multimodal',
: 'reasoning',
: 'structured_output',
: 'digital_human',
'3D 模型': 'model_3d',
'文生 3D': 'text_to_model',
'图生 3D': 'image_to_model',
'多视角 3D': 'multiview_to_model',
'3D 模型编辑': 'mesh_edit',
};
return values[tag] ?? '';
}
function providerInitials(label: string) {
+59 -8
View File
@@ -26,8 +26,8 @@
.modelsPage {
display: grid;
grid-template-columns: 246px minmax(0, 1fr);
gap: 22px;
grid-template-columns: 282px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
@@ -35,10 +35,38 @@
position: sticky;
top: 88px;
display: grid;
max-height: calc(100vh - 104px);
gap: 22px;
padding-left: 14px;
padding-right: 18px;
padding-bottom: 8px;
border-right: 1px solid var(--border);
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-gutter: stable;
scrollbar-width: thin;
scrollbar-color: transparent transparent;
}
.modelFilters:hover {
scrollbar-color: rgba(113, 113, 122, 0.36) transparent;
}
.modelFilters::-webkit-scrollbar {
width: 4px;
}
.modelFilters::-webkit-scrollbar-track {
background: transparent;
}
.modelFilters::-webkit-scrollbar-thumb {
border-radius: 999px;
background: transparent;
}
.modelFilters:hover::-webkit-scrollbar-thumb {
background: rgba(113, 113, 122, 0.32);
}
.filterGroup {
@@ -124,33 +152,53 @@
align-items: center;
justify-content: flex-end;
gap: 16px;
min-height: 36px;
}
.searchField {
display: grid;
width: min(420px, 100%);
grid-template-columns: 34px minmax(0, 1fr);
width: min(360px, 100%);
min-height: 36px;
grid-template-columns: 36px minmax(0, 1fr);
align-items: center;
border: 1px solid var(--border);
border-radius: 999px;
background: #fff;
background: var(--surface);
color: var(--muted-foreground);
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
transition: border-color 0.16s ease, box-shadow 0.16s ease, color 0.16s ease;
}
.searchField.modelHeaderSearch {
width: min(520px, 42vw);
width: min(360px, 34vw);
}
.searchField:hover,
.searchField:focus-within {
border-color: var(--ring);
color: var(--text-normal);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 24%, transparent);
}
.searchField svg {
margin-left: 12px;
margin-left: 13px;
transition: color 0.16s ease;
}
.searchField .shInput {
min-height: 34px;
padding-left: 0;
padding-right: 12px;
border: 0;
border-radius: 999px;
background: transparent;
box-shadow: none;
}
.searchField .shInput::placeholder {
color: var(--text-faint);
}
.providerLogo,
.modelIcon {
display: grid;
@@ -177,7 +225,7 @@
.modelCards {
display: grid;
grid-template-columns: repeat(3, minmax(260px, 1fr));
gap: 10px;
gap: 8px;
}
.modelCard .shCardContent {
@@ -2123,9 +2171,12 @@
}
.modelFilters {
max-height: none;
padding-left: 0;
padding-right: 0;
border-right: 0;
overflow: visible;
scrollbar-width: auto;
}
.modelCards,