Files
healthy-fit/backend/app/ai/kettlebell.py
Carlos Escalante 11e086166c
All checks were successful
Deploy to VPS / deploy (push) Successful in 24s
Add interactive routine wizard, slider controls, value persistence, and timer pause
- Interactive routine generation wizard with AI refinement loop (generate-draft,
  refine, save-draft endpoints + RoutineWizard modal component)
- Replace +/- stepper buttons with slider controls for reps/weight during workout
- Persist user-modified reps/weight across sets of the same exercise
- Add pause/resume by tapping timer dials, with back-button confirmation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 22:29:06 -06:00

102 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import dspy
from pydantic import BaseModel, Field
class ExerciseBlock(BaseModel):
order: int = Field(description="Position in workout sequence")
name: str = Field(description="Exercise name")
description: str = Field(description="Brief description of the movement")
sets: int = Field(description="Number of sets")
reps: int = Field(description="Number of reps per set (0 if timed)")
duration_seconds: int = Field(description="Duration in seconds per set (0 if rep-based)")
weight_kg: float = Field(description="Prescribed weight in kg")
rest_seconds: int = Field(description="Rest time between sets in seconds")
coaching_tip: str = Field(description="Key coaching cue for this exercise")
class KettlebellSessionOutput(BaseModel):
reasoning: str = Field(description="Step-by-step reasoning for session design choices")
title: str = Field(description="Session title")
focus: str = Field(description="Session focus e.g. strength, conditioning, mobility")
total_duration_min: int = Field(description="Estimated total workout duration in minutes")
difficulty: str = Field(description="Difficulty level: beginner, intermediate, advanced")
exercises: list[ExerciseBlock] = Field(description="Ordered list of exercises in the session")
notes: str = Field(description="Coaching notes and any special instructions for the session")
class GenerateKettlebellSession(dspy.Signature):
"""Generate a personalized kettlebell workout session based on user profile and preferences.
Think step-by-step: assess user fitness level, pick movements appropriate to the focus and
difficulty, assign weights respecting progressive overload principles from available weights,
sequence exercises for proper warm-up and fatigue management, and ensure total work time
(sets × reps/duration + rest periods) fits within the requested duration.
"""
user_profile: str = dspy.InputField(desc="User details including age, weight, fitness level, and goals")
available_weights_kg: str = dspy.InputField(desc="Comma-separated list of available kettlebell weights in kg")
focus: str = dspy.InputField(desc="Session focus: strength, conditioning, mobility, fat loss, etc.")
duration_minutes: int = dspy.InputField(desc="Target session duration in minutes")
session: KettlebellSessionOutput = dspy.OutputField(desc="Complete structured kettlebell session")
class KettlebellModule(dspy.Module):
def __init__(self):
super().__init__()
self.generate = dspy.ChainOfThought(GenerateKettlebellSession)
def forward(self, user_profile: str, available_weights_kg: str, focus: str, duration_minutes: int):
return self.generate(
user_profile=user_profile,
available_weights_kg=available_weights_kg,
focus=focus,
duration_minutes=duration_minutes,
)
kettlebell_module = KettlebellModule()
class RefineKettlebellSession(dspy.Signature):
"""Refine an existing kettlebell workout session based on user feedback.
You are given the current session and the user's feedback. Apply the requested changes
while keeping the rest of the session intact. Maintain proper exercise sequencing,
warm-up/cooldown structure, and ensure total work time still fits the target duration.
"""
current_session: str = dspy.InputField(desc="JSON representation of the current session")
user_feedback: str = dspy.InputField(desc="User's requested changes to the session")
user_profile: str = dspy.InputField(desc="User details including age, weight, fitness level, and goals")
available_weights_kg: str = dspy.InputField(desc="Comma-separated list of available kettlebell weights in kg")
focus: str = dspy.InputField(desc="Session focus: strength, conditioning, mobility, fat loss, etc.")
duration_minutes: int = dspy.InputField(desc="Target session duration in minutes")
session: KettlebellSessionOutput = dspy.OutputField(desc="Refined structured kettlebell session")
class KettlebellRefineModule(dspy.Module):
def __init__(self):
super().__init__()
self.refine = dspy.ChainOfThought(RefineKettlebellSession)
def forward(
self,
current_session: str,
user_feedback: str,
user_profile: str,
available_weights_kg: str,
focus: str,
duration_minutes: int,
):
return self.refine(
current_session=current_session,
user_feedback=user_feedback,
user_profile=user_profile,
available_weights_kg=available_weights_kg,
focus=focus,
duration_minutes=duration_minutes,
)
kettlebell_refine_module = KettlebellRefineModule()