// // FoundationModelsReflectionService.swift // Reflect // // Generates personalized AI feedback after a user completes a guided reflection. // import Foundation import FoundationModels @available(iOS 26, *) @MainActor class FoundationModelsReflectionService { // MARK: - Initialization init() {} // MARK: - Feedback Generation /// Generate personalized feedback based on a completed guided reflection /// - Parameters: /// - reflection: The completed guided reflection with Q&A responses /// - mood: The mood associated with this entry /// - Returns: AI-generated reflection feedback func generateFeedback( for reflection: GuidedReflection, mood: Mood ) async throws -> AIReflectionFeedback { let session = LanguageModelSession(instructions: systemInstructions) let prompt = buildPrompt(from: reflection, mood: mood) let response = try await session.respond( to: prompt, generating: AIReflectionFeedback.self, options: GenerationOptions(maximumResponseTokens: 200) ) return response.content } // MARK: - System Instructions private var systemInstructions: String { let personalityPack = UserDefaultsStore.personalityPackable() switch personalityPack { case .Default: return defaultInstructions case .MotivationalCoach: return coachInstructions case .ZenMaster: return zenInstructions case .BestFriend: return bestFriendInstructions case .DataAnalyst: return analystInstructions } } private var defaultInstructions: String { """ You are a warm, supportive companion responding to someone who just completed a guided mood reflection. \ Validate their effort, reflect their own words back to them, and offer a gentle takeaway. \ Be specific — reference what they actually wrote. Keep each field to 1 sentence. \ SF Symbols: sparkles, heart.fill, star.fill, sun.max.fill, leaf.fill """ } private var coachInstructions: String { """ You are a HIGH ENERGY motivational coach responding to someone who just completed a guided mood reflection! \ Celebrate their self-awareness, pump them up about the growth they showed, and give them a power move for tomorrow. \ Reference what they actually wrote. Keep each field to 1 sentence. Use exclamations! \ SF Symbols: trophy.fill, flame.fill, bolt.fill, star.fill, figure.run """ } private var zenInstructions: String { """ You are a calm, mindful guide responding to someone who just completed a guided mood reflection. \ Acknowledge their practice of self-awareness with gentle wisdom. Use nature metaphors. \ Reference what they actually wrote. Keep each field to 1 sentence. Speak with serene clarity. \ SF Symbols: leaf.fill, moon.fill, drop.fill, sunrise.fill, wind """ } private var bestFriendInstructions: String { """ You are their supportive best friend responding after they completed a guided mood reflection. \ Be warm, casual, and validating. Use conversational tone. \ Reference what they actually wrote. Keep each field to 1 sentence. \ SF Symbols: heart.fill, hand.thumbsup.fill, sparkles, star.fill, face.smiling.fill """ } private var analystInstructions: String { """ You are a clinical data analyst providing feedback on a completed mood reflection. \ Note the cognitive patterns observed, the technique application quality, and a data-informed recommendation. \ Reference what they actually wrote. Keep each field to 1 sentence. Be objective but encouraging. \ SF Symbols: chart.bar.fill, brain.head.profile, doc.text.magnifyingglass, chart.line.uptrend.xyaxis """ } // MARK: - Prompt Construction private func buildPrompt(from reflection: GuidedReflection, mood: Mood) -> String { let moodName = mood.widgetDisplayName let technique = reflection.moodCategory.techniqueName let qaPairs = reflection.responses .filter { !$0.answer.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } .map { response in let chips = response.selectedChips.isEmpty ? "" : " [themes: \(response.selectedChips.joined(separator: ", "))]" return "Q: \(response.question)\nA: \(response.answer)\(chips)" } .joined(separator: "\n\n") // Intensity shift — if captured, tells the AI how much the reflection helped. var intensityLine = "" if let pre = reflection.preIntensity, let post = reflection.postIntensity { let delta = post - pre let direction: String if delta < 0 { direction = "dropped by \(abs(delta))" } else if delta > 0 { direction = "rose by \(delta)" } else { direction = "stayed the same" } intensityLine = "\nEmotional intensity: \(pre)/10 before → \(post)/10 after (\(direction)).\n" } else if let pre = reflection.preIntensity { intensityLine = "\nStarting emotional intensity: \(pre)/10.\n" } // Detected cognitive distortion — if present, helps the AI speak to the specific // pattern the user worked through (e.g., "you caught yourself overgeneralizing"). var distortionLine = "" if let distortion = reflection.detectedDistortion, distortion != .unknown { distortionLine = "\nDetected cognitive distortion in their automatic thought: \(distortion.rawValue). " + "Reference this pattern naturally in your observation without being clinical.\n" } return """ The user logged their mood as "\(moodName)" and completed a \(technique) reflection: \(intensityLine)\(distortionLine) \(qaPairs) Respond with personalized feedback that references their specific answers\ \(reflection.preIntensity != nil && reflection.postIntensity != nil ? " and acknowledges the shift in how they're feeling" : ""). """ } }