news 2026/9/12 13:39:56

oh-my-pi 的 tts 工具详解:本地 Kokoro-82M 与云端 xAI/DeepInfra 三后端语音合成实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
oh-my-pi 的 tts 工具详解:本地 Kokoro-82M 与云端 xAI/DeepInfra 三后端语音合成实战指南

oh-my-pi 的 tts 工具详解:本地 Kokoro-82M 与云端 xAI/DeepInfra 三后端语音合成实战指南

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

本文是 oh-my-pi(⌥ Coding agent with the IDE wired in)内置tts自定义工具(Speech Generation)的完整技术指南。它以 tts 工具文档 为骨架,结合 工具实现、本地模型目录、worker 客户端 与 配置模式 等源码展开。读完本文,你将掌握tts工具的启用方式、六个入参与输出契约、providers.tts四种路由语义、三种后端(本地 / xAI / DeepInfra)各自的实现与限制,以及错误诊断与边界行为,能够直接在自己的会话中配置并使用语音文件合成能力。

一、工具概览:何时注入、做什么

tts是一个“写批准”(approval: "write")的自定义工具,作用是:把一段文本合成语音音频文件并写入output_path。它由 SDK 在会话启动时按需注册:只有当设置speechgen.enabled=true时才会被注入到可用工具集,默认是false(见 sdk.ts 与 settings-schema.ts)。

工具的描述文本本身来自提示词模板 prompts/tools/tts.md,模板中的{{localVoices}}{{xaiVoices}}{{maxLength}}占位符在注册时由 tts.ts 调用prompt.render()动态填充(本地 12 个 Kokoro 音色、xAI 5 个内置音色、最大 15000 字符),因此模型看到的描述始终与当前配置一致。

从源码继承关系看,xAI Grok Voice 路径移植自 NousResearch/hermes-agent 的tools/tts_tool.py(MIT 协议),oh-my-pi 在其之上叠加了本地端侧神经 TTS 后端(Kokoro-82M,经 kokoro-js 运行在共享 ONNX worker 上),并通过providers.tts开关统一路由(tts.ts)。

二、输入参数(Schema)

工具通过 omptype 声明参数模式(tts.ts):

字段类型必填说明
textstring待合成文本,长度约束1..15_000字符
voice_idstring音色 ID。xAI 默认为eve;本地后端不使用该字段,改用tts.localVoice设置;DeepInfra 仅在显式设置时才转发给服务端,否则用服务端默认音色
languagestringxAI 语言提示,默认en
output_pathstring输出路径,相对会话 cwd 解析
sample_ratenumber.integerxAI 采样率覆盖(默认 24000);本地与 DeepInfra 后端忽略
bit_ratenumber.integerxAI MP3 比特率覆盖(默认 128000);WAV 以及本地与 DeepInfra 后端忽略

值得注意的细节:voice_id在 schema 中没有默认值"voice_id?"而非"voice_id = ..."),这样做的目的是让“显式传了eve”与“没传 voice”可区分——xAI 由服务端应用自身默认、DeepInfra 只在调用方真正设置过 voice 时才转发(tts.ts)。

三、输出契约

  • 成功时返回一个文本块,格式为Saved <bytes> bytes to <path> (voice=<voice>, codec=<codec>, backend=<backend>...),并附带结构化details = { bytes, voiceId, codec, backend }bytes为写入的字节数,voiceId为实际使用的音色,codecmp3wavbackendlocal/xai/deepinfra之一)。
  • 失败(缺 xAI/DeepInfra 凭证、云端 HTTP 错误、本地 worker 返回null)时返回isError: true,只含一个文本块、不带details
  • 其余异常(调用方取消、60 秒云端超时、文件写入错误、本地 worker 抛出的异常)直接向外传播,不包装成isError结果。

四、后端路由:providers.tts的四种取值

providers.tts为枚举设置,默认auto,可选auto/local/xai/deepinfra(settings-schema.ts)。执行入口先读取该设置,再调用纯函数resolveTtsBackend决策(tts.ts):

export function resolveTtsBackend(opts: { preference: string; wantsMp3: boolean; hasXaiCreds: boolean }): TtsBackend { if (opts.preference === "xai") return "xai"; if (opts.preference === "deepinfra") return "deepinfra"; if (opts.preference === "local") return "local"; if (opts.wantsMp3 && opts.hasXaiCreds) return "xai"; return "local"; }

四种取值语义:

  • local:总是走本地端侧后端;输出永远是 WAV/PCM16。
  • xai:总是走 xAI Grok Voice 云端;缺少凭证时返回错误结果。
  • deepinfra:总是走 DeepInfra 的 OpenAI 兼容语音端点;缺少凭证时返回错误结果。
  • auto(默认):优先本地端侧合成,但当调用方请求.mp3且存在 xAI 凭证时改走 xAI——因为只有云端路径能产出 MP3;否则.mp3路径会被写成同级.wav文件。

调用方是否真的需要 MP3,由output_path的后缀决定:大小写不敏感地以.wav结尾则 codec 为wav,否则一律视为mp3(tts.ts)。

该路由逻辑有对应单元测试 test/tts/tts-backend.test.ts,覆盖了“显式 deepinfra 优先于 codec 与 xAI 凭证”“auto + mp3 + 有 xAI 凭证 → xai”“auto 其余情况 → local”三类关键分支。

五、本地后端:完全离线的 Kokoro-82M

本地后端是全流程端侧的:模型权重就绪后不产生任何网络提供商调用,输出永远是 WAV/PCM16。

5.1 模型与音色目录

默认本地模型 key 为kokoro,对应 Hugging Face 仓库onnx-community/Kokoro-82M-v1.0-ONNX,ONNX 精度 q8(权重约 100 MB,兼顾 CPU 推理速度与质量);默认音色为af_heart(models.ts)。同一个模型覆盖所有音色/口音——选音色即选语言/口音,不需要额外下载

tts.localModel目前仅支持kokoro一个值(通过类型层面的“值必须匹配注册表”编译期校验保证,见 models.ts)。tts.localVoice支持从如下精选的 12 个 Kokoro 音色中选择(基于 Kokoro 自带的overallGrade评分筛选,af_heart为 A 级默认音色):

音色 ID标签音色 ID标签
af_heart(默认)Heart(美国女声)am_michaelMichael(美国男声)
af_bellaBella(美国女声)am_fenrirFenrir(美国男声)
af_nicoleNicole(美国女声)am_puckPuck(美国男声)
af_aoedeAoede(美国女声)bf_emmaEmma(英国女声)
af_koreKore(美国女声)bm_georgeGeorge(英国男声)
af_sarahSarah(美国女声)bm_fableFable(英国男声)

5.2 执行链路

本地合成通过共享的 ONNX tiny-model worker 子进程完成(tts-client.ts):

  1. 读取tts.localModel(非法值回退到kokoro)与tts.localVoice(缺省回退af_heart);
  2. 通过ttsClient.synthesize(modelKey, text, { voice, signal })发起请求,worker 以__omp_worker_tts隐藏子命令被拉起,走transformers.js + onnxruntime运行时调用 kokoro-js 的KokoroTTS.from_pretrained
  3. 得到{ pcm: Float32Array, sampleRate }后,用 wav.ts 的encodeWav编码:手写 44 字节 RIFF/WAVE 头 + 小端有符号 16 位单声道采样,浮点样本先钳制到 [-1, 1] 再量化(无外部编码器依赖);
  4. 写入目标文件。

5.3 本地对 MP3 的处理

本地后端刻意不捆绑 MP3 编码器。若请求的目标路径不是.wav(例如speech.mp3),resolveLocalWavPath会把路径改写成同级.wavspeech.wav),并在工具结果中明确提示 “No local MP3 encoder is bundled, so WAV (PCM16) was written instead of the requested container.”(tts.ts)。

本地合成还会忽略每次调用传入的voice_idlanguagesample_ratebit_rate——音色来自设置而非每次调用枚举,这也是设计意图:模型调用无需在每次调用时枚举本地音色 ID。

六、xAI 后端:Grok Voice 云端合成

  • 凭证:通过resolveXAIHttpCredentials解析,可来自/login → xAI Grok OAuth(SuperGrok 或 X Premium+)或环境变量XAI_API_KEY;缺失时返回错误:No xAI credentials. Run /login → xAI Grok OAuth (SuperGrok or X Premium+) or set XAI_API_KEY.
  • 端点<baseURL>/tts,Bearer 认证,请求体含{ text, voice_id, language }
  • 内置音色araeve(默认)、leorexsal;同时接受自定义 xAI 音色 ID,因此 schema 并不用枚举限制voice_id
  • 默认值:音色eve、语言en、采样率 24000、MP3 比特率 128000。
  • 输出格式:MP3 或 WAV 均可。output_format只在这三项与默认值不一致时才显式发送(继承 hermestts_tool.py的行为):非.wav路径请求 MP3;WAV 与 MP3 分别携带各自的sample_rate/bit_rate覆盖(tts.ts)。
  • 结果:直接写入服务端返回的字节。

七、DeepInfra 后端:OpenAI 兼容语音端点

  • 凭证DEEPINFRA_API_KEY(或/login → DeepInfra);缺失时返回:No DeepInfra credentials. Run /login → DeepInfra or set DEEPINFRA_API_KEY.
  • 端点https://api.deepinfra.com/v1/openai/audio/speech,请求体为{ model, input, response_format, voice? }
  • 模型:默认hexgrad/Kokoro-82M
  • 音色voice字段仅在调用方显式设置了voice_id时才转发(DeepInfra 的音色 ID 与模型绑定,未设置时使用服务端默认音色)。
  • 输出:MP3 或 WAV(由response_format指定),字节直接写入文件。

八、配置项汇总

四个与tts相关的设置全部位于 Providers 设置的 Services 分组(settings-schema.ts):

设置类型/取值默认值说明
speechgen.enabledbooleanfalse总开关,为true时 SDK 才注入tts工具
providers.ttsenum:auto/local/xai/deepinfraauto后端路由:本地优先,仅当 MP3 + 有 xAI 凭证时转云端
tts.localModelenum:kokorokokoro本地端侧模型(Kokoro-82M,q8 ONNX)
tts.localVoiceenum: 12 个 Kokoro 音色af_heart本地后端音色

九、副作用、取消与超时

  • 文件系统:写入output_path;本地合成遇到非 WAV 目标时改写为同级.wav
  • 网络:xAI 后端调用配置的 Grok Voice HTTP 端点;DeepInfra 后端调用api.deepinfra.com;本地后端仅在模型权重首次可用前通过 tiny-model 栈下载/缓存权重。
  • 会话状态读取:cwd、模型注册表、providers.ttstts.localModeltts.localVoice
  • 取消与超时:云端调用(xAI 与 DeepInfra)使用AbortSignal.timeout(60_000)与调用方信号合并的 60 秒超时围栏;本地合成接收调用方 abort 信号(tts.ts)。
  • 无流式进度:合成为单次请求,不发射onUpdate进度事件。

十、错误处理速查

  • 缺 xAI / DeepInfra 凭证 →isError结果,提示对应登录命令或环境变量。
  • 云端 HTTP 失败(xAI 或 DeepInfra)→isError结果,格式<xAI TTS|DeepInfra TTS> failed (<status>): <detail>,detail 最多保留前 300 字符。
  • 本地 worker 返回nullisError结果,注明模型 key,提示 worker 可能不可用或模型下载中断。
  • 调用方取消、60 秒云端超时、文件写入错误、本地 worker 抛出的异常 → 直接传播(不包装为isError)。
  • 本地请求speech.mp3会写成speech.wav并在结果中说明,这是有意为之(本地不捆绑 MP3 编码器)。

十一、注意事项

  • 文本长度上限为1..15_000个 JS 字符串字符(schema 约束)。
  • voice_idlanguage是 xAI 的 payload 字段;本地音色选择来自设置,因此模型调用无需在每次调用时枚举本地音色 ID。
  • 若需要 MP3 输出,请确保配置了 xAI(或 DeepInfra)凭证并选择对应云端后端;纯本地模式下只能得到 WAV/PCM16。

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 13:38:32

从零搭建无人机开发环境:Ubuntu 20.04 + Linux工程基础实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 13:38:13

2026年AIGC降重工具解析与实战指南

1. 2026年AIGC降重工具全景解析在学术写作和内容创作领域&#xff0c;AIGC&#xff08;人工智能生成内容&#xff09;检测已经成为继传统查重之后的第二道质量关卡。根据最新行业调研&#xff0c;2026年主流学术平台对AIGC内容的识别准确率已突破90%&#xff0c;这使得如何有效…

作者头像 李华
网站建设 2026/9/12 13:38:05

Node.js生态核心概念:前端开发者必备指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 13:33:21

Flutter插件鸿蒙适配实战与性能优化

1. Flutter与鸿蒙生态融合的背景与挑战 当Flutter遇上鸿蒙&#xff08;OpenHarmony&#xff09;&#xff0c;这场跨平台框架与国产操作系统的碰撞正在催生新的开发范式。作为同时深耕Flutter和鸿蒙生态的开发者&#xff0c;我发现两者结合的最大痛点在于三方库的适配——那些在…

作者头像 李华
网站建设 2026/9/12 13:30:24

用 153 本极客时间电子书,搭一条 6 周的 Elasticsearch 上手路径

用 153 本极客时间电子书&#xff0c;搭一条 6 周的 Elasticsearch 上手路径 【免费下载链接】geektime-books :books: 极客时间电子书 项目地址: https://gitcode.com/GitHub_Trending/ge/geektime-books Elasticsearch 集群里 2 亿条订单索引的 P99 查询从 180ms 涨到…

作者头像 李华