Files
Reflect/Shared/Services/FoundationModelsReflectionService.swift
Trey T cc4143d3ea Expand guided reflection with CBT thought record and distortion routing
Adds a 5-step negative-mood reflection flow with an evidence-examination
step, Socratic templated questions that back-reference prior answers, and
a deterministic cognitive-distortion detector that routes the perspective-
check prompt to a distortion-specific reframe. Includes CBT plan docs,
flowchart, stats research notes, and MCP config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:49:39 -05:00

153 lines
6.2 KiB
Swift

//
// 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" : "").
"""
}
}