feat(admin): support provider model presets
This commit is contained in:
parent
6f730be0cd
commit
6d99e26e2a
774
apps/api/migrations/0040_canonical_provider_model_presets.sql
Normal file
774
apps/api/migrations/0040_canonical_provider_model_presets.sql
Normal file
@ -0,0 +1,774 @@
|
|||||||
|
-- Represent provider-specific request model names as presets on the canonical
|
||||||
|
-- base model instead of duplicating the base model catalog row.
|
||||||
|
|
||||||
|
WITH preset AS (
|
||||||
|
SELECT jsonb_build_object(
|
||||||
|
'providerKey', 'volces-openai',
|
||||||
|
'providerCode', 'volces-openai',
|
||||||
|
'providerModelName', 'glm-4-7-251222',
|
||||||
|
'modelAlias', 'GLM-4.7',
|
||||||
|
'displayName', 'GLM-4.7',
|
||||||
|
'modelType', jsonb_build_array('text_generate', 'image_analysis', 'tools_call'),
|
||||||
|
'source', 'server-main.integration-platform',
|
||||||
|
'sourceProviderName', '火山引擎(OpenAI兼容)',
|
||||||
|
'baseProviderKey', 'zhipu-openai',
|
||||||
|
'baseProviderModelName', 'glm-4.7'
|
||||||
|
) AS item
|
||||||
|
),
|
||||||
|
target AS (
|
||||||
|
SELECT
|
||||||
|
b.id,
|
||||||
|
COALESCE((
|
||||||
|
SELECT jsonb_agg(existing.item)
|
||||||
|
FROM jsonb_array_elements(
|
||||||
|
COALESCE(b.metadata->'providerModelPresets', '[]'::jsonb)
|
||||||
|
) AS existing(item)
|
||||||
|
WHERE existing.item->>'providerKey' <> 'volces-openai'
|
||||||
|
), '[]'::jsonb) || jsonb_build_array(preset.item) AS presets
|
||||||
|
FROM base_model_catalog b
|
||||||
|
CROSS JOIN preset
|
||||||
|
WHERE b.provider_key = 'zhipu-openai'
|
||||||
|
AND b.provider_model_name = 'glm-4.7'
|
||||||
|
)
|
||||||
|
UPDATE base_model_catalog b
|
||||||
|
SET metadata = (b.metadata - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object('providerModelPresets', target.presets),
|
||||||
|
default_snapshot = CASE
|
||||||
|
WHEN COALESCE(b.default_snapshot, '{}'::jsonb) = '{}'::jsonb THEN b.default_snapshot
|
||||||
|
ELSE jsonb_set(
|
||||||
|
b.default_snapshot,
|
||||||
|
'{metadata}',
|
||||||
|
(COALESCE(b.default_snapshot->'metadata', '{}'::jsonb) - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object('providerModelPresets', target.presets),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
END,
|
||||||
|
updated_at = now()
|
||||||
|
FROM target
|
||||||
|
WHERE b.id = target.id;
|
||||||
|
|
||||||
|
WITH canonical_ref AS (
|
||||||
|
SELECT jsonb_build_object(
|
||||||
|
'providerKey', 'zhipu-openai',
|
||||||
|
'providerCode', 'zhipu-openai',
|
||||||
|
'modelAlias', 'GLM-4.7',
|
||||||
|
'providerModelName', 'glm-4.7',
|
||||||
|
'canonicalModelKey', canonical_model_key
|
||||||
|
) AS ref
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'zhipu-openai'
|
||||||
|
AND provider_model_name = 'glm-4.7'
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
deprecated_payload AS (
|
||||||
|
SELECT jsonb_build_object(
|
||||||
|
'canonicalBaseModelRef', canonical_ref.ref,
|
||||||
|
'realModelNameOverride', 'glm-4-7-251222',
|
||||||
|
'selectable', false,
|
||||||
|
'hiddenReason', 'represented_by_provider_model_preset'
|
||||||
|
) AS metadata_patch
|
||||||
|
FROM canonical_ref
|
||||||
|
)
|
||||||
|
UPDATE base_model_catalog b
|
||||||
|
SET status = 'hidden',
|
||||||
|
metadata = b.metadata || deprecated_payload.metadata_patch,
|
||||||
|
default_snapshot = CASE
|
||||||
|
WHEN COALESCE(b.default_snapshot, '{}'::jsonb) = '{}'::jsonb THEN b.default_snapshot
|
||||||
|
ELSE jsonb_set(
|
||||||
|
b.default_snapshot || jsonb_build_object('status', 'hidden'),
|
||||||
|
'{metadata}',
|
||||||
|
COALESCE(b.default_snapshot->'metadata', '{}'::jsonb) ||
|
||||||
|
deprecated_payload.metadata_patch,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
END,
|
||||||
|
updated_at = now()
|
||||||
|
FROM deprecated_payload
|
||||||
|
WHERE b.provider_key = 'volces-openai'
|
||||||
|
AND b.provider_model_name = 'glm-4-7-251222';
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT id, provider_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'zhipu-openai'
|
||||||
|
AND provider_model_name = 'glm-4.7'
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
deprecated AS (
|
||||||
|
SELECT id
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'volces-openai'
|
||||||
|
AND provider_model_name = 'glm-4-7-251222'
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
UPDATE platform_models model
|
||||||
|
SET base_model_id = canonical.id,
|
||||||
|
model_name = canonical.provider_model_name,
|
||||||
|
provider_model_name = 'glm-4-7-251222',
|
||||||
|
model_alias = COALESCE(NULLIF(model.model_alias, ''), NULLIF(model.display_name, ''), 'GLM-4.7'),
|
||||||
|
display_name = COALESCE(NULLIF(model.display_name, ''), NULLIF(model.model_alias, ''), 'GLM-4.7'),
|
||||||
|
model_type = jsonb_build_array('text_generate', 'image_analysis', 'tools_call'),
|
||||||
|
enabled = true,
|
||||||
|
updated_at = now()
|
||||||
|
FROM integration_platforms platform, canonical, deprecated
|
||||||
|
WHERE model.platform_id = platform.id
|
||||||
|
AND platform.provider = 'volces-openai'
|
||||||
|
AND (
|
||||||
|
model.base_model_id = deprecated.id
|
||||||
|
OR model.model_name = 'glm-4-7-251222'
|
||||||
|
OR model.provider_model_name = 'glm-4-7-251222'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM platform_models peer
|
||||||
|
WHERE peer.platform_id = model.platform_id
|
||||||
|
AND peer.id <> model.id
|
||||||
|
AND peer.model_name = canonical.provider_model_name
|
||||||
|
);
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT id, provider_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'zhipu-openai'
|
||||||
|
AND provider_model_name = 'glm-4.7'
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
deprecated AS (
|
||||||
|
SELECT id
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'volces-openai'
|
||||||
|
AND provider_model_name = 'glm-4-7-251222'
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
UPDATE platform_models peer
|
||||||
|
SET base_model_id = canonical.id,
|
||||||
|
model_name = canonical.provider_model_name,
|
||||||
|
provider_model_name = 'glm-4-7-251222',
|
||||||
|
model_alias = COALESCE(
|
||||||
|
NULLIF((
|
||||||
|
SELECT legacy.model_alias
|
||||||
|
FROM platform_models legacy
|
||||||
|
WHERE legacy.platform_id = peer.platform_id
|
||||||
|
AND legacy.id <> peer.id
|
||||||
|
AND (
|
||||||
|
legacy.base_model_id = deprecated.id
|
||||||
|
OR legacy.model_name = 'glm-4-7-251222'
|
||||||
|
OR legacy.provider_model_name = 'glm-4-7-251222'
|
||||||
|
)
|
||||||
|
LIMIT 1
|
||||||
|
), ''),
|
||||||
|
NULLIF((
|
||||||
|
SELECT legacy.display_name
|
||||||
|
FROM platform_models legacy
|
||||||
|
WHERE legacy.platform_id = peer.platform_id
|
||||||
|
AND legacy.id <> peer.id
|
||||||
|
AND (
|
||||||
|
legacy.base_model_id = deprecated.id
|
||||||
|
OR legacy.model_name = 'glm-4-7-251222'
|
||||||
|
OR legacy.provider_model_name = 'glm-4-7-251222'
|
||||||
|
)
|
||||||
|
LIMIT 1
|
||||||
|
), ''),
|
||||||
|
NULLIF(peer.model_alias, ''),
|
||||||
|
NULLIF(peer.display_name, ''),
|
||||||
|
'GLM-4.7'
|
||||||
|
),
|
||||||
|
display_name = COALESCE(
|
||||||
|
NULLIF((
|
||||||
|
SELECT legacy.display_name
|
||||||
|
FROM platform_models legacy
|
||||||
|
WHERE legacy.platform_id = peer.platform_id
|
||||||
|
AND legacy.id <> peer.id
|
||||||
|
AND (
|
||||||
|
legacy.base_model_id = deprecated.id
|
||||||
|
OR legacy.model_name = 'glm-4-7-251222'
|
||||||
|
OR legacy.provider_model_name = 'glm-4-7-251222'
|
||||||
|
)
|
||||||
|
LIMIT 1
|
||||||
|
), ''),
|
||||||
|
NULLIF((
|
||||||
|
SELECT legacy.model_alias
|
||||||
|
FROM platform_models legacy
|
||||||
|
WHERE legacy.platform_id = peer.platform_id
|
||||||
|
AND legacy.id <> peer.id
|
||||||
|
AND (
|
||||||
|
legacy.base_model_id = deprecated.id
|
||||||
|
OR legacy.model_name = 'glm-4-7-251222'
|
||||||
|
OR legacy.provider_model_name = 'glm-4-7-251222'
|
||||||
|
)
|
||||||
|
LIMIT 1
|
||||||
|
), ''),
|
||||||
|
NULLIF(peer.display_name, ''),
|
||||||
|
NULLIF(peer.model_alias, ''),
|
||||||
|
'GLM-4.7'
|
||||||
|
),
|
||||||
|
model_type = jsonb_build_array('text_generate', 'image_analysis', 'tools_call'),
|
||||||
|
enabled = true,
|
||||||
|
updated_at = now()
|
||||||
|
FROM integration_platforms platform, canonical, deprecated
|
||||||
|
WHERE peer.platform_id = platform.id
|
||||||
|
AND platform.provider = 'volces-openai'
|
||||||
|
AND peer.model_name = canonical.provider_model_name
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM platform_models legacy
|
||||||
|
WHERE legacy.platform_id = peer.platform_id
|
||||||
|
AND legacy.id <> peer.id
|
||||||
|
AND (
|
||||||
|
legacy.base_model_id = deprecated.id
|
||||||
|
OR legacy.model_name = 'glm-4-7-251222'
|
||||||
|
OR legacy.provider_model_name = 'glm-4-7-251222'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT id, provider_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'zhipu-openai'
|
||||||
|
AND provider_model_name = 'glm-4.7'
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
UPDATE platform_models peer
|
||||||
|
SET base_model_id = canonical.id,
|
||||||
|
model_name = canonical.provider_model_name,
|
||||||
|
provider_model_name = 'glm-4-7-251222',
|
||||||
|
model_alias = COALESCE(NULLIF(peer.model_alias, ''), NULLIF(peer.display_name, ''), 'GLM-4.7'),
|
||||||
|
display_name = COALESCE(NULLIF(peer.display_name, ''), NULLIF(peer.model_alias, ''), 'GLM-4.7'),
|
||||||
|
model_type = jsonb_build_array('text_generate', 'image_analysis', 'tools_call'),
|
||||||
|
enabled = true,
|
||||||
|
updated_at = now()
|
||||||
|
FROM integration_platforms platform, canonical
|
||||||
|
WHERE peer.platform_id = platform.id
|
||||||
|
AND platform.provider = 'volces-openai'
|
||||||
|
AND (
|
||||||
|
peer.model_name = canonical.provider_model_name
|
||||||
|
OR peer.provider_model_name = 'glm-4-7-251222'
|
||||||
|
);
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT id, provider_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'zhipu-openai'
|
||||||
|
AND provider_model_name = 'glm-4.7'
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
deprecated AS (
|
||||||
|
SELECT id
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'volces-openai'
|
||||||
|
AND provider_model_name = 'glm-4-7-251222'
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
DELETE FROM platform_models model
|
||||||
|
USING integration_platforms platform, canonical, deprecated
|
||||||
|
WHERE model.platform_id = platform.id
|
||||||
|
AND platform.provider = 'volces-openai'
|
||||||
|
AND (
|
||||||
|
model.base_model_id = deprecated.id
|
||||||
|
OR model.model_name = 'glm-4-7-251222'
|
||||||
|
OR model.provider_model_name = 'glm-4-7-251222'
|
||||||
|
)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM platform_models peer
|
||||||
|
WHERE peer.platform_id = model.platform_id
|
||||||
|
AND peer.id <> model.id
|
||||||
|
AND peer.model_name = canonical.provider_model_name
|
||||||
|
);
|
||||||
|
|
||||||
|
-- DeepSeek V4 belongs to the DeepSeek base-model provider. Aliyun Bailian is
|
||||||
|
-- represented as a provider preset on those canonical base models.
|
||||||
|
WITH source_provider(provider_key, provider_code, provider_type, display_name, icon_path) AS (
|
||||||
|
VALUES (
|
||||||
|
'deepseek-openai',
|
||||||
|
'deepseek-openai',
|
||||||
|
'openai',
|
||||||
|
'DeepSeek',
|
||||||
|
'https://static.51easyai.com/deepseek-color%20%281%29.webp'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
INSERT INTO model_catalog_providers (
|
||||||
|
provider_key, provider_code, provider_type, display_name, icon_path, source,
|
||||||
|
capability_schema, default_rate_limit_policy, metadata, status
|
||||||
|
)
|
||||||
|
SELECT provider_key,
|
||||||
|
provider_code,
|
||||||
|
provider_type,
|
||||||
|
display_name,
|
||||||
|
icon_path,
|
||||||
|
'server-main.integration-platform',
|
||||||
|
'{}'::jsonb,
|
||||||
|
'{}'::jsonb,
|
||||||
|
jsonb_build_object(
|
||||||
|
'seed', 'server-main-provider-defaults',
|
||||||
|
'syncSource', 'server-main.integration-platform',
|
||||||
|
'sourceCode', provider_code,
|
||||||
|
'sourceSpecType', provider_type
|
||||||
|
),
|
||||||
|
'active'
|
||||||
|
FROM source_provider
|
||||||
|
ON CONFLICT (provider_key) DO UPDATE
|
||||||
|
SET provider_code = EXCLUDED.provider_code,
|
||||||
|
display_name = EXCLUDED.display_name,
|
||||||
|
provider_type = EXCLUDED.provider_type,
|
||||||
|
icon_path = EXCLUDED.icon_path,
|
||||||
|
source = EXCLUDED.source,
|
||||||
|
metadata = model_catalog_providers.metadata || EXCLUDED.metadata,
|
||||||
|
status = EXCLUDED.status,
|
||||||
|
updated_at = now();
|
||||||
|
|
||||||
|
WITH source_models(provider_model_name, model_alias, display_name) AS (
|
||||||
|
VALUES
|
||||||
|
('deepseek-v4-pro', 'DeepSeek-V4-Pro', 'DeepSeek-V4-Pro'),
|
||||||
|
('deepseek-v4-flash', 'DeepSeek-V4-Flash', 'DeepSeek-V4-Flash')
|
||||||
|
),
|
||||||
|
source_rows AS (
|
||||||
|
SELECT
|
||||||
|
source_models.provider_model_name,
|
||||||
|
source_models.model_alias,
|
||||||
|
source_models.display_name,
|
||||||
|
COALESCE(
|
||||||
|
aliyun.capabilities,
|
||||||
|
jsonb_build_object('originalTypes', jsonb_build_array('text_generate', 'tools_call'))
|
||||||
|
) AS capabilities,
|
||||||
|
COALESCE(aliyun.base_billing_config, '{}'::jsonb) AS base_billing_config,
|
||||||
|
COALESCE(aliyun.default_rate_limit_policy, '{}'::jsonb) AS default_rate_limit_policy,
|
||||||
|
COALESCE(aliyun.pricing_version, 1) AS pricing_version,
|
||||||
|
COALESCE(aliyun.metadata, '{}'::jsonb) AS metadata
|
||||||
|
FROM source_models
|
||||||
|
LEFT JOIN base_model_catalog aliyun
|
||||||
|
ON aliyun.provider_key = 'aliyun-bailian-openai'
|
||||||
|
AND aliyun.provider_model_name = source_models.provider_model_name
|
||||||
|
),
|
||||||
|
provider AS (
|
||||||
|
SELECT id
|
||||||
|
FROM model_catalog_providers
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
INSERT INTO base_model_catalog (
|
||||||
|
provider_id, provider_key, canonical_model_key, provider_model_name, model_type, display_name,
|
||||||
|
capabilities, base_billing_config, default_rate_limit_policy, pricing_version, status, metadata
|
||||||
|
)
|
||||||
|
SELECT provider.id,
|
||||||
|
'deepseek-openai',
|
||||||
|
'deepseek-openai:' || source_rows.provider_model_name,
|
||||||
|
source_rows.provider_model_name,
|
||||||
|
jsonb_build_array('text_generate', 'tools_call'),
|
||||||
|
source_rows.display_name,
|
||||||
|
source_rows.capabilities,
|
||||||
|
source_rows.base_billing_config,
|
||||||
|
source_rows.default_rate_limit_policy,
|
||||||
|
source_rows.pricing_version,
|
||||||
|
'active',
|
||||||
|
(source_rows.metadata - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object(
|
||||||
|
'source', 'server-main.integration-platform',
|
||||||
|
'sourceProviderCode', 'deepseek-openai',
|
||||||
|
'sourceProviderName', 'DeepSeek',
|
||||||
|
'sourceSpecType', 'openai',
|
||||||
|
'alias', source_rows.model_alias,
|
||||||
|
'iconPath', 'https://static.51easyai.com/deepseek-color%20%281%29.webp',
|
||||||
|
'selectable', true
|
||||||
|
)
|
||||||
|
FROM source_rows, provider
|
||||||
|
ON CONFLICT (canonical_model_key) DO UPDATE
|
||||||
|
SET provider_id = EXCLUDED.provider_id,
|
||||||
|
provider_key = EXCLUDED.provider_key,
|
||||||
|
provider_model_name = EXCLUDED.provider_model_name,
|
||||||
|
model_type = EXCLUDED.model_type,
|
||||||
|
display_name = EXCLUDED.display_name,
|
||||||
|
capabilities = EXCLUDED.capabilities,
|
||||||
|
base_billing_config = EXCLUDED.base_billing_config,
|
||||||
|
default_rate_limit_policy = EXCLUDED.default_rate_limit_policy,
|
||||||
|
metadata = (base_model_catalog.metadata - 'providerModelPresets') || EXCLUDED.metadata,
|
||||||
|
status = 'active',
|
||||||
|
updated_at = now();
|
||||||
|
|
||||||
|
WITH preset_source(provider_model_name, model_alias, display_name) AS (
|
||||||
|
VALUES
|
||||||
|
('deepseek-v4-pro', 'DeepSeek-V4-Pro', 'DeepSeek-V4-Pro'),
|
||||||
|
('deepseek-v4-flash', 'DeepSeek-V4-Flash', 'DeepSeek-V4-Flash')
|
||||||
|
),
|
||||||
|
target AS (
|
||||||
|
SELECT
|
||||||
|
b.id,
|
||||||
|
COALESCE((
|
||||||
|
SELECT jsonb_agg(existing.item)
|
||||||
|
FROM jsonb_array_elements(
|
||||||
|
COALESCE(b.metadata->'providerModelPresets', '[]'::jsonb)
|
||||||
|
) AS existing(item)
|
||||||
|
WHERE existing.item->>'providerKey' <> 'aliyun-bailian-openai'
|
||||||
|
), '[]'::jsonb) ||
|
||||||
|
jsonb_build_array(
|
||||||
|
jsonb_build_object(
|
||||||
|
'providerKey', 'aliyun-bailian-openai',
|
||||||
|
'providerCode', 'aliyun-bailian-openai',
|
||||||
|
'providerModelName', preset_source.provider_model_name,
|
||||||
|
'modelAlias', preset_source.model_alias,
|
||||||
|
'displayName', preset_source.display_name,
|
||||||
|
'modelType', jsonb_build_array('text_generate', 'tools_call'),
|
||||||
|
'source', 'server-main.integration-platform',
|
||||||
|
'sourceProviderName', '阿里云百炼(OpenAI兼容)',
|
||||||
|
'baseProviderKey', 'deepseek-openai',
|
||||||
|
'baseProviderModelName', preset_source.provider_model_name
|
||||||
|
)
|
||||||
|
) AS presets
|
||||||
|
FROM base_model_catalog b
|
||||||
|
JOIN preset_source ON preset_source.provider_model_name = b.provider_model_name
|
||||||
|
WHERE b.provider_key = 'deepseek-openai'
|
||||||
|
)
|
||||||
|
UPDATE base_model_catalog b
|
||||||
|
SET metadata = (b.metadata - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object('providerModelPresets', target.presets),
|
||||||
|
default_snapshot = CASE
|
||||||
|
WHEN COALESCE(b.default_snapshot, '{}'::jsonb) = '{}'::jsonb THEN b.default_snapshot
|
||||||
|
ELSE jsonb_set(
|
||||||
|
b.default_snapshot,
|
||||||
|
'{metadata}',
|
||||||
|
(COALESCE(b.default_snapshot->'metadata', '{}'::jsonb) - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object('providerModelPresets', target.presets),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
END,
|
||||||
|
updated_at = now()
|
||||||
|
FROM target
|
||||||
|
WHERE b.id = target.id;
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT id, provider_model_name, canonical_model_key
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
AND provider_model_name IN ('deepseek-v4-pro', 'deepseek-v4-flash')
|
||||||
|
)
|
||||||
|
UPDATE base_model_catalog b
|
||||||
|
SET status = 'hidden',
|
||||||
|
metadata = b.metadata ||
|
||||||
|
jsonb_build_object(
|
||||||
|
'canonicalBaseModelRef',
|
||||||
|
jsonb_build_object(
|
||||||
|
'providerKey', 'deepseek-openai',
|
||||||
|
'providerCode', 'deepseek-openai',
|
||||||
|
'providerModelName', canonical.provider_model_name,
|
||||||
|
'canonicalModelKey', canonical.canonical_model_key
|
||||||
|
),
|
||||||
|
'selectable', false,
|
||||||
|
'hiddenReason', 'represented_by_provider_model_preset'
|
||||||
|
),
|
||||||
|
updated_at = now()
|
||||||
|
FROM canonical
|
||||||
|
WHERE b.provider_key = 'aliyun-bailian-openai'
|
||||||
|
AND b.provider_model_name = canonical.provider_model_name;
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
provider_model_name,
|
||||||
|
CASE provider_model_name
|
||||||
|
WHEN 'deepseek-v4-pro' THEN 'DeepSeek-V4-Pro'
|
||||||
|
WHEN 'deepseek-v4-flash' THEN 'DeepSeek-V4-Flash'
|
||||||
|
ELSE provider_model_name
|
||||||
|
END AS model_alias
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
AND provider_model_name IN ('deepseek-v4-pro', 'deepseek-v4-flash')
|
||||||
|
)
|
||||||
|
UPDATE platform_models model
|
||||||
|
SET base_model_id = canonical.id,
|
||||||
|
model_name = canonical.provider_model_name,
|
||||||
|
provider_model_name = canonical.provider_model_name,
|
||||||
|
model_alias = COALESCE(NULLIF(model.model_alias, ''), NULLIF(model.display_name, ''), canonical.model_alias),
|
||||||
|
display_name = COALESCE(NULLIF(model.display_name, ''), NULLIF(model.model_alias, ''), canonical.model_alias),
|
||||||
|
model_type = jsonb_build_array('text_generate', 'tools_call'),
|
||||||
|
enabled = true,
|
||||||
|
updated_at = now()
|
||||||
|
FROM integration_platforms platform, canonical
|
||||||
|
WHERE model.platform_id = platform.id
|
||||||
|
AND platform.provider = 'aliyun-bailian-openai'
|
||||||
|
AND (
|
||||||
|
model.model_name = canonical.provider_model_name
|
||||||
|
OR model.provider_model_name = canonical.provider_model_name
|
||||||
|
OR model.base_model_id IN (
|
||||||
|
SELECT deprecated.id
|
||||||
|
FROM base_model_catalog deprecated
|
||||||
|
WHERE deprecated.provider_key = 'aliyun-bailian-openai'
|
||||||
|
AND deprecated.provider_model_name = canonical.provider_model_name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- DeepSeek-R1 and DeepSeek-V3 belong to the DeepSeek base-model provider.
|
||||||
|
-- SiliconFlow exposes them with a deepseek-ai/ request-name prefix.
|
||||||
|
WITH source_models(provider_model_name, model_alias, display_name, siliconflow_model_name) AS (
|
||||||
|
VALUES
|
||||||
|
('DeepSeek-R1', 'DeepSeek-R1', 'DeepSeek-R1', 'deepseek-ai/DeepSeek-R1'),
|
||||||
|
('DeepSeek-V3', 'DeepSeek-V3', 'DeepSeek-V3', 'deepseek-ai/DeepSeek-V3')
|
||||||
|
),
|
||||||
|
source_rows AS (
|
||||||
|
SELECT
|
||||||
|
source_models.provider_model_name,
|
||||||
|
source_models.model_alias,
|
||||||
|
source_models.display_name,
|
||||||
|
source_models.siliconflow_model_name,
|
||||||
|
COALESCE(
|
||||||
|
siliconflow.capabilities,
|
||||||
|
jsonb_build_object('originalTypes', jsonb_build_array('text_generate'))
|
||||||
|
) AS capabilities,
|
||||||
|
COALESCE(siliconflow.base_billing_config, '{}'::jsonb) AS base_billing_config,
|
||||||
|
COALESCE(siliconflow.default_rate_limit_policy, '{}'::jsonb) AS default_rate_limit_policy,
|
||||||
|
COALESCE(siliconflow.pricing_version, 1) AS pricing_version,
|
||||||
|
COALESCE(siliconflow.metadata, '{}'::jsonb) AS metadata
|
||||||
|
FROM source_models
|
||||||
|
LEFT JOIN base_model_catalog siliconflow
|
||||||
|
ON siliconflow.provider_key = 'silicon-flow-openai'
|
||||||
|
AND siliconflow.provider_model_name = source_models.siliconflow_model_name
|
||||||
|
),
|
||||||
|
provider AS (
|
||||||
|
SELECT id
|
||||||
|
FROM model_catalog_providers
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
INSERT INTO base_model_catalog (
|
||||||
|
provider_id, provider_key, canonical_model_key, provider_model_name, model_type, display_name,
|
||||||
|
capabilities, base_billing_config, default_rate_limit_policy, pricing_version, status, metadata
|
||||||
|
)
|
||||||
|
SELECT provider.id,
|
||||||
|
'deepseek-openai',
|
||||||
|
'deepseek-openai:' || source_rows.provider_model_name,
|
||||||
|
source_rows.provider_model_name,
|
||||||
|
jsonb_build_array('text_generate'),
|
||||||
|
source_rows.display_name,
|
||||||
|
source_rows.capabilities,
|
||||||
|
source_rows.base_billing_config,
|
||||||
|
source_rows.default_rate_limit_policy,
|
||||||
|
source_rows.pricing_version,
|
||||||
|
'active',
|
||||||
|
(source_rows.metadata - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object(
|
||||||
|
'source', 'server-main.integration-platform',
|
||||||
|
'sourceProviderCode', 'deepseek-openai',
|
||||||
|
'sourceProviderName', 'DeepSeek',
|
||||||
|
'sourceSpecType', 'openai',
|
||||||
|
'alias', source_rows.model_alias,
|
||||||
|
'iconPath', 'https://static.51easyai.com/deepseek-color%20%281%29.webp',
|
||||||
|
'selectable', true
|
||||||
|
)
|
||||||
|
FROM source_rows, provider
|
||||||
|
ON CONFLICT (canonical_model_key) DO UPDATE
|
||||||
|
SET provider_id = EXCLUDED.provider_id,
|
||||||
|
provider_key = EXCLUDED.provider_key,
|
||||||
|
provider_model_name = EXCLUDED.provider_model_name,
|
||||||
|
model_type = EXCLUDED.model_type,
|
||||||
|
display_name = EXCLUDED.display_name,
|
||||||
|
capabilities = EXCLUDED.capabilities,
|
||||||
|
base_billing_config = EXCLUDED.base_billing_config,
|
||||||
|
default_rate_limit_policy = EXCLUDED.default_rate_limit_policy,
|
||||||
|
metadata = (base_model_catalog.metadata - 'providerModelPresets') || EXCLUDED.metadata,
|
||||||
|
status = 'active',
|
||||||
|
updated_at = now();
|
||||||
|
|
||||||
|
WITH preset_source(provider_model_name, model_alias, display_name, siliconflow_model_name) AS (
|
||||||
|
VALUES
|
||||||
|
('DeepSeek-R1', 'DeepSeek-R1', 'DeepSeek-R1', 'deepseek-ai/DeepSeek-R1'),
|
||||||
|
('DeepSeek-V3', 'DeepSeek-V3', 'DeepSeek-V3', 'deepseek-ai/DeepSeek-V3')
|
||||||
|
),
|
||||||
|
target AS (
|
||||||
|
SELECT
|
||||||
|
b.id,
|
||||||
|
COALESCE((
|
||||||
|
SELECT jsonb_agg(existing.item)
|
||||||
|
FROM jsonb_array_elements(
|
||||||
|
COALESCE(b.metadata->'providerModelPresets', '[]'::jsonb)
|
||||||
|
) AS existing(item)
|
||||||
|
WHERE existing.item->>'providerKey' <> 'silicon-flow-openai'
|
||||||
|
), '[]'::jsonb) ||
|
||||||
|
jsonb_build_array(
|
||||||
|
jsonb_build_object(
|
||||||
|
'providerKey', 'silicon-flow-openai',
|
||||||
|
'providerCode', 'silicon-flow-openai',
|
||||||
|
'providerModelName', preset_source.siliconflow_model_name,
|
||||||
|
'modelAlias', preset_source.model_alias,
|
||||||
|
'displayName', preset_source.display_name,
|
||||||
|
'modelType', jsonb_build_array('text_generate'),
|
||||||
|
'source', 'server-main.integration-platform',
|
||||||
|
'sourceProviderName', '硅基流动',
|
||||||
|
'baseProviderKey', 'deepseek-openai',
|
||||||
|
'baseProviderModelName', preset_source.provider_model_name
|
||||||
|
)
|
||||||
|
) AS presets
|
||||||
|
FROM base_model_catalog b
|
||||||
|
JOIN preset_source ON preset_source.provider_model_name = b.provider_model_name
|
||||||
|
WHERE b.provider_key = 'deepseek-openai'
|
||||||
|
)
|
||||||
|
UPDATE base_model_catalog b
|
||||||
|
SET metadata = (b.metadata - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object('providerModelPresets', target.presets),
|
||||||
|
default_snapshot = CASE
|
||||||
|
WHEN COALESCE(b.default_snapshot, '{}'::jsonb) = '{}'::jsonb THEN b.default_snapshot
|
||||||
|
ELSE jsonb_set(
|
||||||
|
b.default_snapshot,
|
||||||
|
'{metadata}',
|
||||||
|
(COALESCE(b.default_snapshot->'metadata', '{}'::jsonb) - 'providerModelPresets') ||
|
||||||
|
jsonb_build_object('providerModelPresets', target.presets),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
END,
|
||||||
|
updated_at = now()
|
||||||
|
FROM target
|
||||||
|
WHERE b.id = target.id;
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
provider_model_name,
|
||||||
|
canonical_model_key,
|
||||||
|
CASE provider_model_name
|
||||||
|
WHEN 'DeepSeek-R1' THEN 'deepseek-ai/DeepSeek-R1'
|
||||||
|
WHEN 'DeepSeek-V3' THEN 'deepseek-ai/DeepSeek-V3'
|
||||||
|
END AS siliconflow_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
AND provider_model_name IN ('DeepSeek-R1', 'DeepSeek-V3')
|
||||||
|
)
|
||||||
|
UPDATE base_model_catalog b
|
||||||
|
SET status = 'hidden',
|
||||||
|
metadata = b.metadata ||
|
||||||
|
jsonb_build_object(
|
||||||
|
'canonicalBaseModelRef',
|
||||||
|
jsonb_build_object(
|
||||||
|
'providerKey', 'deepseek-openai',
|
||||||
|
'providerCode', 'deepseek-openai',
|
||||||
|
'providerModelName', canonical.provider_model_name,
|
||||||
|
'canonicalModelKey', canonical.canonical_model_key
|
||||||
|
),
|
||||||
|
'realModelNameOverride', canonical.siliconflow_model_name,
|
||||||
|
'selectable', false,
|
||||||
|
'hiddenReason', 'represented_by_provider_model_preset'
|
||||||
|
),
|
||||||
|
default_snapshot = CASE
|
||||||
|
WHEN COALESCE(b.default_snapshot, '{}'::jsonb) = '{}'::jsonb THEN b.default_snapshot
|
||||||
|
ELSE jsonb_set(
|
||||||
|
b.default_snapshot || jsonb_build_object('status', 'hidden'),
|
||||||
|
'{metadata}',
|
||||||
|
COALESCE(b.default_snapshot->'metadata', '{}'::jsonb) ||
|
||||||
|
jsonb_build_object(
|
||||||
|
'canonicalBaseModelRef',
|
||||||
|
jsonb_build_object(
|
||||||
|
'providerKey', 'deepseek-openai',
|
||||||
|
'providerCode', 'deepseek-openai',
|
||||||
|
'providerModelName', canonical.provider_model_name,
|
||||||
|
'canonicalModelKey', canonical.canonical_model_key
|
||||||
|
),
|
||||||
|
'realModelNameOverride', canonical.siliconflow_model_name,
|
||||||
|
'selectable', false,
|
||||||
|
'hiddenReason', 'represented_by_provider_model_preset'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
END,
|
||||||
|
updated_at = now()
|
||||||
|
FROM canonical
|
||||||
|
WHERE b.provider_key = 'silicon-flow-openai'
|
||||||
|
AND b.provider_model_name = canonical.siliconflow_model_name;
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
provider_model_name,
|
||||||
|
provider_model_name AS model_alias,
|
||||||
|
CASE provider_model_name
|
||||||
|
WHEN 'DeepSeek-R1' THEN 'deepseek-ai/DeepSeek-R1'
|
||||||
|
WHEN 'DeepSeek-V3' THEN 'deepseek-ai/DeepSeek-V3'
|
||||||
|
END AS siliconflow_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
AND provider_model_name IN ('DeepSeek-R1', 'DeepSeek-V3')
|
||||||
|
)
|
||||||
|
UPDATE platform_models peer
|
||||||
|
SET base_model_id = canonical.id,
|
||||||
|
provider_model_name = canonical.siliconflow_model_name,
|
||||||
|
model_alias = COALESCE(NULLIF(peer.model_alias, ''), NULLIF(peer.display_name, ''), canonical.model_alias),
|
||||||
|
model_type = jsonb_build_array('text_generate'),
|
||||||
|
display_name = COALESCE(NULLIF(peer.display_name, ''), NULLIF(peer.model_alias, ''), canonical.model_alias),
|
||||||
|
enabled = true,
|
||||||
|
updated_at = now()
|
||||||
|
FROM integration_platforms platform, canonical
|
||||||
|
WHERE peer.platform_id = platform.id
|
||||||
|
AND platform.provider = 'silicon-flow-openai'
|
||||||
|
AND peer.model_name = canonical.provider_model_name;
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
provider_model_name,
|
||||||
|
provider_model_name AS model_alias,
|
||||||
|
CASE provider_model_name
|
||||||
|
WHEN 'DeepSeek-R1' THEN 'deepseek-ai/DeepSeek-R1'
|
||||||
|
WHEN 'DeepSeek-V3' THEN 'deepseek-ai/DeepSeek-V3'
|
||||||
|
END AS siliconflow_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
AND provider_model_name IN ('DeepSeek-R1', 'DeepSeek-V3')
|
||||||
|
)
|
||||||
|
UPDATE platform_models model
|
||||||
|
SET base_model_id = canonical.id,
|
||||||
|
model_name = canonical.provider_model_name,
|
||||||
|
provider_model_name = canonical.siliconflow_model_name,
|
||||||
|
model_alias = COALESCE(NULLIF(model.model_alias, ''), NULLIF(model.display_name, ''), canonical.model_alias),
|
||||||
|
model_type = jsonb_build_array('text_generate'),
|
||||||
|
display_name = COALESCE(NULLIF(model.display_name, ''), NULLIF(model.model_alias, ''), canonical.model_alias),
|
||||||
|
enabled = true,
|
||||||
|
updated_at = now()
|
||||||
|
FROM integration_platforms platform, canonical
|
||||||
|
WHERE model.platform_id = platform.id
|
||||||
|
AND platform.provider = 'silicon-flow-openai'
|
||||||
|
AND (
|
||||||
|
model.model_name = canonical.siliconflow_model_name
|
||||||
|
OR model.provider_model_name = canonical.siliconflow_model_name
|
||||||
|
OR model.base_model_id IN (
|
||||||
|
SELECT deprecated.id
|
||||||
|
FROM base_model_catalog deprecated
|
||||||
|
WHERE deprecated.provider_key = 'silicon-flow-openai'
|
||||||
|
AND deprecated.provider_model_name = canonical.siliconflow_model_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM platform_models peer
|
||||||
|
WHERE peer.platform_id = model.platform_id
|
||||||
|
AND peer.id <> model.id
|
||||||
|
AND peer.model_name = canonical.provider_model_name
|
||||||
|
);
|
||||||
|
|
||||||
|
WITH canonical AS (
|
||||||
|
SELECT
|
||||||
|
provider_model_name,
|
||||||
|
CASE provider_model_name
|
||||||
|
WHEN 'DeepSeek-R1' THEN 'deepseek-ai/DeepSeek-R1'
|
||||||
|
WHEN 'DeepSeek-V3' THEN 'deepseek-ai/DeepSeek-V3'
|
||||||
|
END AS siliconflow_model_name
|
||||||
|
FROM base_model_catalog
|
||||||
|
WHERE provider_key = 'deepseek-openai'
|
||||||
|
AND provider_model_name IN ('DeepSeek-R1', 'DeepSeek-V3')
|
||||||
|
)
|
||||||
|
DELETE FROM platform_models model
|
||||||
|
USING integration_platforms platform, canonical
|
||||||
|
WHERE model.platform_id = platform.id
|
||||||
|
AND platform.provider = 'silicon-flow-openai'
|
||||||
|
AND (
|
||||||
|
model.model_name = canonical.siliconflow_model_name
|
||||||
|
OR model.provider_model_name = canonical.siliconflow_model_name
|
||||||
|
OR model.base_model_id IN (
|
||||||
|
SELECT deprecated.id
|
||||||
|
FROM base_model_catalog deprecated
|
||||||
|
WHERE deprecated.provider_key = 'silicon-flow-openai'
|
||||||
|
AND deprecated.provider_model_name = canonical.siliconflow_model_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM platform_models peer
|
||||||
|
WHERE peer.platform_id = model.platform_id
|
||||||
|
AND peer.id <> model.id
|
||||||
|
AND peer.model_name = canonical.provider_model_name
|
||||||
|
);
|
||||||
@ -7,11 +7,15 @@ import {
|
|||||||
authTypes,
|
authTypes,
|
||||||
applyProviderDefaults,
|
applyProviderDefaults,
|
||||||
baseModelTypes,
|
baseModelTypes,
|
||||||
baseModelTypeText,
|
baseModelTypeTextForProvider,
|
||||||
createEmptyPlatformForm,
|
createEmptyPlatformForm,
|
||||||
|
modelMatchesProvider,
|
||||||
modelsForProvider,
|
modelsForProvider,
|
||||||
platformModelPayloads,
|
platformModelPayloads,
|
||||||
platformPayload,
|
platformPayload,
|
||||||
|
providerModelNameForProvider,
|
||||||
|
providerModelPresetFor,
|
||||||
|
providerModelPresets,
|
||||||
providerLabel,
|
providerLabel,
|
||||||
selectedModelsForForm,
|
selectedModelsForForm,
|
||||||
stableModelAlias,
|
stableModelAlias,
|
||||||
@ -48,7 +52,11 @@ export function PlatformManagementPanel(props: {
|
|||||||
const platformMap = useMemo(() => new Map(props.platforms.map((item) => [item.id, item])), [props.platforms]);
|
const platformMap = useMemo(() => new Map(props.platforms.map((item) => [item.id, item])), [props.platforms]);
|
||||||
const [form, setForm] = useState<PlatformWizardForm>(() => createEmptyPlatformForm(defaultProvider, providerDefaults(providerMap.get(defaultProvider))));
|
const [form, setForm] = useState<PlatformWizardForm>(() => createEmptyPlatformForm(defaultProvider, providerDefaults(providerMap.get(defaultProvider))));
|
||||||
const providerOptions = useMemo(
|
const providerOptions = useMemo(
|
||||||
() => Array.from(new Set([...props.providers.map((item) => item.providerKey), ...props.baseModels.map((item) => item.providerKey)])).filter(Boolean),
|
() => Array.from(new Set([
|
||||||
|
...props.providers.map((item) => item.providerKey),
|
||||||
|
...props.baseModels.map((item) => item.providerKey),
|
||||||
|
...props.baseModels.flatMap((item) => providerModelPresets(item).map((preset) => preset.providerKey)),
|
||||||
|
])).filter(Boolean),
|
||||||
[props.baseModels, props.providers],
|
[props.baseModels, props.providers],
|
||||||
);
|
);
|
||||||
const availableModels = useMemo(() => props.baseModels.filter((item) => item.status !== 'hidden'), [props.baseModels]);
|
const availableModels = useMemo(() => props.baseModels.filter((item) => item.status !== 'hidden'), [props.baseModels]);
|
||||||
@ -763,13 +771,20 @@ function ModelSelection(props: {
|
|||||||
<div className="platformModelChoices">
|
<div className="platformModelChoices">
|
||||||
{selectedModels.map((model) => {
|
{selectedModels.map((model) => {
|
||||||
const modelLabel = stableModelAlias(model) || model.providerModelName;
|
const modelLabel = stableModelAlias(model) || model.providerModelName;
|
||||||
const providerModelName = props.form.modelNameMappings[model.id] ?? model.providerModelName;
|
const defaultProviderModelName = providerModelNameForProvider(model, props.currentProvider);
|
||||||
|
const providerModelName = props.form.modelNameMappings[model.id] ?? defaultProviderModelName;
|
||||||
|
const providerPreset = providerModelPresetFor(model, props.currentProvider);
|
||||||
return (
|
return (
|
||||||
<div className="platformModelChoice" key={model.id}>
|
<div className="platformModelChoice" key={model.id}>
|
||||||
<div className="platformModelChoiceMain">
|
<div className="platformModelChoiceMain">
|
||||||
<span>
|
<span>
|
||||||
<strong>{modelLabel}</strong>
|
<strong>{modelLabel}</strong>
|
||||||
<small>{props.providerMap.get(model.providerKey)?.displayName ?? model.providerKey} · {model.providerModelName} · {baseModelTypeText(model)}</small>
|
<small>
|
||||||
|
{props.providerMap.get(model.providerKey)?.displayName ?? model.providerKey}
|
||||||
|
{providerPreset ? ` · ${props.providerName} 真实名 ${defaultProviderModelName}` : ` · ${model.providerModelName}`}
|
||||||
|
{' · '}
|
||||||
|
{baseModelTypeTextForProvider(model, props.currentProvider)}
|
||||||
|
</small>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="platformModelChoiceFields">
|
<div className="platformModelChoiceFields">
|
||||||
@ -777,7 +792,7 @@ function ModelSelection(props: {
|
|||||||
调用模型名
|
调用模型名
|
||||||
<Input
|
<Input
|
||||||
aria-label={`${modelLabel} 调用模型名`}
|
aria-label={`${modelLabel} 调用模型名`}
|
||||||
placeholder={model.providerModelName}
|
placeholder={defaultProviderModelName}
|
||||||
value={providerModelName}
|
value={providerModelName}
|
||||||
onChange={(event) => updateModelNameMapping(model.id, event.target.value)}
|
onChange={(event) => updateModelNameMapping(model.id, event.target.value)}
|
||||||
/>
|
/>
|
||||||
@ -826,19 +841,30 @@ function ModelPickerDialog(props: {
|
|||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [providerFilter, setProviderFilter] = useState(props.currentProvider || 'all');
|
const [providerFilter, setProviderFilter] = useState(props.currentProvider || 'all');
|
||||||
const [draftIds, setDraftIds] = useState<string[]>(props.selectedModelIds);
|
const [draftIds, setDraftIds] = useState<string[]>(props.selectedModelIds);
|
||||||
const providerOptions = useMemo(() => Array.from(new Set(props.models.map((model) => model.providerKey))).filter(Boolean), [props.models]);
|
const providerOptions = useMemo(() => Array.from(new Set([
|
||||||
|
...props.models.map((model) => model.providerKey),
|
||||||
|
...props.models.flatMap((model) => providerModelPresets(model).map((preset) => preset.providerKey)),
|
||||||
|
])).filter(Boolean), [props.models]);
|
||||||
const draftIdSet = new Set(draftIds);
|
const draftIdSet = new Set(draftIds);
|
||||||
const keyword = query.trim().toLowerCase();
|
const keyword = query.trim().toLowerCase();
|
||||||
const filteredModels = props.models.filter((model) => {
|
const filteredModels = props.models.filter((model) => {
|
||||||
if (providerFilter !== 'all' && model.providerKey !== providerFilter) return false;
|
if (providerFilter !== 'all' && !modelMatchesProvider(model, providerFilter)) return false;
|
||||||
|
const providerForText = providerFilter === 'all' ? props.currentProvider : providerFilter;
|
||||||
if (!keyword) return true;
|
if (!keyword) return true;
|
||||||
return [
|
return [
|
||||||
stableModelAlias(model),
|
stableModelAlias(model),
|
||||||
model.providerModelName,
|
model.providerModelName,
|
||||||
|
providerModelNameForProvider(model, providerForText),
|
||||||
model.canonicalModelKey,
|
model.canonicalModelKey,
|
||||||
baseModelTypeText(model),
|
baseModelTypeTextForProvider(model, providerForText),
|
||||||
model.providerKey,
|
model.providerKey,
|
||||||
props.providerMap.get(model.providerKey)?.displayName,
|
props.providerMap.get(model.providerKey)?.displayName,
|
||||||
|
...providerModelPresets(model).flatMap((preset) => [
|
||||||
|
preset.providerKey,
|
||||||
|
preset.providerModelName,
|
||||||
|
preset.displayName,
|
||||||
|
preset.sourceProviderName,
|
||||||
|
]),
|
||||||
JSON.stringify(model.capabilities ?? {}),
|
JSON.stringify(model.capabilities ?? {}),
|
||||||
].filter(Boolean).join(' ').toLowerCase().includes(keyword);
|
].filter(Boolean).join(' ').toLowerCase().includes(keyword);
|
||||||
});
|
});
|
||||||
@ -919,16 +945,26 @@ function ModelPickerDialog(props: {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="modelPickerList">
|
<div className="modelPickerList">
|
||||||
{filteredModels.length ? filteredModels.map((model) => (
|
{filteredModels.length ? filteredModels.map((model) => {
|
||||||
<label className="modelPickerItem" key={model.id}>
|
const providerForText = providerFilter === 'all' ? props.currentProvider : providerFilter;
|
||||||
<input type="checkbox" checked={draftIdSet.has(model.id)} onChange={() => toggleModel(model.id)} />
|
const preset = providerModelPresetFor(model, providerForText);
|
||||||
<span>
|
const providerModelName = providerModelNameForProvider(model, providerForText);
|
||||||
<strong>{stableModelAlias(model) || model.providerModelName}</strong>
|
return (
|
||||||
<small>{props.providerMap.get(model.providerKey)?.displayName ?? model.providerKey} · {model.providerModelName} · {baseModelTypeText(model)}</small>
|
<label className="modelPickerItem" key={model.id}>
|
||||||
</span>
|
<input type="checkbox" checked={draftIdSet.has(model.id)} onChange={() => toggleModel(model.id)} />
|
||||||
<Badge variant="outline">{baseModelTypeText(model)}</Badge>
|
<span>
|
||||||
</label>
|
<strong>{stableModelAlias(model) || model.providerModelName}</strong>
|
||||||
)) : <div className="platformModelEmpty">没有匹配的模型。</div>}
|
<small>
|
||||||
|
{props.providerMap.get(model.providerKey)?.displayName ?? model.providerKey}
|
||||||
|
{preset ? ` · ${providerForText} 真实名 ${providerModelName}` : ` · ${model.providerModelName}`}
|
||||||
|
{' · '}
|
||||||
|
{baseModelTypeTextForProvider(model, providerForText)}
|
||||||
|
</small>
|
||||||
|
</span>
|
||||||
|
<Badge variant="outline">{baseModelTypeTextForProvider(model, providerForText)}</Badge>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}) : <div className="platformModelEmpty">没有匹配的模型。</div>}
|
||||||
</div>
|
</div>
|
||||||
<footer className="modelPickerActions">
|
<footer className="modelPickerActions">
|
||||||
<Button type="button" variant="outline" onClick={props.onClose}>取消</Button>
|
<Button type="button" variant="outline" onClick={props.onClose}>取消</Button>
|
||||||
|
|||||||
@ -54,6 +54,18 @@ export interface ProviderConnectionDefaults {
|
|||||||
defaultBaseUrl?: string;
|
defaultBaseUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProviderModelPreset {
|
||||||
|
providerKey: string;
|
||||||
|
providerCode?: string;
|
||||||
|
providerModelName?: string;
|
||||||
|
modelAlias?: string;
|
||||||
|
displayName?: string;
|
||||||
|
modelType?: string[];
|
||||||
|
iconPath?: string;
|
||||||
|
sourceProviderName?: string;
|
||||||
|
baseProviderKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export function createEmptyPlatformForm(provider = '', defaults?: ProviderConnectionDefaults): PlatformWizardForm {
|
export function createEmptyPlatformForm(provider = '', defaults?: ProviderConnectionDefaults): PlatformWizardForm {
|
||||||
return {
|
return {
|
||||||
provider,
|
provider,
|
||||||
@ -110,7 +122,7 @@ export function applyProviderDefaults(form: PlatformWizardForm, provider: string
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function modelsForProvider(models: BaseModelCatalogItem[], provider: string) {
|
export function modelsForProvider(models: BaseModelCatalogItem[], provider: string) {
|
||||||
return models.filter((item) => item.providerKey === provider && item.status !== 'hidden');
|
return models.filter((item) => item.status !== 'hidden' && modelMatchesProvider(item, provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectedModelsForForm(models: BaseModelCatalogItem[], form: PlatformWizardForm) {
|
export function selectedModelsForForm(models: BaseModelCatalogItem[], form: PlatformWizardForm) {
|
||||||
@ -152,27 +164,37 @@ export function platformPayload(form: PlatformWizardForm, options: { preserveEmp
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function platformModelPayloads(models: BaseModelCatalogItem[], form: PlatformWizardForm): PlatformModelBindingInput[] {
|
export function platformModelPayloads(models: BaseModelCatalogItem[], form: PlatformWizardForm): PlatformModelBindingInput[] {
|
||||||
return selectedModelsForForm(models, form).map((model) => ({
|
return selectedModelsForForm(models, form).map((model) => {
|
||||||
baseModelId: model.id,
|
const preset = providerModelPresetFor(model, form.provider);
|
||||||
canonicalModelKey: model.canonicalModelKey,
|
const providerModelName =
|
||||||
modelName: model.providerModelName,
|
optionalString(form.modelNameMappings[model.id]) ??
|
||||||
providerModelName: optionalString(form.modelNameMappings[model.id]) ?? model.providerModelName,
|
providerModelNameForProvider(model, form.provider);
|
||||||
modelAlias: stableModelAlias(model),
|
return {
|
||||||
modelType: baseModelTypes(model),
|
baseModelId: model.id,
|
||||||
displayName: stableModelAlias(model) || model.providerModelName,
|
canonicalModelKey: model.canonicalModelKey,
|
||||||
pricingMode: 'inherit_discount',
|
modelName: model.providerModelName,
|
||||||
discountFactor: optionalPositiveNumber(form.modelDiscountFactors[model.id]) ?? optionalPositiveNumber(form.modelDiscountFactor),
|
providerModelName,
|
||||||
retryPolicy: form.modelOverrideRetry
|
modelAlias: preset?.modelAlias || stableModelAlias(model),
|
||||||
? { enabled: form.modelRetryEnabled, maxAttempts: form.modelRetryEnabled ? positiveInt(form.modelRetryMaxAttempts, 2) : 1 }
|
modelType: baseModelTypesForProvider(model, form.provider),
|
||||||
: undefined,
|
displayName:
|
||||||
rateLimitPolicy: form.modelOverrideRateLimit ? rateLimitPolicyPayload({
|
preset?.displayName ||
|
||||||
rpmLimit: form.modelRpmLimit,
|
stableModelAlias(model) ||
|
||||||
rpsLimit: form.modelRpsLimit,
|
providerModelName ||
|
||||||
tpmLimit: form.modelTpmLimit,
|
model.providerModelName,
|
||||||
concurrencyLimit: form.modelConcurrencyLimit,
|
pricingMode: 'inherit_discount',
|
||||||
}) : undefined,
|
discountFactor: optionalPositiveNumber(form.modelDiscountFactors[model.id]) ?? optionalPositiveNumber(form.modelDiscountFactor),
|
||||||
runtimePolicyOverride: platformModelRuntimeOverride(form),
|
retryPolicy: form.modelOverrideRetry
|
||||||
}));
|
? { enabled: form.modelRetryEnabled, maxAttempts: form.modelRetryEnabled ? positiveInt(form.modelRetryMaxAttempts, 2) : 1 }
|
||||||
|
: undefined,
|
||||||
|
rateLimitPolicy: form.modelOverrideRateLimit ? rateLimitPolicyPayload({
|
||||||
|
rpmLimit: form.modelRpmLimit,
|
||||||
|
rpsLimit: form.modelRpsLimit,
|
||||||
|
tpmLimit: form.modelTpmLimit,
|
||||||
|
concurrencyLimit: form.modelConcurrencyLimit,
|
||||||
|
}) : undefined,
|
||||||
|
runtimePolicyOverride: platformModelRuntimeOverride(form),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stableModelAlias(model: BaseModelCatalogItem) {
|
export function stableModelAlias(model: BaseModelCatalogItem) {
|
||||||
@ -195,10 +217,56 @@ export function baseModelTypes(model: BaseModelCatalogItem) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function providerModelPresets(model: BaseModelCatalogItem): ProviderModelPreset[] {
|
||||||
|
const rawPresets = model.metadata?.providerModelPresets;
|
||||||
|
if (!Array.isArray(rawPresets)) return [];
|
||||||
|
return rawPresets
|
||||||
|
.map(readRecord)
|
||||||
|
.filter((item): item is Record<string, unknown> => Boolean(item))
|
||||||
|
.map((item) => ({
|
||||||
|
providerKey: readString(item.providerKey || item.providerCode),
|
||||||
|
providerCode: readString(item.providerCode || item.providerKey),
|
||||||
|
providerModelName: readString(item.providerModelName || item.realModelNameOverride),
|
||||||
|
modelAlias: readString(item.modelAlias || item.alias),
|
||||||
|
displayName: readString(item.displayName || item.modelAlias || item.alias),
|
||||||
|
modelType: readStringArray(item.modelType || item.modelTypes),
|
||||||
|
iconPath: readString(item.iconPath || item.icon_path),
|
||||||
|
sourceProviderName: readString(item.sourceProviderName || item.providerName),
|
||||||
|
baseProviderKey: readString(item.baseProviderKey || item.baseProviderCode),
|
||||||
|
}))
|
||||||
|
.filter((item) => item.providerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function providerModelPresetFor(model: BaseModelCatalogItem, provider: string) {
|
||||||
|
const providerKey = provider.trim();
|
||||||
|
if (!providerKey) return undefined;
|
||||||
|
return providerModelPresets(model).find(
|
||||||
|
(preset) => preset.providerKey === providerKey || preset.providerCode === providerKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function modelMatchesProvider(model: BaseModelCatalogItem, provider: string) {
|
||||||
|
if (!provider) return true;
|
||||||
|
return model.providerKey === provider || Boolean(providerModelPresetFor(model, provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function providerModelNameForProvider(model: BaseModelCatalogItem, provider: string) {
|
||||||
|
return providerModelPresetFor(model, provider)?.providerModelName || model.providerModelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function baseModelTypesForProvider(model: BaseModelCatalogItem, provider: string) {
|
||||||
|
const presetTypes = providerModelPresetFor(model, provider)?.modelType;
|
||||||
|
return presetTypes?.length ? presetTypes : baseModelTypes(model);
|
||||||
|
}
|
||||||
|
|
||||||
export function baseModelTypeText(model: BaseModelCatalogItem) {
|
export function baseModelTypeText(model: BaseModelCatalogItem) {
|
||||||
return baseModelTypes(model).join(', ');
|
return baseModelTypes(model).join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function baseModelTypeTextForProvider(model: BaseModelCatalogItem, provider: string) {
|
||||||
|
return baseModelTypesForProvider(model, provider).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
export function primaryBaseModelType(model: BaseModelCatalogItem) {
|
export function primaryBaseModelType(model: BaseModelCatalogItem) {
|
||||||
return baseModelTypes(model)[0] ?? 'text_generate';
|
return baseModelTypes(model)[0] ?? 'text_generate';
|
||||||
}
|
}
|
||||||
@ -223,6 +291,10 @@ function readString(value: unknown) {
|
|||||||
return typeof value === 'string' ? value.trim() : '';
|
return typeof value === 'string' ? value.trim() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readStringArray(value: unknown) {
|
||||||
|
return Array.isArray(value) ? value.map(String).map((item) => item.trim()).filter(Boolean) : [];
|
||||||
|
}
|
||||||
|
|
||||||
function platformModelRuntimeOverride(form: PlatformWizardForm) {
|
function platformModelRuntimeOverride(form: PlatformWizardForm) {
|
||||||
const override: Record<string, unknown> = {};
|
const override: Record<string, unknown> = {};
|
||||||
if (form.modelOverrideRetry) {
|
if (form.modelOverrideRetry) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user