Skip to main content

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

#FeatureDescriptionDatabase Table
1Memory StorePersistent facts coaches learncoach_memories
2Conversation SummariesCompressed conversation historyconversation_summaries
3User PreferencesCommunication preferencesuser_preferences
4MilestonesTrack and celebrate achievementsuser_milestones
5Mood DetectionInfer emotional stateuser_moods
6InsightsPatterns and recommendationscoach_insights
7Response ConfigTime-based tone adjustmentsComputed
8Time PatternsWhen users are most activeuser_time_patterns
9Struggle DetectionRecurring challengesuser_struggles
10Goal ProgressProgress toward goalscoach_goal_progress
11Interaction RecordingLog coach interactionscoach_interactions
12Coach RelationshipsRapport scores per coachcoach_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.

CacheTTLPurpose
personalizationCache30sFull context
preferencesCache5minUser preferences
relationshipCache2minCoach relationships
timePatternsCache10minActivity 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);
}