Spaces:
Sleeping
Sleeping
| """ | |
| Progressive Overload Engine — standalone, stdlib only. | |
| Given exercise performance history, determines whether the agent's | |
| proposed prescription follows correct double-progression rules. | |
| Used by the grader to score the 'progressive_overload' dimension. | |
| """ | |
| from __future__ import annotations | |
| from typing import Optional | |
| def _parse_rep_range(s: str) -> tuple[int, int]: | |
| if not s: | |
| return (8, 12) | |
| s = s.strip() | |
| if "-" in s: | |
| parts = s.split("-", 1) | |
| try: | |
| return (int(parts[0]), int(parts[1])) | |
| except ValueError: | |
| return (8, 12) | |
| try: | |
| n = int(s) | |
| return (n, n) | |
| except ValueError: | |
| return (8, 12) | |
| def _parse_reps_completed(s: str) -> list[int]: | |
| if not s: | |
| return [] | |
| out = [] | |
| for part in s.split(","): | |
| try: | |
| out.append(int(part.strip())) | |
| except ValueError: | |
| pass | |
| return out | |
| def _is_compound(name: str) -> bool: | |
| compound = ["squat", "deadlift", "bench", "press", "row", | |
| "pull-up", "pullup", "chin-up", "dip", "lunge", "thrust"] | |
| isolation = ["curl", "extension", "raise", "fly", "flye", "kickback"] | |
| nl = name.lower() | |
| if any(k in nl for k in isolation): | |
| return False | |
| return any(k in nl for k in compound) | |
| def expected_progression( | |
| exercise_name: str, | |
| last_weight_kg: float, | |
| last_reps_str: str, | |
| target_reps: str = "8-12", | |
| target_sets: int = 3, | |
| ) -> dict: | |
| """ | |
| Compute the correct next-session prescription given last performance. | |
| Returns {"progression_type", "expected_weight_kg", "expected_reps"}. | |
| """ | |
| last_reps = _parse_reps_completed(last_reps_str) | |
| target_lo, target_hi = _parse_rep_range(target_reps) | |
| compound = _is_compound(exercise_name) | |
| if not last_reps: | |
| return {"progression_type": "repeat", | |
| "expected_weight_kg": last_weight_kg, | |
| "expected_reps": target_reps} | |
| all_hit_top = ( | |
| len(last_reps) >= target_sets | |
| and all(r >= target_hi for r in last_reps[:target_sets]) | |
| ) | |
| heavy_miss = sum(1 for r in last_reps if r < target_lo) >= 2 | |
| single_miss = any(r < target_lo for r in last_reps) and not all_hit_top | |
| if heavy_miss: | |
| dw = round(last_weight_kg * 0.9 / 2.5) * 2.5 if last_weight_kg > 0 else 0 | |
| return {"progression_type": "deload", | |
| "expected_weight_kg": dw, | |
| "expected_reps": target_reps} | |
| if single_miss: | |
| return {"progression_type": "repeat", | |
| "expected_weight_kg": last_weight_kg, | |
| "expected_reps": target_reps} | |
| if all_hit_top: | |
| if last_weight_kg == 0: | |
| new_hi = target_hi + 2 | |
| return {"progression_type": "add_reps", | |
| "expected_weight_kg": 0, | |
| "expected_reps": f"{target_lo + 1}-{new_hi}"} | |
| inc = 2.5 if compound else 1.25 | |
| nw = round((last_weight_kg + inc) / 2.5) * 2.5 | |
| return {"progression_type": "add_weight", | |
| "expected_weight_kg": nw, | |
| "expected_reps": target_reps} | |
| return {"progression_type": "repeat", | |
| "expected_weight_kg": last_weight_kg, | |
| "expected_reps": target_reps} | |
| def verify_agent_overload( | |
| exercise_name: str, | |
| agent_weight_kg: float, | |
| agent_reps: str, | |
| last_weight_kg: float, | |
| last_reps_str: str, | |
| target_reps: str = "8-12", | |
| target_sets: int = 3, | |
| ) -> tuple[bool, str]: | |
| """ | |
| Check whether agent's prescription follows correct overload logic. | |
| Returns (is_correct, explanation). | |
| """ | |
| expected = expected_progression( | |
| exercise_name, last_weight_kg, last_reps_str, target_reps, target_sets | |
| ) | |
| ptype = expected["progression_type"] | |
| exp_w = expected["expected_weight_kg"] | |
| if ptype == "add_weight": | |
| min_ok = last_weight_kg + 1.0 # must be meaningfully heavier | |
| if agent_weight_kg >= min_ok: | |
| return True, ( | |
| f"Correct: added weight " | |
| f"(agent={agent_weight_kg}kg, expected≥{exp_w}kg)." | |
| ) | |
| return False, ( | |
| f"Should add weight to {exp_w}kg " | |
| f"(agent submitted {agent_weight_kg}kg, last was {last_weight_kg}kg)." | |
| ) | |
| if ptype == "deload": | |
| if agent_weight_kg <= last_weight_kg * 0.95: | |
| return True, f"Correct deload (agent={agent_weight_kg}kg < {last_weight_kg}kg)." | |
| return False, f"Should deload to ~{exp_w}kg, agent kept {agent_weight_kg}kg." | |
| if ptype == "add_reps": | |
| _, target_hi = _parse_rep_range(target_reps) | |
| _, agent_hi = _parse_rep_range(agent_reps) | |
| if agent_hi > target_hi: | |
| return True, f"Correct rep progression (agent={agent_reps})." | |
| return False, ( | |
| f"Should increase rep target above {target_hi}, " | |
| f"agent submitted {agent_reps}." | |
| ) | |
| # repeat — weight should stay same ±2.5kg | |
| if abs(agent_weight_kg - last_weight_kg) <= 2.5: | |
| return True, f"Correct: repeating prescription ({last_weight_kg}kg)." | |
| return False, ( | |
| f"Should repeat {last_weight_kg}kg, agent changed to {agent_weight_kg}kg." | |
| ) |