```
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:
@@ -3,10 +3,10 @@ import SwiftUI
|
||||
/// 推理引擎设置:在 MNN(CPU/SME2,考核路径)与 MLX(GPU,兜底)间切换,并展示 SME2 探测状态。
|
||||
/// 切换只改持久化选择;下一次 AI 调用(prepare/generate)按新引擎加载。
|
||||
struct InferenceSettingsView: View {
|
||||
@AppStorage("kk.inferenceEngine") private var engineRaw = InferenceEngine.mnn.rawValue
|
||||
@AppStorage("kk.inferenceEngine") private var engineRaw = EnginePreference.auto.rawValue
|
||||
|
||||
private var selected: InferenceEngine {
|
||||
InferenceEngine(rawValue: engineRaw) ?? .mnn
|
||||
private var selected: EnginePreference {
|
||||
EnginePreference(rawValue: engineRaw) ?? .auto
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -21,7 +21,7 @@ struct InferenceSettingsView: View {
|
||||
.padding(.top, 4)
|
||||
.padding(.bottom, 6)
|
||||
|
||||
ForEach(InferenceEngine.allCases, id: \.self) { engine in
|
||||
ForEach(EnginePreference.allCases, id: \.self) { engine in
|
||||
engineRow(engine)
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ struct InferenceSettingsView: View {
|
||||
.background(Tj.Palette.sand.ignoresSafeArea())
|
||||
}
|
||||
|
||||
private func engineRow(_ engine: InferenceEngine) -> some View {
|
||||
let available = engine.isAvailable
|
||||
private func engineRow(_ engine: EnginePreference) -> some View {
|
||||
let available = isAvailable(engine)
|
||||
let isOn = (selected == engine)
|
||||
return Button {
|
||||
guard available else { return }
|
||||
@@ -44,7 +44,7 @@ struct InferenceSettingsView: View {
|
||||
HStack(spacing: 12) {
|
||||
ZStack {
|
||||
Circle().fill(isOn ? Tj.Palette.amber.opacity(0.25) : Tj.Palette.sand2)
|
||||
Image(systemName: engine == .mnn ? "cpu.fill" : "bolt.fill")
|
||||
Image(systemName: iconName(engine))
|
||||
.font(.tjScaled(18))
|
||||
.foregroundStyle(isOn ? Tj.Palette.ink : Tj.Palette.text2)
|
||||
}
|
||||
@@ -74,8 +74,35 @@ struct InferenceSettingsView: View {
|
||||
.disabled(!available)
|
||||
}
|
||||
|
||||
private func subtitle(_ engine: InferenceEngine, available: Bool) -> String {
|
||||
/// .auto 永远可用;具体引擎看自身可用性。
|
||||
private func isAvailable(_ engine: EnginePreference) -> Bool {
|
||||
switch engine {
|
||||
case .auto: return true
|
||||
case .mnn: return InferenceEngine.mnn.isAvailable
|
||||
case .mlx: return InferenceEngine.mlx.isAvailable
|
||||
}
|
||||
}
|
||||
|
||||
private func iconName(_ engine: EnginePreference) -> String {
|
||||
switch engine {
|
||||
case .auto: return "wand.and.stars"
|
||||
case .mnn: return "cpu.fill"
|
||||
case .mlx: return "bolt.fill"
|
||||
}
|
||||
}
|
||||
|
||||
private func subtitle(_ engine: EnginePreference, available: Bool) -> String {
|
||||
switch engine {
|
||||
case .auto:
|
||||
// 显示自动解析后实际命中的引擎,让用户看清「这台机选了什么」。
|
||||
let resolved = engine.resolved
|
||||
if resolved == .mnn {
|
||||
return InferenceEngine.cpuSupportsSME2
|
||||
? String(appLoc: "按本机配置选择 · 当前 MNN + SME2")
|
||||
: String(appLoc: "按本机配置选择 · 当前 MNN(NEON)")
|
||||
} else {
|
||||
return String(appLoc: "按本机配置选择 · 当前 MLX(MNN 不可用)")
|
||||
}
|
||||
case .mnn:
|
||||
if !available { return String(appLoc: "本设备/模拟器不可用,自动回退 MLX") }
|
||||
return InferenceEngine.cpuSupportsSME2
|
||||
|
||||
@@ -10,8 +10,6 @@ struct ModelManagementView: View {
|
||||
@State private var showCellularConfirm = false
|
||||
@State private var showImporter = false
|
||||
@State private var importError: String?
|
||||
@AppStorage(QuickRegionRecognitionEngine.storageKey)
|
||||
private var quickRegionEngineRaw = QuickRegionRecognitionEngine.defaultValue.rawValue
|
||||
|
||||
private let monitor = NWPathMonitor()
|
||||
private let monitorQueue = DispatchQueue(label: "kk.netmonitor")
|
||||
@@ -27,8 +25,6 @@ struct ModelManagementView: View {
|
||||
modelCard(kind)
|
||||
}
|
||||
|
||||
recognitionEngineCard
|
||||
|
||||
actionButtons
|
||||
.padding(.top, 4)
|
||||
|
||||
@@ -80,46 +76,6 @@ struct ModelManagementView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 拍照识别引擎
|
||||
|
||||
private var selectedRecognitionEngine: QuickRegionRecognitionEngine {
|
||||
QuickRegionRecognitionEngine(storedValue: quickRegionEngineRaw)
|
||||
}
|
||||
|
||||
private var recognitionEngineCard: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
ZStack {
|
||||
Circle().fill(Tj.Palette.sand2)
|
||||
Image(systemName: "camera.metering.center.weighted")
|
||||
.font(.tjScaled( 18))
|
||||
.foregroundStyle(Tj.Palette.text2)
|
||||
}
|
||||
.frame(width: 38, height: 38)
|
||||
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text("异常项拍照识别")
|
||||
.font(.tjScaled( 15, weight: .semibold))
|
||||
.foregroundStyle(Tj.Palette.text)
|
||||
Text(selectedRecognitionEngine.detail)
|
||||
.font(.tjScaled( 12))
|
||||
.foregroundStyle(Tj.Palette.text3)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Picker("异常项拍照识别", selection: $quickRegionEngineRaw) {
|
||||
ForEach(QuickRegionRecognitionEngine.allCases) { engine in
|
||||
Text(engine.title).tag(engine.rawValue)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
.padding(14)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.tjCard()
|
||||
}
|
||||
|
||||
// MARK: - 模型卡片
|
||||
|
||||
private func modelCard(_ kind: ModelKind) -> some View {
|
||||
@@ -198,7 +154,7 @@ struct ModelManagementView: View {
|
||||
} else if allReady {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
Text("Qwen3.5-4B 已就绪")
|
||||
Text("Qwen3.5-2B 已就绪")
|
||||
}
|
||||
.font(.tjScaled( 13, weight: .semibold))
|
||||
.foregroundStyle(Tj.Palette.leaf)
|
||||
|
||||
Reference in New Issue
Block a user