feat(iOS): 更新MNN后端模型配置优化性能

将MNN主模型从Qwen3.5-4B(~2.64GiB)降级为Qwen3.5-2B(~1.1GiB),因为4B版本
实测运行过慢,影响用户体验。iPhone17+/SME2设备使用2B模型,保留MLX
兜底方案用于模拟器和备用场景,确保AI推理性能和存储效率的平衡。
```
This commit is contained in:
link2026
2026-06-09 22:20:07 +08:00
parent ca5a3fa38b
commit b79ae54b7b
40 changed files with 1327 additions and 452 deletions

View File

@@ -13,19 +13,19 @@ struct ModelManifestTests {
}
@Test func llmTotalBytesMatchesManifest() {
#expect(ModelManifest.totalBytes(for: .llm) == 3_061_129_077)
#expect(ModelManifest.totalBytes(for: .llm) == 1_749_079_691)
}
@Test func vlTotalBytesMatchesManifest() {
#expect(ModelManifest.totalBytes(for: .vl) == 3_109_729_929)
}
@Test func mnnHasSevenFunctionalFiles() {
#expect(ModelManifest.files(for: .mnnLLM).count == 7)
@Test func mnnHasSixFunctionalFiles() {
#expect(ModelManifest.files(for: .mnnLLM).count == 6)
}
@Test func mnnTotalBytesMatchesManifest() {
#expect(ModelManifest.totalBytes(for: .mnnLLM) == 2_836_770_850)
#expect(ModelManifest.totalBytes(for: .mnnLLM) == 1_185_759_005)
}
@Test func mnnHasEssentialRuntimeFiles() {
@@ -39,7 +39,7 @@ struct ModelManifestTests {
@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-4B-MNN/config.json")
#expect(url.absoluteString == "https://file.myv0.com/Qwen3.5-2B-MNN/config.json")
}
@Test func excludesReadmeAndGitattributes() {
@@ -62,8 +62,8 @@ struct ModelManifestTests {
}
@Test func fileURLIsBaseSlashRepoSlashPath() {
let file = ModelFile(path: "config.json", bytes: 3_366)
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.5-4B-4bit/config.json")
#expect(url.absoluteString == "https://file.myv0.com/Qwen3.5-2B-4bit/config.json")
}
}

View File

@@ -1,18 +0,0 @@
import Testing
@testable import
struct QuickRegionRecognitionEngineTests {
@Test func defaultsToAppleVisionOCR() {
#expect(QuickRegionRecognitionEngine.defaultValue == .appleVision)
}
@Test func rawValuesAreStableForAppStorage() {
#expect(QuickRegionRecognitionEngine.appleVision.rawValue == "appleVision")
#expect(QuickRegionRecognitionEngine.qwenVL.rawValue == "qwenVL")
}
@Test func unknownStoredValueFallsBackToDefault() {
#expect(QuickRegionRecognitionEngine(storedValue: "missing") == .appleVision)
}
}

View File

@@ -3,7 +3,7 @@ import CoreGraphics
import AVFoundation
@testable import
///
///
/// :, > rect
/// `metadataOutputRectConverted`() x/y ,
/// (2026-05-31 bug)
@@ -113,40 +113,4 @@ final class RegionImageCropperTests: XCTestCase {
.zero)
}
/// Qwen3-VL processor
/// VL ,
func testPrepareForQwenVLUpscalesWideShortCropAndAddsPadding() {
let image = solidImage(size: CGSize(width: 320, height: 80))
let prepared = RegionImageCropper.prepareForQwenVL(image)
XCTAssertGreaterThanOrEqual(prepared.size.height, 448)
XCTAssertGreaterThan(prepared.size.width, 320)
XCTAssertEqual(prepared.size.width / prepared.size.height,
4.0,
accuracy: 0.8,
"预处理应大致保留宽条内容比例,只允许白边造成轻微变化")
}
func testPrepareForQwenVLDoesNotEnlargePastLongEdgeLimit() {
let image = solidImage(size: CGSize(width: 5000, height: 900))
let prepared = RegionImageCropper.prepareForQwenVL(image)
XCTAssertLessThanOrEqual(max(prepared.size.width, prepared.size.height), 2400 + 128)
}
private func solidImage(size: CGSize) -> UIImage {
let format = UIGraphicsImageRendererFormat.default()
format.scale = 1
return UIGraphicsImageRenderer(size: size, format: format).image { ctx in
UIColor.white.setFill()
ctx.fill(CGRect(origin: .zero, size: size))
UIColor.black.setFill()
ctx.fill(CGRect(x: size.width * 0.1,
y: size.height * 0.35,
width: size.width * 0.8,
height: size.height * 0.3))
}
}
}