feat(capture): 统一报告捕获流程并集成视觉语言模型识别
- 替换 QuickCaptureFlow 和 ArchiveFlow 为 UnifiedCaptureFlow 统一流程 - 新增 VLSession 封装 Qwen2.5-VL 模型进行图像文本推理 - 实现 AIRuntime 中 VL 模型的准备和分析功能 - 添加 VLPrompts 定义体检化验单识别的 JSON 输出模板 - 创建 CaptureReviewForm 提供 VL 解析结果的可编辑表单界面 - 集成 VisionKit 文档扫描器支持真机多页文档扫描 - 为模拟器实现 PhotosPicker 回退方案选择已有照片 - 在 RootView 中统一使用 UnifiedCaptureFlow 处理快速和归档流程 - 添加 CustomMetricEditor 支持自定义监测指标的创建编辑删除 - 扩展 KangkangApp 模型配置以支持新数据类型 - 实现档案列表中症状结束功能通过时间线行点击触发
This commit is contained in:
@@ -25,9 +25,11 @@ actor AIRuntime {
|
||||
}
|
||||
|
||||
private(set) var status: Status = .notReady
|
||||
private(set) var vlStatus: Status = .notReady
|
||||
private(set) var lastDecodeRate: Double = 0
|
||||
|
||||
private var llmSession: LLMSession?
|
||||
private var vlSession: VLSession?
|
||||
|
||||
private init() {}
|
||||
|
||||
@@ -96,4 +98,53 @@ actor AIRuntime {
|
||||
private func recordRate(_ rate: Double) {
|
||||
if rate > 0 { lastDecodeRate = rate }
|
||||
}
|
||||
|
||||
// MARK: - VL
|
||||
|
||||
/// 加载 VL 模型。幂等,首调真正 load。
|
||||
func prepareVL() async throws {
|
||||
switch vlStatus {
|
||||
case .ready, .loading:
|
||||
return
|
||||
case .error, .notReady:
|
||||
break
|
||||
}
|
||||
|
||||
guard ModelStore.shared.isReady(.vl) else {
|
||||
vlStatus = .error("VL 模型未就绪")
|
||||
throw AIRuntimeError.notReady
|
||||
}
|
||||
|
||||
vlStatus = .loading
|
||||
do {
|
||||
let session = try await VLSession.load(
|
||||
folderURL: ModelStore.shared.localURL(for: .vl)
|
||||
)
|
||||
self.vlSession = session
|
||||
vlStatus = .ready
|
||||
} catch {
|
||||
vlStatus = .error("\(error)")
|
||||
throw AIRuntimeError.modelLoadFailed("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/// 图像 → JSON 字符串(由 VLPrompts.reportExtraction 引导)。
|
||||
/// 调用方负责解析 + 失败回退(§3.2)。
|
||||
/// AIRuntime 是 actor,本调用与 LLM.generate() 自然串行,不会 OOM。
|
||||
func analyzeReport(imageURLs: [URL],
|
||||
prompt: String,
|
||||
maxTokens: Int = 512) async throws -> String {
|
||||
guard vlStatus == .ready, let session = vlSession else {
|
||||
throw AIRuntimeError.notReady
|
||||
}
|
||||
do {
|
||||
return try await session.analyze(
|
||||
imageURLs: imageURLs,
|
||||
prompt: prompt,
|
||||
maxTokens: maxTokens
|
||||
)
|
||||
} catch {
|
||||
throw AIRuntimeError.inferenceFailed("\(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user