From 5eb724ab86ed43810a70b9debd3cf2e9f5536208 Mon Sep 17 00:00:00 2001 From: link2026 Date: Wed, 10 Jun 2026 06:11:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AF=AD=E9=9F=B3=E6=97=A5=E8=AE=B0):=20D?= =?UTF-8?q?iaryAssistService.organize=20=E8=BD=AC=E5=86=99=E7=A8=BF?= =?UTF-8?q?=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- 康康/Services/DiaryAssistService.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/康康/Services/DiaryAssistService.swift b/康康/Services/DiaryAssistService.swift index b5fdd15..5f2ed52 100644 --- a/康康/Services/DiaryAssistService.swift +++ b/康康/Services/DiaryAssistService.swift @@ -98,4 +98,29 @@ struct DiaryAssistService { guard !questions.isEmpty else { throw AssistError.empty } return (Array(questions.prefix(4)), lastRate) } + + /// 把语音转写稿整理成健康日记草稿(spec 2026-06-10-voice-diary)。 + /// 失败(模型未就绪 / 输出为空)抛错,调用方回退为直接使用原话,不卡死。 + /// 与 suggest 同样走 AIRuntime actor 队列,自然与追问/拍照串行。 + func organize(transcript: String) async throws -> (text: String, decodeRate: Double) { + do { + try await AIRuntime.shared.prepare() + } catch { + throw AssistError.modelNotReady + } + + let prompt = DiaryAssistPrompts.organize(transcript: transcript) + var collected = "" + var lastRate: Double = 0 + let stream = await AIRuntime.shared.generate(prompt: prompt, maxTokens: 400) + for try await chunk in stream { + collected += chunk.text + if chunk.decodeRate > 0 { lastRate = chunk.decodeRate } + } + + let text = HealthExportService.stripThinkBlocks(collected) + .trimmingCharacters(in: .whitespacesAndNewlines) + guard !text.isEmpty else { throw AssistError.empty } + return (text, lastRate) + } }