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
+23 -12
View File
@@ -18,6 +18,7 @@ type modelCatalogSnapshot struct {
ID string
ProviderKey string
CanonicalModelKey string
InvocationName string
ProviderModelName string
ModelType StringList
DisplayName string
@@ -27,6 +28,7 @@ type modelCatalogSnapshot struct {
DefaultRateLimitPolicy map[string]any
RuntimePolicySetID string
RuntimePolicyOverride map[string]any
Status string
}
func (s *Store) CreatePlatformModel(ctx context.Context, input CreatePlatformModelInput) (PlatformModel, error) {
@@ -95,6 +97,18 @@ func (s *Store) createPlatformModel(ctx context.Context, q platformModelQuerier,
if err != nil && !IsNotFound(err) {
return PlatformModel{}, err
}
if base.ID != "" && base.Status != "" && base.Status != "active" {
var existingBinding bool
if err := q.QueryRow(ctx, `
SELECT EXISTS (
SELECT 1 FROM platform_models WHERE platform_id = $1::uuid AND base_model_id = $2::uuid
)`, input.PlatformID, base.ID).Scan(&existingBinding); err != nil {
return PlatformModel{}, err
}
if !existingBinding {
return PlatformModel{}, fmt.Errorf("%w: base model %q is %s and cannot be newly bound", ErrInvalidPlatformModelConfiguration, base.InvocationName, base.Status)
}
}
if len(input.ModelType) == 0 {
input.ModelType = base.ModelType
}
@@ -102,7 +116,9 @@ func (s *Store) createPlatformModel(ctx context.Context, q platformModelQuerier,
if len(input.ModelType) == 0 {
input.ModelType = StringList{"text_generate"}
}
if input.ModelName == "" {
if base.InvocationName != "" {
input.ModelName = base.InvocationName
} else if input.ModelName == "" {
input.ModelName = base.ProviderModelName
}
if input.ProviderModelName == "" {
@@ -370,9 +386,9 @@ func (s *Store) lookupBaseModel(ctx context.Context, q platformModelQuerier, id
var runtimePolicyOverride []byte
var modelTypeBytes []byte
err := q.QueryRow(ctx, `
SELECT id::text, provider_key, canonical_model_key, provider_model_name, model_type, display_name,
SELECT id::text, provider_key, canonical_model_key, invocation_name, provider_model_name, model_type, display_name,
capabilities, base_billing_config, COALESCE(pricing_rule_set_id::text, ''), default_rate_limit_policy,
COALESCE(runtime_policy_set_id::text, ''), runtime_policy_override
COALESCE(runtime_policy_set_id::text, ''), runtime_policy_override, status
FROM base_model_catalog
WHERE ($1 <> '' AND id = NULLIF($1, '')::uuid)
OR ($2 <> '' AND canonical_model_key = $2)
@@ -382,6 +398,7 @@ LIMIT 1`, strings.TrimSpace(id), strings.TrimSpace(canonicalKey), strings.TrimSp
&item.ID,
&item.ProviderKey,
&item.CanonicalModelKey,
&item.InvocationName,
&item.ProviderModelName,
&modelTypeBytes,
&item.DisplayName,
@@ -391,6 +408,7 @@ LIMIT 1`, strings.TrimSpace(id), strings.TrimSpace(canonicalKey), strings.TrimSp
&rateLimitPolicy,
&item.RuntimePolicySetID,
&runtimePolicyOverride,
&item.Status,
)
if err != nil {
if err == pgx.ErrNoRows {
@@ -407,15 +425,8 @@ LIMIT 1`, strings.TrimSpace(id), strings.TrimSpace(canonicalKey), strings.TrimSp
}
func normalizePlatformModelAlias(alias string, base modelCatalogSnapshot) string {
alias = strings.TrimSpace(alias)
if alias == "" {
alias = firstNonEmpty(base.ProviderModelName, base.DisplayName, base.CanonicalModelKey)
}
if base.ProviderKey != "" {
alias = strings.TrimPrefix(alias, base.ProviderKey+":")
}
if alias == base.CanonicalModelKey {
alias = stripAliasPrefix(alias)
if base.InvocationName != "" {
return base.InvocationName
}
return strings.TrimSpace(alias)
}