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:
@@ -38,7 +38,7 @@ actor AIRuntime {
|
||||
private(set) var mnnStatus: Status = .notReady
|
||||
/// MNN 模型目录(下载/旁路导入到 Models/Qwen3.5-2B-MNN)。
|
||||
nonisolated static var mnnModelFolder: URL {
|
||||
ModelStore.shared.rootURL.appendingPathComponent("Qwen3.5-2B-MNN", isDirectory: true)
|
||||
ModelStore.shared.localURL(for: .mnnLLM)
|
||||
}
|
||||
|
||||
// MARK: - 串行推理闸门(§3.1 OOM 防护的真正落地)
|
||||
@@ -92,8 +92,7 @@ actor AIRuntime {
|
||||
func prepare() async throws {
|
||||
// 选了 MNN 且模型已就绪才走 MNN;否则(选 MLX,或 MNN 模型尚未下载)回退 MLX,
|
||||
// 保证过渡期 App 始终可用。引擎指示器(Phase 5)展示实际生效后端。
|
||||
let mnnReady = FileManager.default.fileExists(
|
||||
atPath: Self.mnnModelFolder.appendingPathComponent("config.json").path)
|
||||
let mnnReady = ModelStore.shared.isComplete(for: .mnnLLM)
|
||||
if InferenceEngine.current == .mnn, mnnReady {
|
||||
try await prepareMNN()
|
||||
return
|
||||
@@ -147,8 +146,7 @@ actor AIRuntime {
|
||||
if mnnStatus == .ready { return }
|
||||
|
||||
let folder = Self.mnnModelFolder
|
||||
let config = folder.appendingPathComponent("config.json").path
|
||||
guard FileManager.default.fileExists(atPath: config) else {
|
||||
guard ModelStore.shared.isComplete(for: .mnnLLM) else {
|
||||
mnnStatus = .error("MNN 模型未就绪")
|
||||
throw AIRuntimeError.notReady
|
||||
}
|
||||
|
||||
@@ -59,6 +59,19 @@ nonisolated enum ModelManifest {
|
||||
ModelFile(path: "preprocessor_config.json", bytes: 782),
|
||||
ModelFile(path: "video_preprocessor_config.json", bytes: 817),
|
||||
]
|
||||
case .mnnLLM:
|
||||
// taobao-mnn/Qwen3.5-2B-MNN 预转换 MNN 格式(HF API 实测,2026-06)。
|
||||
// 运行时必需:config.json(MNN llm 配置)+ llm_config.json(超参)+ llm.mnn(图)
|
||||
// + llm.mnn.weight(量化权重 ~1.1GB)+ tokenizer.txt + visual.mnn(多模态,文本路径不用但配置含 mllm)。
|
||||
// 排除 README/.gitattributes 与可读 dump(llm.mnn.json / export_args.json)。
|
||||
return [
|
||||
ModelFile(path: "config.json", bytes: 652),
|
||||
ModelFile(path: "llm_config.json", bytes: 8_692),
|
||||
ModelFile(path: "llm.mnn", bytes: 2_148_136),
|
||||
ModelFile(path: "llm.mnn.weight", bytes: 1_176_647_702),
|
||||
ModelFile(path: "tokenizer.txt", bytes: 6_465_727),
|
||||
ModelFile(path: "visual.mnn", bytes: 488_096),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import Foundation
|
||||
|
||||
nonisolated enum ModelKind: String, CaseIterable {
|
||||
/// 与 HuggingFace mlx-community 仓库名一一对应,也是沙盒 Models/ 下的子目录名。
|
||||
/// 文本 LLM 用 Qwen3.5-2B(多模态权重,走 mlx-swift-lm 的 qwen3_5 → Qwen35Model 文本路径加载)。
|
||||
case llm = "Qwen3.5-2B-4bit"
|
||||
case vl = "Qwen3-VL-4B-Instruct-4bit"
|
||||
/// 也是沙盒 Models/ 下的子目录名 / CDN 路径段。
|
||||
/// - llm:MLX(GPU)文本兜底,Qwen3.5-2B(多模态权重,走 qwen3_5 文本路径)。
|
||||
/// - vl :MLX(GPU)拍照识别,Qwen3-VL-4B。
|
||||
/// - mnnLLM:MNN(CPU/SME2,挑战赛考核路径)文本,Qwen3.5-2B 预转换 MNN 格式(taobao-mnn)。
|
||||
case llm = "Qwen3.5-2B-4bit"
|
||||
case vl = "Qwen3-VL-4B-Instruct-4bit"
|
||||
case mnnLLM = "Qwen3.5-2B-MNN"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .llm: return "Qwen3.5-2B"
|
||||
case .vl: return "Qwen3-VL-4B"
|
||||
case .llm: return "Qwen3.5-2B (MLX)"
|
||||
case .vl: return "Qwen3-VL-4B"
|
||||
case .mnnLLM: return "Qwen3.5-2B (MNN/SME2)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -218,8 +218,9 @@ struct ModelManagementView: View {
|
||||
|
||||
private func subtitle(_ kind: ModelKind) -> String {
|
||||
switch kind {
|
||||
case .llm: return String(appLoc: "文本解读 · 趋势 / 问答")
|
||||
case .vl: return String(appLoc: "拍照识别报告 → 结构化指标")
|
||||
case .llm: return String(appLoc: "文本解读 · 趋势 / 问答(MLX 兜底)")
|
||||
case .vl: return String(appLoc: "拍照识别报告 → 结构化指标")
|
||||
case .mnnLLM: return String(appLoc: "文本解读 · MNN + SME2 端侧加速")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user