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
+103 -32
View File
@@ -985,31 +985,66 @@ func modelCatalogCapabilityFilters(items []ModelCatalogItem) []ModelCatalogFilte
}
}
options := []ModelCatalogFilterOption{{Value: "all", Label: "全部", Count: len(items)}}
for _, option := range []ModelCatalogFilterOption{
{Value: "chat", Label: "对话"},
{Value: "image", Label: "图像"},
{Value: "video", Label: "视频"},
{Value: "audio", Label: "音频"},
{Value: "embedding", Label: "Embedding"},
{Value: "tools", Label: "工具调用"},
{Value: "omni", Label: "全模态"},
} {
known := map[string]bool{}
for _, option := range modelCatalogCapabilityDefinitions() {
known[option.Value] = true
if count := counts[option.Value]; count > 0 {
option.Count = count
options = append(options, option)
}
}
extraValues := make([]string, 0)
for value := range counts {
if !known[value] {
extraValues = append(extraValues, value)
}
}
sort.Slice(extraValues, func(i, j int) bool {
return capabilityLabel(extraValues[i]) < capabilityLabel(extraValues[j])
})
for _, value := range extraValues {
options = append(options, ModelCatalogFilterOption{Value: value, Label: capabilityLabel(value), Count: counts[value]})
}
return options
}
func modelCatalogCapabilityDefinitions() []ModelCatalogFilterOption {
return []ModelCatalogFilterOption{
{Value: "text_generate", Label: "文本生成"},
{Value: "chat", Label: "对话"},
{Value: "responses", Label: "Responses"},
{Value: "image_generate", Label: "图像生成"},
{Value: "image_edit", Label: "图像编辑"},
{Value: "image_analysis", Label: "图像分析"},
{Value: "video_generate", Label: "视频生成"},
{Value: "image_to_video", Label: "图生视频"},
{Value: "text_to_video", Label: "文生视频"},
{Value: "video_edit", Label: "视频编辑"},
{Value: "video_understanding", Label: "视频理解"},
{Value: "audio_generate", Label: "音频生成"},
{Value: "text_to_speech", Label: "语音合成"},
{Value: "audio_understanding", Label: "音频理解"},
{Value: "text_embedding", Label: "Embedding"},
{Value: "omni", Label: "全模态"},
{Value: "omni_video", Label: "全模态视频"},
{Value: "multimodal", Label: "多模态"},
{Value: "model_3d", Label: "3D 模型"},
{Value: "text_to_model", Label: "文生 3D"},
{Value: "image_to_model", Label: "图生 3D"},
{Value: "multiview_to_model", Label: "多视角 3D"},
{Value: "mesh_edit", Label: "3D 模型编辑"},
{Value: "tools_call", Label: "工具调用"},
{Value: "structured_output", Label: "结构化输出"},
{Value: "reasoning", Label: "推理"},
{Value: "digital_human", Label: "数字人"},
}
}
func capabilityFilterValuesForItem(item ModelCatalogItem) []string {
values := capabilityFilterValues(item.ModelType, nil)
for _, tag := range item.CapabilityTags {
switch tag {
case "工具调用":
values = append(values, "tools")
case "全模态", "多模态":
values = append(values, "omni")
if value := capabilityFilterValueForTag(tag); value != "" {
values = append(values, value)
}
}
return uniqueStrings(values)
@@ -1067,33 +1102,62 @@ func capabilityTagsForGroup(modelTypes []string, capabilities map[string]any) []
func capabilityFilterValues(modelTypes []string, capabilities map[string]any) []string {
values := []string{}
for _, modelType := range modelTypes {
normalized := strings.ToLower(strings.TrimSpace(modelType))
switch {
case normalized == "text_embedding" || normalized == "embedding":
values = append(values, "embedding")
case normalized == "tools_call":
values = append(values, "tools")
case normalized == "omni":
values = append(values, "omni")
case strings.Contains(normalized, "video"):
values = append(values, "video")
case strings.Contains(normalized, "audio") || strings.Contains(normalized, "speech"):
values = append(values, "audio")
case strings.Contains(normalized, "image"):
values = append(values, "image")
case normalized == "text_generate" || normalized == "chat" || normalized == "responses" || strings.Contains(normalized, "text"):
values = append(values, "chat")
if value := canonicalCapabilityFilterValue(modelType); value != "" {
values = append(values, value)
}
}
if boolAnyValue(capabilities["tools_call"]) || boolAnyValue(capabilities["toolCall"]) || boolAnyValue(capabilities["function_call"]) {
values = append(values, "tools")
values = append(values, "tools_call")
}
if boolAnyValue(capabilities["multimodal"]) {
values = append(values, "omni")
values = append(values, "multimodal")
}
return uniqueStrings(values)
}
func canonicalCapabilityFilterValue(value string) string {
normalized := strings.ReplaceAll(strings.ToLower(strings.TrimSpace(value)), "-", "_")
switch normalized {
case "embedding":
return "text_embedding"
case "model":
return "model_3d"
default:
return normalized
}
}
func capabilityFilterValueForTag(tag string) string {
switch tag {
case "工具调用":
return "tools_call"
case "全模态":
return "omni"
case "全模态视频":
return "omni_video"
case "多模态":
return "multimodal"
case "推理":
return "reasoning"
case "结构化输出":
return "structured_output"
case "数字人":
return "digital_human"
case "3D 模型":
return "model_3d"
case "文生 3D":
return "text_to_model"
case "图生 3D":
return "image_to_model"
case "多视角 3D":
return "multiview_to_model"
case "3D 模型编辑":
return "mesh_edit"
default:
return ""
}
}
func capabilityLabel(value string) string {
labels := map[string]string{
"text_generate": "文本生成",
@@ -1115,9 +1179,16 @@ func capabilityLabel(value string) string {
"tools_call": "工具调用",
"omni": "全模态",
"omni_video": "全模态视频",
"multimodal": "多模态",
"reasoning": "推理",
"structured_output": "结构化输出",
"digital_human": "数字人",
"model": "3D 模型",
"model_3d": "3D 模型",
"text_to_model": "文生 3D",
"image_to_model": "图生 3D",
"multiview_to_model": "多视角 3D",
"mesh_edit": "3D 模型编辑",
}
if label, ok := labels[value]; ok {
return label
@@ -106,8 +106,8 @@ func TestBuildModelCatalogAggregatesSources(t *testing.T) {
if !hasFilterCount(response.Filters.Providers, "volces", 1) || !hasFilterCount(response.Filters.Providers, "gemini", 1) {
t.Fatalf("expected provider filters to count merged model for each provider: %+v", response.Filters.Providers)
}
if !hasFilterCount(response.Filters.Capabilities, "image", 1) {
t.Fatalf("expected image capability filter: %+v", response.Filters.Capabilities)
if !hasFilterCount(response.Filters.Capabilities, "image_generate", 1) {
t.Fatalf("expected image generation capability filter: %+v", response.Filters.Capabilities)
}
if got := item.Pricing.Lines[0]; got != "图像:1K 10 / 2K 20" {
t.Fatalf("unexpected pricing line %q", got)
@@ -168,6 +168,58 @@ func TestBuildModelCatalogUsesBaseModelProviderForProviderFilters(t *testing.T)
}
}
func TestBuildModelCatalogIncludes3DModelCapabilityFilter(t *testing.T) {
models := []store.PlatformModel{
{
ID: "tripo-image-to-model",
PlatformID: "platform-tripo",
ModelName: "tripo-3d",
ModelAlias: "Tripo 3D",
ModelType: store.StringList{"image_to_model"},
DisplayName: "Tripo 3D",
Enabled: true,
},
}
platforms := []store.Platform{
{ID: "platform-tripo", Provider: "tripo3d", Name: "Tripo3D", Status: "enabled"},
}
response := buildModelCatalog(models, platforms, nil, nil, nil, nil, nil)
if !hasFilterCount(response.Filters.Capabilities, "image_to_model", 1) {
t.Fatalf("expected 3D model capability filter, got %+v", response.Filters.Capabilities)
}
if hasFilterCount(response.Filters.Capabilities, "image_generate", 1) {
t.Fatalf("did not expect image_to_model to be classified as image: %+v", response.Filters.Capabilities)
}
if len(response.Items) != 1 || len(response.Items[0].CapabilityTags) != 1 || response.Items[0].CapabilityTags[0] != "图生 3D" {
t.Fatalf("expected image-to-model capability tag, got %+v", response.Items)
}
}
func TestBuildModelCatalogKeepsDistinctCapabilityFilters(t *testing.T) {
models := []store.PlatformModel{
{ID: "image-generate", PlatformID: "platform-a", ModelName: "image-generate", ModelAlias: "Image Generate", ModelType: store.StringList{"image_generate"}, DisplayName: "Image Generate", Enabled: true},
{ID: "image-analysis", PlatformID: "platform-a", ModelName: "image-analysis", ModelAlias: "Image Analysis", ModelType: store.StringList{"image_analysis"}, DisplayName: "Image Analysis", Enabled: true},
{ID: "video-generate", PlatformID: "platform-a", ModelName: "video-generate", ModelAlias: "Video Generate", ModelType: store.StringList{"video_generate"}, DisplayName: "Video Generate", Enabled: true},
{ID: "video-understanding", PlatformID: "platform-a", ModelName: "video-understanding", ModelAlias: "Video Understanding", ModelType: store.StringList{"video_understanding"}, DisplayName: "Video Understanding", Enabled: true},
{ID: "omni", PlatformID: "platform-a", ModelName: "omni", ModelAlias: "Omni", ModelType: store.StringList{"omni"}, DisplayName: "Omni", Enabled: true},
{ID: "omni-video", PlatformID: "platform-a", ModelName: "omni-video", ModelAlias: "Omni Video", ModelType: store.StringList{"omni_video"}, DisplayName: "Omni Video", Enabled: true},
}
platforms := []store.Platform{
{ID: "platform-a", Provider: "test", Name: "测试平台", Status: "enabled"},
}
response := buildModelCatalog(models, platforms, nil, nil, nil, nil, nil)
for _, value := range []string{"image_generate", "image_analysis", "video_generate", "video_understanding", "omni", "omni_video"} {
if !hasFilterCount(response.Filters.Capabilities, value, 1) {
t.Fatalf("expected distinct capability filter %s, got %+v", value, response.Filters.Capabilities)
}
}
if hasFilterCount(response.Filters.Capabilities, "image", 2) || hasFilterCount(response.Filters.Capabilities, "video", 2) {
t.Fatalf("did not expect broad image/video filters: %+v", response.Filters.Capabilities)
}
}
func TestBuildModelCatalogOnlyUsesEnabledPlatformModels(t *testing.T) {
models := []store.PlatformModel{
{