模型工件(Model Artifact SPI)
第三方本地离线 ASR / MT / TTS 插件声明模型工件后,宿主自动接管 下载 / 镜像切换 / 就绪检查 / 设置页卡片 / 硬件评级——你不需要写任何下载 UI。
不要在
config_schema里手写「模型下载路径」「国内镜像开关」「下载按钮」!0.2.4 起:每个声明源都必须有真实、可验证的仓库地址。 ModelScope 不再复用 Hugging Face 的 repo id;没有
repos.modelscope就视为该源不存在。请同时填写manual_url,让下载失败时能把手动下载页展示给用户。
声明方式(二选一,model_artifacts() 优先)
方式一:mod.json(推荐)
{
"requires_cuda": false,
"model_artifacts": [
{
"id": "my-asr-weights",
"name": "My ASR 1.2B",
"category": "asr",
"repo": "org/my-asr-1.2b",
"repos": {
"huggingface": "org/my-asr-1.2b",
"hf-mirror": "org/my-asr-1.2b",
"modelscope": "RealOrg/My-ASR-1.2B"
},
"manual_url": "https://huggingface.co/org/my-asr-1.2b/tree/main",
"est_bytes": 1200000000,
"sources": ["huggingface", "modelscope"],
"ready_globs": ["*.safetensors", "*.bin"],
"allow_patterns": ["*.safetensors", "*.bin"],
"requires_cuda": false
}
]
}方式二:Provider 覆盖
def model_artifacts(self) -> list[dict]: ...返回的字典结构与上面完全一致。优先于 manifest,适合按配置/已下载情况动态挑变体。
字段全表
| 字段 | 必填 | 说明 |
|---|---|---|
id | 是 | 工件 id(宿主 API 里用它定位) |
name | 建议 | 展示名 |
category | 建议 | asr / mt / tts——决定挂到哪个设置页 |
repo | 见下 | 规范 id(通常 HF 侧),决定本地目录 data/models/<org>--<name> |
est_bytes | 建议 | 预估体积(进度条与磁盘预检用);缺失时显示「未知大小」 |
sources | 否 | 允许的下载源,缺省 ["huggingface", "modelscope"]。宿主按用户选择的节点决定顺序:全球官方 → HF;hf-mirror → hf-mirror → HF 官方(镜像大文件可能 308 回源);国内 → ModelScope(若声明)→ hf-mirror。★ 若模型仅在 HF 存在,请声明 ["huggingface", "hf-mirror"],宿主国内模式下直接走 hf-mirror,绝不向 ModelScope 撞墙。 |
repos | 否 | 按源覆写的真实 repo id,键为 huggingface / hf-mirror / modelscope。ModelScope 必须显式声明;没有 repos.modelscope 就视为该源不存在,宿主不会拿 HF id 去猜。 |
manual_url | 0.2.4 起建议必填 | 该工件的模型文件页/官方下载页。下载失败时设置页的「无法下载?手动下载」直接打开它。必须指向真实存在的页面;不要写猜测地址。 |
homepage | 否 | manual_url 的兼容别名;两者同时存在时优先 manual_url。 |
ready_globs | 是 | 就绪判定:这些通配符在目标目录的直接子文件里都能匹配到才算就绪 |
allow_patterns | 否 | 下载白名单。未显式提供时宿主自动复用 ready_globs,避免把多量化分支仓库(GGUF repo,几十 GB)全量拖下来 |
requires_cuda | 否 | false = 不强依赖 NVIDIA(支持 AMD ROCm / Vulkan / DirectML / 多核 CPU)→ 宿主在 AMD/Intel/纯 CPU 上不弹受限红框,改评级为良好/极佳 |
dir | 否 | 自定义落盘目录名(相对 <数据目录>/models)。只接受单层安全路径段(禁 / \ .. :),非法值自动忽略 |
parts | 否 | 一个工件由多个仓库拼成时使用,≤ 8 项。见下 |
常用 repos 示例(ModelScope 与 HF 的组织名常不同):
"repos": {
"huggingface": "tencent/Hy-MT2-1.8B-GGUF",
"hf-mirror": "tencent/Hy-MT2-1.8B-GGUF",
"modelscope": "Tencent-Hunyuan/Hy-MT2-1.8B-GGUF"
}真实地址补齐清单(发布前逐项检查)
- 打开每个
repos地址,确认页面/仓库真实存在,且文件列表包含ready_globs。 - ModelScope 与 HF 的组织名/仓库名经常不同;不要复制 HF id。没有 ModelScope 仓库就从
sources删除modelscope,不要保留一个必然 404 的声明。 manual_url指向用户能直接打开、能看到文件列表的页面(通常是https://huggingface.co/<org>/<repo>/tree/main或对应的 ModelScope 文件页)。ready_globs写最终文件名,不要写模糊的*.gguf;这样手动下载面板才能明确 告诉用户下哪几个文件、放到哪个目录。- 多仓库
parts的每个 part 都要有自己的真实repo;part 的repos不会继承工件级。
宿主在下载失败后的模型卡里提供「无法下载?手动下载」:展示
manual_url、ready_globs和目标目录。地址写错会直接把用户带到 404,因此发布前必须实际点开验证。
仅 Hugging Face 权重(无 ModelScope 源)最佳实践
如果你的模型仅发布在 Hugging Face(没有在 ModelScope 上架搬运),务必在工件或各 part 中声明:
"repo": "org/my-model-gguf",
"repos": {
"huggingface": "org/my-model-gguf",
"hf-mirror": "org/my-model-gguf"
},
"manual_url": "https://huggingface.co/org/my-model-gguf/tree/main",
"sources": ["huggingface", "hf-mirror"],
"ready_globs": ["my-model-Q4_K_M.gguf"]宿主行为保证:
- 拒绝盲目撞墙:当国内用户采用默认的「国内高速镜像」下载时,宿主会严格遵循工件的源声明,识别到未包含
modelscope,直接使用hf-mirror国内镜像高速拉取 Hugging Face 资产,彻底避免去 ModelScope 尝试一个不存在的仓库而遭遇 404 或超时。 - 多节点降级与看门狗时间重置:如果模型同时支持 ModelScope 与 hf-mirror,当首选的 ModelScope 节点由于网络波动或服务端断流发生异常时,宿主会自动无缝降级至
hf-mirror。在切换到备用镜像的瞬间,宿主会立即清空重置看门狗的停滞计数器,为备用镜像提供完整的首包握手(宽限 96 秒)与传输等待时间,彻底杜绝前序节点的耗时拖垮后序备用节点。
多仓库拼接 parts + 自定义 dir
问题:权重与 tokenizer / 编解码器 / 视觉塔分属不同仓库时,一个工件 = 一个仓库表达不了 (文件永远凑不齐,卡在「未就绪」)。
{
"id": "qwen3-tts-1.7b-customvoice-q8",
"name": "Qwen3-TTS 1.7B CustomVoice (Q8_0)",
"category": "tts",
"dir": "qwen3-tts-1.7b-customvoice",
"est_bytes": 2480000000,
"sources": ["huggingface"],
"ready_globs": ["qwen3-tts-12hz-1.7b-customvoice-q8_0.gguf",
"qwen3-tts-tokenizer-12hz-q8_0.gguf"],
"parts": [
{ "repo": "cstr/qwen3-tts-1.7b-customvoice-GGUF",
"allow_patterns": ["*customvoice-q8_0.gguf"] },
{ "repo": "cstr/qwen3-tts-tokenizer-12hz-GGUF",
"allow_patterns": ["*tokenizer-12hz-q8_0.gguf"] }
]
}- 每个 part 支持自己的
repo/repos/sources/allow_patterns(按 part 各自生效),上限 8 项; 同一个repo写两次会合并allow_patterns。 - part 未写
sources时继承工件级sources;repos不继承(它是对具体仓库的覆写)。 - 所有 part 的文件落进同一个目录(
dir→repo规范名 → 首个 part 仓库名); 宿主先下主仓库,再依次补齐其余片段;全部成功才算就绪。 - 任一片段失败即整体未就绪;重试会跳过已落盘文件(可续传),进度文案带
1/2、2/2。 ready_globs依旧只在该目录的直接子文件里判定。
两个坑
- 每个 part 都要写
allow_patterns:没写白名单的片段会被整仓下载 (主链路的第一个片段例外——它会回落到工件级allow_patterns/ready_globs)。 - 不要让不同 part 产出同名文件(后下的会静默覆盖前面的)。同名只应出现在被
allow_patterns收窄到互不重叠的仓库里。
宿主 API
host.list_model_artifacts() -> list[dict]
host.inspect_model_artifact(plugin_id: str, artifact_id: str) -> dict
host.download_model_artifact(plugin_id: str, artifact_id: str) -> Noneinspect_model_artifact() 返回:
{
"plugin_id": str,
"artifact_id": str,
"name": str,
"category": str,
"repo": str,
"est_bytes": int,
"is_downloaded": bool,
"model_dir": str, # 实际命中权重的目录;未就绪时 ""
"target_dir": str, # ★ 确定落盘目录,无论是否已下载都有值
"size_str": str,
}用
target_dir定位文件(model_dir未就绪为空)。 不要自己拼<org>--<name>——多仓库parts/ 自定义dir时拼不出来。
info = self.host.inspect_model_artifact(self.manifest.id, "my-asr-weights")
root = Path(info["target_dir"]) # 该去哪读文件
exe = root / "model.gguf"
ready = bool(info.get("is_downloaded"))QML 侧对应槽:listModelArtifacts() / inspectModelArtifact(pid, aid) / downloadModelArtifact(pid, aid)。
就绪检查钩子
def check_ready(self) -> tuple[bool, str, str]:
"""返回 (ok, issue_type, message)。
issue_type: '' | 'missing_key' | 'model_not_ready' | 'unassigned'"""未实现时宿主降级为跳过该校验。
语义:
check_ready()回答的是「能不能用」(运行时在不在、权重齐不齐、Key 配了没), 不是「服务此刻在不在跑」。本地服务型插件请把「服务未启动」判为可用(True), 否则用户会被拒绝开启音轨(宿主只在真正要用的路径上才调ensure_loaded()拉起服务)。 见 本地服务型引擎强制契约。
多能力模型(变体)
同一模型里有多个互斥变体时(例:Qwen3-TTS 的 base 克隆 / customvoice 内置音色 / voicedesign 文字描述音色),把它们声明成多个 model_artifacts(各写自己的 ready_globs), 并在 model_artifacts() 里把当前生效的那份排到第一位:
def model_artifacts(self) -> list[dict]:
arts = [...] # 全部变体
active = self._resolve_variant() # 按配置 / 已下载情况选出生效的
arts.sort(key=lambda a: 0 if a["id"] == active else 1)
return arts宿主模型卡按第一个同 category 的工件展示与下载,用户因此只需要下当前变体: 既不会被误报「未就绪」,也不会被迫下全部变体(以 Qwen3-TTS 为例:每个变体都是一份完整 LM,约 1.1 GB)。
实测参考:extensions/community_qwen3_tts 的 _VARIANTS / _ARTIFACT_BY_VARIANT。
设置页零代码呈现
只要插件声明了模型工件,并且 tags 里含 ["local"] 或 ["offline"]:
HardwareRatingBanner:自动按模型体量与 GPU/CPU 环境给出算力评级 (「极佳配置: RTX 4070 Ti(显存充足,极速毫秒级离线秒翻)」/「极速轻量: 纯 CPU 离线运行良好」…)LocalModelStorageCard:全球官方 / 国内镜像 / hf-mirror 节点切换、代理开关与地址、 真实字节进度、存放路径打开、自定义迁移重定向、一键清除缓存GpuReleaseRow:统一显存释放与加速框架状态
config_schema 里只放推理超参。
其他相关属性
@property
def supports_concurrent_streams(self) -> bool:
"""默认 True。底层为单活句柄时返回 False,宿主才降级另一轨。"""
@property
def requires_hf_runtime(self) -> bool | None:
"""True=需要 HF;False=纯 API/云端可跳过预热;None=按 manifest.is_local 推断。"""requires_hf_runtime 也可在 mod.json 写 "requires_hf": false。 第三方云端插件应声明 false 或 tags: ["cloud"],避免无谓的 transformers 预热。
搜索路径(宿主解析已下载权重时):插件目录 models/{artifact_id} → HF cache models--{org--name} → ModelScope cache。
