From bd607a8ad76f13b555c4c1a61b9f485619f1d072 Mon Sep 17 00:00:00 2001 From: Johnson Date: Mon, 27 Jul 2026 15:29:56 +0800 Subject: [PATCH] feat(compute): add MiMo speech protocol metadata --- __tests__/validate.test.mjs | 105 +++++++++++++++++++++++++++++++ compute/model-specs/xiaomi.json | 63 ++++++++++++++++++- compute/providers/xiaomi.json | 30 ++++++--- manifest.json | 4 +- schemas/model-spec.schema.json | 107 +++++++++++++++++++++++++++++++- 5 files changed, 296 insertions(+), 13 deletions(-) diff --git a/__tests__/validate.test.mjs b/__tests__/validate.test.mjs index a6aec77..9e6af3a 100644 --- a/__tests__/validate.test.mjs +++ b/__tests__/validate.test.mjs @@ -201,8 +201,55 @@ 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') + + assert.ok(tts, 'model-specs 中缺少 mimo-v2.5-tts') + assert.ok(providerTts, 'provider 中缺少 mimo-v2.5-tts') + 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('MiMo V2.5 仅非 Pro 型号应声明多模态能力', () => { @@ -501,6 +548,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/xiaomi.json b/compute/model-specs/xiaomi.json index 877f4e9..47a06d2 100644 --- a/compute/model-specs/xiaomi.json +++ b/compute/model-specs/xiaomi.json @@ -156,6 +156,7 @@ }, "spec": { "contextWindow": 8192, + "maxOutputTokens": 8192, "capabilities": [ "tts", "multilingual", @@ -164,7 +165,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" } }, { @@ -177,6 +214,8 @@ ] }, "spec": { + "contextWindow": 8192, + "maxOutputTokens": 2048, "capabilities": [ "asr", "multilingual" @@ -184,7 +223,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" } }, { 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 e9e9fc7..64598d7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "presetDataVersion": 87, - "updatedAt": "2026-08-08", + "presetDataVersion": 88, + "updatedAt": "2026-08-09", "description": "DesireCore 官方配置中心" } diff --git a/schemas/model-spec.schema.json b/schemas/model-spec.schema.json index 24c921f..9742de9 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"], @@ -98,8 +199,12 @@ }, "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" } + }, "additionalProperties": true }, "releasedAt": {