Skip to content

能力声明与只读查询契约 ​

宿主设置页需要在不启动引擎的前提下知道:这个插件有没有音色表?支不支持克隆音色 / 删除音色 / 原生语速?


声明方式(两种等价,宿主取并集) ​

方式一:mod.json(推荐) ​

json
{
  "capabilities": {
    "voices": true,
    "voice_clone": true,
    "voice_remove": true,
    "speed": false
  }
}
键含义等价类属性 / 方法
voices提供音色表(设置页显示「朗读音色」下拉)重写 list_voices()
voice_clone支持注册克隆音色(设置页显示「注册克隆音色」面板,含软件内录制参考音频)supports_voice_clone = True
voice_remove支持删除已注册音色(仅 kind="registered" 的音色旁显示「删除音色」)重写 remove_voice()
speed原生支持语速(模型内部时长预测,质量更好)supports_speed = True

方式二:类属性 / 方法覆盖 ​

python
class MyTTS(BaseTTSProvider):
    supports_voice_clone = True    # 支持克隆音色
    supports_speed = True          # 原生语速

    def list_voices(self): ...     # 有音色表
    def remove_voice(self, name): ...

只读查询契约(强制) ​

宿主查能力时绝不创建插件实例,更不会启动你的本地服务。

宿主只读两个来源:

  1. mod.json 的 capabilities;
  2. 你的类属性(只 import 模块,不调用 __init__ / initialize)。

由此得出两条硬要求:

  1. 能力必须是类级可读的。 只在 __init__ / initialize 里赋值 → 宿主在没实例时读不到。 (宿主已有实例时会优先读实例属性,但不能依赖这一点。) 👉 结论:supports_voice_clone / supports_speed 写在类体里,或在 mod.json 声明。 voices / voice_remove 看的是方法有没有被覆盖,实例属性覆盖不了方法。

  2. 任何只读方法都不得启动服务: list_voices()、check_ready()、以及任何 get_* / 状态查询。 数据没准备好就返回空表 / (False, code, msg),宿主按「未就绪」处理。

宿主在哪些路径上只读探测 ​

  • 打开设置页 / 切换 Tab
  • 查看模型卡状态
  • 读取音色列表、查「是否支持音色克隆 / 删除」
  • 恢复上次生效的引擎(set_active_provider())—— 生效引擎只是配置变更,不预热、不加载

⚠️ 反例(真实 BUG):把 _start_server() 写在 initialize() 里。 用户「只是打开设置页或切到该引擎页签」就会白等数秒并吃掉数 GB 显存。 见 本地服务型引擎强制契约。


参考实现 ​

extensions/community_qwen3_tts(capabilities + 类属性双写,且只读路径全部不启服务)。


相关 ​

TransBox - 赋能全人类无界限跨语言交流