import Foundation protocol SummarizationProvider { func summarize(transcript: String, date: Date) async throws -> String } @Observable final class SummarizationService { var isSummarizing = false private let provider: SummarizationProvider init(provider: SummarizationProvider = LocalSummarizationProvider()) { self.provider = provider } func generateSummary(for entry: DiaryEntry) async throws -> String { guard !entry.combinedTranscript.isEmpty else { throw SummarizationError.noTranscript } isSummarizing = true defer { isSummarizing = false } return try await provider.summarize(transcript: entry.combinedTranscript, date: entry.date) } } enum SummarizationError: LocalizedError { case noTranscript case generationFailed var errorDescription: String? { switch self { case .noTranscript: String(localized: "summarization.error.noTranscript") case .generationFailed: String(localized: "summarization.error.failed") } } } /// Placeholder provider that structures transcripts into a basic diary format. /// Replace with llama.cpp or other on-device LLM integration. struct LocalSummarizationProvider: SummarizationProvider { func summarize(transcript: String, date: Date) async throws -> String { let dateString = date.formatted(.dateTime.day().month(.wide).year().locale(Locale(identifier: "de-DE"))) // For now, structure the transcript into a basic diary format. // This will be replaced with actual LLM inference. let lines = transcript.components(separatedBy: .newlines) .map { $0.trimmingCharacters(in: .whitespaces) } .filter { !$0.isEmpty } var result = "# \(dateString)\n\n" result += "## Erlebnisse des Tages\n\n" result += lines.joined(separator: "\n\n") result += "\n" return result } }