Files
FeelAloud/FeelAloud/MoodAnalysisService.swift
2026-07-22 22:18:33 +02:00

93 lines
3.5 KiB
Swift

import Foundation
import FoundationModels
@Generable
private struct GeneratedDiaryEntry {
@Guide(description: "Aktuelle Stimmung", .anyOf(["Sehr gut", "Gut", "Mittel", "Schlecht", "Sehr schlecht"]))
var mood: String
@Guide(description: "Ob die Person gerade Musik oder einen Song im Kopf hat")
var musicInHead: Bool
@Guide(description: "Name des Songs; leer lassen, wenn kein Song genannt wurde")
var songName: String
@Guide(description: "Stärke des Ohrwurms", .anyOf(["Leise", "Mittel", "Laut"]))
var earwormStrength: String
@Guide(description: "Ob heute Sozialkontakt stattgefunden hat")
var socialContact: Bool
@Guide(description: "Leidensdruck durch den Ohrwurm", .anyOf(["Niedrig", "Mittel", "Hoch"]))
var distress: String
@Guide(description: "Ob Gegenmaßnahmen gegen den Ohrwurm versucht wurden")
var countermeasures: Bool
@Guide(description: "Genannte Freizeitaktivität oder Gegenmaßnahme; sonst leer")
var countermeasureActivity: String
@Guide(description: "Ob Stress vorhanden ist")
var stress: Bool
}
enum MoodAnalysisError: LocalizedError {
case emptyTranscript
case modelUnavailable
var errorDescription: String? {
switch self {
case .emptyTranscript:
"Es wurde noch kein Text erkannt. Sprich etwas länger oder trage die Angaben manuell ein."
case .modelUnavailable:
"Apples lokales KI-Modell ist noch nicht verfügbar. Prüfe, ob Apple Intelligence aktiviert und vollständig geladen ist."
}
}
}
struct MoodAnalysisService {
static func analyze(_ transcript: String) async throws -> DraftMoodEntry {
let cleanTranscript = transcript.trimmingCharacters(in: .whitespacesAndNewlines)
guard !cleanTranscript.isEmpty else {
throw MoodAnalysisError.emptyTranscript
}
let model = SystemLanguageModel.default
guard model.availability == .available else {
throw MoodAnalysisError.modelUnavailable
}
let session = LanguageModelSession(
model: model,
instructions: """
Du extrahierst ausschließlich strukturierte Angaben aus einem privaten deutschen Stimmungstagebuch.
Erfinde keine Songnamen, Aktivitäten oder Erlebnisse.
Wenn etwas nicht erwähnt wird, verwende für Text ein leeres Feld und für Ja/Nein den Wert false.
Wenn Musik im Kopf vorhanden ist, ist Stress für diesen Eintrag false.
Wenn keine Musik im Kopf vorhanden ist, bleiben Songname, Ohrwurmstärke, Leidensdruck und Gegenmaßnahmen auf ihren neutralen Standardwerten.
Ordne die Stimmung auf der Skala Sehr gut, Gut, Mittel, Schlecht, Sehr schlecht ein.
Du gibst keine medizinische Einschätzung und keinen Rat.
"""
)
let response = try await session.respond(
to: "Analysiere diesen Tagebucheintrag:\n\n\(cleanTranscript)",
generating: GeneratedDiaryEntry.self
)
let value = response.content
return DraftMoodEntry(
mood: .normalized(value.mood),
musicInHead: value.musicInHead,
songName: value.songName,
earwormStrength: .normalized(value.earwormStrength),
socialContact: value.socialContact,
distress: .normalized(value.distress),
countermeasures: value.countermeasures,
countermeasureActivity: value.countermeasureActivity,
stress: value.stress,
transcript: cleanTranscript
)
}
}