Skip to content
TransBoxTransBox

插件规范(AI / 人类可执行契约)

本文是 TransBox 扩展的权威实现说明。所有字段、签名、文件名以仓库源码为准(manifest.pybase.pyregistry.pyglossary.pyplugins_mixin.pyextensions/ 示例)。

阅读顺序建议:mod.json → 选 type → 抄最小模板 → 本地热调 → 导出。


1. 目录结构

每个 MOD 是一个独立文件夹,根目录必须有清单:

text
my_mod/
├── mod.json                 # 必选(或 manifest.json)
├── icon.svg                 # 推荐;也会自动探测 icon.png/webp/jpg
├── README.md                # 可选,工坊展示说明
├── theme.json               # type=theme
├── terms.json               # type=glossary(推荐)
├── terms.txt                # type=glossary(= 或 Tab 分隔)
├── ja.json                  # type=i18n
├── my_provider.py           # Python 类入口
└── CustomBackground.qml     # ui_extensions 引用的组件

2. mod.json 字段表

字段类型必填默认说明
idstring无 id 整包拒绝。建议 作者.类型.名称,勿用 builtin.*
namestring建议"未命名 MOD"工坊显示名
versionstring建议"1.0.0"无强制 semver 校验
authorstring建议"未知作者"
descriptionstring""简介
typestring建议"glossary"见 §3
entrypointstring视类型""见 §4
iconstring|nullnull相对路径
preview_imagestring|nullnull工坊预览图
steam_item_idint|nullnull可读入;当前无额外运行时逻辑
config_schemaarray[]见 §6
tagsstring[][]
supported_langsstring[][]
min_app_versionstring"1.0.0"当前无运行时校验
tierstring"free"无强制校验
enabledbool类型相关i18n/theme 默认 false,其余默认 true
has_settings_tabboolfalse设置中心独立 Tab
tab_idstring|nullnull缺省用 plugin id
tab_titlestring|nullnull缺省用 name
style_layerstring"full"仅主题:full/material/typography/effects
ui_extensionsobject{}插槽名 → QML 相对路径
language_codestring|nullnulli18n,如 ja-JP
language_namestring|nullnulli18n,如 日本語

不要写在 mod.json 里的:

  • window_backdrop会被忽略。窗口材质写在 theme.json 里。
  • source_path / is_builtin / icon_url / category_tier — 运行时注入。

安全分级(只读衍生):theme/glossary/i18ndata;其余 → script


3. 支持的 type

type基类/实现说明
glossary内置 GlossaryMod词表 JSON/TXT
theme数据 + ui_extensions主题与视觉
i18n纯 JSON界面语言包
middlewareBasePipelineMiddleware流水线拦截
asrBaseASRProvider语音识别引擎
mtBaseMTEngineProvider机器翻译引擎
ttsBaseTTSProvider语音合成引擎
caption_bridge内置第三方一般不用

不要使用不存在的历史类型名(如 audio_filterbundle)——代码不会处理。


4. entrypoint 规则

类型格式示例
Python(asr/mt/tts/middleware)相对文件.py:类名my_translator.py:CustomDemoMTProvider
Python(省略类名)相对文件.py类名默认 Plugin
glossary词表文件terms.json(默认)/ terms.txt / terms.tsv
theme主题 JSONtheme.json(默认)
i18n字典 JSONja.json(默认 locale.json

真实示例(仓库 extensions/):

text
example_custom_mt/my_translator.py:CustomDemoMTProvider
example_text_filter_mod/filter_middleware.py:TextFilterMiddleware
example_glossary_gaming/terms.json
example_i18n_japanese/ja.json
example_theme_cyberpunk/theme.json

5. 零代码类型:怎么做

5.1 Glossary(术语库)

推荐 terms.json

json
{
  "Elden Ring": "艾尔登法环",
  "Malenia": "玛莲妮亚"
}

或:

json
[{ "src": "Elden Ring", "tgt": "艾尔登法环" }]

terms.txt / terms.tsv

text
# 注释行
Elden Ring=艾尔登法环
Malenia	玛莲妮亚
  • 分隔符:第一个 =,否则 Tab
  • # 注释、空行跳过
  • 不支持 ->=>(整行丢弃)
json
{
  "id": "workshop.glossary.elden_ring",
  "name": "《艾尔登法环》游戏专有术语库",
  "version": "1.0.0",
  "author": "TransBox",
  "type": "glossary",
  "entrypoint": "terms.json",
  "description": "游戏专有名词对照"
}

5.2 Theme(主题)

mod.json

json
{
  "id": "workshop.theme.cyberpunk",
  "name": "赛博朋克霓虹",
  "version": "1.0.0",
  "author": "TransBox",
  "type": "theme",
  "style_layer": "full",
  "entrypoint": "theme.json",
  "enabled": false,
  "ui_extensions": {
    "overlay_background": "CyberDeepNeonBackground.qml",
    "audio_level": "CyberDeepNeonAudioLevel.qml"
  }
}

theme.jsonwindow_backdrop 只写在这里):

json
{
  "theme_id": "workshop.theme.cyberpunk",
  "name": "赛博朋克霓虹",
  "style_layer": "full",
  "window_backdrop": "none",
  "tokens": {
    "primary": "#00f0ff",
    "primaryHover": "#38f9d7",
    "primaryGlow": "rgba(0, 240, 255, 0.45)",
    "glassSurface": "rgba(7, 11, 25, 0.75)",
    "glassBorder": "rgba(0, 240, 255, 0.35)"
  },
  "style_overrides": {
    "bgColor": "#070b19",
    "textColor": "#00f0ff",
    "sourceColor": "#ff2a85",
    "fontSize": 28,
    "fontFamily": "MiSans VF",
    "bgOpacity": 0.65,
    "fontOpacity": 1.0
  }
}

style_overrides 也接受 snake_case 别名(如 text_colorfont_size)。
window_backdrop 有效值:acrylic | mica | mica_alt | none | blur

5.3 i18n(语言包)

json
{
  "id": "example.i18n.japanese",
  "name": "日本語 UI 言語パック",
  "version": "1.0.0",
  "type": "i18n",
  "author": "TransBox",
  "language_code": "ja-JP",
  "language_name": "日本語",
  "entrypoint": "ja.json"
}

字典为扁平 "key": "译文"。可保留 _meta(含 code/name/short_label)。缺失键回退 en-US → zh-CN。


6. config_schema

数组元素必须同时含 keylabel,否则该项被跳过。

类型必填说明
keystringself._config["key"]
labelstring界面标签
typestringstring(默认)/ password / number / boolean / selectfile 仅在清单注释中出现,界面会落成文本框,不推荐)
defaultany默认值
descriptionstring说明
choicesstring[]select 的选项
is_secretbool密文显示

实际控件:

  • select → 下拉
  • boolean → 开关
  • number → 数字输入(校验)
  • passwordis_secret: true → 密码框
  • 其它 → 文本框

Python 侧:

python
def initialize(self, config=None):
    super().initialize(config)
    self.api_key = self._config.get("api_key", "")

UI 修改会持久化并调用 update_config


7. Python SPI 基类(签名)

7.1 BasePlugin

python
class BasePlugin(ABC):
    def __init__(self, manifest: ModManifest) -> None: ...
    @property
    def id(self) -> str: ...
    @property
    def name(self) -> str: ...
    @property
    def plugin_type(self) -> str: ...
    def initialize(self, config: dict | None = None) -> None: ...
    def shutdown(self) -> None: ...
    def update_config(self, new_config: dict) -> None: ...
    def handle_action(self, action: str, params: dict) -> Any: ...
    # handle_action 调用 action_<action>(**params)

7.2 数据类型

python
@dataclass
class WordTime:
    word: str
    start: float
    end: float

@dataclass
class Transcript:
    text: str
    language: str | None = None
    words: list[WordTime] | None = None

@dataclass
class Translation:
    text: str
    source_prefix: str | None = None
    complete: bool = True  # False 表示增量片段

7.3 BaseASRProvider

python
@dataclass
class ASRCapabilities:
    has_native_vad: bool = False
    supports_streaming: bool = False
    supports_word_timestamps: bool = False
    supports_language_detection: bool = True

class BaseASRProvider(BasePlugin):
    @property
    def capabilities(self) -> ASRCapabilities: ...
    @property
    def is_streaming_capable(self) -> bool: ...  # default False
    def ensure_loaded(self, progress_callback=None) -> None: ...
    @abstractmethod
    def transcribe(
        self,
        audio: np.ndarray,          # float32 mono 16 kHz, [-1, 1]
        sample_rate: int = 16000,
        language: str = "auto",
        stream_id: str = "default",
        with_alignment: bool = False,
        on_token=None,
    ) -> Transcript: ...
    # 可选流式:
    def stream_open(self, stream_id, language="auto"): ...
    def stream_feed(self, stream_id, pcm_chunk): ...
    def stream_events(self, stream_id): ...  # yield (text, is_final)
    def stream_close(self, stream_id) -> str: ...
    def close_all_streams(self) -> None: ...

7.4 BaseMTEngineProvider

python
class BaseMTEngineProvider(BasePlugin):
    def ensure_loaded(self) -> None: ...
    @abstractmethod
    def translate(
        self,
        text: str,
        source_lang: str = "en",
        target_lang: str = "zh",
        glossary_context: str | None = None,
        target: str | None = None,
        source: str | None = None,
    ) -> Translation: ...
    def translate_partial(
        self, text, source_lang="en", target_lang="zh",
        committed_prefix="", glossary_context=None,
        target=None, source=None,
    ) -> Translation: ...  # 默认回退整句 translate

7.5 BaseTTSProvider

python
class BaseTTSProvider(BasePlugin):
    @property
    def sample_rate(self) -> int: ...  # default 24000
    @property
    def is_streaming_capable(self) -> bool: ...
    def ensure_loaded(self) -> None: ...
    @abstractmethod
    def synthesize(
        self,
        text: str,
        voice_id: str = "",
        voice: str | None = None,
        language: str = "zh",
    ) -> tuple[np.ndarray, int]: ...  # float32 mono
    def stream_tts(self, text, voice_id="", voice=None, language="zh"):
        ...
    def list_voices(self) -> list[dict]: ...  # [{'id','name','lang'}]
    def register_voice(self, voice_name, audio_samples,
                       reference_text="", sample_rate=24000) -> bool: ...

7.6 BasePipelineMiddleware

python
@dataclass
class PipelineContext:
    session_id: str = ""
    sample_rate: int = 16000
    source: str = "peer"   # 'peer' | 'self'
    source_lang: str = "auto"
    target_lang: str = "zh"
    extra: dict = field(default_factory=dict)

class BasePipelineMiddleware(BasePlugin):
    @property
    def priority(self) -> int: ...  # default 100,越小越先
    def process_audio(self, audio: np.ndarray, context) -> np.ndarray: ...
    def process_transcript(self, transcript: Transcript, context) -> Transcript: ...
    def process_translation(self, translation: Translation, context) -> Translation: ...
    def process_tts_audio(self, pcm_audio, sample_rate, context) -> tuple[np.ndarray, int]: ...

8. 最小可运行模板

8.1 自定义 MT(可直接改名使用)

mod.json

json
{
  "id": "myteam.mt.demo",
  "name": "我的示范翻译引擎",
  "version": "1.0.0",
  "author": "Me",
  "type": "mt",
  "entrypoint": "my_translator.py:MyMTProvider",
  "config_schema": [
    {
      "key": "prefix_tag",
      "label": "译文前缀",
      "type": "string",
      "default": "【Demo】"
    }
  ]
}

my_translator.py

python
from __future__ import annotations

from typing import Any, Dict, Optional

from transbox.plugins.base import BaseMTEngineProvider, Translation
from transbox.plugins.manifest import ModManifest


class MyMTProvider(BaseMTEngineProvider):
    def __init__(self, manifest: ModManifest) -> None:
        super().__init__(manifest)
        self.prefix = "【Demo】"

    def initialize(self, config: Optional[Dict[str, Any]] = None) -> None:
        super().initialize(config)
        self.prefix = self._config.get("prefix_tag", "【Demo】")

    def translate(
        self,
        text: str,
        source_lang: str = "en",
        target_lang: str = "zh",
        glossary_context: Optional[str] = None,
        target: Optional[str] = None,
        source: Optional[str] = None,
    ) -> Translation:
        tgt = target or target_lang
        clean = (text or "").strip()
        return Translation(text=f"{self.prefix} {clean}{tgt}", complete=True)

启用后:设置 → 机器翻译 → 引擎下拉中选中该引擎。

8.2 中间件

python
from transbox.plugins.base import BasePipelineMiddleware, Transcript, Translation


class TextFilterMiddleware(BasePipelineMiddleware):
    @property
    def priority(self) -> int:
        return 50

    def process_transcript(self, transcript: Transcript, context) -> Transcript:
        text = transcript.text.replace("那个...", "")
        return Transcript(text=text, language=transcript.language)

    def process_translation(self, translation: Translation, context) -> Translation:
        text = translation.text.replace("坏词", "***")
        return Translation(text=text, complete=translation.complete)

entrypoint: "filter_middleware.py:TextFilterMiddleware"


9. 本地热调试

  1. 启动 TransBox → 设置 → 扩展工坊
  2. 打开本地 MOD 目录,放入你的文件夹
  3. 刷新
  4. 开关启用;引擎类再在对应设置 Tab 选中
  5. 改代码后:停用 → 刷新/重载 → 再启用(或重启客户端)

10. 导出与发布

  1. 扩展工坊中对你的 MOD 点 导出
  2. 生成 transbox_mod_<id>_v<version>.zip
  3. 上传 Steam 创意工坊(AppID 1316080

事实澄清: 当前客户端没有
python -m transbox.plugins.steam_ugc --upload
这一 CLI。steam_ugc 模块提供发现工坊路径与 export_mod_for_workshop 打包;上传请用 Steam 客户端/创意工坊网页流程。详见 工坊打包发布


11. 运行环境与红线

事实
Python宿主 3.11–3.12
常见可用numpyPySide6sounddevicerequests
勿假设可用websockets(非保证依赖)
运行时 pip禁止 os.system("pip install ...")
沙箱,与主程序同权限
隐私不得私自上传麦克风/字幕数据
配置只读写本插件配置,勿改主程序全局设置

需要重模型:独立进程 + 本地 HTTP,插件内只做请求。


12. 官方示例索引

目录type演示
example_glossary_gamingglossaryterms.json
example_theme_cyberpunktheme全景 QML + theme.json
community_theme_glassmorphismtheme多窗口毛玻璃
example_custom_waveform_uithemeaudio_level
example_cyberpunk_caption_uitheme背景插槽
example_text_filter_modmiddleware文本过滤
example_custom_mtmt自定义翻译 + config_schema
example_i18n_japanesei18n日语包

13. 给 AI 代理的实现检查清单

实现前逐项核对:

  • [ ] mod.json 含非空唯一 id
  • [ ] type
  • [ ] entrypoint 路径与类名在包内真实存在
  • [ ] Python 类继承正确基类,实现全部 @abstractmethod
  • [ ] glossary 文本分隔符为 = 或 Tab(不用 ->
  • [ ] window_backdrop 只出现在 theme.json
  • [ ] UI 插槽名使用下表已消费名称(§14)
  • [ ] 不依赖未文档化的第三方包;不在运行时 pip
  • [ ] config_schema 每项都有 keylabel
  • [ ] 本地放入 extensions/ 后可在工坊列表看到并启用

14. UI 插槽(摘要)

已消费、可替换:

overlay_background · caption_view · audio_level · alert_capsule · overlay_decorations · style_bar_actions · settings_background · history_background · dialog_background · window_background

声明了但当前无 Loader 消费(勿承诺可替换):
overlay_root · style_bar

特殊键 settings_view(不在标准插槽列表):给插件设置页自定义 QML,注入 pluginId / bridge / rootWin / pluginConfig

完整属性见 UI 插槽与 HostContext

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