新增官方调用名、供应商真实名、显示名和兼容别名的独立契约,调整路由、目录聚合、管理端与 OpenAPI。 增加 Gemini、Qwen、DeepSeek、Claude 和 MiniMax 生命周期迁移、别名观测及引用保护,并补充数据库与聚合测试。
335 lines
15 KiB
SQL
335 lines
15 KiB
SQL
-- Gemini models that Google has retired or superseded stay callable only through
|
|
-- the compatibility window; they are no longer eligible for new catalog cards.
|
|
UPDATE base_model_catalog
|
|
SET status = 'deprecated',
|
|
metadata = metadata || jsonb_build_object(
|
|
'officialModelId', invocation_name,
|
|
'officialDisplayName', COALESCE(NULLIF(display_name, ''), invocation_name),
|
|
'lifecycleStage', 'deprecated',
|
|
'replacementModel', CASE
|
|
WHEN invocation_name = 'gemini-2.0-flash-exp-image-generation' THEN 'gemini-3.1-flash-image'
|
|
WHEN invocation_name = 'gemini-3-pro-preview' THEN 'gemini-3.1-pro-preview'
|
|
WHEN invocation_name = 'gemini-3.1-flash-lite-preview' THEN 'gemini-3.5-flash-lite'
|
|
WHEN invocation_name = 'gemini-3-pro-image-preview' THEN 'gemini-3-pro-image'
|
|
WHEN invocation_name = 'gemini-3.1-flash-image-preview' THEN 'gemini-3.1-flash-image'
|
|
WHEN invocation_name = 'gemini-3-flash-preview' THEN 'gemini-3.6-flash'
|
|
ELSE metadata->>'replacementModel'
|
|
END,
|
|
'retireAt', '2026-07-22',
|
|
'sourceUrl', 'https://ai.google.dev/gemini-api/docs/deprecations',
|
|
'verifiedAt', '2026-07-22'
|
|
),
|
|
updated_at = now()
|
|
WHERE catalog_type = 'system'
|
|
AND customized_at IS NULL
|
|
AND invocation_name IN (
|
|
'gemini-2.0-flash-exp-image-generation',
|
|
'gemini-3-pro-preview',
|
|
'gemini-3.1-flash-lite-preview',
|
|
'gemini-3-pro-image-preview',
|
|
'gemini-3.1-flash-image-preview',
|
|
'gemini-3-flash-preview'
|
|
);
|
|
|
|
WITH targets(model_id, display_name, template_model, fallback_type, lifecycle_stage) AS (
|
|
VALUES
|
|
('gemini-3.1-pro-preview', 'Gemini 3.1 Pro Preview', 'gemini-3-pro-preview', 'text_generate', 'preview'),
|
|
('gemini-3.5-flash-lite', 'Gemini 3.5 Flash-Lite', 'gemini-3.1-flash-lite-preview', 'text_generate', 'stable'),
|
|
('gemini-3-pro-image', 'Gemini 3 Pro Image', 'gemini-3-pro-image-preview', 'image_generate', 'stable'),
|
|
('gemini-3.1-flash-image', 'Gemini 3.1 Flash Image', 'gemini-3.1-flash-image-preview', 'image_generate', 'stable'),
|
|
('gemini-3.6-flash', 'Gemini 3.6 Flash', 'gemini-3-flash-preview', 'text_generate', 'stable')
|
|
)
|
|
INSERT INTO base_model_catalog (
|
|
provider_id, provider_key, canonical_model_key, invocation_name, provider_model_name,
|
|
model_type, display_name, capabilities, base_billing_config, default_rate_limit_policy,
|
|
pricing_rule_set_id, runtime_policy_set_id, runtime_policy_override, metadata,
|
|
catalog_type, pricing_version, status
|
|
)
|
|
SELECT provider.id,
|
|
'gemini',
|
|
'gemini:' || target.model_id,
|
|
target.model_id,
|
|
target.model_id,
|
|
COALESCE(template.model_type, jsonb_build_array(target.fallback_type)),
|
|
target.display_name,
|
|
COALESCE(template.capabilities, '{}'::jsonb),
|
|
COALESCE(template.base_billing_config, '{}'::jsonb),
|
|
COALESCE(template.default_rate_limit_policy, '{}'::jsonb),
|
|
template.pricing_rule_set_id,
|
|
template.runtime_policy_set_id,
|
|
COALESCE(template.runtime_policy_override, '{}'::jsonb),
|
|
jsonb_build_object(
|
|
'officialModelId', target.model_id,
|
|
'officialDisplayName', target.display_name,
|
|
'lifecycleStage', target.lifecycle_stage,
|
|
'sourceUrl', 'https://ai.google.dev/gemini-api/docs/deprecations',
|
|
'verifiedAt', '2026-07-22',
|
|
'bindingStatus', 'unverified'
|
|
),
|
|
'system',
|
|
COALESCE(template.pricing_version, 1),
|
|
'active'
|
|
FROM targets target
|
|
LEFT JOIN model_catalog_providers provider ON provider.provider_key = 'gemini' OR provider.provider_code = 'gemini'
|
|
LEFT JOIN LATERAL (
|
|
SELECT source.model_type, source.capabilities, source.base_billing_config,
|
|
source.default_rate_limit_policy, source.pricing_rule_set_id,
|
|
source.runtime_policy_set_id, source.runtime_policy_override, source.pricing_version
|
|
FROM base_model_catalog source
|
|
WHERE source.invocation_name = target.template_model
|
|
ORDER BY CASE WHEN source.provider_key = 'gemini' THEN 0 ELSE 1 END, source.updated_at DESC
|
|
LIMIT 1
|
|
) template ON true
|
|
ON CONFLICT (canonical_model_key) DO UPDATE
|
|
SET invocation_name = CASE WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.invocation_name ELSE base_model_catalog.invocation_name END,
|
|
provider_model_name = CASE WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.provider_model_name ELSE base_model_catalog.provider_model_name END,
|
|
display_name = CASE WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.display_name ELSE base_model_catalog.display_name END,
|
|
metadata = CASE WHEN base_model_catalog.customized_at IS NULL THEN base_model_catalog.metadata || EXCLUDED.metadata ELSE base_model_catalog.metadata END,
|
|
status = CASE WHEN base_model_catalog.customized_at IS NULL THEN 'active' ELSE base_model_catalog.status END,
|
|
updated_at = now();
|
|
|
|
-- Alibaba Cloud announced the following Qwen IDs for retirement by 2026-10-10.
|
|
UPDATE base_model_catalog
|
|
SET status = 'deprecated',
|
|
metadata = metadata || jsonb_build_object(
|
|
'officialModelId', invocation_name,
|
|
'officialDisplayName', COALESCE(NULLIF(display_name, ''), invocation_name),
|
|
'lifecycleStage', 'deprecated',
|
|
'replacementModel', CASE
|
|
WHEN invocation_name = 'qwen3-max' THEN 'qwen3.7-max'
|
|
WHEN invocation_name = 'qwen-turbo' THEN 'qwen3.7-plus'
|
|
WHEN invocation_name IN ('qwen-vl-max', 'qwen-vl-plus') THEN 'qwen3.6-flash'
|
|
WHEN invocation_name = 'qwen3-235b-a22b' THEN 'qwen3.7-plus'
|
|
ELSE metadata->>'replacementModel'
|
|
END,
|
|
'retireAt', '2026-10-10',
|
|
'sourceUrl', 'https://help.aliyun.com/en/model-studio/model-depreciation',
|
|
'verifiedAt', '2026-07-22'
|
|
),
|
|
updated_at = now()
|
|
WHERE catalog_type = 'system'
|
|
AND customized_at IS NULL
|
|
AND invocation_name IN ('qwen3-max', 'qwen-turbo', 'qwen-vl-max', 'qwen-vl-plus', 'qwen3-235b-a22b');
|
|
|
|
WITH targets(model_id, display_name, template_model, fallback_type) AS (
|
|
VALUES
|
|
('qwen3.7-max', 'Qwen3.7-Max', 'qwen3-max', 'text_generate'),
|
|
('qwen3.7-plus', 'Qwen3.7-Plus', 'qwen3-235b-a22b', 'text_generate'),
|
|
('qwen3.6-flash', 'Qwen3.6-Flash', 'qwen-vl-plus', 'text_generate')
|
|
)
|
|
INSERT INTO base_model_catalog (
|
|
provider_id, provider_key, canonical_model_key, invocation_name, provider_model_name,
|
|
model_type, display_name, capabilities, base_billing_config, default_rate_limit_policy,
|
|
pricing_rule_set_id, runtime_policy_set_id, runtime_policy_override, metadata,
|
|
catalog_type, pricing_version, status
|
|
)
|
|
SELECT provider.id,
|
|
'aliyun',
|
|
'aliyun:' || target.model_id,
|
|
target.model_id,
|
|
target.model_id,
|
|
COALESCE(template.model_type, jsonb_build_array(target.fallback_type)),
|
|
target.display_name,
|
|
COALESCE(template.capabilities, '{}'::jsonb),
|
|
COALESCE(template.base_billing_config, '{}'::jsonb),
|
|
COALESCE(template.default_rate_limit_policy, '{}'::jsonb),
|
|
template.pricing_rule_set_id,
|
|
template.runtime_policy_set_id,
|
|
COALESCE(template.runtime_policy_override, '{}'::jsonb),
|
|
jsonb_build_object(
|
|
'officialModelId', target.model_id,
|
|
'officialDisplayName', target.display_name,
|
|
'lifecycleStage', 'stable',
|
|
'sourceUrl', 'https://help.aliyun.com/en/model-studio/model-depreciation',
|
|
'verifiedAt', '2026-07-22',
|
|
'bindingStatus', 'unverified'
|
|
),
|
|
'system',
|
|
COALESCE(template.pricing_version, 1),
|
|
'active'
|
|
FROM targets target
|
|
LEFT JOIN model_catalog_providers provider ON provider.provider_key = 'aliyun' OR provider.provider_code = 'aliyun'
|
|
LEFT JOIN LATERAL (
|
|
SELECT source.model_type, source.capabilities, source.base_billing_config,
|
|
source.default_rate_limit_policy, source.pricing_rule_set_id,
|
|
source.runtime_policy_set_id, source.runtime_policy_override, source.pricing_version
|
|
FROM base_model_catalog source
|
|
WHERE source.invocation_name = target.template_model
|
|
ORDER BY CASE WHEN source.provider_key = 'aliyun' THEN 0 ELSE 1 END, source.updated_at DESC
|
|
LIMIT 1
|
|
) template ON true
|
|
ON CONFLICT (canonical_model_key) DO UPDATE
|
|
SET invocation_name = CASE WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.invocation_name ELSE base_model_catalog.invocation_name END,
|
|
provider_model_name = CASE WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.provider_model_name ELSE base_model_catalog.provider_model_name END,
|
|
display_name = CASE WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.display_name ELSE base_model_catalog.display_name END,
|
|
metadata = CASE WHEN base_model_catalog.customized_at IS NULL THEN base_model_catalog.metadata || EXCLUDED.metadata ELSE base_model_catalog.metadata END,
|
|
status = CASE WHEN base_model_catalog.customized_at IS NULL THEN 'active' ELSE base_model_catalog.status END,
|
|
updated_at = now();
|
|
|
|
-- DeepSeek direct API has two official public IDs. Aggregator-only V4 names are
|
|
-- kept but explicitly classified as provider identities.
|
|
UPDATE base_model_catalog
|
|
SET metadata = metadata || jsonb_build_object(
|
|
'officialModelId', invocation_name,
|
|
'officialDisplayName', COALESCE(NULLIF(display_name, ''), invocation_name),
|
|
'lifecycleStage', 'stable',
|
|
'sourceUrl', 'https://api-docs.deepseek.com/quick_start/pricing-details-usd',
|
|
'verifiedAt', '2026-07-22',
|
|
'identityAuthority', 'official'
|
|
),
|
|
updated_at = now()
|
|
WHERE invocation_name IN ('deepseek-chat', 'deepseek-reasoner');
|
|
|
|
UPDATE base_model_catalog
|
|
SET metadata = metadata || jsonb_build_object(
|
|
'lifecycleStage', 'provider',
|
|
'verifiedAt', '2026-07-22',
|
|
'identityAuthority', 'provider'
|
|
),
|
|
updated_at = now()
|
|
WHERE lower(invocation_name) LIKE 'deepseek-v4-%';
|
|
|
|
-- OpenRouter keeps its namespaced upstream ID, while callers use Anthropic's
|
|
-- official model ID.
|
|
WITH claude_mapping(provider_model_name, invocation_name, display_name) AS (
|
|
VALUES
|
|
('anthropic/claude-opus-4.5', 'claude-opus-4-5', 'Claude Opus 4.5'),
|
|
('anthropic/claude-opus-4.6', 'claude-opus-4-6', 'Claude Opus 4.6'),
|
|
('anthropic/claude-sonnet-4.5', 'claude-sonnet-4-5', 'Claude Sonnet 4.5'),
|
|
('anthropic/claude-sonnet-4.6', 'claude-sonnet-4-6', 'Claude Sonnet 4.6'),
|
|
('anthropic/claude-haiku-4.5', 'claude-haiku-4-5', 'Claude Haiku 4.5')
|
|
), affected AS (
|
|
SELECT base.id, base.invocation_name AS old_invocation_name, mapping.invocation_name, mapping.display_name
|
|
FROM base_model_catalog base
|
|
JOIN claude_mapping mapping ON mapping.provider_model_name = base.provider_model_name
|
|
WHERE base.catalog_type = 'system' AND base.customized_at IS NULL
|
|
), old_aliases AS (
|
|
INSERT INTO model_compatibility_aliases (base_model_id, alias, model_type, expires_at)
|
|
SELECT affected.id, affected.old_invocation_name, model_type.value, now() + interval '14 days'
|
|
FROM affected
|
|
JOIN base_model_catalog base ON base.id = affected.id
|
|
CROSS JOIN LATERAL jsonb_array_elements_text(base.model_type) AS model_type(value)
|
|
WHERE affected.old_invocation_name <> affected.invocation_name
|
|
ON CONFLICT (base_model_id, alias, model_type) DO NOTHING
|
|
)
|
|
UPDATE base_model_catalog base
|
|
SET invocation_name = affected.invocation_name,
|
|
display_name = affected.display_name,
|
|
metadata = base.metadata || jsonb_build_object(
|
|
'officialModelId', affected.invocation_name,
|
|
'officialDisplayName', affected.display_name,
|
|
'lifecycleStage', 'stable',
|
|
'sourceUrl', 'https://platform.claude.com/docs/en/about-claude/models/model-ids-and-versions',
|
|
'verifiedAt', '2026-07-22',
|
|
'identityAuthority', 'official'
|
|
),
|
|
updated_at = now()
|
|
FROM affected
|
|
WHERE base.id = affected.id;
|
|
|
|
WITH ranked_targets AS (
|
|
SELECT platform_model.id,
|
|
base.invocation_name,
|
|
COALESCE(NULLIF(base.display_name, ''), base.invocation_name) AS display_name,
|
|
row_number() OVER (
|
|
PARTITION BY platform_model.platform_id, base.invocation_name
|
|
ORDER BY CASE WHEN platform_model.model_name = base.invocation_name THEN 0 ELSE 1 END,
|
|
platform_model.created_at ASC,
|
|
platform_model.id ASC
|
|
) AS identity_rank
|
|
FROM platform_models platform_model
|
|
JOIN base_model_catalog base ON base.id = platform_model.base_model_id
|
|
)
|
|
UPDATE platform_models platform_model
|
|
SET model_name = target.invocation_name,
|
|
model_alias = target.invocation_name,
|
|
display_name = target.display_name,
|
|
updated_at = now()
|
|
FROM ranked_targets target
|
|
WHERE target.id = platform_model.id
|
|
AND target.identity_rank = 1
|
|
AND platform_model.model_name <> target.invocation_name
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM platform_models conflict
|
|
WHERE conflict.platform_id = platform_model.platform_id
|
|
AND conflict.id <> platform_model.id
|
|
AND conflict.model_name = target.invocation_name
|
|
);
|
|
|
|
-- MiniMax keeps current main and fallback families; older text/speech previews
|
|
-- remain present only for compatibility and reference-safe migration.
|
|
UPDATE base_model_catalog
|
|
SET status = 'deprecated',
|
|
metadata = metadata || jsonb_build_object(
|
|
'lifecycleStage', 'deprecated',
|
|
'replacementModel', CASE
|
|
WHEN lower(invocation_name) LIKE '%speech-2.5%' THEN 'speech-2.8'
|
|
ELSE 'minimax-m2.5'
|
|
END,
|
|
'sourceUrl', 'https://platform.minimaxi.com/docs/api-reference/api-overview',
|
|
'verifiedAt', '2026-07-22'
|
|
),
|
|
updated_at = now()
|
|
WHERE catalog_type = 'system'
|
|
AND customized_at IS NULL
|
|
AND (
|
|
lower(invocation_name) IN ('minimax-m2', 'minimax-m2.1', 'minimax-m2-highspeed', 'minimax-m2.1-highspeed')
|
|
OR lower(invocation_name) LIKE '%speech-2.5%preview%'
|
|
);
|
|
|
|
-- Consolidate duplicate qwen-max rows conservatively. A row is hidden only
|
|
-- after every safe platform binding has moved to the selected keeper.
|
|
WITH ranked AS (
|
|
SELECT base.id,
|
|
first_value(base.id) OVER (
|
|
PARTITION BY base.provider_key, base.invocation_name
|
|
ORDER BY (SELECT count(*) FROM platform_models model WHERE model.base_model_id = base.id) DESC,
|
|
CASE WHEN base.customized_at IS NULL THEN 0 ELSE 1 END,
|
|
base.created_at ASC,
|
|
base.id ASC
|
|
) AS keeper_id
|
|
FROM base_model_catalog base
|
|
WHERE lower(base.invocation_name) = 'qwen-max'
|
|
), rebound AS (
|
|
UPDATE platform_models model
|
|
SET base_model_id = ranked.keeper_id,
|
|
updated_at = now()
|
|
FROM ranked
|
|
WHERE model.base_model_id = ranked.id
|
|
AND ranked.id <> ranked.keeper_id
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM platform_models keeper_model
|
|
WHERE keeper_model.platform_id = model.platform_id
|
|
AND keeper_model.base_model_id = ranked.keeper_id
|
|
)
|
|
RETURNING model.id
|
|
)
|
|
UPDATE base_model_catalog base
|
|
SET status = 'hidden',
|
|
metadata = metadata || jsonb_build_object(
|
|
'lifecycleStage', 'duplicate',
|
|
'cleanupCandidate', true,
|
|
'verifiedAt', '2026-07-22'
|
|
),
|
|
updated_at = now()
|
|
FROM ranked
|
|
WHERE base.id = ranked.id
|
|
AND ranked.id <> ranked.keeper_id
|
|
AND base.catalog_type = 'system'
|
|
AND base.customized_at IS NULL
|
|
AND NOT EXISTS (SELECT 1 FROM platform_models model WHERE model.base_model_id = base.id);
|
|
|
|
-- Release B may physically delete only candidates which remain unreferenced
|
|
-- after the observation window. Custom and manually modified rows are excluded.
|
|
UPDATE base_model_catalog base
|
|
SET metadata = metadata || jsonb_build_object(
|
|
'cleanupCandidate', true,
|
|
'cleanupEligibleAfter', '2026-08-05'
|
|
),
|
|
updated_at = now()
|
|
WHERE base.status IN ('deprecated', 'hidden')
|
|
AND base.catalog_type = 'system'
|
|
AND base.customized_at IS NULL
|
|
AND NOT EXISTS (SELECT 1 FROM platform_models model WHERE model.base_model_id = base.id);
|