mirror of
https://github.com/escalante29/healthy-fit.git
synced 2026-03-21 14:08:48 +01:00
All checks were successful
Deploy to VPS / deploy (push) Successful in 24s
- 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>
102 lines
4.7 KiB
Python
102 lines
4.7 KiB
Python
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()
|