diff --git a/__tests__/validate.test.mjs b/__tests__/validate.test.mjs index ac6bcaa..0ee66ae 100644 --- a/__tests__/validate.test.mjs +++ b/__tests__/validate.test.mjs @@ -105,7 +105,7 @@ describe('真实数据全量校验', () => { if (spec.routing) routed.push(spec) } } - assert.equal(routed.length, 41) + assert.equal(routed.length, 43) assert.equal(routed.every((spec) => spec.routing.reasoning.supportedModes.includes(spec.routing.reasoning.defaultMode)), true) assert.equal(routed.every((spec) => Array.isArray(spec.spec.capabilities)), true) }) diff --git a/compute/model-specs/stealth.json b/compute/model-specs/stealth.json index 0fbdcd4..c5c904b 100644 --- a/compute/model-specs/stealth.json +++ b/compute/model-specs/stealth.json @@ -1,9 +1,9 @@ { - "description": "Stealth 系列模型规格。参数来源:OpenRouter Models API。", + "description": "Stealth 系列模型规格。参数来源:OpenRouter Models API。这些是厂商匿名发布的临时别名,揭晓后用 spec.extra.modelOrigin 标注真身,条目本身保留——云端算力可能仍以旧别名下发该模型。", "specs": [ { "id": "ox-alpha", - "displayName": "Ox Alpha", + "displayName": "GLM-5.3-Flash (Ox Alpha)", "family": "ox-alpha", "match": { "exact": [ @@ -34,10 +34,15 @@ "defaultTemperature": 1, "defaultTopP": 0.95, "supportsReasoning": true, - "description": "Ox Alpha 多模态推理模型,面向编程、长程 Agent、复杂推理与生产工作负载", + "description": "Ox Alpha 是智谱 GLM-5.3-Flash 的匿名发布别名(2026-08-26 揭晓)。原生多模态推理模型,面向高效编码与长程 Agent 任务,100 万上下文;强制思考,无法关闭", "extra": { "thinkingDefault": true, - "thinkingOnly": true + "thinkingOnly": true, + "modelOrigin": { + "vendor": "zhipu", + "canonicalModelId": "glm-5.3-flash", + "family": "glm-5.3-flash" + } }, "releasedAt": "2026-08-21" } diff --git a/compute/model-specs/zhipu.json b/compute/model-specs/zhipu.json index 3bda3ce..0054eed 100644 --- a/compute/model-specs/zhipu.json +++ b/compute/model-specs/zhipu.json @@ -1,6 +1,115 @@ { "description": "智谱 GLM 系列模型规格。参数来源:config-center compute/providers/zhipu.json。glm-5 与 glm-5.1/glm-5-turbo/glm-5v-turbo 前缀相近,故各自仅用 exact 主键匹配,不用宽 pattern 以防误吞。", "specs": [ + { + "id": "glm-5.3", + "displayName": "GLM-5.3", + "family": "glm-5.3", + "match": { + "exact": [ + "glm-5.3", + "z-ai/glm-5.3" + ] + }, + "spec": { + "contextWindow": 1048576, + "maxOutputTokens": 131072, + "capabilities": [ + "chat", + "reasoning", + "deep_thinking", + "code", + "math", + "multilingual", + "tool_use", + "agent", + "long_context" + ], + "serviceType": [ + "chat" + ], + "defaultTemperature": 1, + "defaultTopP": 0.95, + "supportsReasoning": true, + "description": "智谱 GLM-5.3 大推理模型,面向复杂软件工程与长程 Agent 任务,100 万上下文;强制思考,无法关闭", + "extra": { + "thinkingDefault": true, + "thinkingOnly": true + }, + "releasedAt": "2026-08-16" + }, + "routing": { + "tier": "flagship", + "routingPriority": 50, + "eligibleForAgent": true, + "defaultReference": false, + "reasoning": { + "supportedModes": [ + "auto", + "low", + "high", + "max" + ], + "defaultMode": "high" + } + } + }, + { + "id": "glm-5.3-flash", + "displayName": "GLM-5.3-Flash", + "family": "glm-5.3-flash", + "match": { + "exact": [ + "glm-5.3-flash", + "z-ai/glm-5.3-flash" + ] + }, + "spec": { + "contextWindow": 1048576, + "maxOutputTokens": 131072, + "capabilities": [ + "chat", + "reasoning", + "deep_thinking", + "code", + "tool_use", + "agent", + "long_context", + "vision", + "image_understanding", + "video_understanding" + ], + "serviceType": [ + "chat", + "reasoning", + "vision" + ], + "defaultTemperature": 1, + "defaultTopP": 0.95, + "supportsReasoning": true, + "description": "智谱 GLM-5.3-Flash 原生多模态模型,混合稀疏与线性注意力,面向高效编码与长程 Agent 任务,100 万上下文;强制思考,无法关闭", + "extra": { + "thinkingDefault": true, + "thinkingOnly": true + }, + "releasedAt": "2026-08-26" + }, + "routing": { + "tier": "balanced", + "routingPriority": 48, + "eligibleForAgent": true, + "defaultReference": false, + "reasoning": { + "supportedModes": [ + "auto", + "low", + "high", + "max" + ], + "defaultMode": "high" + } + } + }, { "id": "glm-5.2", "displayName": "GLM-5.2", diff --git a/manifest.json b/manifest.json index 51005e0..889a263 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "presetDataVersion": 94, - "updatedAt": "2026-08-25", + "presetDataVersion": 95, + "updatedAt": "2026-08-26", "description": "DesireCore 官方配置中心" } diff --git a/schemas/model-spec.schema.json b/schemas/model-spec.schema.json index ddc82f7..4d50484 100644 --- a/schemas/model-spec.schema.json +++ b/schemas/model-spec.schema.json @@ -160,6 +160,16 @@ "type": "object", "description": "模型内在附加配置(如 apiModelId 全名、推理预算等)。不含价格,也不得声明 Provider/接入面的 reasoning effort 能力。", "not": { "required": ["reasoning"] }, + "properties": { + "thinkingOnly": { + "type": "boolean", + "description": "模型强制思考、无法关闭(如 GLM-5.3 系列 thinking.type 仅接受 enabled)。true 时客户端把 off 从可选档位移除,并把内部 suppressThinking 降级为 auto,避免发出必然被上游拒绝的关闭参数。只描述模型固有属性,不声明接入面支持哪些深度档位——后者只能写在 provider model 的 extra.reasoning 中。" + }, + "thinkingDefault": { + "type": "boolean", + "description": "模型默认开启思考。当前不改变客户端行为,仅供维护者参考。" + } + }, "additionalProperties": true }, "releasedAt": { diff --git a/schemas/provider.schema.json b/schemas/provider.schema.json index eb099ad..0a49769 100644 --- a/schemas/provider.schema.json +++ b/schemas/provider.schema.json @@ -256,6 +256,7 @@ "properties": { "reasoning": { "type": "object", + "description": "当前 Provider 接入面接受哪些 reasoning effort 值——精确档位矩阵。只能写在 provider model 的 extra 里:同一模型经不同网关支持的集合可能不同,因此 model-spec 的 spec.extra 里写本键会被客户端 applySpec 主动丢弃。想表达「这个模型根本关不掉思考」的用 model-spec 的 spec.extra.thinkingOnly,那只回答 off 能否使用、不声明任何深度档位。", "required": ["supportedEfforts"], "properties": { "supportedEfforts": { diff --git a/scripts/validate.mjs b/scripts/validate.mjs index f784ccb..9dc6250 100644 --- a/scripts/validate.mjs +++ b/scripts/validate.mjs @@ -105,6 +105,62 @@ export function validateFile(absPath, validators = loadSchemas()) { return { ok: false, schemaKey, errors: validator.errors } } +/** + * 已知会静默失效的 extra 键名。 + * + * `extra` 是开放对象:写错键名既不会被 schema 拒绝,也没有任何运行时告警——数据看着 + * 在那儿,客户端却永远读不到。desirecore#2307 就是这么来的:model-spec 的 + * extra.thinkingOnly 写下后零消费,provider 侧另有 10 处扁平 extra.reasoningEffort + * 同样从未生效,直到用户撞上一个上游 400 才暴露。 + * + * 刻意只警告、不失败:存量条目的正确取值必须逐个核实各自接入面实际接受哪些 effort, + * 一次批量改写的风险远大于收益(声明过窄会削掉模型能力,漏写 none 会让原本能关思考 + * 的模型失去该选项)。 + */ +const SUSPICIOUS_EXTRA_KEYS = { + provider: { + reasoningEffort: '客户端只读嵌套的 extra.reasoning.supportedEfforts,扁平写法永不生效', + defaultReasoningEffort: '同上,应并入 extra.reasoning.defaultEffort', + }, + spec: { + reasoningEffort: 'reasoning effort 是接入面能力,只能声明在 provider model 的 extra.reasoning 中', + }, +} + +/** 巡检静默失效键名,返回告警列表(不影响退出码)。 */ +function lintSuspiciousExtraKeys(targets) { + const warnings = [] + for (const file of targets) { + const rel = relative(ROOT, file) + let data + try { + data = JSON.parse(readFileSync(file, 'utf8')) + } catch { + continue // 解析失败由 schema 校验负责报错 + } + if (Array.isArray(data.models)) { + for (const model of data.models) { + for (const [key, hint] of Object.entries(SUSPICIOUS_EXTRA_KEYS.provider)) { + if (model?.extra && Object.hasOwn(model.extra, key)) { + warnings.push(`${rel} → models[${model.modelName}].extra.${key}:${hint}`) + } + } + } + } + if (Array.isArray(data.specs)) { + for (const entry of data.specs) { + const extra = entry?.spec?.extra + for (const [key, hint] of Object.entries(SUSPICIOUS_EXTRA_KEYS.spec)) { + if (extra && Object.hasOwn(extra, key)) { + warnings.push(`${rel} → specs[${entry.id}].spec.extra.${key}:${hint}`) + } + } + } + } + } + return warnings +} + function main() { const args = process.argv.slice(2) const fileArgIdx = args.indexOf('--file') @@ -143,6 +199,14 @@ function main() { } } + const warnings = lintSuspiciousExtraKeys(targets) + if (warnings.length > 0) { + console.log() + console.log(`静默失效键名告警(${warnings.length} 处,不影响校验结果):`) + for (const warning of warnings) console.log(` warn ${warning}`) + console.log(' 这些键写在 extra 里不会报错,但客户端从不读取;修正前请逐个核实该接入面实际接受的 effort。') + } + console.log() console.log(`Summary: ${validated} validated, ${skipped} skipped, ${failures} failed`)