87 lines
3.4 KiB
Swift
87 lines
3.4 KiB
Swift
import Testing
|
|
import Foundation
|
|
@testable import 康康
|
|
|
|
struct ModelManifestTests {
|
|
|
|
@Test func llmHasEightFunctionalFiles() {
|
|
// 主模型已切 Gemma 4 E2B(text-only),8 个运行文件
|
|
//(无 tokenizer.model / special_tokens_map.json,新增 processor_config.json)。
|
|
#expect(ModelManifest.files(for: .llm).count == 8)
|
|
}
|
|
|
|
@Test func vlHasFourteenFunctionalFiles() {
|
|
#expect(ModelManifest.files(for: .vl).count == 14)
|
|
}
|
|
|
|
@Test func llmTotalBytesMatchesManifest() {
|
|
// gemma-4-e2b-it-4bit 全部运行文件字节之和(ModelScope repo/files 实测,2026-07)。
|
|
#expect(ModelManifest.totalBytes(for: .llm) == 3_613_528_388)
|
|
}
|
|
|
|
@Test func vlTotalBytesMatchesManifest() {
|
|
#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() {
|
|
// .mnnLLM 已停用但仍配 ModelScope 源备查;fileURL 走 modelscope resolve/master。
|
|
let file = ModelFile(path: "config.json", bytes: 652)
|
|
let url = ModelManifest.fileURL(for: .mnnLLM, file: file)
|
|
#expect(url.absoluteString ==
|
|
"https://modelscope.cn/models/MNN/Qwen3.5-2B-MNN/resolve/master/config.json")
|
|
}
|
|
|
|
@Test func excludesReadmeAndGitattributes() {
|
|
for kind in [ModelKind.llm, .vl] {
|
|
let names = ModelManifest.files(for: kind).map(\.path)
|
|
#expect(!names.contains("README.md"))
|
|
#expect(!names.contains(".gitattributes"))
|
|
}
|
|
}
|
|
|
|
@Test func includesEssentialFiles() {
|
|
let llm = ModelManifest.files(for: .llm).map(\.path)
|
|
#expect(llm.contains("config.json"))
|
|
#expect(llm.contains("model.safetensors"))
|
|
#expect(llm.contains("tokenizer.json"))
|
|
|
|
let vl = ModelManifest.files(for: .vl).map(\.path)
|
|
#expect(vl.contains("preprocessor_config.json")) // VL 拍照识别必需
|
|
#expect(vl.contains("model.safetensors"))
|
|
}
|
|
|
|
@Test func llmFileURLUsesModelScopeRepo() {
|
|
// 首选源走 ModelScope 官方 resolve/master(大陆可达,302 跳 OSS 支持 Range 续传)。
|
|
let file = ModelFile(path: "config.json", bytes: 5_996)
|
|
let url = ModelManifest.fileURL(for: .llm, file: file)
|
|
#expect(url.absoluteString ==
|
|
"https://modelscope.cn/models/mlx-community/gemma-4-e2b-it-4bit/resolve/master/config.json")
|
|
}
|
|
|
|
@Test func llmFileURLsAreModelScopeThenHFMirror() {
|
|
// 用户选择:魔搭优先 + hf-mirror 回退。候选源顺序必须为 [ModelScope master, hf-mirror main]。
|
|
let file = ModelFile(path: "config.json", bytes: 5_996)
|
|
let urls = ModelManifest.fileURLs(for: .llm, file: file).map(\.absoluteString)
|
|
#expect(urls == [
|
|
"https://modelscope.cn/models/mlx-community/gemma-4-e2b-it-4bit/resolve/master/config.json",
|
|
"https://hf-mirror.com/mlx-community/gemma-4-e2b-it-4bit/resolve/main/config.json",
|
|
])
|
|
}
|
|
}
|