feat(catalog): 统一模型调用身份并梳理生命周期

新增官方调用名、供应商真实名、显示名和兼容别名的独立契约,调整路由、目录聚合、管理端与 OpenAPI。

增加 Gemini、Qwen、DeepSeek、Claude 和 MiniMax 生命周期迁移、别名观测及引用保护,并补充数据库与聚合测试。
This commit is contained in:
2026-07-22 20:23:36 +08:00
parent e3f6c0fa3e
commit 8d86c4b3b3
20 changed files with 1282 additions and 284 deletions
+36 -32
View File
@@ -62,17 +62,19 @@ type ModelCatalogProviderSummary struct {
}
type ModelCatalogSource struct {
ID string `json:"id"`
PlatformID string `json:"platformId,omitempty"`
PlatformName string `json:"platformName,omitempty"`
ProviderKey string `json:"providerKey"`
ProviderName string `json:"providerName"`
ModelName string `json:"modelName"`
ModelAlias string `json:"modelAlias,omitempty"`
DisplayName string `json:"displayName"`
ModelType []string `json:"modelType"`
Enabled bool `json:"enabled"`
RateLimits ModelCatalogRateLimits `json:"rateLimits"`
ID string `json:"id"`
PlatformID string `json:"platformId,omitempty"`
PlatformName string `json:"platformName,omitempty"`
ProviderKey string `json:"providerKey"`
ProviderName string `json:"providerName"`
ModelName string `json:"modelName"`
ProviderModelName string `json:"providerModelName,omitempty"`
ModelAlias string `json:"modelAlias,omitempty"`
LegacyAliases []string `json:"legacyAliases,omitempty"`
DisplayName string `json:"displayName"`
ModelType []string `json:"modelType"`
Enabled bool `json:"enabled"`
RateLimits ModelCatalogRateLimits `json:"rateLimits"`
}
type ModelCatalogText struct {
@@ -202,10 +204,10 @@ func buildModelCatalog(
sourceCount := 0
for _, model := range models {
platform := platformMap[model.PlatformID]
if !catalogSourceEnabled(model, platform) {
baseModel := baseModelMap[model.BaseModelID]
if !catalogSourceEnabled(model, platform, baseModel) {
continue
}
baseModel := baseModelMap[model.BaseModelID]
providerKey := modelProviderKey(model, baseModel, platform)
provider := providerMap[providerKey]
providerName := firstNonEmpty(
@@ -232,14 +234,14 @@ func buildModelCatalog(
rateLimits: effectiveModelRateLimits(model, platform, runtimePolicyMap),
}
key := catalogAliasKey(model)
key := catalogIdentityKey(model, baseModel)
current := groups[key]
if current == nil {
current = &catalogGroup{
key: key,
alias: firstNonEmpty(model.ModelAlias, model.DisplayName, model.ModelName),
displayName: firstNonEmpty(model.DisplayName, model.ModelAlias, model.ModelName),
modelName: model.ModelName,
alias: firstNonEmpty(baseModel.InvocationName, model.ModelName),
displayName: firstNonEmpty(baseModel.DisplayName, model.DisplayName, baseModel.InvocationName, model.ModelName),
modelName: firstNonEmpty(baseModel.InvocationName, model.ModelName),
modelType: cloneStringSlice(model.ModelType),
capabilities: cloneObject(model.Capabilities),
}
@@ -274,8 +276,8 @@ func buildModelCatalog(
}
}
func catalogSourceEnabled(model store.PlatformModel, platform store.Platform) bool {
return model.Enabled && platform.ID != "" && platform.Status == "enabled"
func catalogSourceEnabled(model store.PlatformModel, platform store.Platform, baseModel store.BaseModel) bool {
return model.Enabled && platform.ID != "" && platform.Status == "enabled" && (baseModel.ID == "" || baseModel.Status == "" || baseModel.Status == "active")
}
func modelCatalogItem(group *catalogGroup, accessRuleGroups map[string]accessRuleGroup) ModelCatalogItem {
@@ -304,17 +306,19 @@ func modelCatalogItem(group *catalogGroup, accessRuleGroups map[string]accessRul
itemSources := make([]ModelCatalogSource, 0, len(sources))
for _, source := range sources {
itemSources = append(itemSources, ModelCatalogSource{
ID: source.model.ID,
PlatformID: source.model.PlatformID,
PlatformName: firstNonEmpty(source.platform.Name, source.model.PlatformName),
ProviderKey: source.providerKey,
ProviderName: source.providerName,
ModelName: source.model.ModelName,
ModelAlias: source.model.ModelAlias,
DisplayName: firstNonEmpty(source.model.DisplayName, source.model.ModelAlias, source.model.ModelName),
ModelType: cloneStringSlice(source.model.ModelType),
Enabled: source.model.Enabled && source.platform.Status == "enabled",
RateLimits: source.rateLimits,
ID: source.model.ID,
PlatformID: source.model.PlatformID,
PlatformName: firstNonEmpty(source.platform.Name, source.model.PlatformName),
ProviderKey: source.providerKey,
ProviderName: source.providerName,
ModelName: firstNonEmpty(source.baseModel.InvocationName, source.model.ModelName),
ProviderModelName: source.model.ProviderModelName,
ModelAlias: firstNonEmpty(source.baseModel.InvocationName, source.model.ModelName),
LegacyAliases: cloneStringSlice(source.baseModel.LegacyAliases),
DisplayName: firstNonEmpty(source.baseModel.DisplayName, source.model.DisplayName, source.baseModel.InvocationName, source.model.ModelName),
ModelType: cloneStringSlice(source.model.ModelType),
Enabled: source.model.Enabled && source.platform.Status == "enabled",
RateLimits: source.rateLimits,
})
}
@@ -1260,8 +1264,8 @@ func modelIconPath(model store.PlatformModel, baseModel store.BaseModel, provide
)
}
func catalogAliasKey(model store.PlatformModel) string {
key := strings.ToLower(strings.TrimSpace(firstNonEmpty(model.ModelAlias, model.DisplayName, model.ModelName)))
func catalogIdentityKey(model store.PlatformModel, baseModel store.BaseModel) string {
key := strings.ToLower(strings.TrimSpace(firstNonEmpty(baseModel.InvocationName, baseModel.CanonicalModelKey, model.ModelName)))
if key == "" {
return model.ID
}