背景h2
小米 MIMO API(api.xiaomimimo.com)提供了一个免费的 AI 对话接口,但它并非标准的 OpenAI 兼容格式——需要先通过 /bootstrap 端点获取 JWT,再用 JWT 调用 /v1/chat/completions。这让绝大多数支持自定义 API 的聊天客户端(如 ChatBox、NextChat、LobeChat 等)无法直接接入。
本文将记录从零搭建 MIMO OpenAI 兼容代理的完整过程,涵盖两种方案:
| 方案 | 适用场景 | 难度 |
|---|---|---|
| 本地 Python Flask 代理 | 本机使用、调试方便 | 简单 |
| Cloudflare Worker 代理 | 多人共享、无需本地运行 | 中等 |
技术原理h2
MIMO API 认证流程h3
┌──────────┐ GET /bootstrap ┌──────────────┐│ │ ───────────────────────→ │ ││ 客户端 │ ←─────────────────────── │ MIMO API ││ │ { jwt: "xxx.xxx.xxx" } │ ││ │ │ ││ │ POST /v1/chat/completions ││ │ Authorization: Bearer xxx ││ │ X-Mimo-Source: mimocode-cli-free ││ │ ───────────────────────→ │ ││ │ ←─────────────────────── │ ││ │ SSE stream response │ │└──────────┘ └──────────────┘关键点:
- Bootstrap:无认证,返回 JWT,有效期约 5 分钟
- Chat:需
Authorization: Bearer {jwt}+X-Mimo-Source: mimocode-cli-free两个 Header - 响应:标准 SSE(Server-Sent Events)流式格式,
data:前缀,最后以data: [DONE]结束
代理要做的事h3
聊天客户端(OpenAI 格式) → 代理 → MIMO API- 暴露
/v1/models—— 返回可用模型列表 - 暴露
/v1/chat/completions—— 接收 OpenAI 格式请求 - 自动管理 JWT:到期前 5 分钟自动刷新
- 转发请求到 MIMO,透传 SSE 流
方案一:本地 Python Flask 代理h2
完整代码h3
创建 mimo-proxy.py:
import timeimport requestsfrom flask import Flask, Response, request, stream_with_contextfrom flask_cors import CORS
app = Flask(__name__)CORS(app)
MIMO_BASE = "https://api.xiaomimimo.com/api/free-ai"_jwt_cache = {"token": None, "expires_at": 0}
def get_jwt(): """获取 JWT,缓存 5 分钟""" now = time.time() if _jwt_cache["token"] and now < _jwt_cache["expires_at"]: return _jwt_cache["token"]
resp = requests.get(f"{MIMO_BASE}/bootstrap", timeout=10) resp.raise_for_status() token = resp.json()["jwt"] _jwt_cache["token"] = token _jwt_cache["expires_at"] = now + 270 # 4.5 分钟刷新 return token
@app.route("/v1/models", methods=["GET", "OPTIONS"])def list_models(): return { "object": "list", "data": [{"id": "mimo-v2.5-pro", "object": "model"}], }
@app.route("/v1/chat/completions", methods=["POST", "OPTIONS"])def chat_completions(): jwt = get_jwt() body = request.get_json(force=True)
headers = { "Authorization": f"Bearer {jwt}", "X-Mimo-Source": "mimocode-cli-free", "Content-Type": "application/json", }
resp = requests.post( f"{MIMO_BASE}/v1/chat/completions", json=body, headers=headers, stream=True, timeout=120, )
def generate(): for chunk in resp.iter_content(chunk_size=None): if chunk: yield chunk
return Response( stream_with_context(generate()), content_type="text/event-stream; charset=utf-8", headers={ "Cache-Control": "no-cache", "X-Accel-Buffering": "no", }, )
if __name__ == "__main__": app.run(host="127.0.0.1", port=8787, threaded=True)部署步骤h3
# 安装依赖pip install flask flask-cors requests
# 启动代理python mimo-proxy.py客户端配置h3
将聊天客户端的 API Base URL 设为:
http://127.0.0.1:8787/v1API Key 任意填写(代理层不校验),模型选 mimo-v2.5-pro。
CORS 修复h3
前端页面直连代理时,浏览器会发送 OPTIONS 预检请求。flask-cors 的 CORS(app) 一行即可自动处理所有跨域问题。
如果遇到 “non ISO-8859-1 code point” 错误,那是浏览器 fetch API 层面的限制——请求 Header 值必须为纯 ASCII。排查方向:
- API Key 中是否含中文
- 自定义 Header 中是否含特殊字符
- Base URL 是否正确编码
方案二:Cloudflare Worker 代理h2
Cloudflare Worker 是一个免费(每天 10 万次请求)的边缘计算平台,适合搭建无需本地常驻的代理服务。
第一版:通用 CORS Workerh3
最初使用了一个通用 CORS Worker,接收 POST 请求后转发:
// 通用 CORS Worker —— 不推荐export default { async fetch(request) { const { url, method, headers, data } = await request.json(); const resp = await fetch(url, { method, headers, body: data }); const body = await resp.text(); return new Response(JSON.stringify({ status: resp.status, body_text: body }), { headers: { "Content-Type": "application/json" }, }); },};前端页面先调用 /bootstrap 获取 JWT,再通过 Worker 调用 /v1/chat/completions。
问题:Bootstrap 成功返回 JWT,但 Chat 请求返回 401 Invalid Token。
根因分析:JWT IP 绑定h3
请求 1 (bootstrap): 用户 → CF 边缘节点 A → MIMO API → JWT (绑定节点 A 的 IP)请求 2 (chat): 用户 → CF 边缘节点 B → MIMO API → 401 (IP 不匹配!)Cloudflare 采用 Anycast 架构,两次请求可能路由到不同边缘节点,导致出口 IP 不一致。MIMO API 的 JWT 绑定了请求来源 IP,IP 不一致则校验失败。
本地代理不会出现此问题,因为本机 IP 始终一致。
第二版:专用 Worker + 重试机制h3
思路:将 Bootstrap 和 Chat 放在同一个 Worker 实例内,加入重试逻辑——401 时清除 JWT 缓存 → 重新 Bootstrap → 立即重试 Chat,利用重试落在同一节点的概率。
// mimo-worker.js —— 推荐方案let _jwt = null;let _jwtExp = 0;
async function getJwt(forceRefresh = false) { const now = Date.now(); if (!forceRefresh && _jwt && now < _jwtExp) return _jwt;
const resp = await fetch(`${MIMO_BASE}/bootstrap`); const data = await resp.json(); _jwt = data.jwt; _jwtExp = now + 270_000; // 4.5 分钟 return _jwt;}
async function chatWithRetry(messages, retries = 2) { for (let i = 0; i < retries; i++) { const forceRefresh = i > 0; const jwt = await getJwt(forceRefresh);
const resp = await fetch(`${MIMO_BASE}/v1/chat/completions`, { method: "POST", headers: { Authorization: `Bearer ${jwt}`, "X-Mimo-Source": "mimocode-cli-free", "Content-Type": "application/json", }, body: JSON.stringify({ model: "mimo-v2.5-pro", messages }), });
if (resp.status === 401 && i < retries - 1) continue; return resp; }}
// 暴露 OpenAI 兼容端点// - GET /v1/models → 返回模型列表// - POST /v1/chat/completions → 代理到 MIMO部署到 Cloudflareh3
# 安装 Wrangler CLInpm install -g wrangler
# 登录wrangler login
# 创建 Workerwrangler init mimo-proxy
# 部署wrangler deploy部署后获得 Worker URL,例如 https://mimo.xxx.workers.dev,客户端配置:
API Base URL: https://mimo.xxx.workers.dev/v1重试机制的有效性h3
重试不是 100% 可靠的解决方案,但在实践中:
| 重试次数 | 成功率(估计) |
|---|---|
| 0(不重试) | ~40% — 取决于两次请求是否落在同一节点 |
| 1 次重试 | ~70% |
| 2 次重试 | ~88% |
如果两次重试仍失败,Worker 返回 401,前端可提示用户重试。这只是概率问题,多试几次通常能通过。
进一步优化方向h3
- Scheduled Trigger:用 Cron Trigger 定时预热 JWT,减少首次请求延迟
- KV 持久化:将 JWT 存入 Cloudflare KV,跨 Worker 实例共享(但无法解决核心 IP 绑定问题)
- Durable Objects:用 DO 保证 Bootstrap 和 Chat 在同一实例内执行,从根本上解决 IP 一致性问题
- 回退到本地代理:最稳妥的方案,本机 IP 始终一致
方案对比与选择h2
| 维度 | 本地 Flask 代理 | CF Worker + 重试 |
|---|---|---|
| 部署难度 | 一条命令 | 需 Wrangler CLI |
| 稳定性 | JWT 100% 有效 | 有小概率 401 需重试 |
| 可用范围 | 仅本机 | 任意设备 |
| 运行依赖 | 需本地 Python 进程常驻 | 无,CF 托管 |
| 首包延迟 | 低(localhost) | 中(走 CF 网络) |
| 适用场景 | 个人开发调试 | 团队共享、移动端使用 |
结论:个人使用推荐本地代理,简单可靠;多人共享或希望在手机上使用推荐 Worker 方案。
附录:完整文件清单h2
| 文件 | 说明 |
|---|---|
mimo-proxy.py | Python Flask 本地代理 |
mimo-worker.js | Cloudflare Worker 专用代理 |
mimo-chat.html | 纯前端聊天测试页面(通过 Worker 调用 MIMO) |
mimo-test.html | 最小化连通性测试页面 |
所有代码均为 MIT 协议,可自由修改使用。
Comments