MacOSLocalAPI FastAPI + MLX(可选 mlx-lm) · OpenAI 兼容 Chat & TTS · macOS 本地运行
OpenAI-compatible在 macOS 上把本地模型包装成 OpenAI 风格 API(Chat + TTS),方便你的应用/SDK 直接接入。
- Chat:
POST /v1/chat/completions- 支持
stream=true的 SSE 流式输出 - 优先使用 tokenizer 的
apply_chat_template(),并对输出做基础清洗(尽量只返回本轮 assistant 内容)
- 支持
- TTS:
POST /v1/audio/speech- 默认使用 macOS 自带
say+afconvert(不依赖额外大模型)
- 默认使用 macOS 自带
- 模型并存:Chat 与 Audio(TTS) 模型独立配置
CHAT_MODEL_ID/CHAT_MODEL_PATHAUDIO_MODEL_ID/AUDIO_MODEL_PATH
| 项目 | 说明 |
|---|---|
| OS | macOS(Apple Silicon 推荐) |
| Python | >= 3.11(项目声明;uv 也可能使用更高版本运行) |
| 依赖管理 | uv |
uv syncEcho 模式不加载模型,主要用于验证路由与 OpenAI 客户端兼容性。
ECHO_MODE=1 uv run uvicorn main:app --reload默认模型名:
- Chat:
local-chat - Audio:
local-audio
CHAT_MODEL_PATH=/path/to/mlx-chat-model \
uv run uvicorn main:app --reload自定义对外模型名:
CHAT_MODEL_ID=qwen-chat CHAT_MODEL_PATH=/path/to/mlx-chat-model \
uv run uvicorn main:app --reload需要
mlx-lm才能加载/生成:uv add mlx-lm
不设置 AUDIO_MODEL_PATH 时,默认使用 macOS 自带 say + afconvert。
本项目支持通过 AUDIO_MODEL_PATH 加载本地 Piper TTS 模型(ONNX)。
如果你的 TTS 模型是 MLX 格式(例如 CosyVoice2、CosyVoice3、Chatterbox),推荐使用 mlx-audio-plus 作为统一后端。
为什么有时需要
ref_audio?
mlx-audio-plus覆盖的不少模型并不是“固定音色的普通 TTS”,而是更偏 语音克隆 / 说话人条件(speaker conditioning) 的 TTS:
- 模型需要一段参考音频来决定输出的音色/说话人特征(例如 CosyVoice3、Chatterbox 常见用法)。
- 这不是服务端强制要求,而是具体模型/模式的必需输入。如果模型本身要求
ref_audio,请求里不提供就会报错。如果你想要“纯文本直接合成、无需参考音频”的体验,有三种选择:
- 使用
macos-say后端(不需要模型,也不需要ref_audio)- 使用
piper后端(典型的固定音色 TTS,不需要ref_audio)- 在
mlx-audio-plus里选择支持“无 ref_audio 模式”的模型/配置(取决于模型本身)
启动示例(HF repo id):
AUDIO_BACKEND=mlx-audio-plus \
AUDIO_MODEL_ID=local-audio \
AUDIO_MODEL_PATH=mlx-community/Fun-CosyVoice3-0.5B-2512-4bit \
uv run uvicorn main:app --reload请求示例(CosyVoice3 zero-shot,包含参考音频与转写文本):
curl http://127.0.0.1:8000/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{
"model":"local-audio",
"input":"Hello, this is a test.",
"format":"wav",
"ref_audio":"reference.wav",
"ref_text":"This is what I said in the reference audio."
}' \
--output out.wav支持透传的常用扩展字段(不同模型/模式需要其中部分):
ref_audio:参考音频路径(CosyVoice/Chatterbox 常用)ref_text:参考音频的文本(CosyVoice3 zero-shot 常用)instruct_text:风格/情绪控制(CosyVoice3 instruct 常用)source_audio:用于 voice conversion(CosyVoice3 VC)
这些字段不是 OpenAI 官方
/v1/audio/speech标准的一部分,但在本地 TTS 场景中非常实用。
| 类别 | 变量 | 默认值 | 说明 |
|---|---|---|---|
| 通用 | HOST |
127.0.0.1 |
监听地址 |
| 通用 | PORT |
8000 |
监听端口 |
| Chat | CHAT_MODEL_ID |
local-chat |
Chat 对外模型名 |
| Chat | CHAT_MODEL_PATH |
(空) | MLX Chat 模型路径(不填通常回退到 Echo chat) |
| Audio | AUDIO_MODEL_ID |
local-audio |
TTS 对外模型名 |
| Audio | AUDIO_BACKEND |
auto |
TTS 后端:auto、macos-say、piper、mlx-audio-plus(统一 MLX TTS) |
| Audio | AUDIO_MODEL_PATH |
(空) | TTS 模型路径:piper 为 .onnx 文件或仅含一个 .onnx 的目录;mlx-audio-plus 为本地模型目录或 HF repo id。 |
| Audio | AUDIO_REF_AUDIO |
(空) | 启动时默认参考音频(路径或 base64/data URL)。请求未提供 ref_audio 时自动使用。 |
| Audio | AUDIO_REF_TEXT |
(空) | 启动时默认 ref_text(可选)。 |
| Audio | AUDIO_INSTRUCT_TEXT |
(空) | 启动时默认 instruct_text(可选)。 |
| Audio | AUDIO_SOURCE_AUDIO |
(空) | 启动时默认 source_audio(可选,voice conversion)。 |
| 兼容 | MODEL_ID/MODEL_PATH |
(空) | 兼容旧变量:映射到 Chat 配置 |
返回当前服务已注册的模型 id(扁平 list,含 chat + audio)。
路由:POST /v1/chat/completions
最小请求(非流式):
curl http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"local-chat","messages":[{"role":"user","content":"你好"}],"stream":false,"max_tokens":128}'流式 SSE:
curl http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
-d '{"model":"local-chat","messages":[{"role":"user","content":"用一句话介绍 MLX"}],"stream":true,"max_tokens":128}'路由:POST /v1/audio/speech
返回音频 bytes(不是 JSON)。
curl http://127.0.0.1:8000/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"model":"local-audio","input":"你好,我是本地语音合成。","voice":"Ting-Ting","format":"wav"}' \
--output out.wav说明:
macos-say后端下,voice会透传给 macOSsay -v。mlx-audio-plus后端下,可额外传ref_audio/ref_text/instruct_text/source_audio等字段(见上文 TTS 章节)。
以下示例展示“如何把 OpenAI 客户端指向本地服务”。 代码请复制到你的业务项目中运行(你的业务项目里安装
openaiSDK 即可)。uv add openai
示例(伪代码,展示关键参数):
# client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="local")
# resp = client.chat.completions.create(
# model="local-chat",
# messages=[{"role": "user", "content": "你好"}],
# )
# print(resp.choices[0].message.content)main.py:Uvicorn 入口app/app_factory.py:创建 FastAPI app,初始化并注册模型app/registry.py:模型注册表(chat/tts 分开管理)app/api/v1/openai.py:OpenAI 风格的 chat/models 路由app/api/v1/audio.py:OpenAI 风格的 TTS 路由app/engine/mlx_engine.py:MLX Chat 推理引擎(基于mlx-lm)app/engine/macos_say_tts.py:macOSsay的 TTS 引擎tests/:pytest 用例test_main.http:PyCharm HTTP Client 示例
uv run pytest -q