Files
kangkang/康康/Services/ReportInsightService.swift

62 lines
2.6 KiB
Swift

import Foundation
import SwiftData
/// (§3.1: AIRuntime,UI )
/// :();
/// : summary VL
@MainActor
final class ReportInsightService {
static let shared = ReportInsightService()
private init() {}
/// ID,
private var inFlight: Set<String> = []
func pregenerateIfNeeded(report: Report, in ctx: ModelContext) async {
guard (report.summary ?? "").isEmpty, !report.indicators.isEmpty else { return }
let key = String(describing: report.persistentModelID)
guard !inFlight.contains(key) else { return }
inFlight.insert(key)
defer { inFlight.remove(key) }
do {
try await AIRuntime.shared.prepare()
} catch {
return // :,
}
let prompt = InsightPrompts.reportPlainSummary(
title: report.title,
typeLabel: report.type.label,
indicatorLines: Self.indicatorLines(for: report.indicators)
)
var collected = ""
do {
let stream = await AIRuntime.shared.generate(
prompt: prompt, maxTokens: 200, priority: .background)
for try await chunk in stream { collected += chunk.text }
} catch {
return // (CancellationError):,
}
let text = HealthExportService.stripThinkBlocks(collected)
.trimmingCharacters(in: .whitespacesAndNewlines)
guard !text.isEmpty, (report.summary ?? "").isEmpty else { return }
report.summary = text
try? ctx.save()
}
/// ( range)status;, 15 prompt
static func indicatorLines(for indicators: [Indicator]) -> String {
let sorted = indicators.sorted {
($0.status == .normal ? 1 : 0) < ($1.status == .normal ? 1 : 0)
}
return sorted.prefix(15).map { i in
var line = "\(i.name) \(i.value)"
if !i.unit.isEmpty { line += " \(i.unit)" }
if !i.range.isEmpty { line += "(参考 \(i.range))" }
line += " \(i.status.rawValue)"
return line
}.joined(separator: "\n")
}
}