Skip to content

Commit

Permalink
more accurate & consistent weekly gen
Browse files Browse the repository at this point in the history
  • Loading branch information
voynow committed Aug 16, 2024
1 parent 4fb8821 commit 24876f4
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 111 deletions.
2 changes: 1 addition & 1 deletion src/constants.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
COACH_ROLE = "You are a talented running coach with years of experience. You have been hired by a client to help them improve their running performance. Your client has provided you with the following preferences: "
COACH_ROLE = "You are a talented running coach with years of experience. You have been hired by a client to help them improve their running performance. Note: convert pace values where applicable e.g. 7.5 -> 7m 30s."
31 changes: 11 additions & 20 deletions src/email_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sib_api_v3_sdk
from dotenv import load_dotenv

from src.types.training_week_with_coaching import TrainingWeekWithCoaching
from src.types.training_week import TrainingWeekWithCoaching

load_dotenv()

Expand All @@ -26,11 +26,7 @@ def training_week_to_html(training_week_with_coaching: TrainingWeekWithCoaching)
"""
uid = str(uuid.uuid4())
total_miles = sum(
[
session["distance"]
for session in training_week_with_coaching.training_week.dict().values()
if isinstance(session, dict)
]
[session.distance for session in training_week_with_coaching.training_week]
)

html_content = """
Expand Down Expand Up @@ -115,24 +111,19 @@ def training_week_to_html(training_week_with_coaching: TrainingWeekWithCoaching)
<h2>Get pumped for this week's training.</h2>
<ul>
"""
for day, session in training_week_with_coaching.training_week.dict().items():
if isinstance(session, dict): # Skip non-session fields
html_content += f"""
<li>
<strong>{day.capitalize()}</strong>
<span>{session['session_type'].value} {session['distance']} miles</span><br>
<span>Notes: {session['notes']}</span>
</li>
"""
for session in training_week_with_coaching.training_week:
html_content += f"""
<li>
<strong>{session.day.capitalize()}</strong>
<span>{session.session_type.value} {session.distance} miles</span><br>
<span>Notes: {session.notes}</span>
</li>
"""
html_content += f"""
</ul>
<h2>Total Miles Planned: {total_miles}</h2>
<div class="review-section">
<h2>Coach's Review</h2>
<p>{training_week_with_coaching.typical_week_training_review}</p>
</div>
<div class="mileage-target-section">
<h2>Weekly Mileage Target</h2>
<h2>Coach's Recommendation</h2>
<p>{training_week_with_coaching.weekly_mileage_target}</p>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

def get_completion(
messages: List[ChatCompletionMessage],
model: str = "gpt-4o",
model: str = "gpt-4o-2024-08-06",
response_format: Optional[Dict] = None,
):
response = client.chat.completions.create(
Expand All @@ -22,7 +22,7 @@ def get_completion(
def get_completion_json(
message: str,
response_model: Type[BaseModel],
model: str = "gpt-4o",
model: str = "gpt-4o-2024-08-06",
) -> BaseModel:

response_model_content = (
Expand Down
7 changes: 5 additions & 2 deletions src/supabase_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from postgrest.base_request_builder import APIResponse
from supabase import Client, create_client

from src.types.training_week_with_coaching import TrainingWeekWithCoaching
from src.types.training_week import TrainingWeekWithCoaching
from src.types.user_auth_row import UserAuthRow

load_dotenv()
Expand Down Expand Up @@ -70,7 +70,10 @@ def upsert_training_week_with_coaching(
"""
row_data = {
"athlete_id": athlete_id,
"training_week": training_week_with_coaching.training_week.json(),
"training_week_planning": training_week_with_coaching.training_week_planning,
"training_week": json.dumps(
[session.dict() for session in training_week_with_coaching.training_week]
),
"typical_week_training_review": training_week_with_coaching.typical_week_training_review,
"weekly_mileage_target": training_week_with_coaching.weekly_mileage_target,
}
Expand Down
6 changes: 3 additions & 3 deletions src/training_week_generation/lambda_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ def lambda_handler(event, context):
# gen training week pipeline
day_of_week_summaries = get_day_of_week_summaries(activities_df)
weekly_summaries = get_weekly_summaries(activities_df)
training_week = generate_training_week(
training_week_with_coaching = generate_training_week(
client_preferences=client_preferences,
weekly_summaries=weekly_summaries,
day_of_week_summaries=day_of_week_summaries,
)

# save data to db and trigger email
upsert_training_week_with_coaching(
athlete_id=athlete_id, training_week_with_coaching=training_week
athlete_id=athlete_id, training_week_with_coaching=training_week_with_coaching
)
send_email(
subject="Training Schedule Just Dropped 🏃",
html_content=training_week_to_html(training_week),
html_content=training_week_to_html(training_week_with_coaching),
)
35 changes: 17 additions & 18 deletions src/training_week_generation/training_week.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
from src.constants import COACH_ROLE
from src.llm import get_completion, get_completion_json
from src.types.day_of_week_summary import DayOfWeekSummary
from src.types.training_week import TrainingWeek
from src.types.training_week_with_coaching import TrainingWeekWithCoaching
from src.types.training_week import (
TrainingWeekWithCoaching,
TrainingWeekWithPlanning,
)
from src.types.week_summary import WeekSummary


def get_typical_week_training_review(
sysmsg_base: str,
day_of_week_summaries: List[DayOfWeekSummary],
) -> str:
sysmsg = f"{sysmsg_base} You will be provided summary statistics (aggregated by day of the week) of your client's training over the past several weeks."
usermsg = (
str(day_of_week_summaries)
+ "As the coach, provide a very concise training review for your client on their typical week. e.g. Which days are best for their schedule? When do they rest? When (if ever) is their long run? When do they incorporate speed work? Write in paragraph form."
)
sysmsg = f"{sysmsg_base}\nYou will be provided summary statistics (aggregated by day of the week) of your client's training over the past several weeks."
usermsg = f"""{day_of_week_summaries}
As the coach, write a review for your client on their typical week. e.g. Which days are best for their schedule? When do they rest? When (if ever) is their long run? When do they incorporate speed work? Write 1-2 sentences while being as concise as possible."""
return get_completion(
[{"role": "assistant", "content": sysmsg}, {"role": "user", "content": usermsg}]
)
Expand All @@ -26,10 +26,10 @@ def get_weekly_mileage_target(
sysmsg_base: str,
weekly_summaries: List[WeekSummary],
) -> str:
sysmsg = f"""{sysmsg_base} You will be provided summary statistics (aggregated by week) of your client's training over the past several weeks."""
sysmsg = f"""{sysmsg_base}\nYou will be provided summary statistics (aggregated by week) of your client's training over the past several weeks."""
usermsg = f"""Starting from earliest to most recent, here are the weekly summaries:
{weekly_summaries}
As the coach, provide very concise feedback and prescribe your client a target weekly mileage for next week. Be specific and refer to the data provided. Write in paragraph form."""
As the coach, prescribe your client a target weekly mileage and long run range for next week. Be specific and refer to the data provided. Write 2-3 sentences while being as concise as possible."""
return get_completion(
[{"role": "assistant", "content": sysmsg}, {"role": "user", "content": usermsg}]
)
Expand All @@ -39,18 +39,17 @@ def get_training_week(
sysmsg_base: str,
typical_week_training_review: str,
weekly_mileage_target: str,
) -> TrainingWeek:
message = f"""{sysmsg_base} Here is your review of your client's typical week:
) -> TrainingWeekWithPlanning:
message = f"""{sysmsg_base}\nHere is your review of your client's typical week:
{typical_week_training_review}
Additionally, here is the weekly mileage target you provided:
{weekly_mileage_target}
Build out their next week of training. As a general rule, distribute volume and intensity evenly throughout the week as much as possible."""

Build out their next week of training."""
return get_completion_json(
message=message,
response_model=TrainingWeek,
response_model=TrainingWeekWithPlanning,
)


Expand All @@ -61,23 +60,23 @@ def generate_training_week(
) -> TrainingWeekWithCoaching:

sysmsg_base = f"""{COACH_ROLE}
Your client has included the following preferenced: {client_preferences}
Note: convert pace values where applicable e.g. 7.5 -> 7m 30s"""
Your client has included the following preferenced: {client_preferences}\n"""

typical_week_training_review = get_typical_week_training_review(
sysmsg_base=sysmsg_base, day_of_week_summaries=day_of_week_summaries
)
weekly_mileage_target = get_weekly_mileage_target(
sysmsg_base=sysmsg_base, weekly_summaries=weekly_summaries
)
training_week = get_training_week(
training_week_with_planning = get_training_week(
sysmsg_base=sysmsg_base,
typical_week_training_review=typical_week_training_review,
weekly_mileage_target=weekly_mileage_target,
)

return TrainingWeekWithCoaching(
training_week=training_week,
training_week_planning=training_week_with_planning.planning,
training_week=training_week_with_planning.training_week,
typical_week_training_review=typical_week_training_review,
weekly_mileage_target=weekly_mileage_target,
)
9 changes: 0 additions & 9 deletions src/types/session_type.py

This file was deleted.

65 changes: 46 additions & 19 deletions src/types/training_week.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,63 @@
from enum import StrEnum
from typing import Dict, List

from pydantic import BaseModel, Field

from src.types.session_type import SessionType

class Day(StrEnum):
MON = "mon"
TUES = "tues"
WED = "wed"
THURS = "thurs"
FRI = "fri"
SAT = "sat"
SUN = "sun"


class SessionType(StrEnum):
EASY = "easy run"
LONG = "long run"
SPEED = "speed workout"
REST = "rest day"
MODERATE = "moderate run"


class TrainingSession(BaseModel):
day: Day
session_type: SessionType
distance: float = Field(description="Distance in miles")
notes: str = Field(description="notes for the session e.g. pace, terrain, etc.")
notes: str = Field(
description="Concise notes about the session, e.g. '2x2mi @ 10k pace' or 'easy pace'"
)

def __str__(self):
return f"session_type={self.session_type}, distance={self.distance}, notes={self.notes}"
return f"TrainingSession(session_type={self.session_type}, distance={self.distance}, weekly_mileage_cumulative={self.weekly_mileage_cumulative}, notes={self.notes})"

def __repr__(self):
return self.__str__()

class TrainingWeekWithPlanning(BaseModel):
planning: str = Field(
description="Draft a plan (used internally) to aid in training week generation. You must adhere to the weekly mileage target and long run range. Do required math (step by step out loud) to plan the week successfully. If you end up exceeding the weekly mileage target, adjust one of the easy runs to be shorter."
)
training_week: List[TrainingSession] = Field(
description="Unordered collection of training sessions for the week"
)


class TrainingWeekWithCoaching(BaseModel):
typical_week_training_review: str
"""Coach's review of the client's typical week of training"""

weekly_mileage_target: str
"""Coach's prescribed weekly mileage target for the client"""

training_week_planning: str
"""Internal planning for the client's training week"""

class TrainingWeek(BaseModel):
mon: TrainingSession
tues: TrainingSession
wed: TrainingSession
thurs: TrainingSession
fri: TrainingSession
sat: TrainingSession
sun: TrainingSession
training_week: List[TrainingSession]
"""Client's recommended training week"""

def __str__(self):
return "\n".join(
[
f"{day}: {session['session_type']}, {session['distance']} miles, {session['notes']}"
for day, session in self.dict().items()
]
)
return f"{self.typical_week_training_review=}\n{self.weekly_mileage_target=}\n{self.training_week_planning=}\n{self.training_week=}"

def __repr__(self):
return self.__str__()
20 changes: 0 additions & 20 deletions src/types/training_week_with_coaching.py

This file was deleted.

Loading

0 comments on commit 24876f4

Please sign in to comment.