diff --git a/康康.xcodeproj/project.pbxproj b/康康.xcodeproj/project.pbxproj index 5eeb8f8..1962343 100644 --- a/康康.xcodeproj/project.pbxproj +++ b/康康.xcodeproj/project.pbxproj @@ -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 */ diff --git a/康康.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/康康.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6fffd8a..872fd76 100644 --- a/康康.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/康康.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -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" } } ], diff --git a/康康/AI/AIRuntime.swift b/康康/AI/AIRuntime.swift index 6363a3f..32d16bb 100644 --- a/康康/AI/AIRuntime.swift +++ b/康康/AI/AIRuntime.swift @@ -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 引导)。 diff --git a/康康/AI/ModelManifest.swift b/康康/AI/ModelManifest.swift index 105d525..18b1e99 100644 --- a/康康/AI/ModelManifest.swift +++ b/康康/AI/ModelManifest.swift @@ -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 大小 diff --git a/康康/AI/ModelStore.swift b/康康/AI/ModelStore.swift index dcfa9c0..dd31e67 100644 --- a/康康/AI/ModelStore.swift +++ b/康康/AI/ModelStore.swift @@ -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" } } diff --git a/康康/Models/HealthExport.swift b/康康/Models/HealthExport.swift index cee32cb..c6c0edf 100644 --- a/康康/Models/HealthExport.swift +++ b/康康/Models/HealthExport.swift @@ -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 diff --git a/康康Tests/ModelManifestTests.swift b/康康Tests/ModelManifestTests.swift index 91717c1..acbab04 100644 --- a/康康Tests/ModelManifestTests.swift +++ b/康康Tests/ModelManifestTests.swift @@ -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") } }