feat: add MiMo speech recognition and migrate chat models to V2.6

- Add mimo-v2.5-asr voice-to-text and list MiMo in the ASR card
- Switch defaults to mimo-v2.6-pro / mimo-v2.6-flash; retired ids are mapped automatically
- Update docs

Co-authored-by: cowagent <cow@cowagent.ai>
This commit is contained in:
zhayujieandcowagent committed 2026-10-21 20:52:48 +08:00
1 parent 07eecca33a
commit 20522e870b
20 files changed
+389 -92

No files matched your search

+1 -1
View File
@@ -140,7 +140,7 @@ CowAgent supports all mainstream LLM providers. **Chat, vision, image generation
| [Kimi](https://docs.cowagent.ai/models/kimi) | kimi-k3 | ✅ | ✅ | | | | |
| [Doubao](https://docs.cowagent.ai/models/doubao) | doubao-seed-2.1 series | ✅ | ✅ | ✅ | | | ✅ |
| [ERNIE](https://docs.cowagent.ai/models/qianfan) | ernie-5.1 | ✅ | ✅ | | | | |
| [MiMo](https://docs.cowagent.ai/models/mimo) | mimo-v2.5 / pro | ✅ | ✅ | | | ✅ | |
| [MiMo](https://docs.cowagent.ai/models/mimo) | mimo-v2.6-pro / flash | ✅ | ✅ | | ✅ | ✅ | |
| [LinkAI](https://docs.cowagent.ai/models/linkai) | One key for 100+ models | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [Custom](https://docs.cowagent.ai/models/custom) | Local models / third-party proxy | ✅ | | | | | |
+1 -1
View File
@@ -61,7 +61,7 @@ _DISCOVERABLE_MODELS = [
("qianfan_api_key", const.QIANFAN, const.ERNIE_45_TURBO_VL, "Qianfan"),
("zhipu_ai_api_key", const.ZHIPU_AI, const.GLM_4_7, "ZhipuAI"),
("minimax_api_key", const.MiniMax, const.MINIMAX_M3, "MiniMax"),
("mimo_api_key", const.MIMO, const.MIMO_V2_5_PRO, "MiMo"),
("mimo_api_key", const.MIMO, const.MIMO_V2_6_PRO, "MiMo"),
("deepseek_api_key", const.DEEPSEEK, const.DEEPSEEK_FLASH, "DeepSeek"),
]
+2 -1
View File
@@ -113,7 +113,7 @@ class Bridge(object):
def _auto_pick_voice_to_text() -> str:
"""Pick an ASR provider by configured api keys when voice_to_text is
unset. Order matches the web console: openai → dashscope → zhipu →
linkai. Falls back to 'openai' when nothing is configured so the
mimo → linkai. Falls back to 'openai' when nothing is configured so the
original "missing key" error is preserved.
"""
def has(k: str) -> bool:
@@ -124,6 +124,7 @@ class Bridge(object):
("open_ai_api_key", "openai"),
("dashscope_api_key", "dashscope"),
("zhipu_ai_api_key", "zhipu"),
("mimo_api_key", "mimo"),
("linkai_api_key", "linkai"),
):
if has(key):
+10 -7
View File
@@ -319,7 +319,7 @@ class ModelsHandler:
"""
# Capability -> provider ids drawn from PROVIDER_MODELS.
_ASR_PROVIDERS = ["openai", "dashscope", "zhipu", "linkai"]
_ASR_PROVIDERS = ["openai", "dashscope", "zhipu", "mimo", "linkai"]
# Web-console white-list. Other vendors stay usable via direct config.
_TTS_PROVIDERS = ["openai", "minimax", "dashscope", "mimo", "linkai"]
@@ -365,6 +365,9 @@ class ModelsHandler:
"zhipu": [
{"value": "glm-asr-2512", "hint": "智谱语音识别"},
],
"mimo": [
{"value": "mimo-v2.5-asr", "hint": "中英文 · 支持方言"},
],
# LinkAI gateway routes ASR by `model` (see
# https://docs.link-ai.tech/platform/api/voice-recognition). An empty
# value means "let the gateway pick its default engine".
@@ -655,8 +658,8 @@ class ModelsHandler:
# MiniMax-M3 natively accepts text, image, and video input. The M2.x
# chat family remains text-only and is not offered for vision.
"minimax": [const.MINIMAX_M3],
# MiMo 原生全模态模型:v2.5-pro / v2.5 支持图像/音频/视频输入
"mimo": [const.MIMO_V2_5_PRO, const.MIMO_V2_5],
# MiMo 原生全模态模型:v2.6-pro / v2.6-flash 支持图像/音频/视频输入
"mimo": [const.MIMO_V2_6_PRO, const.MIMO_V2_6_FLASH],
# LinkAI proxies the underlying vendor; surface a curated set of
# multimodal models. Order: gpt-4.1-mini → gpt-5.4-mini as the
# cross-vendor baselines, then each vendor's recommended default.
@@ -1071,7 +1074,7 @@ class ModelsHandler:
("qianfan", "qianfan_api_key", const.ERNIE_45_TURBO_VL),
("zhipu", "zhipu_ai_api_key", const.GLM_5V_TURBO),
("minimax", "minimax_api_key", const.MINIMAX_M3),
("mimo", "mimo_api_key", const.MIMO_V2_5_PRO),
("mimo", "mimo_api_key", const.MIMO_V2_6_PRO),
]
@classmethod
@@ -1639,8 +1642,8 @@ class ModelsHandler:
"MiniMax-M2.7": {"context_window": 204800, "max_output_tokens": 196608},
"MiniMax-M2.7-highspeed": {"context_window": 204800, "max_output_tokens": 196608},
"MiniMax-Text-01": {"context_window": 1000000},
"mimo-v2.5-pro": {"context_window": 1000000, "max_output_tokens": 131072},
"mimo-v2.5": {"context_window": 1000000, "max_output_tokens": 131072},
"mimo-v2.6-pro": {"context_window": 1000000, "max_output_tokens": 131072},
"mimo-v2.6-flash": {"context_window": 1000000, "max_output_tokens": 131072},
"gpt-6.1-sol": {"context_window": 1000000, "max_output_tokens": 128000},
"gpt-6-luna": {"context_window": 1000000, "max_output_tokens": 128000},
"gpt-6-sol": {"context_window": 1000000, "max_output_tokens": 128000},
@@ -1678,7 +1681,7 @@ class ModelsHandler:
Membership in the per-capability preset lists IS the type: the chat
list -> "text", the vision list -> "vision", etc. Tags merge across
lists (gpt-4o -> text+vision, mimo-v2.5 -> text+vision+tts), so the
lists (gpt-4o -> text+vision, mimo-v2.6-pro -> text+vision), so the
catalog editor seeds each preset with its real capabilities."""
merged: "OrderedDict[str, dict]" = OrderedDict()
+2 -2
View File
@@ -29,7 +29,7 @@ _RECOMMENDED_MODELS = [
const.DOUBAO_SEED_2_1_PRO, const.DOUBAO_SEED_2_1_TURBO, const.DOUBAO_SEED_2_CODE,
const.KIMI_K3, const.KIMI_K2_7_CODE, const.KIMI_K2_7_CODE_HIGHSPEED, const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2,
const.ERNIE_5_1, const.ERNIE_5, const.ERNIE_X1_1, const.ERNIE_45_TURBO_128K, const.ERNIE_45_TURBO_32K,
const.MIMO_V2_5_PRO, const.MIMO_V2_5,
const.MIMO_V2_6_PRO, const.MIMO_V2_6_FLASH,
]
# Generic placeholder hints surfaced in the web console. We deliberately
@@ -130,7 +130,7 @@ PROVIDER_MODELS = OrderedDict([
"api_base_key": "mimo_api_base",
"api_base_default": "https://api.xiaomimimo.com/v1",
"api_base_placeholder": _PLACEHOLDER_V1,
"models": [const.MIMO_V2_5_PRO, const.MIMO_V2_5],
"models": [const.MIMO_V2_6_PRO, const.MIMO_V2_6_FLASH],
}),
("linkai", {
"label": "LinkAI",
+10 -6
View File
@@ -166,11 +166,15 @@ KIMI_K2_5 = "kimi-k2.5"
KIMI_K2_6 = "kimi-k2.6"
# Xiaomi MiMo
MIMO_V2_5_PRO = "mimo-v2.5-pro" # MiMo V2.5 Pro - flagship, long context (default recommendation)
MIMO_V2_5 = "mimo-v2.5" # MiMo V2.5 - multimodal (text/image/audio/video)
MIMO_V2_PRO = "mimo-v2-pro" # MiMo V2 Pro
MIMO_V2_OMNI = "mimo-v2-omni" # MiMo V2 Omni - multimodal
MIMO_V2_FLASH = "mimo-v2-flash" # MiMo V2 Flash - high-speed
MIMO_V2_6_PRO = "mimo-v2.6-pro" # MiMo V2.6 Pro - flagship, omni-modal, 1M context (default recommendation)
MIMO_V2_6_FLASH = "mimo-v2.6-flash" # MiMo V2.6 Flash - omni-modal, faster and cheaper
# Retired by the vendor; requests to these ids now fail, so MimoBot maps them
# onto the V2.6 replacements.
MIMO_V2_5_PRO = "mimo-v2.5-pro"
MIMO_V2_5 = "mimo-v2.5"
MIMO_V2_PRO = "mimo-v2-pro"
MIMO_V2_OMNI = "mimo-v2-omni"
MIMO_V2_FLASH = "mimo-v2-flash"
# Doubao (Volcengine Ark)
DOUBAO = "doubao"
@@ -217,7 +221,7 @@ MODEL_LIST = [
MiniMax, MINIMAX_M3, MINIMAX_M2_7, MINIMAX_M2_7_HIGHSPEED, MINIMAX_ABAB6_5,
# Xiaomi MiMo
MIMO, MIMO_V2_5_PRO, MIMO_V2_5, MIMO_V2_PRO, MIMO_V2_OMNI, MIMO_V2_FLASH,
MIMO, MIMO_V2_6_PRO, MIMO_V2_6_FLASH, MIMO_V2_5_PRO, MIMO_V2_5, MIMO_V2_PRO, MIMO_V2_OMNI, MIMO_V2_FLASH,
# Claude
CLAUDE_OPUS_5_5, CLAUDE_OPUS_5, CLAUDE_SONNET_5, CLAUDE_FABLE_5_1, CLAUDE_FABLE_5, CLAUDE3, CLAUDE_4_8_OPUS, CLAUDE_4_7_OPUS, CLAUDE_4_6_SONNET, CLAUDE_4_6_OPUS, CLAUDE_4_OPUS, CLAUDE_4_5_SONNET, CLAUDE_4_SONNET, CLAUDE_3_OPUS, CLAUDE_3_OPUS_0229,
+1 -1
View File
@@ -140,7 +140,7 @@ CowAgent は主要な LLM プロバイダーすべてに対応しています。
| [Kimi](https://docs.cowagent.ai/ja/models/kimi) | kimi-k3 | ✅ | ✅ | | | | |
| [Doubao](https://docs.cowagent.ai/ja/models/doubao) | doubao-seed-2.1 シリーズ | ✅ | ✅ | ✅ | | | ✅ |
| [ERNIE](https://docs.cowagent.ai/ja/models/qianfan) | ernie-5.1 | ✅ | ✅ | | | | |
| [MiMo](https://docs.cowagent.ai/ja/models/mimo) | mimo-v2.5-pro / v2.5 | ✅ | ✅ | | | ✅ | |
| [MiMo](https://docs.cowagent.ai/ja/models/mimo) | mimo-v2.6-pro / v2.6-flash | ✅ | ✅ | | ✅ | ✅ | |
| [LinkAI](https://docs.cowagent.ai/ja/models/linkai) | 1 つの Key で 100+ モデルに接続 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [カスタム](https://docs.cowagent.ai/ja/models/custom) | ローカルモデル / サードパーティプロキシ | ✅ | | | | | |
+26 -10
View File
@@ -1,9 +1,9 @@
---
title: Xiaomi MiMo
description: "CowAgent で Xiaomi MiMo のネイティブなオムニモーダルモデルを設定します。API キーひとつでテキスト対話、画像理解、音声合成が使え、V2.5 系は思考モードが既定で有効です。"
description: "CowAgent で Xiaomi MiMo のネイティブなオムニモーダルモデルを設定します。API キーひとつでテキスト対話、画像理解、音声認識、音声合成が使え、V2.6 系は思考モードが既定で有効です。"
---
Xiaomi MiMo はネイティブ全モーダル大規模言語モデルです。1 つの `mimo_api_key` でテキスト対話、画像理解、音声合成を同時に有効化できます。
Xiaomi MiMo はネイティブ全モーダル大規模言語モデルです。1 つの `mimo_api_key` でテキスト対話、画像理解、音声認識、音声合成を同時に有効化できます。
<Tip>
Web コンソールの「モデル管理」ページから、以下のすべての機能をワンストップで設定でき、設定ファイルを手動で編集する必要はありません。
@@ -13,7 +13,7 @@ Xiaomi MiMo はネイティブ全モーダル大規模言語モデルです。1
```json
{
"model": "mimo-v2.5-pro",
"model": "mimo-v2.6-pro",
"mimo_api_key": "YOUR_API_KEY",
"mimo_api_base": "https://api.xiaomimimo.com/v1"
}
@@ -21,7 +21,7 @@ Xiaomi MiMo はネイティブ全モーダル大規模言語モデルです。1
| パラメータ | 説明 |
| --- | --- |
| `model` | 推奨は `mimo-v2.5-pro`。`mimo-v2.5` も使用可能 |
| `model` | 推奨は `mimo-v2.6-pro`。`mimo-v2.6-flash` も使用可能 |
| `mimo_api_key` | [MiMo Open Platform](https://platform.xiaomimimo.com/console/api-keys) で作成 |
| `mimo_api_base` | 任意。デフォルトは `https://api.xiaomimimo.com/v1` |
@@ -29,12 +29,12 @@ Xiaomi MiMo はネイティブ全モーダル大規模言語モデルです。1
| モデル | ユースケース |
| --- | --- |
| `mimo-v2.5-pro` | フラッグシップ。ネイティブ全モーダル + Agent 能力、最大 100 万トークンのコンテキスト |
| `mimo-v2.5` | 汎用版。ネイティブ全モーダル(テキスト / 画像 / 動画 / 音声) |
| `mimo-v2.6-pro` | フラッグシップ。ネイティブ全モーダル + Agent 能力、最大 100 万トークンのコンテキスト |
| `mimo-v2.6-flash` | 高速・低コスト版。ネイティブ全モーダル(テキスト / 画像 / 動画 / 音声)、最大 100 万トークンのコンテキスト |
## 思考モード
MiMo V2.5 シリーズはデフォルトで「思考モード」が有効です。最終回答の前に `reasoning_content`(思考過程)を出力することで、複雑なタスクのパフォーマンスを高めます。
MiMo V2.6 シリーズはデフォルトで「思考モード」が有効です。最終回答の前に `reasoning_content`(思考過程)を出力することで、複雑なタスクのパフォーマンスを高めます。
表示の有無はグローバル設定 `enable_thinking` で切り替え可能です(Web コンソールの設定ページからも変更できます):
@@ -48,8 +48,8 @@ MiMo V2.5 シリーズはデフォルトで「思考モード」が有効です
`mimo_api_key` を設定すると、Agent の Vision ツールは自動的に MiMo のビジョンモデルを利用します:
- メインモデル自体がマルチモーダル(`mimo-v2.5-pro` / `mimo-v2.5`)の場合は、画像はメインモデルが直接処理し、追加設定は不要です。
- メインモデルが他社製の場合、Vision ツールは順序に従い `mimo-v2.5-pro` にフォールバックします。
- メインモデル自体がマルチモーダル(`mimo-v2.6-pro` / `mimo-v2.6-flash`)の場合は、画像はメインモデルが直接処理し、追加設定は不要です。
- メインモデルが他社製の場合、Vision ツールは順序に従い `mimo-v2.6-pro` にフォールバックします。
特定の Vision モデルを強制したい場合は、設定ファイルで明示的に指定してください:
@@ -58,12 +58,28 @@ MiMo V2.5 シリーズはデフォルトで「思考モード」が有効です
"tools": {
"vision": {
"provider": "mimo",
"model": "mimo-v2.5-pro"
"model": "mimo-v2.6-pro"
}
}
}
```
## 音声認識
```json
{
"voice_to_text": "mimo",
"voice_to_text_model": "mimo-v2.5-asr"
}
```
| パラメータ | 説明 |
| --- | --- |
| `voice_to_text` | `mimo` に設定すると MiMo の音声認識が有効になります |
| `voice_to_text_model` | 任意。デフォルトは `mimo-v2.5-asr`(中国語・英語の自動判別に加え、広東語・呉語・閩南語・四川語などの方言に対応) |
認証情報は `mimo_api_key` を自動的に再利用します。MiMo は MP3 / WAV のみ対応のため、その他の形式の音声メッセージ(AMR / SILK / Opus など)は認識前に自動で MP3 に変換されます。エンコード後の音声は 10MB 未満である必要があります。
## 音声合成
```json
+1 -1
View File
@@ -21,7 +21,7 @@ A snapshot of each provider's capabilities. "Text" refers to the main chat model
| [Kimi](/models/kimi) | kimi-k3 | ✅ | ✅ | | | | |
| [Doubao](/models/doubao) | doubao-seed-2.1 series | ✅ | ✅ | ✅ | | | ✅ |
| [ERNIE](/models/qianfan) | ernie-5.1 | ✅ | ✅ | | | | |
| [MiMo](/models/mimo) | mimo-v2.5-pro / v2.5 | ✅ | ✅ | | | ✅ | |
| [MiMo](/models/mimo) | mimo-v2.6-pro / v2.6-flash | ✅ | ✅ | | ✅ | ✅ | |
| [LinkAI](/models/linkai) | 100+ models from multiple providers | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [Custom](/models/custom) | Local models / third-party proxies | ✅ | | | | | |
+26 -10
View File
@@ -1,9 +1,9 @@
---
title: MiMo
description: "Configure Xiaomi MiMo, a natively omni-modal model, in CowAgent: one API key enables chat, image understanding and text-to-speech, with thinking mode on by default for V2.5."
description: "Configure Xiaomi MiMo, a natively omni-modal model, in CowAgent: one API key enables chat, image understanding, speech recognition and text-to-speech, with thinking mode on by default for V2.6."
---
Xiaomi MiMo is a native omni-modal large model. A single `mimo_api_key` enables text chat, image understanding, and text-to-speech all at once.
Xiaomi MiMo is a native omni-modal large model. A single `mimo_api_key` enables text chat, image understanding, speech recognition, and text-to-speech all at once.
<Tip>
All capabilities below can be configured in one place via the "Model Management" page in the Web Console — no need to manually edit the configuration file.
@@ -13,7 +13,7 @@ Xiaomi MiMo is a native omni-modal large model. A single `mimo_api_key` enables
```json
{
"model": "mimo-v2.5-pro",
"model": "mimo-v2.6-pro",
"mimo_api_key": "YOUR_API_KEY",
"mimo_api_base": "https://api.xiaomimimo.com/v1"
}
@@ -21,7 +21,7 @@ Xiaomi MiMo is a native omni-modal large model. A single `mimo_api_key` enables
| Parameter | Description |
| --- | --- |
| `model` | Default recommendation: `mimo-v2.5-pro`; `mimo-v2.5` is also supported |
| `model` | Default recommendation: `mimo-v2.6-pro`; `mimo-v2.6-flash` is also supported |
| `mimo_api_key` | Create one in the [MiMo Open Platform](https://platform.xiaomimimo.com/console/api-keys) |
| `mimo_api_base` | Optional, defaults to `https://api.xiaomimimo.com/v1` |
@@ -29,12 +29,12 @@ Xiaomi MiMo is a native omni-modal large model. A single `mimo_api_key` enables
| Model | Use Case |
| --- | --- |
| `mimo-v2.5-pro` | Flagship: native omni-modal + Agent capability, up to 1M tokens context |
| `mimo-v2.5` | General-purpose, native omni-modal (text / image / video / audio) |
| `mimo-v2.6-pro` | Flagship: native omni-modal + Agent capability, up to 1M tokens context |
| `mimo-v2.6-flash` | Faster and cheaper, native omni-modal (text / image / video / audio), up to 1M tokens context |
## Thinking Mode
The MiMo V2.5 series enables "thinking mode" by default: the model emits `reasoning_content` (chain-of-thought) before the final answer, improving performance on complex tasks.
The MiMo V2.6 series enables "thinking mode" by default: the model emits `reasoning_content` (chain-of-thought) before the final answer, improving performance on complex tasks.
Use the global `enable_thinking` flag to toggle visibility (also switchable from the Web Console settings):
@@ -48,8 +48,8 @@ Use the global `enable_thinking` flag to toggle visibility (also switchable from
Once `mimo_api_key` is configured, the Agent's Vision tool can automatically use MiMo's vision models:
- When the main model itself is multimodal (`mimo-v2.5-pro` / `mimo-v2.5`), images are handled directly by the main model with no extra setup.
- When the main model belongs to another provider, the Vision tool falls back to `mimo-v2.5-pro` in order.
- When the main model itself is multimodal (`mimo-v2.6-pro` / `mimo-v2.6-flash`), images are handled directly by the main model with no extra setup.
- When the main model belongs to another provider, the Vision tool falls back to `mimo-v2.6-pro` in order.
To force a specific Vision model, set it explicitly in the configuration:
@@ -58,12 +58,28 @@ To force a specific Vision model, set it explicitly in the configuration:
"tools": {
"vision": {
"provider": "mimo",
"model": "mimo-v2.5-pro"
"model": "mimo-v2.6-pro"
}
}
}
```
## Speech-to-Text (ASR)
```json
{
"voice_to_text": "mimo",
"voice_to_text_model": "mimo-v2.5-asr"
}
```
| Parameter | Description |
| --- | --- |
| `voice_to_text` | Set to `mimo` to enable MiMo ASR |
| `voice_to_text_model` | Optional, defaults to `mimo-v2.5-asr` (Chinese and English with automatic language detection, plus dialects such as Cantonese, Wu, Minnan and Sichuanese) |
Credentials are automatically reused from `mimo_api_key`. MiMo only accepts MP3 / WAV audio, so voice messages in other formats (e.g. AMR / SILK / Opus) are converted to MP3 before recognition. The encoded audio must stay under 10MB.
## Text-to-Speech (TTS)
```json
+1 -1
View File
@@ -140,7 +140,7 @@ CowAgent 支援國內外主流廠商的大語言模型。**文字對話、影像
| [Kimi](https://docs.cowagent.ai/zh/models/kimi) | kimi-k3 | ✅ | ✅ | | | | |
| [豆包 Doubao](https://docs.cowagent.ai/zh/models/doubao) | doubao-seed-2.1 系列 | ✅ | ✅ | ✅ | | | ✅ |
| [百度ERNIE](https://docs.cowagent.ai/zh/models/qianfan) | ernie-5.1 | ✅ | ✅ | | | | |
| [小米 MiMo](https://docs.cowagent.ai/zh/models/mimo) | mimo-v2.5-pro / v2.5 | ✅ | ✅ | | | ✅ | |
| [小米 MiMo](https://docs.cowagent.ai/zh/models/mimo) | mimo-v2.6-pro / v2.6-flash | ✅ | ✅ | | ✅ | ✅ | |
| [LinkAI](https://docs.cowagent.ai/zh/models/linkai) | 一個 Key 接入 100+ 模型 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [自定義](https://docs.cowagent.ai/zh/models/custom) | 本地模型 / 三方代理 | ✅ | | | | | |
+1 -1
View File
@@ -140,7 +140,7 @@ CowAgent 支持国内外主流厂商的大语言模型。**文本对话、图像
| [Kimi](https://docs.cowagent.ai/zh/models/kimi) | kimi-k3 | ✅ | ✅ | | | | |
| [豆包 Doubao](https://docs.cowagent.ai/zh/models/doubao) | doubao-seed-2.1 系列 | ✅ | ✅ | ✅ | | | ✅ |
| [百度ERNIE](https://docs.cowagent.ai/zh/models/qianfan) | ernie-5.1 | ✅ | ✅ | | | | |
| [小米 MiMo](https://docs.cowagent.ai/zh/models/mimo) | mimo-v2.5-pro / v2.5 | ✅ | ✅ | | | ✅ | |
| [小米 MiMo](https://docs.cowagent.ai/zh/models/mimo) | mimo-v2.6-pro / v2.6-flash | ✅ | ✅ | | ✅ | ✅ | |
| [LinkAI](https://docs.cowagent.ai/zh/models/linkai) | 一个 Key 接入 100+ 模型 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [自定义](https://docs.cowagent.ai/zh/models/custom) | 本地模型 / 三方代理 | ✅ | | | | | |
+1 -1
View File
@@ -22,7 +22,7 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在
| [Kimi](/zh/models/kimi) | kimi-k3 | ✅ | ✅ | | | | |
| [豆包 Doubao](/zh/models/doubao) | doubao-seed-2.1 系列 | ✅ | ✅ | ✅ | | | ✅ |
| [百度千帆](/zh/models/qianfan) | ernie-5.1 | ✅ | ✅ | | | | |
| [小米 MiMo](/zh/models/mimo) | mimo-v2.5-pro / v2.5 | ✅ | ✅ | | | ✅ | |
| [小米 MiMo](/zh/models/mimo) | mimo-v2.6-pro / v2.6-flash | ✅ | ✅ | | ✅ | ✅ | |
| [LinkAI](/zh/models/linkai) | 多厂商 100+ 模型统一接入 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [自定义](/zh/models/custom) |本地模型 / 三方代理 | ✅ | | | | | |
+26 -10
View File
@@ -1,9 +1,9 @@
---
title: 小米 MiMo
description: "在 CowAgent 中配置小米 MiMo 原生全模态模型:一份 API Key 即可同时启用文本对话、图像理解与语音合成,V2.5 系列默认开启思考模式,可在 Web 控制台一站式配置。"
description: "在 CowAgent 中配置小米 MiMo 原生全模态模型:一份 API Key 即可同时启用文本对话、图像理解、语音识别与语音合成,V2.6 系列默认开启思考模式,可在 Web 控制台一站式配置。"
---
小米 MiMo 是原生全模态大模型,单 `mimo_api_key` 即可同时启用文本对话、图像理解与语音合成。
小米 MiMo 是原生全模态大模型,单 `mimo_api_key` 即可同时启用文本对话、图像理解、语音识别与语音合成。
<Tip>
通过 Web 控制台的「模型管理」页面可一站式配置以下全部能力,无需手动改配置文件。
@@ -13,7 +13,7 @@ description: "在 CowAgent 中配置小米 MiMo 原生全模态模型:一份 A
```json
{
"model": "mimo-v2.5-pro",
"model": "mimo-v2.6-pro",
"mimo_api_key": "YOUR_API_KEY",
"mimo_api_base": "https://api.xiaomimimo.com/v1"
}
@@ -21,7 +21,7 @@ description: "在 CowAgent 中配置小米 MiMo 原生全模态模型:一份 A
| 参数 | 说明 |
| --- | --- |
| `model` | 默认推荐 `mimo-v2.5-pro`,也可使用 `mimo-v2.5` |
| `model` | 默认推荐 `mimo-v2.6-pro`,也可使用 `mimo-v2.6-flash` |
| `mimo_api_key` | 在 [MiMo 开放平台](https://platform.xiaomimimo.com/console/api-keys) 创建 |
| `mimo_api_base` | 可选,默认为 `https://api.xiaomimimo.com/v1` |
@@ -29,12 +29,12 @@ description: "在 CowAgent 中配置小米 MiMo 原生全模态模型:一份 A
| 模型 | 适用场景 |
| --- | --- |
| `mimo-v2.5-pro` | 旗舰,原生全模态 + Agent 能力,最高 100 万 tokens 上下文 |
| `mimo-v2.5` | 综合版,原生全模态(文本 / 图像 / 视频 / 音频) |
| `mimo-v2.6-pro` | 旗舰,原生全模态 + Agent 能力,最高 100 万 tokens 上下文 |
| `mimo-v2.6-flash` | 更快更省,原生全模态(文本 / 图像 / 视频 / 音频),最高 100 万 tokens 上下文 |
## 思考模式
MiMo V2.5 系列默认开启「思考模式」:模型在输出最终回答前会先输出 `reasoning_content`(思维链),提升复杂任务表现。
MiMo V2.6 系列默认开启「思考模式」:模型在输出最终回答前会先输出 `reasoning_content`(思维链),提升复杂任务表现。
通过全局配置 `enable_thinking` 控制是否展示(也可在 Web 控制台 - 配置页面切换):
@@ -48,8 +48,8 @@ MiMo V2.5 系列默认开启「思考模式」:模型在输出最终回答前
配置 `mimo_api_key` 后,Agent 的 Vision 工具可以自动使用 MiMo 视觉模型:
- 当主模型本身是多模态时(`mimo-v2.5-pro` / `mimo-v2.5`),直接由主模型识别图像,无需额外配置
- 当主模型是其他厂商时,Vision 工具会根据顺序自动 fallback 到 `mimo-v2.5-pro`
- 当主模型本身是多模态时(`mimo-v2.6-pro` / `mimo-v2.6-flash`),直接由主模型识别图像,无需额外配置
- 当主模型是其他厂商时,Vision 工具会根据顺序自动 fallback 到 `mimo-v2.6-pro`
如需手动指定 Vision 模型,可在配置文件中显式配置:
@@ -58,12 +58,28 @@ MiMo V2.5 系列默认开启「思考模式」:模型在输出最终回答前
"tools": {
"vision": {
"provider": "mimo",
"model": "mimo-v2.5-pro"
"model": "mimo-v2.6-pro"
}
}
}
```
## 语音识别
```json
{
"voice_to_text": "mimo",
"voice_to_text_model": "mimo-v2.5-asr"
}
```
| 参数 | 说明 |
| --- | --- |
| `voice_to_text` | 设为 `mimo` 启用 MiMo 语音识别 |
| `voice_to_text_model` | 可选,默认 `mimo-v2.5-asr`(中英文自动识别,支持粤语、吴语、闽南语、四川话等方言) |
凭证自动复用 `mimo_api_key`。MiMo 仅支持 MP3 / WAV 音频,其他格式的语音消息(如 AMR / SILK / Opus)会先自动转为 MP3 再识别。编码后的音频需小于 10MB。
## 语音合成
```json
+38 -25
View File
@@ -4,15 +4,15 @@
小米 MiMo Bot —— OpenAI 兼容协议,使用独立 API key / base 配置。
支持模型:
- mimo-v2.5-pro (旗舰,长上下文,默认开启思考)
- mimo-v2.5 (多模态:文/图/音/视频,默认开启思考)
- mimo-v2-pro (V2 Pro,默认开启思考)
- mimo-v2-omni (V2 多模态,默认开启思考)
- mimo-v2-flash (V2 极速版,默认关闭思考)
- mimo-v2.6-pro (旗舰,全模态,1M 上下文,默认开启思考)
- mimo-v2.6-flash (全模态,更快更省,默认开启思考)
已下线的 mimo-v2.5-pro / mimo-v2.5 / mimo-v2-* 会自动映射到对应的 V2.6 模型,
避免旧配置直接报错。
思考模式说明:
- 开关参数:``{"thinking": {"type": "enabled" | "disabled"}}``
- mimo-v2.5-pro / mimo-v2.5 在思考模式下 ``temperature`` 会被强制为 1.0,
- 思考模式下 ``temperature`` / ``top_p`` 会被强制为 1.0 / 0.95,
本地直接剥离 ``temperature`` / ``top_p`` 等参数避免歧义。
- 多轮工具调用过程中,若历史包含 tool_calls,所有后续 assistant 消息必须回传
``reasoning_content``,否则 API 返回 400 错误。
@@ -36,20 +36,32 @@ from models.session_manager import SessionManager
from .mimo_session import MimoSession
DEFAULT_API_BASE = "https://api.xiaomimimo.com/v1"
DEFAULT_MODEL = const.MIMO_V2_5_PRO
DEFAULT_MODEL = const.MIMO_V2_6_PRO
# 支持多模态输入(图/音/视频)的模型
MULTIMODAL_MODELS = {const.MIMO_V2_5_PRO, const.MIMO_V2_5, const.MIMO_V2_OMNI}
MULTIMODAL_MODELS = {const.MIMO_V2_6_PRO, const.MIMO_V2_6_FLASH}
# Vendor-retired model ids and their official replacements.
RETIRED_MODELS = {
const.MIMO_V2_5_PRO: const.MIMO_V2_6_PRO,
const.MIMO_V2_PRO: const.MIMO_V2_6_PRO,
const.MIMO_V2_5: const.MIMO_V2_6_FLASH,
const.MIMO_V2_OMNI: const.MIMO_V2_6_FLASH,
const.MIMO_V2_FLASH: const.MIMO_V2_6_FLASH,
}
def resolve_model(model_name: Optional[str]) -> str:
"""Map a retired model id onto its replacement; empty falls back to the default."""
name = (model_name or "").strip() or DEFAULT_MODEL
return RETIRED_MODELS.get(name.lower(), name)
class MimoBot(Bot, OpenAICompatibleBot):
def __init__(self):
super().__init__()
self.sessions = SessionManager(
MimoSession,
model=conf().get("model") or DEFAULT_MODEL,
)
conf_model = conf().get("model") or DEFAULT_MODEL
conf_model = resolve_model(conf().get("model"))
self.sessions = SessionManager(MimoSession, model=conf_model)
self.args = {
"model": conf_model,
"temperature": conf().get("temperature", 1.0),
@@ -72,7 +84,7 @@ class MimoBot(Bot, OpenAICompatibleBot):
return {
"api_key": self.api_key,
"api_base": self.api_base,
"model": conf().get("model", DEFAULT_MODEL),
"model": resolve_model(conf().get("model")),
"default_temperature": conf().get("temperature", 1.0),
"default_top_p": conf().get("top_p", 0.95),
}
@@ -80,8 +92,7 @@ class MimoBot(Bot, OpenAICompatibleBot):
@property
def supports_vision(self) -> bool:
"""主模型为多模态模型时,允许 vision tool 走主 bot 通道。"""
model_name = (conf().get("model") or "").lower()
return model_name in MULTIMODAL_MODELS
return resolve_model(conf().get("model")).lower() in MULTIMODAL_MODELS
@staticmethod
def _model_supports_thinking(model_name: str) -> bool:
@@ -155,9 +166,10 @@ class MimoBot(Bot, OpenAICompatibleBot):
headers = self._build_headers()
body = dict(args) if args else dict(self.args)
body["messages"] = session.messages
body["model"] = resolve_model(body.get("model"))
model_name = str(body.get("model", ""))
# 思考模式下 mimo-v2.5-pro / mimo-v2.5 不支持自定义 temperature/top_p,
model_name = body["model"]
# 思考模式下不支持自定义 temperature/top_p,
# 简单起见,所有支持思考的模型按默认配置走,剥离这些参数。
if self._model_supports_thinking(model_name) and self._thinking_default_enabled(model_name):
for k in ("temperature", "top_p", "presence_penalty", "frequency_penalty"):
@@ -232,7 +244,7 @@ class MimoBot(Bot, OpenAICompatibleBot):
if tools:
converted_tools = self._convert_tools_to_openai_format(tools)
model = kwargs.pop("model", None) or self.args["model"]
model = resolve_model(kwargs.pop("model", None) or self.args["model"])
max_tokens = kwargs.pop("max_tokens", None)
request_body = {
@@ -263,7 +275,7 @@ class MimoBot(Bot, OpenAICompatibleBot):
request_body["thinking"] = thinking_param
thinking_active = thinking_param.get("type") == "enabled"
# 思考模式下 v2.5-pro / v2.5 不支持自定义 temperature;干脆全部剥离避免被静默忽略
# 思考模式下不支持自定义 temperature 等采样参数;干脆全部剥离避免被静默忽略
if thinking_active:
for k in ("temperature", "top_p", "presence_penalty", "frequency_penalty"):
request_body.pop(k, None)
@@ -641,11 +653,12 @@ class MimoBot(Bot, OpenAICompatibleBot):
max_tokens: int = 1000) -> dict:
"""通过 MiMo OpenAI 兼容的 /chat/completions 端点进行图像理解。"""
try:
# 主模型若不支持视觉(如 mimo-v2-flash),自动切到 mimo-v2.5-pro
vision_model = model
if not vision_model:
cur = self.args.get("model") or DEFAULT_MODEL
vision_model = cur if cur in MULTIMODAL_MODELS else const.MIMO_V2_5_PRO
# 主模型若不支持视觉,自动切到 mimo-v2.6-pro
if model:
vision_model = resolve_model(model)
else:
cur = resolve_model(self.args.get("model"))
vision_model = cur if cur in MULTIMODAL_MODELS else const.MIMO_V2_6_PRO
payload = {
"model": vision_model,
+1 -1
View File
@@ -3,7 +3,7 @@ from models.session_manager import Session
class MimoSession(Session):
def __init__(self, session_id, system_prompt=None, model="mimo-v2.5-pro"):
def __init__(self, session_id, system_prompt=None, model="mimo-v2.6-pro"):
super().__init__(session_id, system_prompt)
self.model = model
self.reset()
+2 -2
View File
@@ -596,7 +596,7 @@ select_model() {
"Qwen (qwen3.8-flash, qwen3.8-max, etc.)" \
"Kimi (kimi-k3, etc.)" \
"Doubao (doubao-seed-2.1, etc.)" \
"MiMo (mimo-v2.5-pro, etc.)" \
"MiMo (mimo-v2.6-pro, etc.)" \
"LinkAI ($(t "一个 Key 接入所有模型" "access all models via one API"))" \
"$(t "⏭ 跳过(稍后在 Web 控制台配置)" "⏭ Skip (configure later in the web console)")"
model_choice="$sel"
@@ -628,7 +628,7 @@ configure_model() {
7) read_model_config "Qwen (DashScope)" "qwen3.8-flash" "DASHSCOPE_KEY" ;;
8) read_model_config "Kimi (Moonshot)" "kimi-k3" "MOONSHOT_KEY" ;;
9) read_model_config "Doubao (Volcengine Ark)" "doubao-seed-2-1-pro-260628" "ARK_KEY" ;;
10) read_model_config "MiMo" "mimo-v2.5-pro" "MIMO_KEY" ;;
10) read_model_config "MiMo" "mimo-v2.6-pro" "MIMO_KEY" ;;
11)
# Show where to obtain a LinkAI key (zh users -> console page).
echo -e "${CYAN}$(t "获取 LinkAI Key" "Get your LinkAI Key"): https://link-ai.tech/console/interface${NC}"
+2 -2
View File
@@ -539,7 +539,7 @@ $ModelChoices = @{
7 = @{ Provider = "Qwen (DashScope)"; Default = "qwen3.8-flash"; Field = "dashscope_api_key" }
8 = @{ Provider = "Doubao (Volcengine Ark)"; Default = "doubao-seed-2-1-pro-260628"; Field = "ark_api_key" }
9 = @{ Provider = "Kimi (Moonshot)"; Default = "kimi-k3"; Field = "moonshot_api_key" }
10 = @{ Provider = "MiMo"; Default = "mimo-v2.5-pro"; Field = "mimo_api_key" }
10 = @{ Provider = "MiMo"; Default = "mimo-v2.6-pro"; Field = "mimo_api_key" }
11 = @{ Provider = "LinkAI"; Default = "deepseek-flash"; Field = "linkai_api_key"; Linkai = $true }
}
@@ -556,7 +556,7 @@ function Select-Model {
"Qwen (qwen3.8-flash, qwen3.8-max, etc.)",
"Doubao (doubao-seed-2.1, etc.)",
"Kimi (kimi-k3, etc.)",
"MiMo (mimo-v2.5-pro, etc.)",
"MiMo (mimo-v2.6-pro, etc.)",
("LinkAI (" + (T "一个 Key 接入所有模型" "access all models via one API") + ")"),
(T "⏭ 跳过(稍后在 Web 控制台配置)" "⏭ Skip (configure later in the web console)")
)
+144
View File
@@ -0,0 +1,144 @@
# encoding:utf-8
"""
Unit tests for Xiaomi MiMo speech recognition and model id handling.
Covers:
- voiceToText posts a base64 data URI to /chat/completions and returns the text
- non mp3 / wav input is converted to mp3 before upload
- HTTP / API errors become an ERROR reply
- retired chat model ids map onto their V2.6 replacements
- the web console ASR card offers MiMo with its preset model
"""
import base64
import os
import sys
import tempfile
import unittest
from unittest.mock import MagicMock, patch
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from bridge.reply import ReplyType
from voice.mimo.mimo_voice import MimoVoice
def _conf(**values):
cfg = MagicMock()
cfg.get = MagicMock(side_effect=lambda key, default=None: values.get(key, default))
return MagicMock(return_value=cfg)
def _response(status=200, body=None):
resp = MagicMock()
resp.status_code = status
resp.json.return_value = body or {}
resp.text = str(body)
return resp
class TestMimoVoiceToText(unittest.TestCase):
def _write(self, tmp, name, data=b"RIFFfake"):
path = os.path.join(tmp, name)
with open(path, "wb") as f:
f.write(data)
return path
def test_posts_data_uri_and_returns_text(self):
ok = _response(body={"choices": [{"message": {"content": " 你好 "}}]})
with tempfile.TemporaryDirectory() as tmp:
audio = self._write(tmp, "a.wav")
with patch("voice.mimo.mimo_voice.conf", _conf(mimo_api_key="k")), \
patch("voice.mimo.mimo_voice.requests.post", return_value=ok) as post:
reply = MimoVoice().voiceToText(audio)
self.assertEqual(reply.type, ReplyType.TEXT)
self.assertEqual(reply.content, "你好")
url = post.call_args.args[0]
self.assertEqual(url, "https://api.xiaomimimo.com/v1/chat/completions")
payload = post.call_args.kwargs["json"]
self.assertEqual(payload["model"], "mimo-v2.5-asr")
part = payload["messages"][0]["content"][0]
self.assertEqual(part["type"], "input_audio")
expected = "data:audio/wav;base64," + base64.b64encode(b"RIFFfake").decode()
self.assertEqual(part["input_audio"]["data"], expected)
self.assertEqual(post.call_args.kwargs["headers"]["Authorization"], "Bearer k")
self.assertIsNotNone(post.call_args.kwargs.get("timeout"))
def test_converts_unsupported_format_to_mp3(self):
ok = _response(body={"choices": [{"message": {"content": "hi"}}]})
def _fake_convert(src, dst):
with open(dst, "wb") as f:
f.write(b"ID3fake")
with tempfile.TemporaryDirectory() as tmp:
audio = self._write(tmp, "a.amr")
with patch("voice.mimo.mimo_voice.conf", _conf(mimo_api_key="k")), \
patch("voice.mimo.mimo_voice.audio_convert.any_to_mp3", side_effect=_fake_convert) as conv, \
patch("voice.mimo.mimo_voice.requests.post", return_value=ok) as post:
reply = MimoVoice().voiceToText(audio)
self.assertEqual(reply.type, ReplyType.TEXT)
conv.assert_called_once()
data = post.call_args.kwargs["json"]["messages"][0]["content"][0]["input_audio"]["data"]
self.assertTrue(data.startswith("data:audio/mpeg;base64,"))
def test_http_error_becomes_error_reply(self):
with tempfile.TemporaryDirectory() as tmp:
audio = self._write(tmp, "a.mp3")
with patch("voice.mimo.mimo_voice.conf", _conf(mimo_api_key="k")), \
patch("voice.mimo.mimo_voice.requests.post", return_value=_response(401, {"error": "bad key"})):
reply = MimoVoice().voiceToText(audio)
self.assertEqual(reply.type, ReplyType.ERROR)
def test_api_error_body_becomes_error_reply(self):
with tempfile.TemporaryDirectory() as tmp:
audio = self._write(tmp, "a.mp3")
with patch("voice.mimo.mimo_voice.conf", _conf(mimo_api_key="k")), \
patch("voice.mimo.mimo_voice.requests.post", return_value=_response(200, {"error": {"message": "x"}})):
reply = MimoVoice().voiceToText(audio)
self.assertEqual(reply.type, ReplyType.ERROR)
def test_missing_key_skips_request(self):
with patch("voice.mimo.mimo_voice.conf", _conf()), \
patch("voice.mimo.mimo_voice.requests.post") as post:
reply = MimoVoice().voiceToText("/nonexistent.wav")
self.assertEqual(reply.type, ReplyType.ERROR)
post.assert_not_called()
class TestMimoModelResolution(unittest.TestCase):
def test_retired_ids_map_to_v26(self):
from models.mimo.mimo_bot import resolve_model
self.assertEqual(resolve_model("mimo-v2.5-pro"), "mimo-v2.6-pro")
self.assertEqual(resolve_model("MiMo-V2.5-Pro"), "mimo-v2.6-pro")
self.assertEqual(resolve_model("mimo-v2.5"), "mimo-v2.6-flash")
self.assertEqual(resolve_model("mimo-v2-pro"), "mimo-v2.6-pro")
self.assertEqual(resolve_model("mimo-v2-omni"), "mimo-v2.6-flash")
self.assertEqual(resolve_model("mimo-v2-flash"), "mimo-v2.6-flash")
def test_current_and_empty_ids(self):
from models.mimo.mimo_bot import resolve_model
self.assertEqual(resolve_model("mimo-v2.6-flash"), "mimo-v2.6-flash")
self.assertEqual(resolve_model(""), "mimo-v2.6-pro")
self.assertEqual(resolve_model(None), "mimo-v2.6-pro")
class TestMimoAsrCapability(unittest.TestCase):
def test_asr_card_offers_mimo(self):
from channel.web.api.models import ModelsHandler
cap = ModelsHandler._asr_capability({
"voice_to_text": "mimo",
"voice_to_text_model": "mimo-v2.5-asr",
})
self.assertEqual(cap["current_provider"], "mimo")
self.assertIn("mimo", cap["providers"])
models = [m["value"] if isinstance(m, dict) else m for m in cap["provider_models"]["mimo"]]
self.assertIn("mimo-v2.5-asr", models)
if __name__ == "__main__":
unittest.main()
+93 -9
View File
@@ -1,16 +1,20 @@
# encoding:utf-8
"""
小米 MiMo TTS - 基于 mimo-v2.5-tts 模型的语音合成。
小米 MiMo 语音:mimo-v2.5-asr 语音识别 + mimo-v2.5-tts 语音合成。
通过 /chat/completions 接口实现:assistant 消息内容为待合成文本,
audio 字段指定预置音色(如 冰糖/茉莉/苏打/Mia/Chloe 等),返回 base64
编码的音频字节。
两者都走 /chat/completions 接口:
- ASR:user 消息携带一段 base64 编码的 input_audio(仅支持 mp3 / wav),
返回识别出的文本。
- TTS:assistant 消息内容为待合成文本,audio 字段指定预置音色
(如 冰糖/茉莉/苏打/Mia/Chloe 等),返回 base64 编码的音频字节。
文档:https://platform.xiaomimimo.com/docs/zh-CN/usage-guide/speech-synthesis-v2.5
注意:MiMo 不提供 ASR 端点,因此 voiceToText 不实现。
文档:
- https://platform.xiaomimimo.com/docs/zh-CN/usage-guide/speech-recognition
- https://platform.xiaomimimo.com/docs/zh-CN/usage-guide/speech-synthesis-v2.5
"""
import base64
import datetime
import os
import random
import requests
@@ -19,12 +23,18 @@ from bridge.reply import Reply, ReplyType
from common.log import logger
from common.tmp_dir import TmpDir
from config import conf
from voice import audio_convert
from voice.voice import Voice
DEFAULT_API_BASE = "https://api.xiaomimimo.com/v1"
DEFAULT_ASR_MODEL = "mimo-v2.5-asr"
DEFAULT_TTS_MODEL = "mimo-v2.5-tts"
DEFAULT_TTS_VOICE = "冰糖" # 默认音色:中国集群事实默认值
REQUEST_TIMEOUT = (5, 120)
# The ASR endpoint rejects a base64 audio payload above 10 MB.
MAX_ASR_BASE64_BYTES = 10 * 1024 * 1024
ASR_MIME_TYPES = {".mp3": "audio/mpeg", ".wav": "audio/wav"}
ASR_FAILED_MSG = "我暂时还无法听清您的语音,请稍后再试吧~"
class MimoVoice(Voice):
@@ -32,9 +42,83 @@ class MimoVoice(Voice):
pass
def voiceToText(self, voice_file: str):
# MiMo 没有独立 ASR 端点;建议使用其他 provider(如 openai/zhipu/dashscope)
logger.warning("[MimoVoice] voiceToText is not supported by MiMo API")
return Reply(ReplyType.ERROR, "MiMo 暂不支持语音识别,请配置其他 voice_to_text provider")
try:
api_key = conf().get("mimo_api_key", "")
if not api_key:
logger.error("[MimoVoice] mimo_api_key is not configured")
return Reply(ReplyType.ERROR, "未配置 MiMo API key")
voice_file = self._ensure_asr_format(voice_file)
mime = ASR_MIME_TYPES.get(os.path.splitext(voice_file)[1].lower())
if not mime:
logger.error(f"[MimoVoice] voiceToText unsupported audio format: {voice_file}")
return Reply(ReplyType.ERROR, ASR_FAILED_MSG)
with open(voice_file, "rb") as f:
audio_b64 = base64.b64encode(f.read()).decode("ascii")
if len(audio_b64) > MAX_ASR_BASE64_BYTES:
logger.warning(
f"[MimoVoice] audio payload {len(audio_b64)}B exceeds "
f"{MAX_ASR_BASE64_BYTES}B; mimo-v2.5-asr may reject it"
)
api_base = (conf().get("mimo_api_base") or DEFAULT_API_BASE).rstrip("/")
model = conf().get("voice_to_text_model") or DEFAULT_ASR_MODEL
payload = {
"model": model,
"messages": [{
"role": "user",
"content": [{
"type": "input_audio",
"input_audio": {"data": f"data:{mime};base64,{audio_b64}"},
}],
}],
"asr_options": {"language": "auto"},
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
response = requests.post(
f"{api_base}/chat/completions", headers=headers, json=payload, timeout=REQUEST_TIMEOUT
)
if response.status_code != 200:
logger.error(
f"[MimoVoice] voiceToText failed: status={response.status_code} "
f"body={response.text[:500]} model={model}"
)
return Reply(ReplyType.ERROR, ASR_FAILED_MSG)
data = response.json()
if "error" in data:
logger.error(f"[MimoVoice] voiceToText api error: {data['error']}")
return Reply(ReplyType.ERROR, ASR_FAILED_MSG)
message = (data.get("choices") or [{}])[0].get("message", {}) or {}
text = (message.get("content") or "").strip()
if not text:
logger.error(f"[MimoVoice] voiceToText empty text: {data}")
return Reply(ReplyType.ERROR, ASR_FAILED_MSG)
logger.info(f"[MimoVoice] voiceToText model={model} text={text}")
return Reply(ReplyType.TEXT, text)
except Exception as e:
logger.exception(f"[MimoVoice] voiceToText exception: {e}")
return Reply(ReplyType.ERROR, ASR_FAILED_MSG)
@staticmethod
def _ensure_asr_format(voice_file: str) -> str:
# mimo-v2.5-asr only accepts mp3 / wav; channels hand over amr / silk /
# opus / ogg etc., so convert those to mp3 first.
if os.path.splitext(voice_file)[1].lower() in ASR_MIME_TYPES:
return voice_file
mp3_file = os.path.splitext(voice_file)[0] + ".mp3"
try:
audio_convert.any_to_mp3(voice_file, mp3_file)
return mp3_file
except Exception as e:
logger.warning(f"[MimoVoice] mp3 convert failed: {e}")
return voice_file
def textToVoice(self, text: str):
try: