feat(AI): LLM 迁移到 mlx-swift-lm 2.31.3 + Qwen3.5-2B
将 SPM 依赖从 mlx-swift-examples 2.29.1 迁到改名延续仓库 mlx-swift-lm 2.31.3(含 qwen3_5 架构、旧 loadContainer API 兼容),文本 LLM 由 Qwen3-1.7B 换为 Qwen3.5-2B-4bit(走 qwen3_5→Qwen35Model 文本路径)。 连带 mlx-swift 0.29.1→0.31.4,顺修弃用 API: - MLX.GPU.clearCache() → MLX.Memory.clearCache() - MLX.GPU.set(cacheLimit:) → MLX.Memory.cacheLimit 更新 ModelManifest(.llm 文件清单+精确字节数,~1.63GiB)、ModelManifestTests、 HealthExport.modelTag 默认值。App BUILD SUCCEEDED + ModelManifestTests 通过。 保留作 MNN 改造的 GPU 兜底基线。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -211,7 +211,7 @@
|
||||
mainGroup = 5E463CF02FC403BB0089145B;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
packageReferences = (
|
||||
5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-examples" */,
|
||||
5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-lm" */,
|
||||
);
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 5E463CFA2FC403BB0089145B /* Products */;
|
||||
@@ -659,12 +659,12 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-examples" */ = {
|
||||
5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-lm" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/ml-explore/mlx-swift-examples";
|
||||
repositoryURL = "https://github.com/ml-explore/mlx-swift-lm";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.29.1;
|
||||
kind = exactVersion;
|
||||
version = 2.31.3;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
@@ -672,17 +672,17 @@
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
FEED000000000000DEAD0003 /* MLXLLM */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-examples" */;
|
||||
package = 5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-lm" */;
|
||||
productName = MLXLLM;
|
||||
};
|
||||
FEED000000000000DEAD0004 /* MLXLMCommon */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-examples" */;
|
||||
package = 5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-lm" */;
|
||||
productName = MLXLMCommon;
|
||||
};
|
||||
FEED000000000000DEAD0006 /* MLXVLM */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-examples" */;
|
||||
package = 5E9A1F872FC43C9A0097DD29 /* XCRemoteSwiftPackageReference "mlx-swift-lm" */;
|
||||
productName = MLXVLM;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"originHash" : "6b8265ebd61c6fdfca835dd1f90f17439ca9abc5c11a8b7b5db8790be0349e4d",
|
||||
"originHash" : "facc0ac7c70363ea20f6cd1235de91dea6b06f0d00190946045a6c8ae753abc2",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "gzipswift",
|
||||
"identity" : "eventsource",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/1024jp/GzipSwift",
|
||||
"location" : "https://github.com/mattt/EventSource.git",
|
||||
"state" : {
|
||||
"revision" : "731037f6cc2be2ec01562f6597c1d0aa3fe6fd05",
|
||||
"version" : "6.0.1"
|
||||
"revision" : "a3a85a85214caf642abaa96ae664e4c772a59f6e",
|
||||
"version" : "1.4.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -15,17 +15,35 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ml-explore/mlx-swift",
|
||||
"state" : {
|
||||
"revision" : "072b684acaae80b6a463abab3a103732f33774bf",
|
||||
"version" : "0.29.1"
|
||||
"revision" : "dc43e62d7055353c7f99fa071a4e71d29dfddc44",
|
||||
"version" : "0.31.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "mlx-swift-examples",
|
||||
"identity" : "mlx-swift-lm",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ml-explore/mlx-swift-examples",
|
||||
"location" : "https://github.com/ml-explore/mlx-swift-lm",
|
||||
"state" : {
|
||||
"revision" : "9bff95ca5f0b9e8c021acc4d71a2bbe4a7441631",
|
||||
"version" : "2.29.1"
|
||||
"revision" : "25b00d4e22e61ec9c41efda47990cd2084ec87ff",
|
||||
"version" : "2.31.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-asn1",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-asn1.git",
|
||||
"state" : {
|
||||
"revision" : "eb50cbd14606a9161cbc5d452f18797c90ef0bab",
|
||||
"version" : "1.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-atomics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-atomics.git",
|
||||
"state" : {
|
||||
"revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -37,6 +55,24 @@
|
||||
"version" : "1.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-crypto",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-crypto.git",
|
||||
"state" : {
|
||||
"revision" : "1b6b2e274e85105bfa155183145a1dcfd63331f1",
|
||||
"version" : "4.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-huggingface",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/huggingface/swift-huggingface.git",
|
||||
"state" : {
|
||||
"revision" : "b721959445b617d0bf03910b2b4aced345fd93bf",
|
||||
"version" : "0.9.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-jinja",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -46,6 +82,15 @@
|
||||
"version" : "2.3.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio.git",
|
||||
"state" : {
|
||||
"revision" : "57c0a08a331aaea9f5d7a932ad94ef43be942a95",
|
||||
"version" : "2.100.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-numerics",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -55,13 +100,31 @@
|
||||
"version" : "1.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-system",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-system.git",
|
||||
"state" : {
|
||||
"revision" : "669763cfd5806a67e21972d7e5e2d6b80b1ea985",
|
||||
"version" : "1.6.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-transformers",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/huggingface/swift-transformers",
|
||||
"state" : {
|
||||
"revision" : "a2e184dddb4757bc943e77fbe99ac6786c53f0b2",
|
||||
"version" : "1.0.0"
|
||||
"revision" : "58c4bc11963a140358d791f678a60a2745a23146",
|
||||
"version" : "1.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "yyjson",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ibireme/yyjson.git",
|
||||
"state" : {
|
||||
"revision" : "8b4a38dc994a110abaec8a400615567bd996105f",
|
||||
"version" : "0.12.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -74,7 +74,7 @@ actor AIRuntime {
|
||||
nonisolated static func configureMLXMemory() {
|
||||
#if !targetEnvironment(simulator)
|
||||
// 256MB cache 上限:够复用、不至于在 3GB 模型之上再囤几百 MB 空闲缓冲。
|
||||
MLX.GPU.set(cacheLimit: 256 * 1024 * 1024)
|
||||
MLX.Memory.cacheLimit = 256 * 1024 * 1024
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ actor AIRuntime {
|
||||
guard llmSession != nil else { return }
|
||||
llmSession = nil
|
||||
status = .notReady
|
||||
MLX.GPU.clearCache()
|
||||
MLX.Memory.clearCache()
|
||||
}
|
||||
|
||||
/// 卸载 VL,释放 ModelContainer 引用并清 MLX 显存缓存。幂等。
|
||||
@@ -216,7 +216,7 @@ actor AIRuntime {
|
||||
guard vlSession != nil else { return }
|
||||
vlSession = nil
|
||||
vlStatus = .notReady
|
||||
MLX.GPU.clearCache()
|
||||
MLX.Memory.clearCache()
|
||||
}
|
||||
|
||||
/// 图像 → JSON 字符串(由 VLPrompts.reportExtraction 引导)。
|
||||
|
||||
@@ -18,16 +18,23 @@ nonisolated enum ModelManifest {
|
||||
static func files(for kind: ModelKind) -> [ModelFile] {
|
||||
switch kind {
|
||||
case .llm:
|
||||
// Qwen3.5-2B-4bit:多模态仓库,但走 LLMModelFactory 的 qwen3_5 文本路径加载。
|
||||
// 字节数取自 mlx-community/Qwen3.5-2B-4bit 仓库实际 blob 大小(HF API,2026-06 核对)。
|
||||
// 该仓库 tokenizer 体系为 vocab.json + tokenizer.json(无 merges.txt /
|
||||
// special_tokens_map.json / added_tokens.json),chat_template 改为 .jinja。
|
||||
// 一并镜像视觉预处理配置(preprocessor / processor / video_preprocessor),
|
||||
// 文本加载用不到但体积可忽略,保持与仓库一致避免漏文件。
|
||||
return [
|
||||
ModelFile(path: "config.json", bytes: 937),
|
||||
ModelFile(path: "model.safetensors", bytes: 968_080_210),
|
||||
ModelFile(path: "model.safetensors.index.json", bytes: 49_731),
|
||||
ModelFile(path: "tokenizer.json", bytes: 11_422_654),
|
||||
ModelFile(path: "tokenizer_config.json", bytes: 9_706),
|
||||
ModelFile(path: "vocab.json", bytes: 2_776_833),
|
||||
ModelFile(path: "merges.txt", bytes: 1_671_853),
|
||||
ModelFile(path: "special_tokens_map.json", bytes: 613),
|
||||
ModelFile(path: "added_tokens.json", bytes: 707),
|
||||
ModelFile(path: "config.json", bytes: 3_113),
|
||||
ModelFile(path: "model.safetensors", bytes: 1_722_271_785),
|
||||
ModelFile(path: "model.safetensors.index.json", bytes: 81_722),
|
||||
ModelFile(path: "tokenizer.json", bytes: 19_989_343),
|
||||
ModelFile(path: "tokenizer_config.json", bytes: 1_139),
|
||||
ModelFile(path: "vocab.json", bytes: 6_722_759),
|
||||
ModelFile(path: "chat_template.jinja", bytes: 7_755),
|
||||
ModelFile(path: "preprocessor_config.json", bytes: 390),
|
||||
ModelFile(path: "processor_config.json", bytes: 1_300),
|
||||
ModelFile(path: "video_preprocessor_config.json", bytes: 385),
|
||||
]
|
||||
case .vl:
|
||||
// Qwen3-VL-4B-Instruct-4bit:字节数取自 mlx-community 仓库实际 blob 大小
|
||||
|
||||
@@ -2,12 +2,13 @@ import Foundation
|
||||
|
||||
nonisolated enum ModelKind: String, CaseIterable {
|
||||
/// 与 HuggingFace mlx-community 仓库名一一对应,也是沙盒 Models/ 下的子目录名。
|
||||
case llm = "Qwen3-1.7B-4bit"
|
||||
/// 文本 LLM 用 Qwen3.5-2B(多模态权重,走 mlx-swift-lm 的 qwen3_5 → Qwen35Model 文本路径加载)。
|
||||
case llm = "Qwen3.5-2B-4bit"
|
||||
case vl = "Qwen3-VL-4B-Instruct-4bit"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .llm: return "Qwen3-1.7B"
|
||||
case .llm: return "Qwen3.5-2B"
|
||||
case .vl: return "Qwen3-VL-4B"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ final class HealthExport {
|
||||
var inferredLabelCN: String?
|
||||
|
||||
// demo 卖点凭证
|
||||
/// 模型 tag,如 "Qwen3-1.7B-4bit"。截图能证明本地推理。
|
||||
/// 模型 tag,如 "Qwen3.5-2B-4bit"。截图能证明本地推理。
|
||||
var modelTag: String
|
||||
/// 末次 tok/s,对应 demo 卖点 #6 Live Activity 数据。
|
||||
var decodeRate: Double
|
||||
@@ -44,7 +44,7 @@ final class HealthExport {
|
||||
inferredTimeToDate: Date? = nil,
|
||||
inferredIntent: String? = nil,
|
||||
inferredLabelCN: String? = nil,
|
||||
modelTag: String = "Qwen3-1.7B-4bit",
|
||||
modelTag: String = "Qwen3.5-2B-4bit",
|
||||
decodeRate: Double = 0) {
|
||||
self.prompt = prompt
|
||||
self.content = content
|
||||
|
||||
@@ -4,8 +4,8 @@ import Foundation
|
||||
|
||||
struct ModelManifestTests {
|
||||
|
||||
@Test func llmHasNineFunctionalFiles() {
|
||||
#expect(ModelManifest.files(for: .llm).count == 9)
|
||||
@Test func llmHasTenFunctionalFiles() {
|
||||
#expect(ModelManifest.files(for: .llm).count == 10)
|
||||
}
|
||||
|
||||
@Test func vlHasFourteenFunctionalFiles() {
|
||||
@@ -13,7 +13,7 @@ struct ModelManifestTests {
|
||||
}
|
||||
|
||||
@Test func llmTotalBytesMatchesManifest() {
|
||||
#expect(ModelManifest.totalBytes(for: .llm) == 984_013_244)
|
||||
#expect(ModelManifest.totalBytes(for: .llm) == 1_749_079_691)
|
||||
}
|
||||
|
||||
@Test func vlTotalBytesMatchesManifest() {
|
||||
@@ -40,8 +40,8 @@ struct ModelManifestTests {
|
||||
}
|
||||
|
||||
@Test func fileURLIsBaseSlashRepoSlashPath() {
|
||||
let file = ModelFile(path: "config.json", bytes: 937)
|
||||
let file = ModelFile(path: "config.json", bytes: 3_113)
|
||||
let url = ModelManifest.fileURL(for: .llm, file: file)
|
||||
#expect(url.absoluteString == "https://file.myv0.com/Qwen3-1.7B-4bit/config.json")
|
||||
#expect(url.absoluteString == "https://file.myv0.com/Qwen3.5-2B-4bit/config.json")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user