85 lines
3.2 KiB
Swift
85 lines
3.2 KiB
Swift
import Foundation
|
|
|
|
/// 端侧推理引擎选择。
|
|
/// - mnn:Qwen + MNN + SME2(CPU),挑战赛考核路径,真机默认。
|
|
/// - mlx:Qwen + MLX(Metal GPU),兜底 / 对照。模拟器只有它可用。
|
|
nonisolated enum InferenceEngine: String, CaseIterable, Sendable {
|
|
case mnn
|
|
case mlx
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .mnn: return "MNN · CPU/SME2"
|
|
case .mlx: return "MLX · GPU"
|
|
}
|
|
}
|
|
|
|
/// 本构建/设备是否可用。MNN 仅 device 切片有真实内核,模拟器回退 MLX。
|
|
var isAvailable: Bool {
|
|
switch self {
|
|
case .mlx: return true
|
|
case .mnn: return MNNLLMBridge.isAvailable()
|
|
}
|
|
}
|
|
|
|
// MARK: - 持久化(UserDefaults,跨 actor 安全)
|
|
|
|
private static let key = "kk.inferenceEngine"
|
|
|
|
/// 由偏好(可能是 .auto)解析出的、本次调用实际使用的具体引擎。
|
|
/// AIRuntime / MeView 等消费方只看这个,永远拿到 .mnn 或 .mlx。
|
|
/// 解析后仍做一次可用性兜底,保证总有可用引擎。
|
|
///
|
|
/// 项目已切 Gemma-3n(只走 MLX/GPU):Gemma-3n 跑不了 MNN(无转换模型),
|
|
/// 故主模型不再下载 MNN 切片。即便历史偏好写了 "mnn",只要本机没有完整的 MNN 模型,
|
|
/// 一律回退 MLX —— 杜绝「标签显示 MNN、实际却跑 MLX」的不一致。
|
|
static var current: InferenceEngine {
|
|
let resolved = preference.resolved
|
|
guard resolved.isAvailable else { return .mlx }
|
|
if resolved == .mnn, !ModelStore.shared.isComplete(for: .mnnLLM) { return .mlx }
|
|
return resolved
|
|
}
|
|
|
|
/// 运行时探测:CPU 是否支持 SME2(A19/iPhone17+)。用于 UI 展示加速状态。
|
|
/// CPU 能力进程内不变,缓存一次,避免每次 UI 刷新都做 sysctl 系统调用。
|
|
static let cpuSupportsSME2: Bool = MNNLLMBridge.cpuSupportsSME2()
|
|
|
|
// MARK: - 用户偏好(auto / mnn / mlx)
|
|
|
|
/// 用户在设置页的选择。默认 .auto:按本机配置自动择优。
|
|
/// 与具体引擎共用同一 UserDefaults key——历史写入的 "mnn"/"mlx" 仍兼容。
|
|
static var preference: EnginePreference {
|
|
get {
|
|
let raw = UserDefaults.standard.string(forKey: key)
|
|
return raw.flatMap(EnginePreference.init(rawValue:)) ?? .auto
|
|
}
|
|
set { UserDefaults.standard.set(newValue.rawValue, forKey: key) }
|
|
}
|
|
}
|
|
|
|
/// 推理引擎的「用户偏好」,比具体引擎多一个 .auto。
|
|
/// - auto:本项目已切 Gemma-3n,主模型只走 MLX/GPU,故 auto 一律解析为 .mlx。
|
|
/// (Gemma-3n 跑不了 MNN/SME2;MNN 路径已停用。)
|
|
nonisolated enum EnginePreference: String, CaseIterable, Sendable {
|
|
case auto
|
|
case mnn
|
|
case mlx
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .auto: return "自动"
|
|
case .mnn: return InferenceEngine.mnn.displayName
|
|
case .mlx: return InferenceEngine.mlx.displayName
|
|
}
|
|
}
|
|
|
|
/// 把偏好解析成具体引擎(不做可用性兜底,那一步留给 `InferenceEngine.current`)。
|
|
var resolved: InferenceEngine {
|
|
switch self {
|
|
case .mnn: return .mnn
|
|
case .mlx: return .mlx
|
|
case .auto: return .mlx // Gemma-3n 只走 MLX/GPU,auto 即 MLX
|
|
}
|
|
}
|
|
}
|