diff --git a/.sync-watermark b/.sync-watermark index 87c20b5..d8156dc 100644 --- a/.sync-watermark +++ b/.sync-watermark @@ -4,6 +4,6 @@ # 下次增量同步时,只处理 OpenRouter 中 created > 此值的新模型。 # # 格式: Unix timestamp(秒) -# 人读: 2026-06-24T04:45:03Z +# 人读: 2026-07-21T03:37:38Z # -1782276303 +1784554658 diff --git a/__tests__/validate.test.mjs b/__tests__/validate.test.mjs index 53ae7ce..3469373 100644 --- a/__tests__/validate.test.mjs +++ b/__tests__/validate.test.mjs @@ -271,8 +271,101 @@ describe('真实数据全量校验', () => { assert.equal(specs.length, 1, 'mimo-v2.5-asr 应且仅应有一条规格') assert.deepEqual(specs[0].match.exact, ['mimo-v2.5-asr']) + assert.equal(specs[0].spec.contextWindow, 8192) + assert.equal(specs[0].spec.maxOutputTokens, 2048) assert.deepEqual(specs[0].spec.serviceType, ['asr']) assert.ok(specs[0].spec.capabilities.includes('asr')) + assert.deepEqual(specs[0].spec.extra.modelOrigin, { + vendor: 'xiaomi', + canonicalModelId: 'mimo-v2.5-asr', + family: 'mimo-asr', + }) + assert.deepEqual(specs[0].spec.extra.speech, { + protocol: 'xiaomi-mimo-chat-asr', + protocolVersion: '1', + modes: ['batch'], + inputFormats: ['wav', 'mp3'], + languageHints: true, + }) + }) + + it('MiMo V2.5 TTS 应声明稳定协议与官方音色', () => { + const specFile = JSON.parse(readFileSync(join(ROOT, 'compute', 'model-specs', 'xiaomi.json'), 'utf8')) + const provider = JSON.parse(readFileSync(join(ROOT, 'compute', 'providers', 'xiaomi.json'), 'utf8')) + const tts = specFile.specs.find((item) => item.id === 'mimo-v2.5-tts') + const providerTts = provider.models.find((item) => item.modelName === 'mimo-v2.5-tts') + const providerAsr = provider.models.find((item) => item.modelName === 'mimo-v2.5-asr') + + assert.ok(tts, 'model-specs 中缺少 mimo-v2.5-tts') + assert.ok(providerTts, 'provider 中缺少 mimo-v2.5-tts') + assert.ok(providerAsr, 'provider 中缺少 mimo-v2.5-asr') + assert.ok(provider.services.includes('asr')) + assert.equal(tts.spec.contextWindow, 8192) + assert.equal(tts.spec.maxOutputTokens, 8192) + assert.deepEqual(tts.spec.extra.modelOrigin, { + vendor: 'xiaomi', + canonicalModelId: 'mimo-v2.5-tts', + family: 'mimo-tts', + }) + assert.equal(tts.spec.extra.speech.protocol, 'xiaomi-mimo-chat-tts') + assert.equal(tts.spec.extra.speech.protocolVersion, '1') + assert.deepEqual(tts.spec.extra.speech.modes, ['batch', 'streaming']) + assert.deepEqual(tts.spec.extra.speech.outputFormats, ['wav', 'mp3', 'pcm16le']) + assert.deepEqual(tts.spec.extra.speech.sampleRates, [24000]) + assert.equal(tts.spec.extra.speech.defaultVoice, 'mimo_default') + assert.deepEqual( + tts.spec.extra.speech.voices.map((voice) => voice.id), + ['mimo_default', '冰糖', '茉莉', '苏打', '白桦', 'Mia', 'Chloe', 'Milo', 'Dean'], + ) + assert.deepEqual( + providerTts.extra.voices.map((voice) => voice.id), + ['mimo_default', '冰糖', '茉莉', '苏打', '白桦', 'Mia', 'Chloe', 'Milo', 'Dean'], + ) + assert.deepEqual(providerTts.extra.audioFormats, ['wav', 'mp3', 'pcm16le']) + assert.equal(providerTts.extra.sampleRate, 24000) + }) + + it('OpenRouter 2026-07-21 水位线前的缺失模型应使用当前精确规格', () => { + const openai = JSON.parse(readFileSync(join(ROOT, 'compute', 'model-specs', 'openai.json'), 'utf8')) + const google = JSON.parse(readFileSync(join(ROOT, 'compute', 'model-specs', 'google.json'), 'utf8')) + const tencent = JSON.parse(readFileSync(join(ROOT, 'compute', 'model-specs', 'tencent.json'), 'utf8')) + const xai = JSON.parse(readFileSync(join(ROOT, 'compute', 'model-specs', 'xai.json'), 'utf8')) + + for (const id of ['gpt-5.6-sol-pro', 'gpt-5.6-terra-pro', 'gpt-5.6-luna-pro']) { + const modelSpec = openai.specs.find((item) => item.id === id) + assert.ok(modelSpec, `model-specs/openai.json 缺少 ${id}`) + assert.equal(modelSpec.family, id) + assert.deepEqual(modelSpec.match.exact, [id]) + assert.equal('patterns' in modelSpec.match, false) + assert.equal(modelSpec.spec.contextWindow, 1050000) + assert.equal(modelSpec.spec.maxOutputTokens, 128000) + assert.equal(modelSpec.spec.supportsReasoning, true) + } + + const gemini = google.specs.find((item) => item.id === 'gemini-3.1-flash-lite-image') + assert.ok(gemini, 'model-specs/google.json 缺少 gemini-3.1-flash-lite-image') + assert.deepEqual(gemini.match.exact, ['gemini-3.1-flash-lite-image']) + assert.equal(gemini.spec.contextWindow, 65536) + assert.equal(gemini.spec.maxOutputTokens, 4096) + assert.ok(gemini.spec.capabilities.includes('image_generation')) + assert.ok(gemini.spec.capabilities.includes('image_editing')) + + const hy3 = tencent.specs.find((item) => item.id === 'hy3') + assert.ok(hy3, 'model-specs/tencent.json 缺少 hy3') + assert.deepEqual(hy3.match.exact, ['hy3']) + assert.equal(hy3.spec.contextWindow, 262144) + assert.equal(hy3.spec.maxOutputTokens, 128000) + + const grok = xai.specs.find((item) => item.id === 'grok-4.5') + assert.ok(grok, 'model-specs/xai.json 缺少 grok-4.5') + assert.deepEqual(grok.match.exact, ['grok-4.5']) + assert.equal(grok.spec.contextWindow, 500000) + assert.equal(grok.spec.maxOutputTokens, 450000) + assert.equal(grok.spec.extra.thinkingOnly, true) + + const watermark = readFileSync(join(ROOT, '.sync-watermark'), 'utf8') + assert.match(watermark, /# 人读: 2026-07-21T03:37:38Z/) + assert.equal(Number(watermark.trim().split(/\s+/).at(-1)), 1784554658) }) it('Ox Alpha 应提供与 OpenRouter 一致的多模态推理规格', () => { @@ -751,6 +844,64 @@ describe('model-spec schema 接入面边界', () => { } assert.equal(validate(data), true, JSON.stringify(validate.errors)) }) + + it('接受 extra.modelOrigin/speech,同时保持其他扩展开放', () => { + const data = { + specs: [{ + id: 'mimo-v2.5-asr', + spec: { + extra: { + modelOrigin: { + vendor: 'xiaomi', + canonicalModelId: 'mimo-v2.5-asr', + family: 'mimo-asr', + }, + speech: { + protocol: 'xiaomi-mimo-chat-asr', + protocolVersion: '1', + modes: ['batch', 'streaming'], + inputFormats: ['wav', 'mp3'], + languageHints: true, + }, + intrinsicBudgetHint: 8192, + }, + }, + }], + } + assert.equal(validate(data), true, JSON.stringify(validate.errors)) + }) + + it('严格拒绝非法 speech 二级结构和字段越界', () => { + const invalidProfile = { + specs: [{ + id: 'bad-asr', + spec: { + extra: { + speech: { + protocol: 'xiaomi-mimo-chat-asr', + protocolVersion: '1', + modes: ['batch', 'batch'], + }, + }, + }, + }], + } + assert.equal(validate(invalidProfile), false) + + const misplaced = { + specs: [{ + id: 'bad-asr', + spec: { + speech: { + protocol: 'xiaomi-mimo-chat-asr', + protocolVersion: '1', + modes: ['batch'], + }, + }, + }], + } + assert.equal(validate(misplaced), false) + }) }) // ==================== Manifest schema ==================== diff --git a/compute/model-specs/google.json b/compute/model-specs/google.json index 651eaf2..54e3b40 100644 --- a/compute/model-specs/google.json +++ b/compute/model-specs/google.json @@ -1,6 +1,33 @@ { "description": "Google Gemini 系列模型规格。参数来源:config-center compute/providers/google.json。", "specs": [ + { + "id": "gemini-3.1-flash-lite-image", + "displayName": "Gemini 3.1 Flash Lite Image (Nano Banana 2 Lite)", + "family": "gemini-3.1-flash-lite-image", + "match": { + "exact": [ + "gemini-3.1-flash-lite-image" + ] + }, + "spec": { + "contextWindow": 65536, + "maxOutputTokens": 4096, + "capabilities": [ + "chat", + "vision", + "image_generation", + "image_editing", + "tool_use" + ], + "serviceType": [ + "image_gen" + ], + "supportsReasoning": true, + "description": "Nano Banana 2 Lite,Google 面向高吞吐图像生成与编辑的轻量模型", + "releasedAt": "2026-06-30" + } + }, { "id": "gemini-3.5-flash", "displayName": "Gemini 3.5 Flash", diff --git a/compute/model-specs/openai.json b/compute/model-specs/openai.json index 1cbf39e..a101e76 100644 --- a/compute/model-specs/openai.json +++ b/compute/model-specs/openai.json @@ -749,6 +749,35 @@ } } }, + { + "id": "gpt-5.6-sol-pro", + "displayName": "GPT-5.6 Sol Pro", + "family": "gpt-5.6-sol-pro", + "match": { + "exact": [ + "gpt-5.6-sol-pro" + ] + }, + "spec": { + "contextWindow": 1050000, + "maxOutputTokens": 128000, + "capabilities": [ + "chat", + "reasoning", + "code", + "vision", + "long_context", + "tool_use", + "agent" + ], + "serviceType": [ + "chat" + ], + "supportsReasoning": true, + "description": "GPT-5.6 Sol 的 Pro 固定推理变体,由上游以 reasoning.mode=pro 提供", + "releasedAt": "2026-07-09" + } + }, { "id": "gpt-5.6-terra", "displayName": "GPT-5.6 Terra", @@ -788,6 +817,35 @@ } } }, + { + "id": "gpt-5.6-terra-pro", + "displayName": "GPT-5.6 Terra Pro", + "family": "gpt-5.6-terra-pro", + "match": { + "exact": [ + "gpt-5.6-terra-pro" + ] + }, + "spec": { + "contextWindow": 1050000, + "maxOutputTokens": 128000, + "capabilities": [ + "chat", + "reasoning", + "code", + "vision", + "long_context", + "tool_use", + "agent" + ], + "serviceType": [ + "chat" + ], + "supportsReasoning": true, + "description": "GPT-5.6 Terra 的 Pro 固定推理变体,由上游以 reasoning.mode=pro 提供", + "releasedAt": "2026-07-09" + } + }, { "id": "gpt-5.6-luna", "displayName": "GPT-5.6 Luna", @@ -827,6 +885,36 @@ } } }, + { + "id": "gpt-5.6-luna-pro", + "displayName": "GPT-5.6 Luna Pro", + "family": "gpt-5.6-luna-pro", + "match": { + "exact": [ + "gpt-5.6-luna-pro" + ] + }, + "spec": { + "contextWindow": 1050000, + "maxOutputTokens": 128000, + "capabilities": [ + "chat", + "reasoning", + "code", + "vision", + "long_context", + "tool_use", + "agent", + "fast" + ], + "serviceType": [ + "chat" + ], + "supportsReasoning": true, + "description": "GPT-5.6 Luna 的 Pro 固定推理变体,由上游以 reasoning.mode=pro 提供", + "releasedAt": "2026-07-09" + } + }, { "id": "gpt-5.4-mini", "displayName": "GPT-5.4 Mini", diff --git a/compute/model-specs/tencent.json b/compute/model-specs/tencent.json index bdd75f2..8a3bec9 100644 --- a/compute/model-specs/tencent.json +++ b/compute/model-specs/tencent.json @@ -1,6 +1,35 @@ { "description": "腾讯混元系列模型规格。参数来源:config-center compute/providers/tencent.json。hunyuan-2.0-thinking 和 hunyuan-t1 为推理模型。", "specs": [ + { + "id": "hy3", + "displayName": "混元 Hy3", + "family": "hy3", + "match": { + "exact": [ + "hy3" + ] + }, + "spec": { + "contextWindow": 262144, + "maxOutputTokens": 128000, + "capabilities": [ + "chat", + "reasoning", + "code", + "tool_use", + "agent" + ], + "serviceType": [ + "chat", + "reasoning" + ], + "defaultTemperature": 0.9, + "supportsReasoning": true, + "description": "腾讯混元 Hy3 正式版,295B MoE(21B active),支持可配置推理强度", + "releasedAt": "2026-07-06" + } + }, { "id": "hy3-preview", "displayName": "混元 Hy3 Preview", diff --git a/compute/model-specs/xai.json b/compute/model-specs/xai.json index c8255e7..5a3dc44 100644 --- a/compute/model-specs/xai.json +++ b/compute/model-specs/xai.json @@ -1,6 +1,39 @@ { "description": "xAI Grok 系列模型规格。参数来源:config-center compute/providers/xai.json。Grok 网关命名变体较多(含日期戳/-fast/-reasoning 后缀),主要依赖 family=grok-4 兜底匹配。", "specs": [ + { + "id": "grok-4.5", + "displayName": "Grok 4.5", + "family": "grok-4.5", + "match": { + "exact": [ + "grok-4.5" + ] + }, + "spec": { + "contextWindow": 500000, + "maxOutputTokens": 450000, + "capabilities": [ + "chat", + "reasoning", + "code", + "vision", + "long_context", + "tool_use" + ], + "serviceType": [ + "chat", + "reasoning" + ], + "supportsReasoning": true, + "description": "xAI Grok 4.5,面向编程、知识工作和 STEM 的旗舰推理模型", + "extra": { + "thinkingOnly": true, + "thinkingDefault": true + }, + "releasedAt": "2026-07-08" + } + }, { "id": "grok-4-3", "displayName": "Grok 4.3", diff --git a/compute/model-specs/xiaomi.json b/compute/model-specs/xiaomi.json index 6ca248d..49f6c9f 100644 --- a/compute/model-specs/xiaomi.json +++ b/compute/model-specs/xiaomi.json @@ -192,6 +192,7 @@ }, "spec": { "contextWindow": 8192, + "maxOutputTokens": 8192, "capabilities": [ "tts", "multilingual", @@ -200,7 +201,43 @@ "serviceType": [ "tts" ], - "description": "小米 MiMo 语音合成,支持多语言与风格控制" + "description": "小米 MiMo 语音合成,支持多语言与风格控制", + "extra": { + "modelOrigin": { + "vendor": "xiaomi", + "canonicalModelId": "mimo-v2.5-tts", + "family": "mimo-tts" + }, + "speech": { + "protocol": "xiaomi-mimo-chat-tts", + "protocolVersion": "1", + "modes": [ + "batch", + "streaming" + ], + "outputFormats": [ + "wav", + "mp3", + "pcm16le" + ], + "sampleRates": [ + 24000 + ], + "defaultVoice": "mimo_default", + "voices": [ + { "id": "mimo_default", "label": "MiMo 默认" }, + { "id": "冰糖", "label": "冰糖" }, + { "id": "茉莉", "label": "茉莉" }, + { "id": "苏打", "label": "苏打" }, + { "id": "白桦", "label": "白桦" }, + { "id": "Mia", "label": "Mia" }, + { "id": "Chloe", "label": "Chloe" }, + { "id": "Milo", "label": "Milo" }, + { "id": "Dean", "label": "Dean" } + ] + } + }, + "releasedAt": "2026-04-23" }, "routing": { "tier": "lightweight", @@ -225,6 +262,8 @@ ] }, "spec": { + "contextWindow": 8192, + "maxOutputTokens": 2048, "capabilities": [ "asr", "multilingual" @@ -232,7 +271,27 @@ "serviceType": [ "asr" ], - "description": "小米 MiMo 语音识别模型" + "description": "小米 MiMo 语音识别模型,支持中英文语言提示与整段音频识别", + "extra": { + "modelOrigin": { + "vendor": "xiaomi", + "canonicalModelId": "mimo-v2.5-asr", + "family": "mimo-asr" + }, + "speech": { + "protocol": "xiaomi-mimo-chat-asr", + "protocolVersion": "1", + "modes": [ + "batch" + ], + "inputFormats": [ + "wav", + "mp3" + ], + "languageHints": true + } + }, + "releasedAt": "2026-06-02" }, "routing": { "tier": "lightweight", diff --git a/compute/providers/xiaomi.json b/compute/providers/xiaomi.json index bae4bac..1f0d594 100644 --- a/compute/providers/xiaomi.json +++ b/compute/providers/xiaomi.json @@ -14,7 +14,8 @@ "services": [ "chat", "reasoning", - "tts" + "tts", + "asr" ], "models": [ { @@ -49,24 +50,37 @@ ], "extra": { "voices": [ - {"id": "default_zh", "label": "默认中文女声"}, - {"id": "default_en", "label": "默认英文女声"}, {"id": "mimo_default", "label": "MiMo 默认"}, - {"id": "Bingtang", "label": "冰糖"}, - {"id": "Moli", "label": "茉莉"}, - {"id": "Suda", "label": "苏打"}, - {"id": "Baihua", "label": "白桦"}, + {"id": "冰糖", "label": "冰糖"}, + {"id": "茉莉", "label": "茉莉"}, + {"id": "苏打", "label": "苏打"}, + {"id": "白桦", "label": "白桦"}, {"id": "Mia", "label": "Mia"}, {"id": "Chloe", "label": "Chloe"}, {"id": "Milo", "label": "Milo"}, {"id": "Dean", "label": "Dean"} ], - "audioFormats": ["mp3", "wav"], + "audioFormats": ["wav", "mp3", "pcm16le"], "sampleRate": 24000, "endpoint": "/chat/completions", "authHeader": "api-key" } }, + { + "modelName": "mimo-v2.5-asr", + "displayName": "MiMo-V2.5-ASR", + "serviceType": [ + "asr" + ], + "description": "小米 MiMo 语音识别模型,支持中英文提示与整段 WAV/MP3 识别", + "contextWindow": 8192, + "maxOutputTokens": 2048, + "capabilities": [ + "asr", + "multilingual" + ], + "extra": {} + }, { "modelName": "mimo-v2.5-tts-voicedesign", "displayName": "MiMo-V2.5-TTS-VoiceDesign", diff --git a/manifest.json b/manifest.json index 1e1eb10..1bdd44f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "presetDataVersion": 106, + "presetDataVersion": 107, "updatedAt": "2026-08-31", "description": "DesireCore 官方配置中心" } diff --git a/schemas/model-spec.schema.json b/schemas/model-spec.schema.json index 4d50484..ba6e135 100644 --- a/schemas/model-spec.schema.json +++ b/schemas/model-spec.schema.json @@ -18,6 +18,107 @@ }, "additionalProperties": false, "definitions": { + "modelOrigin": { + "type": "object", + "required": ["vendor", "canonicalModelId"], + "properties": { + "vendor": { + "type": "string", + "minLength": 1, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "description": "规范化原厂商 ID,如 xiaomi、openai、alibaba、minimax;不得填写当前算力 Provider ID。" + }, + "canonicalModelId": { + "type": "string", + "minLength": 1, + "description": "原厂模型的稳定识别 ID,用于适配器匹配、诊断和日志;不替代实际路由使用的 modelName/apiModelId。" + }, + "family": { + "type": "string", + "minLength": 1, + "description": "跨小版本稳定的模型家族,如 mimo-asr;同一协议下的后续模型版本可复用该家族语义。" + } + }, + "additionalProperties": false + }, + "speechVoice": { + "type": "object", + "required": ["id", "label"], + "properties": { + "id": { + "type": "string", + "minLength": 1, + "description": "传给上游语音接口的稳定音色 ID。" + }, + "label": { + "type": "string", + "minLength": 1, + "description": "面向用户展示的音色名称。" + } + }, + "additionalProperties": false + }, + "speechModelProfile": { + "type": "object", + "required": ["protocol", "protocolVersion", "modes"], + "properties": { + "protocol": { + "type": "string", + "minLength": 1, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "description": "稳定语音协议族,如 xiaomi-mimo-chat-asr;模型常规迭代不得改变此值。" + }, + "protocolVersion": { + "type": "string", + "pattern": "^[1-9][0-9]*$", + "description": "协议不兼容版本号;只有请求、认证、响应或流式状态机发生不兼容变化时才递增。" + }, + "modes": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "enum": ["batch", "streaming"] }, + "description": "模型协议支持的调用模式:batch(整段)和/或 streaming(流式)。" + }, + "inputFormats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 }, + "description": "ASR 上游接受的音频格式,如 wav、mp3、webm;格式名使用适配器可识别的规范值。" + }, + "outputFormats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 }, + "description": "TTS 上游可返回的音频格式,如 pcm16le、wav、mp3;运行时需与适配器和播放器能力求交集。" + }, + "sampleRates": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "integer", "minimum": 1 }, + "description": "支持的采样率(Hz);运行时不得假设固定采样率,需完成能力协商。" + }, + "defaultVoice": { + "type": "string", + "minLength": 1, + "description": "TTS 推荐默认音色 ID;声明 voices 时必须能在 voices 中找到同 ID 条目。" + }, + "voices": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/speechVoice" }, + "description": "TTS 可选音色列表,统一为稳定 id 与用户可读 label。" + }, + "languageHints": { + "type": "boolean", + "description": "ASR 协议是否接受语言提示参数;不是模型支持语言范围清单。" + } + }, + "additionalProperties": false + }, "modelSpec": { "type": "object", "required": ["id", "spec"], @@ -158,9 +259,11 @@ }, "extra": { "type": "object", - "description": "模型内在附加配置(如 apiModelId 全名、推理预算等)。不含价格,也不得声明 Provider/接入面的 reasoning effort 能力。", + "description": "模型内在开放扩展:modelOrigin 描述真实原厂身份,speech 描述可跨小版本复用的语音协议;不含价格,也不得声明 Provider/接入面的 reasoning effort 能力。", "not": { "required": ["reasoning"] }, "properties": { + "modelOrigin": { "$ref": "#/definitions/modelOrigin" }, + "speech": { "$ref": "#/definitions/speechModelProfile" }, "thinkingOnly": { "type": "boolean", "description": "模型强制思考、无法关闭(如 GLM-5.3 系列 thinking.type 仅接受 enabled)。true 时客户端把 off 从可选档位移除,并把内部 suppressThinking 降级为 auto,避免发出必然被上游拒绝的关闭参数。只描述模型固有属性,不声明接入面支持哪些深度档位——后者只能写在 provider model 的 extra.reasoning 中。"