From 09d26db92be69e82836a054f5f06519835d6b7c4 Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 21:16:27 -0300 Subject: [PATCH 1/8] build: library version update --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index f983443..143a27f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,6 @@ numpy==1.26.4 pandas==2.2.3 python-dotenv==1.0.1 questionary==2.0.1 -scipy==1.14.1 +scipy==1.13.1 tcxreader==0.4.10 tqdm==4.67.0 diff --git a/setup.py b/setup.py index 3b96ee5..2eeebef 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ "pandas==2.2.3", "python-dotenv==1.0.1", "questionary==2.0.1", - "scipy==1.14.1", + "scipy==1.13.1", "tcxreader==0.4.10", "tqdm==4.67.0" ], From 363ec7322c9b91886a4458e3c8790b5d12eefb1d Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 21:29:17 -0300 Subject: [PATCH 2/8] feat: add language selection for LLM analysis --- src/main.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main.py b/src/main.py index e9e7b67..a0e509c 100644 --- a/src/main.py +++ b/src/main.py @@ -61,8 +61,9 @@ def main(): _, tcx_data = validate_tcx_file(file_path) if ask_llm_analysis(): plan = ask_training_plan() + language = ask_desired_language() logger.info("Performing LLM analysis") - perform_llm_analysis(tcx_data, sport, plan) + perform_llm_analysis(tcx_data, sport, plan, language) else: logger.error("Invalid sport selected") raise ValueError("Invalid sport selected") @@ -193,13 +194,19 @@ def ask_training_plan() -> str: "Was there anything planned for this training?" ).ask() +def ask_desired_language() -> str: + return questionary.text( + "In which language do you want the analysis to be provided? (Default is Portuguese)", + default="Portuguese (Brazil)" + ).ask() + -def perform_llm_analysis(data: TCXReader, sport: str, plan: str) -> str: +def perform_llm_analysis(data: TCXReader, sport: str, plan: str, language: str) -> str: dataframe = preprocess_trackpoints_data(data) prompt_template = """ SYSTEM: You are an AI coach helping athletes optimize and improve their performance. - Based on the provided {sport} training session data, perform the following analysis: + Based on the provided {sport} training session data, perform the following analysis and provide feedback to the athlete in {language} language: 1. Identify key performance metrics. 2. Highlight the athlete's strengths during the session. @@ -211,18 +218,19 @@ def perform_llm_analysis(data: TCXReader, sport: str, plan: str) -> str: """ if plan: - prompt_template += "\nTraining plan details: {plan}" + prompt_template += "\n\nTraining Plan Details:\n{plan}" prompt = PromptTemplate.from_template(prompt_template).format( sport=sport, training_data=dataframe.to_csv(index=False), + language=language, plan=plan ) openai_llm = ChatOpenAI( openai_api_key=os.getenv("OPENAI_API_KEY"), model_name="gpt-4o", - max_tokens=1500, + max_tokens=2000, temperature=0.6, max_retries=5 ) From 69ddaebbc4710f1cb53fd699e4f591136c4e1510 Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 21:30:49 -0300 Subject: [PATCH 3/8] test: include language parameter in LLM analysis tests --- tests/test_main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index 59f63fc..f04e996 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -323,8 +323,9 @@ def test_perform_llm_analysis(self, mock_chat): tcx_data = self.running_example_data sport = "Run" plan = "Training Plan" + lang = "Portuguese" - result = perform_llm_analysis(tcx_data, sport, plan) + result = perform_llm_analysis(tcx_data, sport, plan, lang) self.assertEqual(result, "Training Plan") def test_preprocess_running_trackpoints_data(self): From 125420a77883f96d41d5a26aff29de6c7664b54a Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 21:32:35 -0300 Subject: [PATCH 4/8] test: add unit test for ask_language function --- tests/test_main.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_main.py b/tests/test_main.py index f04e996..74fd104 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -24,6 +24,7 @@ get_latest_download, validation, ask_training_plan, + ask_language, ask_llm_analysis, perform_llm_analysis, preprocess_trackpoints_data, @@ -306,6 +307,15 @@ def test_ask_training_plan(self): ) self.assertEqual(result, "") + def test_ask_language(self): + with patch('src.main.questionary.text') as mock_text: + mock_text.return_value.ask.return_value = "Portuguese" + result = ask_language() + mock_text.assert_called_once_with( + "Enter the language you want to use for the AI analysis:" + ) + self.assertEqual(result, "Portuguese") + def test_ask_llm_analysis(self): with patch('src.main.questionary.confirm') as mock_confirm: mock_confirm.return_value.ask.return_value = True From 3693a343dc036aad184df4a36cebcd8337e2e8fb Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 21:38:33 -0300 Subject: [PATCH 5/8] refactor: rename ask_language function to ask_desired_language and update test case --- tests/test_main.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 74fd104..1a8d05a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -24,7 +24,7 @@ get_latest_download, validation, ask_training_plan, - ask_language, + ask_desired_language, ask_llm_analysis, perform_llm_analysis, preprocess_trackpoints_data, @@ -307,12 +307,13 @@ def test_ask_training_plan(self): ) self.assertEqual(result, "") - def test_ask_language(self): + def test_ask_desired_language(self): with patch('src.main.questionary.text') as mock_text: mock_text.return_value.ask.return_value = "Portuguese" - result = ask_language() + result = ask_desired_language() mock_text.assert_called_once_with( - "Enter the language you want to use for the AI analysis:" + 'In which language do you want the analysis to be provided? (Default is Portuguese)', + default='Portuguese (Brazil)' ) self.assertEqual(result, "Portuguese") From 5dec2ea9f9d5e6cff2cb7f17b0f7f5f4178cdefb Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 21:53:40 -0300 Subject: [PATCH 6/8] test: add mock for ask_desired_language in main bike sport test --- tests/test_main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index 1a8d05a..0414af5 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -199,6 +199,7 @@ def test_main_invalid_sport(self, mock_indent, mock_validate, mock_format, mock_ mock_validate.assert_not_called() mock_indent.assert_not_called() + @patch('src.main.ask_desired_language') @patch('src.main.ask_training_plan') @patch('src.main.perform_llm_analysis') @patch('src.main.ask_llm_analysis') @@ -212,7 +213,7 @@ def test_main_invalid_sport(self, mock_indent, mock_validate, mock_format, mock_ @patch('src.main.indent_xml_file') def test_main_bike_sport(self, mock_indent, mock_validate, mock_format, mock_ask_path, mock_download, mock_ask_id, mock_ask_location, mock_ask_sport, mock_llm_analysis, mock_perform_llm, - mock_training_plan): + mock_training_plan, mock_language): mock_ask_sport.return_value = "Bike" mock_ask_location.return_value = "Local" mock_ask_path.return_value = "assets/bike.tcx" @@ -220,6 +221,7 @@ def test_main_bike_sport(self, mock_indent, mock_validate, mock_format, mock_ask mock_validate.return_value = True, "TCX Data" mock_perform_llm.return_value = "Training Plan" mock_training_plan.return_value = "" + mock_language.return_value = "Portuguese" main() From 5dafb9dcfc54eacb02adbc5413702c5e041343a7 Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 21:54:31 -0300 Subject: [PATCH 7/8] refactor: enhance prompt template for LLM analysis with clearer structure and motivational language --- src/main.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main.py b/src/main.py index a0e509c..5a2e65c 100644 --- a/src/main.py +++ b/src/main.py @@ -205,15 +205,18 @@ def perform_llm_analysis(data: TCXReader, sport: str, plan: str, language: str) dataframe = preprocess_trackpoints_data(data) prompt_template = """ - SYSTEM: You are an AI coach helping athletes optimize and improve their performance. - Based on the provided {sport} training session data, perform the following analysis and provide feedback to the athlete in {language} language: - - 1. Identify key performance metrics. - 2. Highlight the athlete's strengths during the session. - 3. Pinpoint areas where the athlete can improve. - 4. Offer actionable suggestions for enhancing performance in future {sport} sessions. - - Training session data: + SYSTEM: You are an AI performance coach specializing in analyzing athletic performance to help athletes with their trainings. + Using the provided {sport} training session data, analyze the athlete's performance and deliver a detailed analysis and practical advice in {language} language. + Your analysis should include: + + 1. Key Performance Metrics: Identify and Evaluate the most relevant metrics from the session, understanding the athlete's overall performance + 2. Strengths: Highlight the athlete's strongest aspects during the session, supported by specific metrics. + 3. Improvement Opportunities: Pinpoint specific areas for growth and improvement. + 4. Actionable Suggestions: Provide clear, practical recommendations to help the athlete enhance their performance in future {sport} sessions. + + Ensure your response is data-driven, clear, and motivational, helping the athlete make measurable progress. + + Training Session Data: {training_data} """ From 2c58d58b337920994df0a95be2e5694eb477f254 Mon Sep 17 00:00:00 2001 From: Lucas Brito Date: Tue, 19 Nov 2024 22:15:16 -0300 Subject: [PATCH 8/8] refactor: update model name to gpt-4o-mini in LLM analysis --- src/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index 5a2e65c..707ba7a 100644 --- a/src/main.py +++ b/src/main.py @@ -194,6 +194,7 @@ def ask_training_plan() -> str: "Was there anything planned for this training?" ).ask() + def ask_desired_language() -> str: return questionary.text( "In which language do you want the analysis to be provided? (Default is Portuguese)", @@ -232,7 +233,7 @@ def perform_llm_analysis(data: TCXReader, sport: str, plan: str, language: str) openai_llm = ChatOpenAI( openai_api_key=os.getenv("OPENAI_API_KEY"), - model_name="gpt-4o", + model_name="gpt-4o-mini", max_tokens=2000, temperature=0.6, max_retries=5