All files / src model-provider.ts

0% Statements 0/0
0% Branches 1/1
0% Functions 1/1
0% Lines 0/0

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126                                                                                                                                                                                                                                                           
import type { PrescribedExercise, MovementPattern, SessionType } from '@strengthsys/shared';
 
// ── AI Tier Distinction ─────────────────────────────────────
 
/**
 * Simple tier: low-stakes, model-agnostic tasks (review questions,
 * notifications, adherence messages).
 *
 * Complex tier: high-stakes tasks requiring creativity within constraints
 * (workout generation, adaptation, replanning).
 */
export type ModelTier = 'simple' | 'complex';
 
// ── Context Types ───────────────────────────────────────────
 
export interface WorkoutGenerationContext {
  athlete_id: string;
  block_id: string;
  session_type: SessionType;
  time_cap_minutes: number;
  rep_range_low: number;
  rep_range_high: number;
  rest_seconds_target: number;
  is_deload: boolean;
  available_equipment: string[];
  movement_pattern_history: Partial<Record<MovementPattern, number>>;
  familiar_exercises: string[];
  constraints: string[];
  previous_violations?: Array<{ rule: string; message: string }>;
  /** True if athlete has a minimal profile with system-assigned defaults */
  is_sparse_profile?: boolean;
}
 
export interface WorkoutAdaptationContext {
  workout_id: string;
  athlete_id: string;
  kind: 'not_feeling_it' | 'exercise_substitution' | 'pain_reported';
  context_description: string | null;
  current_exercises: PrescribedExercise[];
  completed_sets: number;
}
 
export interface ReviewContext {
  athlete_id: string;
  workout_id?: string;
  block_id?: string;
  cycle_id?: string;
  scope: 'workout' | 'block' | 'cycle';
  /** 'system_default' if schedule was auto-assigned, 'athlete_specified' if athlete chose */
  schedule_source?: 'system_default' | 'athlete_specified';
  /** True if any of the athlete's training locations has source = system_default */
  has_system_default_location?: boolean;
  /** True if athlete has at least one active goal */
  has_active_goals?: boolean;
}
 
export interface NarrativeContext {
  athlete_id: string;
  timeframe: 'recent' | 'block' | 'cycle' | 'all_time';
}
 
// ── Response Types ──────────────────────────────────────────
 
export interface WorkoutPlan {
  prescribed_exercises: PrescribedExercise[];
}
 
export interface AdaptedWorkout {
  prescribed_exercises: PrescribedExercise[];
  explanation: string;
}
 
export interface ReviewPrompt {
  question: string;
  options: string[] | null;
  kind: 'standard' | 'profile_enrichment' | 'goal_refinement' | 'goal_discovery';
}
 
// ── AI Interaction Logging ──────────────────────────────────
 
export interface AIInteraction {
  athlete_id: string;
  action_type: string;
  context_sent: unknown;
  response_received: unknown;
  tools_called: string[];
  tool_results: unknown[];
  /** Per-call details for computation tools — used to populate computation_tool_calls rows. */
  tool_call_details?: Array<{
    tool_name: string;
    input_parameters: unknown;
    output_result: unknown;
    called_at: string; // ISO timestamp
  }>;
  duration_ms: number;
}
 
// ── Model Provider Interface ────────────────────────────────
 
/**
 * Model-agnostic interface for AI-powered operations.
 * Implementations can wrap Claude, OpenAI, or any other provider.
 *
 * All methods return the AI response alongside an AIInteraction log
 * entry for audit purposes.
 */
export interface ModelProvider {
  readonly tier: ModelTier;
 
  generateWorkout(
    context: WorkoutGenerationContext,
  ): Promise<{ result: WorkoutPlan; interaction: AIInteraction }>;
 
  adaptWorkout(
    context: WorkoutAdaptationContext,
  ): Promise<{ result: AdaptedWorkout; interaction: AIInteraction }>;
 
  generateReviewQuestions(
    context: ReviewContext,
  ): Promise<{ result: ReviewPrompt[]; interaction: AIInteraction }>;
 
  generateNarrative(
    context: NarrativeContext,
  ): Promise<{ result: string; interaction: AIInteraction }>;
}