easyai-ai-gateway/apps/api/migrations/0041_model_context_and_thinking_capabilities.sql
chensipeng b6c4105a94
fix(api): 补全模型能力继承与响应推导
- 合并 base model 默认能力与平台覆盖项
- 在模型响应中补齐 text_generate 的上下文与推理能力字段
- 为相关逻辑补充测试和迁移脚本
2026-05-25 20:36:32 +08:00

166 lines
8.4 KiB
PL/PgSQL

-- 为内置文本模型补充上下文窗口配置,并为官方明确支持
-- reasoning_effort / thinking_level 的模型补充可用推理深度。
CREATE OR REPLACE FUNCTION pg_temp._tmp_merge_model_capability_defaults(base jsonb, patch jsonb)
RETURNS jsonb AS $$
DECLARE
out jsonb := COALESCE(base, '{}'::jsonb);
patch_key text;
patch_value jsonb;
BEGIN
IF patch IS NULL OR patch = '{}'::jsonb THEN
RETURN out;
END IF;
FOR patch_key, patch_value IN SELECT key, value FROM jsonb_each(patch) LOOP
IF jsonb_typeof(patch_value) = 'object' AND jsonb_typeof(out -> patch_key) = 'object' THEN
-- 仅补默认值,保留已有/人工配置的精确模型能力。
out := jsonb_set(out, ARRAY[patch_key], patch_value || (out -> patch_key), true);
ELSIF NOT out ? patch_key THEN
out := jsonb_set(out, ARRAY[patch_key], patch_value, true);
END IF;
END LOOP;
RETURN out;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION pg_temp._tmp_model_capability_patches()
RETURNS TABLE(provider_key text, provider_model_name text, capability_patch jsonb) AS $$
WITH patch_defs(patch_key, capability_patch) AS (
VALUES
('doubao_seed_2', '{
"text_generate": {"max_context_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportThinking": true, "supportThinkingModeSwitch": true},
"image_analysis": {"max_context_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportThinking": true, "supportThinkingModeSwitch": true},
"tools_call": {"max_context_tokens": 262144, "max_input_tokens": 262144, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportTool": true, "supportThinking": true, "supportThinkingModeSwitch": true}
}'::jsonb),
('doubao_seed_18', '{
"text_generate": {"max_context_tokens": 262144, "max_input_tokens": 229376, "max_output_tokens": 32768, "max_thinking_tokens": 32768, "supportThinking": true, "supportThinkingModeSwitch": true, "supportStructuredOutput": true},
"image_analysis": {"max_context_tokens": 262144, "max_input_tokens": 229376, "max_output_tokens": 32768, "max_thinking_tokens": 32768, "supportThinking": true, "supportThinkingModeSwitch": true},
"tools_call": {"max_context_tokens": 262144, "max_input_tokens": 229376, "max_output_tokens": 32768, "max_thinking_tokens": 32768, "supportTool": true, "supportThinking": true, "supportThinkingModeSwitch": true, "supportStructuredOutput": true}
}'::jsonb),
('glm_47', '{
"text_generate": {"max_context_tokens": 204800, "max_input_tokens": 204800, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportThinking": true, "supportThinkingModeSwitch": true},
"image_analysis": {"max_context_tokens": 204800, "max_input_tokens": 204800, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportThinking": true, "supportThinkingModeSwitch": true},
"tools_call": {"max_context_tokens": 204800, "max_input_tokens": 204800, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportTool": true, "supportThinking": true, "supportThinkingModeSwitch": true}
}'::jsonb),
('glm_47_text_tools', '{
"text_generate": {"max_context_tokens": 204800, "max_input_tokens": 204800, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportThinking": true, "supportThinkingModeSwitch": true},
"tools_call": {"max_context_tokens": 204800, "max_input_tokens": 204800, "max_output_tokens": 131072, "max_thinking_tokens": 131072, "supportTool": true, "supportThinking": true, "supportThinkingModeSwitch": true}
}'::jsonb),
('gemini_3_text', '{
"text_generate": {"thinkingEffortLevels": ["minimal", "low", "medium", "high"]},
"tools_call": {"thinkingEffortLevels": ["minimal", "low", "medium", "high"]},
"omni": {"thinkingEffortLevels": ["minimal", "low", "medium", "high"]}
}'::jsonb),
('deepseek_v4', '{
"text_generate": {"thinkingEffortLevels": ["high", "max"]},
"tools_call": {"thinkingEffortLevels": ["high", "max"]}
}'::jsonb)
),
model_patches(provider_key, provider_model_name, patch_key) AS (
VALUES
('easyai', 'Doubao Seed 2.0 Pro', 'doubao_seed_2'),
('easyai', 'Doubao Seed 2.0 Lite', 'doubao_seed_2'),
('easyai', 'Doubao Seed 2.0 Mini', 'doubao_seed_2'),
('easyai', 'Doubao Seed 2.0 Code Preview', 'doubao_seed_2'),
('volces-openai', 'doubao-seed-2-0-pro-260215', 'doubao_seed_2'),
('volces-openai', 'doubao-seed-2-0-lite-260215', 'doubao_seed_2'),
('volces-openai', 'doubao-seed-2-0-mini-260215', 'doubao_seed_2'),
('volces-openai', 'doubao-seed-2-0-code-preview-260215', 'doubao_seed_2'),
('easyai', 'Doubao Seed 1.8', 'doubao_seed_18'),
('volces-openai', 'doubao-seed-1-8-251228', 'doubao_seed_18'),
('easyai', 'GLM-4.7', 'glm_47'),
('volces-openai', 'glm-4-7-251222', 'glm_47'),
('zhipu-openai', 'glm-4.7', 'glm_47_text_tools'),
('easyai', 'Gemini-3 Pro 预览版', 'gemini_3_text'),
('easyai', 'Gemini-3 Flash 预览版', 'gemini_3_text'),
('easyai', 'Gemini-3.1 Flash Lite 预览版', 'gemini_3_text'),
('easyai', 'Gemini-3.1 Pro 预览版', 'gemini_3_text'),
('gemini', 'gemini-3-pro-preview', 'gemini_3_text'),
('gemini', 'gemini-3-flash-preview', 'gemini_3_text'),
('gemini', 'gemini-3.1-flash-lite-preview', 'gemini_3_text'),
('gemini', 'gemini-3.1-pro-preview', 'gemini_3_text'),
('gemini-openai', 'gemini-3-pro-preview', 'gemini_3_text'),
('gemini-openai', 'gemini-3-flash-preview', 'gemini_3_text'),
('gemini-openai', 'gemini-3.1-flash-lite-preview', 'gemini_3_text'),
('gemini-openai', 'gemini-3.1-pro-preview', 'gemini_3_text'),
('easyai', 'DeepSeek-V4-Pro', 'deepseek_v4'),
('easyai', 'DeepSeek-V4-Flash', 'deepseek_v4'),
('aliyun-bailian-openai', 'deepseek-v4-pro', 'deepseek_v4'),
('aliyun-bailian-openai', 'deepseek-v4-flash', 'deepseek_v4'),
('deepseek-openai', 'deepseek-v4-pro', 'deepseek_v4'),
('deepseek-openai', 'deepseek-v4-flash', 'deepseek_v4')
)
SELECT model_patches.provider_key, model_patches.provider_model_name, patch_defs.capability_patch
FROM model_patches
JOIN patch_defs ON patch_defs.patch_key = model_patches.patch_key;
$$ LANGUAGE sql;
UPDATE base_model_catalog b
SET capabilities = pg_temp._tmp_merge_model_capability_defaults(b.capabilities, patches.capability_patch),
default_snapshot = CASE
WHEN COALESCE(b.default_snapshot, '{}'::jsonb) = '{}'::jsonb THEN b.default_snapshot
WHEN jsonb_typeof(b.default_snapshot->'metadata'->'rawModel') = 'object' THEN jsonb_set(
jsonb_set(
b.default_snapshot,
'{capabilities}',
pg_temp._tmp_merge_model_capability_defaults(
COALESCE(b.default_snapshot->'capabilities', '{}'::jsonb),
patches.capability_patch
),
true
),
'{metadata,rawModel,capabilities}',
pg_temp._tmp_merge_model_capability_defaults(
COALESCE(b.default_snapshot->'metadata'->'rawModel'->'capabilities', '{}'::jsonb),
patches.capability_patch
),
true
)
ELSE jsonb_set(
b.default_snapshot,
'{capabilities}',
pg_temp._tmp_merge_model_capability_defaults(
COALESCE(b.default_snapshot->'capabilities', '{}'::jsonb),
patches.capability_patch
),
true
)
END,
metadata = CASE
WHEN jsonb_typeof(b.metadata->'rawModel') = 'object' THEN jsonb_set(
b.metadata,
'{rawModel,capabilities}',
pg_temp._tmp_merge_model_capability_defaults(
COALESCE(b.metadata->'rawModel'->'capabilities', '{}'::jsonb),
patches.capability_patch
),
true
)
ELSE b.metadata
END,
updated_at = now()
FROM pg_temp._tmp_model_capability_patches() patches
WHERE b.provider_key = patches.provider_key
AND b.provider_model_name = patches.provider_model_name
AND b.catalog_type = 'system';
UPDATE platform_models m
SET capabilities = pg_temp._tmp_merge_model_capability_defaults(m.capabilities, patches.capability_patch),
updated_at = now()
FROM integration_platforms p, pg_temp._tmp_model_capability_patches() patches
WHERE m.platform_id = p.id
AND p.provider = patches.provider_key
AND (
m.model_name = patches.provider_model_name
OR m.provider_model_name = patches.provider_model_name
);
DROP FUNCTION pg_temp._tmp_model_capability_patches();
DROP FUNCTION pg_temp._tmp_merge_model_capability_defaults(jsonb, jsonb);