feat(AI): MNN 模型纳入下载体系 ModelKind.mnnLLM(Phase 4)

文本 MNN 模型用 taobao-mnn/Qwen3.5-2B-MNN 官方预转换格式(~1.10GiB),
不再从头转换(避开多模态转文本风险,官方转更可靠)。

- ModelStore.ModelKind 新增 .mnnLLM = "Qwen3.5-2B-MNN"
- ModelManifest:.mnnLLM 文件清单(config.json/llm_config.json/llm.mnn/
  llm.mnn.weight 1.1GB/tokenizer.txt/visual.mnn,HF API 实测字节)
- AIRuntime:mnnModelFolder + 就绪判定改走 ModelStore.isComplete(.mnnLLM)
- ModelManagementView:subtitle 加 .mnnLLM 文案(仅此一处,未动其它 WIP)
- ModelManifestTests:+4 条 mnnLLM 断言(文件数/总字节/必需文件/URL)

模拟器 ModelManifestTests TEST SUCCEEDED。下载经现有链路,需上传到
file.myv0.com/Qwen3.5-2B-MNN/(CDN 清单随附)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
link2026
2026-06-08 19:38:16 +08:00
parent 9da3fbc87e
commit 39b1521f00
5 changed files with 51 additions and 13 deletions

View File

@@ -20,6 +20,28 @@ struct ModelManifestTests {
#expect(ModelManifest.totalBytes(for: .vl) == 3_109_729_929)
}
@Test func mnnHasSixFunctionalFiles() {
#expect(ModelManifest.files(for: .mnnLLM).count == 6)
}
@Test func mnnTotalBytesMatchesManifest() {
#expect(ModelManifest.totalBytes(for: .mnnLLM) == 1_185_759_005)
}
@Test func mnnHasEssentialRuntimeFiles() {
let names = ModelManifest.files(for: .mnnLLM).map(\.path)
#expect(names.contains("config.json"))
#expect(names.contains("llm.mnn"))
#expect(names.contains("llm.mnn.weight"))
#expect(names.contains("tokenizer.txt"))
}
@Test func mnnFileURLUsesRepoPath() {
let file = ModelFile(path: "config.json", bytes: 652)
let url = ModelManifest.fileURL(for: .mnnLLM, file: file)
#expect(url.absoluteString == "https://file.myv0.com/Qwen3.5-2B-MNN/config.json")
}
@Test func excludesReadmeAndGitattributes() {
for kind in [ModelKind.llm, .vl] {
let names = ModelManifest.files(for: kind).map(\.path)