Personalization System (Phase 7)
Memory, preferences, and context that make coaching personal.
Overview
Phase 7 introduced 12 personalization features that allow coaches to remember users, track progress, and adapt responses based on context.
Features
| # | Feature | Description | Database Table |
|---|---|---|---|
| 1 | Memory Store | Persistent facts coaches learn | coach_memories |
| 2 | Conversation Summaries | Compressed conversation history | conversation_summaries |
| 3 | User Preferences | Communication preferences | user_preferences |
| 4 | Milestones | Track and celebrate achievements | user_milestones |
| 5 | Mood Detection | Infer emotional state | user_moods |
| 6 | Insights | Patterns and recommendations | coach_insights |
| 7 | Response Config | Time-based tone adjustments | Computed |
| 8 | Time Patterns | When users are most active | user_time_patterns |
| 9 | Struggle Detection | Recurring challenges | user_struggles |
| 10 | Goal Progress | Progress toward goals | coach_goal_progress |
| 11 | Interaction Recording | Log coach interactions | coach_interactions |
| 12 | Coach Relationships | Rapport scores per coach | coach_relationships |
Memory System
Memory Categories
type MemoryCategory =
| 'preference' // User likes/dislikes
| 'struggle' // Recurring challenges
| 'achievement' // Past accomplishments
| 'personal' // Life context (job, family)
| 'health' // Health conditions
| 'motivation' // What drives them
| 'behavior' // Observed patterns
| 'feedback'; // User feedback on coaching
Memory Importance
type MemoryImportance = 'low' | 'medium' | 'high' | 'critical';
Creating Memories
import { createMemory, upsertMemory } from '@/lib/coaches';
// Create a new memory
await createMemory({
userId: 'user-123',
coachId: 'max', // Optional: coach-specific memory
category: 'preference',
key: 'prefers_compound_lifts',
value: 'User enjoys compound movements over isolation',
importance: 'medium',
source: 'conversation',
confidence: 0.85,
});
// Upsert (update if exists, create if not)
await upsertMemory({
userId: 'user-123',
category: 'motivation',
key: 'primary_motivation',
value: 'Training for upcoming wedding in June',
importance: 'high',
});
Getting Memories for Prompts
import { getMemoriesForPrompt } from '@/lib/coaches';
// Get formatted memories for AI prompt
const memoryPrompt = await getMemoriesForPrompt('user-123', {
coachId: 'max',
limit: 10,
});
// Returns formatted string like:
// WHAT I REMEMBER ABOUT THIS USER:
// - Preference: User enjoys compound movements over isolation
// - Motivation: Training for upcoming wedding in June
Struggle Tracking
Track and address recurring challenges users face.
Recording Struggles
import { upsertStruggle, resolveStruggle, markStruggleImproving } from '@/lib/coaches';
// Record a new struggle
await upsertStruggle({
userId: 'user-123',
category: 'form',
description: 'Difficulty maintaining neutral spine during deadlifts',
muscleGroup: 'back',
coachingApproach: 'Focus on hip hinge pattern with Romanian deadlifts first',
suggestedByCoachId: 'max',
});
// Mark as improving
await markStruggleImproving(struggleId);
// Mark as resolved
await resolveStruggle(struggleId);
Struggle Status Flow
active → improving → resolved
↓ ↓
└─────────┴──→ recurring (if it comes back)
User Preferences
Communication preferences that affect how coaches respond.
Preference Options
interface UserPreferences {
responseLength: 'brief' | 'moderate' | 'detailed';
useEmoji: boolean;
useMetrics: boolean;
useScienceExplanations: boolean;
useMotivationalTone: boolean;
preferDirectFeedback: boolean;
preferGentleEncouragement: boolean;
preferDataDrivenAdvice: boolean;
preferHumor: boolean;
preferMorningCheckins: boolean;
preferEveningRecaps: boolean;
preferWorkoutReminders: boolean;
}
Usage
import { getUserPreferences, updateUserPreferences } from '@/lib/coaches';
// Get preferences
const prefs = await getUserPreferences('user-123');
// Update preferences
await updateUserPreferences('user-123', {
responseLength: 'brief',
useEmoji: false,
});
Mood Detection
Infer user's emotional state from messages.
Mood States
type MoodState =
| 'energized'
| 'motivated'
| 'neutral'
| 'tired'
| 'stressed'
| 'frustrated'
| 'anxious'
| 'excited'
| 'accomplished';
Detection
import { detectMoodFromMessage, recordMood } from '@/lib/coaches';
// Detect mood from message
const analysis = detectMoodFromMessage("I'm exhausted today, didn't sleep well");
// { state: 'tired', intensity: 7, confidence: 0.8 }
// Record explicit mood
await recordMood({
userId: 'user-123',
moodState: 'stressed',
intensity: 6,
source: 'explicit',
context: 'Work deadline this week',
});
Insights & Milestones
Creating Insights
import { createInsight } from '@/lib/coaches';
await createInsight({
userId: 'user-123',
coachId: 'nova',
insightType: 'pattern',
title: 'Stronger in Mornings',
content: 'Your performance is 15% better in morning sessions',
confidence: 0.85,
actionable: true,
suggestedAction: 'Consider scheduling heavy lifts before noon',
priority: 'medium',
});
Recording Milestones
import { recordMilestone, celebrateMilestone } from '@/lib/coaches';
await recordMilestone({
userId: 'user-123',
milestoneType: 'strength_pr',
title: 'New Bench Press PR',
description: 'Hit 225 lbs for 5 reps',
value: 225,
unit: 'lbs',
});
// Later, when showing to user
await celebrateMilestone(milestoneId);
Building Personalization Context
The main function that assembles all personalization data for AI prompts.
Full Context
import { buildPersonalizationContext } from '@/lib/coaches';
const context = await buildPersonalizationContext(userId, {
coachId: 'max',
includeMemories: true,
includeMood: true,
includeInsights: true,
includeStruggles: true,
includeGoalProgress: true,
includeRelationship: true,
includeTimePatterns: true,
});
// Returns:
{
memories: [...],
preferences: {...},
mood: {...},
struggles: [...],
insights: [...],
goalProgress: [...],
relationship: {...},
timePatterns: {...},
responseConfig: {...},
}
Lightweight Context
For quick operations that don't need everything:
import { getLightweightContext } from '@/lib/coaches';
const context = await getLightweightContext(userId);
// Returns: preferences + mood + basic relationship
Using with Prompt Builder
import { buildCoachPrompt, buildPersonalizationContext } from '@/lib/coaches';
// Get personalization
const personalization = await buildPersonalizationContext(userId, {
coachId: 'max',
includeMemories: true,
includeMood: true,
});
// Build prompt with personalization
const prompt = buildCoachPrompt('max', 'chat', {
taskContext: { message: userMessage },
userContext: personalization,
});
// The prompt now includes:
// - User's memories
// - Current mood
// - Preferences
// - Coach relationship history
Caching
Personalization data is cached to reduce database load.
| Cache | TTL | Purpose |
|---|---|---|
personalizationCache | 30s | Full context |
preferencesCache | 5min | User preferences |
relationshipCache | 2min | Coach relationships |
timePatternsCache | 10min | Activity patterns |
Invalidation
import { invalidateUserCaches } from '@/lib/coaches';
// Call when user data changes significantly
invalidateUserCaches(userId);
Validation
All inputs are validated using Zod schemas.
import {
createMemoryInputSchema,
upsertStruggleInputSchema,
validateInput
} from '@/lib/coaches';
// Validates and throws if invalid
const validated = validateInput(createMemoryInputSchema, input);
// Or safe validation that returns result
const result = safeValidateInput(createMemoryInputSchema, input);
if (!result.success) {
console.error(result.error);
}