feat(models): 注册 Kimi K3 和 Qwen3.8
This commit is contained in:
@@ -78,6 +78,15 @@ func applyAliyunReasoning(body map[string]any, candidate store.RuntimeModelCandi
|
||||
|
||||
body["enable_thinking"] = true
|
||||
model := chatReasoningModelName(body, candidate)
|
||||
if isAliyunQwen38MaxPreview(model) {
|
||||
if budget, ok := positiveIntFromAny(body["thinking_budget_tokens"]); ok {
|
||||
body["thinking_budget"] = budget
|
||||
delete(body, "reasoning_effort")
|
||||
return
|
||||
}
|
||||
body["reasoning_effort"] = qwen38MaxPreviewReasoningEffort(effort)
|
||||
return
|
||||
}
|
||||
if isAliyunHighMaxReasoningModel(model) {
|
||||
body["reasoning_effort"] = highMaxReasoningEffort(effort)
|
||||
return
|
||||
@@ -200,6 +209,10 @@ func isAliyunHighMaxReasoningModel(model string) bool {
|
||||
return strings.Contains(model, "deepseek-v4") || strings.HasPrefix(model, "glm-")
|
||||
}
|
||||
|
||||
func isAliyunQwen38MaxPreview(model string) bool {
|
||||
return strings.Contains(model, "qwen3.8-max-preview")
|
||||
}
|
||||
|
||||
func isAliyunThinkingBudgetModel(model string) bool {
|
||||
return strings.Contains(model, "qwen") || strings.Contains(model, "qwq") || strings.Contains(model, "qvq") || strings.Contains(model, "kimi")
|
||||
}
|
||||
@@ -219,6 +232,17 @@ func highMaxReasoningEffort(effort string) string {
|
||||
return "high"
|
||||
}
|
||||
|
||||
func qwen38MaxPreviewReasoningEffort(effort string) string {
|
||||
switch effort {
|
||||
case "low", "minimal":
|
||||
return "low"
|
||||
case "medium":
|
||||
return "medium"
|
||||
default:
|
||||
return "xhigh"
|
||||
}
|
||||
}
|
||||
|
||||
func zhipuReasoningEffort(effort string) string {
|
||||
if effort == "max" {
|
||||
return "xhigh"
|
||||
|
||||
@@ -529,6 +529,36 @@ func TestOpenAIClientChatReasoningParamsByProvider(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "aliyun qwen 3.8 preview preserves supported effort",
|
||||
provider: "aliyun-bailian-openai",
|
||||
model: "qwen3.8-max-preview",
|
||||
body: map[string]any{
|
||||
"reasoning_effort": "high",
|
||||
},
|
||||
assertion: func(t *testing.T, body map[string]any) {
|
||||
if body["enable_thinking"] != true || body["reasoning_effort"] != "xhigh" {
|
||||
t.Fatalf("aliyun qwen 3.8 should use xhigh reasoning effort, got %+v", body)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "aliyun qwen 3.8 preview uses explicit budget exclusively",
|
||||
provider: "aliyun-bailian-openai",
|
||||
model: "qwen3.8-max-preview",
|
||||
body: map[string]any{
|
||||
"reasoning_effort": "medium",
|
||||
"thinking_budget_tokens": 16384,
|
||||
},
|
||||
assertion: func(t *testing.T, body map[string]any) {
|
||||
if body["enable_thinking"] != true || body["thinking_budget"] != float64(16384) {
|
||||
t.Fatalf("aliyun qwen 3.8 should use the explicit thinking budget, got %+v", body)
|
||||
}
|
||||
if _, ok := body["reasoning_effort"]; ok {
|
||||
t.Fatalf("aliyun qwen 3.8 must not combine reasoning effort with thinking budget: %+v", body)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "same deepseek model on aliyun uses aliyun protocol",
|
||||
provider: "aliyun-bailian-openai",
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
-- Register the latest Kimi and Qwen foundation models in both the EasyAI
|
||||
-- catalog and their real upstream providers.
|
||||
--
|
||||
-- Official references:
|
||||
-- https://www.volcengine.com/docs/82379/2578669
|
||||
-- https://help.aliyun.com/zh/model-studio/token-plan-personal-overview
|
||||
-- https://help.aliyun.com/zh/model-studio/qwen-function-calling
|
||||
-- https://help.aliyun.com/zh/model-studio/qwen-api-via-openai-responses
|
||||
|
||||
WITH model_defs AS (
|
||||
SELECT *
|
||||
FROM (
|
||||
VALUES
|
||||
(
|
||||
'easyai',
|
||||
'easyai',
|
||||
'EasyAI',
|
||||
'easyai',
|
||||
'easyai:Kimi-K3',
|
||||
'Kimi-K3',
|
||||
'Kimi-K3',
|
||||
'Kimi-K3',
|
||||
'Kimi-K3',
|
||||
'Kimi 最新旗舰基础模型,原生支持视觉理解,面向 Coding 与 Agent 场景,统一模型 ID 持续升级,支持 100 万上下文。',
|
||||
'https://static.51easyai.com/kimi-logo-124.png',
|
||||
'["text_generate","image_analysis","tools_call"]'::jsonb,
|
||||
'{
|
||||
"text_generate": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"max_context_tokens": 1000000,
|
||||
"supportTool": true,
|
||||
"supportThinking": true
|
||||
},
|
||||
"image_analysis": {
|
||||
"max_context_tokens": 1000000,
|
||||
"supportThinking": true
|
||||
},
|
||||
"tools_call": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"max_context_tokens": 1000000,
|
||||
"supportTool": true,
|
||||
"supportThinking": true
|
||||
},
|
||||
"originalTypes": ["text_generate", "image_analysis", "tools_call"]
|
||||
}'::jsonb,
|
||||
'easyai:Kimi-K2.7-Code',
|
||||
'https://www.volcengine.com/docs/82379/2578669',
|
||||
false
|
||||
),
|
||||
(
|
||||
'volces-openai',
|
||||
'volces-openai',
|
||||
'火山引擎(OpenAI兼容)',
|
||||
'openai',
|
||||
'volces-openai:kimi-k3',
|
||||
'kimi-k3',
|
||||
'kimi-k3',
|
||||
'Kimi-K3',
|
||||
'Kimi-K3',
|
||||
'Kimi 最新旗舰基础模型,原生支持视觉理解,面向 Coding 与 Agent 场景,统一模型 ID 持续升级,支持 100 万上下文。',
|
||||
'https://static.51easyai.com/kimi-logo-124.png',
|
||||
'["text_generate","image_analysis","tools_call"]'::jsonb,
|
||||
'{
|
||||
"text_generate": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"max_context_tokens": 1000000,
|
||||
"supportTool": true,
|
||||
"supportThinking": true
|
||||
},
|
||||
"image_analysis": {
|
||||
"max_context_tokens": 1000000,
|
||||
"supportThinking": true
|
||||
},
|
||||
"tools_call": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"max_context_tokens": 1000000,
|
||||
"supportTool": true,
|
||||
"supportThinking": true
|
||||
},
|
||||
"originalTypes": ["text_generate", "image_analysis", "tools_call"]
|
||||
}'::jsonb,
|
||||
'volces-openai:doubao-seed-2-0-pro-260215',
|
||||
'https://www.volcengine.com/docs/82379/2578669',
|
||||
false
|
||||
),
|
||||
(
|
||||
'easyai',
|
||||
'easyai',
|
||||
'EasyAI',
|
||||
'easyai',
|
||||
'easyai:Qwen3.8-Max-Preview',
|
||||
'Qwen3.8-Max-Preview',
|
||||
'Qwen3.8-Max-Preview',
|
||||
'Qwen3.8-Max-Preview',
|
||||
'Qwen3.8-Max-Preview',
|
||||
'通义千问最新一代 2.4T 旗舰基础模型预览版,支持推理、视觉理解、文本生成与工具调用;仅面向阿里云百炼 Token Plan,需使用订阅专属 Base URL 与 API Key。',
|
||||
'https://static.51easyai.com/qwen-color.webp',
|
||||
'["text_generate","image_analysis","tools_call"]'::jsonb,
|
||||
'{
|
||||
"text_generate": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"supportTool": true,
|
||||
"supportThinking": true,
|
||||
"supportThinkingModeSwitch": true,
|
||||
"thinkingEffortLevels": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
||||
"thinkingBudgetTokensRange": [4096, 262144],
|
||||
"supportWebSearch": true
|
||||
},
|
||||
"image_analysis": {
|
||||
"supportThinking": true,
|
||||
"supportThinkingModeSwitch": true,
|
||||
"thinkingEffortLevels": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
||||
"thinkingBudgetTokensRange": [4096, 262144]
|
||||
},
|
||||
"tools_call": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"supportTool": true,
|
||||
"supportThinking": true,
|
||||
"supportThinkingModeSwitch": true,
|
||||
"thinkingEffortLevels": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
||||
"thinkingBudgetTokensRange": [4096, 262144],
|
||||
"supportWebSearch": true
|
||||
},
|
||||
"originalTypes": ["text_generate", "image_analysis", "tools_call"]
|
||||
}'::jsonb,
|
||||
'easyai:Qwen3.7-Max',
|
||||
'https://help.aliyun.com/zh/model-studio/token-plan-personal-overview',
|
||||
true
|
||||
),
|
||||
(
|
||||
'aliyun-bailian-openai',
|
||||
'aliyun-bailian-openai',
|
||||
'阿里云百炼(OpenAI兼容)',
|
||||
'openai',
|
||||
'aliyun-bailian-openai:qwen3.8-max-preview',
|
||||
'qwen3.8-max-preview',
|
||||
'qwen3.8-max-preview',
|
||||
'Qwen3.8-Max-Preview',
|
||||
'Qwen3.8-Max-Preview',
|
||||
'通义千问最新一代 2.4T 旗舰基础模型预览版,支持推理、视觉理解、文本生成与工具调用;仅面向阿里云百炼 Token Plan,需使用订阅专属 Base URL 与 API Key。',
|
||||
'https://static.51easyai.com/qwen-color.webp',
|
||||
'["text_generate","image_analysis","tools_call"]'::jsonb,
|
||||
'{
|
||||
"text_generate": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"supportTool": true,
|
||||
"supportThinking": true,
|
||||
"supportThinkingModeSwitch": true,
|
||||
"thinkingEffortLevels": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
||||
"thinkingBudgetTokensRange": [4096, 262144],
|
||||
"supportWebSearch": true
|
||||
},
|
||||
"image_analysis": {
|
||||
"supportThinking": true,
|
||||
"supportThinkingModeSwitch": true,
|
||||
"thinkingEffortLevels": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
||||
"thinkingBudgetTokensRange": [4096, 262144]
|
||||
},
|
||||
"tools_call": {
|
||||
"supportedApiProtocols": ["openai_chat_completions", "openai_responses"],
|
||||
"supportTool": true,
|
||||
"supportThinking": true,
|
||||
"supportThinkingModeSwitch": true,
|
||||
"thinkingEffortLevels": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
||||
"thinkingBudgetTokensRange": [4096, 262144],
|
||||
"supportWebSearch": true
|
||||
},
|
||||
"originalTypes": ["text_generate", "image_analysis", "tools_call"]
|
||||
}'::jsonb,
|
||||
'aliyun-bailian-openai:qwen3.7-max',
|
||||
'https://help.aliyun.com/zh/model-studio/token-plan-personal-overview',
|
||||
true
|
||||
)
|
||||
) AS defs(
|
||||
provider_key,
|
||||
source_provider_code,
|
||||
source_provider_name,
|
||||
source_spec_type,
|
||||
canonical_model_key,
|
||||
provider_model_name,
|
||||
raw_model_name,
|
||||
display_name,
|
||||
model_alias,
|
||||
description,
|
||||
icon_path,
|
||||
model_type,
|
||||
capabilities,
|
||||
template_key,
|
||||
reference_document,
|
||||
token_plan_only
|
||||
)
|
||||
),
|
||||
source_rows AS (
|
||||
SELECT
|
||||
providers.id AS provider_id,
|
||||
model_defs.*,
|
||||
COALESCE(
|
||||
template.base_billing_config,
|
||||
'{"text":{"basePrice":0.01,"baseWeight":1},"image":{"basePrice":10,"baseWeight":1},"currency":"resource"}'::jsonb
|
||||
) AS base_billing_config,
|
||||
COALESCE(
|
||||
template.default_rate_limit_policy,
|
||||
jsonb_build_object(
|
||||
'platformLimits',
|
||||
jsonb_build_object('max_concurrent_requests', 5)
|
||||
)
|
||||
) AS default_rate_limit_policy,
|
||||
COALESCE(
|
||||
template.pricing_rule_set_id,
|
||||
(SELECT id FROM model_pricing_rule_sets WHERE rule_set_key = 'default-multimodal-v1' LIMIT 1)
|
||||
) AS pricing_rule_set_id,
|
||||
COALESCE(
|
||||
template.runtime_policy_set_id,
|
||||
(SELECT id FROM model_runtime_policy_sets WHERE policy_key = 'default-runtime-v1' LIMIT 1)
|
||||
) AS runtime_policy_set_id,
|
||||
COALESCE(template.runtime_policy_override, '{}'::jsonb) AS runtime_policy_override,
|
||||
COALESCE(template.pricing_version, 1) AS pricing_version
|
||||
FROM model_defs
|
||||
LEFT JOIN model_catalog_providers providers
|
||||
ON providers.provider_key = model_defs.provider_key
|
||||
OR providers.provider_code = model_defs.provider_key
|
||||
LEFT JOIN base_model_catalog template
|
||||
ON template.canonical_model_key = model_defs.template_key
|
||||
),
|
||||
payload AS (
|
||||
SELECT
|
||||
source_rows.*,
|
||||
jsonb_build_object(
|
||||
'source', 'server-main.integration-platform',
|
||||
'sourceProviderCode', source_provider_code,
|
||||
'sourceProviderName', source_provider_name,
|
||||
'sourceSpecType', source_spec_type,
|
||||
'officialModelId', provider_model_name,
|
||||
'officialDisplayName', display_name,
|
||||
'originalTypes', model_type,
|
||||
'alias', model_alias,
|
||||
'description', description,
|
||||
'iconPath', icon_path,
|
||||
'billingType', 'external-api',
|
||||
'billingMode', '',
|
||||
'referenceModel', '',
|
||||
'modelWeight', NULL,
|
||||
'selectable', true,
|
||||
'referenceDocument', reference_document,
|
||||
'tokenPlanOnly', token_plan_only,
|
||||
'rawModel', jsonb_build_object(
|
||||
'name', raw_model_name,
|
||||
'types', model_type,
|
||||
'icon_path', icon_path,
|
||||
'alias', model_alias,
|
||||
'description', description,
|
||||
'capabilities', capabilities - 'originalTypes'
|
||||
)
|
||||
) AS metadata
|
||||
FROM source_rows
|
||||
),
|
||||
snapshot AS (
|
||||
SELECT
|
||||
payload.*,
|
||||
jsonb_build_object(
|
||||
'providerKey', provider_key,
|
||||
'canonicalModelKey', canonical_model_key,
|
||||
'invocationName', provider_model_name,
|
||||
'providerModelName', provider_model_name,
|
||||
'modelType', model_type,
|
||||
'modelAlias', provider_model_name,
|
||||
'displayName', display_name,
|
||||
'capabilities', capabilities,
|
||||
'baseBillingConfig', base_billing_config,
|
||||
'defaultRateLimitPolicy', default_rate_limit_policy,
|
||||
'pricingRuleSetId', COALESCE(pricing_rule_set_id::text, ''),
|
||||
'runtimePolicySetId', COALESCE(runtime_policy_set_id::text, ''),
|
||||
'runtimePolicyOverride', runtime_policy_override,
|
||||
'metadata', metadata,
|
||||
'pricingVersion', pricing_version,
|
||||
'status', 'active'
|
||||
) AS default_snapshot
|
||||
FROM payload
|
||||
)
|
||||
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,
|
||||
default_snapshot,
|
||||
pricing_version,
|
||||
status
|
||||
)
|
||||
SELECT
|
||||
provider_id,
|
||||
provider_key,
|
||||
canonical_model_key,
|
||||
provider_model_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,
|
||||
'system',
|
||||
default_snapshot,
|
||||
pricing_version,
|
||||
'active'
|
||||
FROM snapshot
|
||||
ON CONFLICT (canonical_model_key) DO UPDATE
|
||||
SET provider_id = EXCLUDED.provider_id,
|
||||
provider_key = EXCLUDED.provider_key,
|
||||
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,
|
||||
model_type = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.model_type
|
||||
ELSE base_model_catalog.model_type
|
||||
END,
|
||||
display_name = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.display_name
|
||||
ELSE base_model_catalog.display_name
|
||||
END,
|
||||
capabilities = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.capabilities
|
||||
ELSE base_model_catalog.capabilities
|
||||
END,
|
||||
base_billing_config = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.base_billing_config
|
||||
ELSE base_model_catalog.base_billing_config
|
||||
END,
|
||||
default_rate_limit_policy = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.default_rate_limit_policy
|
||||
ELSE base_model_catalog.default_rate_limit_policy
|
||||
END,
|
||||
pricing_rule_set_id = COALESCE(base_model_catalog.pricing_rule_set_id, EXCLUDED.pricing_rule_set_id),
|
||||
runtime_policy_set_id = COALESCE(base_model_catalog.runtime_policy_set_id, EXCLUDED.runtime_policy_set_id),
|
||||
runtime_policy_override = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.runtime_policy_override
|
||||
ELSE base_model_catalog.runtime_policy_override
|
||||
END,
|
||||
metadata = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.metadata
|
||||
ELSE base_model_catalog.metadata
|
||||
END,
|
||||
catalog_type = 'system',
|
||||
default_snapshot = EXCLUDED.default_snapshot,
|
||||
pricing_version = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN EXCLUDED.pricing_version
|
||||
ELSE base_model_catalog.pricing_version
|
||||
END,
|
||||
status = CASE
|
||||
WHEN base_model_catalog.customized_at IS NULL THEN 'active'
|
||||
ELSE base_model_catalog.status
|
||||
END,
|
||||
updated_at = now();
|
||||
|
||||
INSERT INTO platform_models (
|
||||
platform_id,
|
||||
base_model_id,
|
||||
model_name,
|
||||
provider_model_name,
|
||||
model_alias,
|
||||
model_type,
|
||||
display_name,
|
||||
capabilities,
|
||||
pricing_mode,
|
||||
billing_config,
|
||||
retry_policy,
|
||||
rate_limit_policy,
|
||||
enabled
|
||||
)
|
||||
SELECT
|
||||
platform.id,
|
||||
base_model.id,
|
||||
base_model.invocation_name,
|
||||
base_model.provider_model_name,
|
||||
base_model.invocation_name,
|
||||
base_model.model_type,
|
||||
base_model.display_name,
|
||||
base_model.capabilities,
|
||||
'inherit_discount',
|
||||
base_model.base_billing_config,
|
||||
'{"enabled":true,"maxAttempts":1}'::jsonb,
|
||||
base_model.default_rate_limit_policy,
|
||||
true
|
||||
FROM integration_platforms platform
|
||||
JOIN base_model_catalog base_model
|
||||
ON base_model.provider_key = platform.provider
|
||||
WHERE base_model.canonical_model_key IN (
|
||||
'easyai:Kimi-K3',
|
||||
'volces-openai:kimi-k3',
|
||||
'easyai:Qwen3.8-Max-Preview',
|
||||
'aliyun-bailian-openai:qwen3.8-max-preview'
|
||||
)
|
||||
AND platform.deleted_at IS NULL
|
||||
ON CONFLICT (platform_id, model_name) DO UPDATE
|
||||
SET base_model_id = EXCLUDED.base_model_id,
|
||||
provider_model_name = EXCLUDED.provider_model_name,
|
||||
model_alias = EXCLUDED.model_alias,
|
||||
display_name = EXCLUDED.display_name,
|
||||
model_type = EXCLUDED.model_type,
|
||||
capabilities = EXCLUDED.capabilities,
|
||||
pricing_mode = EXCLUDED.pricing_mode,
|
||||
billing_config = EXCLUDED.billing_config,
|
||||
retry_policy = EXCLUDED.retry_policy,
|
||||
rate_limit_policy = EXCLUDED.rate_limit_policy,
|
||||
enabled = EXCLUDED.enabled,
|
||||
updated_at = now();
|
||||
Reference in New Issue
Block a user