Dockerfile CHANGED
@@ -4,6 +4,13 @@ FROM python:3.10.9
4
  # Set up a new user named "user" with user ID 1000
5
  RUN useradd -m -u 1000 user
6
 
 
 
 
 
 
 
 
7
  # Switch to the "user" user
8
  USER user
9
 
 
4
  # Set up a new user named "user" with user ID 1000
5
  RUN useradd -m -u 1000 user
6
 
7
+ # Install wkhtmltopdf and its dependencies as root
8
+ USER root
9
+ RUN apt-get update && apt-get install -y --no-install-recommends \
10
+ wkhtmltopdf \
11
+ && apt-get clean \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
  # Switch to the "user" user
15
  USER user
16
 
app/__pycache__/assistants.cpython-312.pyc CHANGED
Binary files a/app/__pycache__/assistants.cpython-312.pyc and b/app/__pycache__/assistants.cpython-312.pyc differ
 
app/__pycache__/flows.cpython-312.pyc CHANGED
Binary files a/app/__pycache__/flows.cpython-312.pyc and b/app/__pycache__/flows.cpython-312.pyc differ
 
app/__pycache__/main.cpython-312.pyc CHANGED
Binary files a/app/__pycache__/main.cpython-312.pyc and b/app/__pycache__/main.cpython-312.pyc differ
 
app/__pycache__/user.cpython-312.pyc CHANGED
Binary files a/app/__pycache__/user.cpython-312.pyc and b/app/__pycache__/user.cpython-312.pyc differ
 
app/__pycache__/utils.cpython-312.pyc CHANGED
Binary files a/app/__pycache__/utils.cpython-312.pyc and b/app/__pycache__/utils.cpython-312.pyc differ
 
app/assistants.py CHANGED
@@ -547,6 +547,14 @@ class Assistant:
547
  logger.info(f"Successfully cancelled run",
548
  extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_change_goal"})
549
  return "change_goal", just_finish_intro
 
 
 
 
 
 
 
 
550
  elif tool.function.name == "process_reminder":
551
  reminder = json.loads(tool.function.arguments)["content"]
552
  timestamp = json.loads(tool.function.arguments)["timestamp"]
 
547
  logger.info(f"Successfully cancelled run",
548
  extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_change_goal"})
549
  return "change_goal", just_finish_intro
550
+ elif tool.function.name == "complete_goal":
551
+ logger.info(f"Completing user goal...", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_complete_goal"})
552
+ goal = self.cm.user.update_goal(None, 'COMPLETED')
553
+ logger.info(f"Marked users' goal: {goal} as COMPLETED", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_complete_goal"})
554
+ tool_outputs.append({
555
+ "tool_call_id": tool.id,
556
+ "output": f"Marked users' goal: {goal} as COMPLETED"
557
+ })
558
  elif tool.function.name == "process_reminder":
559
  reminder = json.loads(tool.function.arguments)["content"]
560
  timestamp = json.loads(tool.function.arguments)["timestamp"]
app/flows.py CHANGED
@@ -44,13 +44,23 @@ MICRO_ACTION_STATE = f"""
44
  Objective: Build momentum toward the user’s goal with small, actionable tasks that feel achievable immediately. Avoid saying “Today’s micro-action is:”—state the action naturally.
45
 
46
  User’s Context
47
- • Goal: {{}}
48
- • Day: {{}}/{{}} of their journey.
49
- • User's Legendary Persona: {{}} (incorporate quotes conversationally, not as a lecture).
 
 
 
 
 
 
 
 
50
 
51
  The Order of Your Conversation Flow (Do these step-by-step instructions):
 
52
  Step 1. Micro-Action Suggestion: Propose one clear, actionable task for the day. Ensure it is relevant, easy to begin, and framed positively. Avoid repeating previous actions.
53
  Step 2. If the user has decided to try out the micro action, let the user **try** the micro action and get back when they have done it. Don't ask any question, yet
 
54
  Step 3. After the user has said that they've done the micro action, you ask one **creative** and engaging follow-up question to ask their experience.
55
  Step 4. End Gracefully: After the user's reply, immediately call the end_conversation(). Conclude with a strong and motivational statement (by chanelling the energy, mindset, and knowledge of the user's legendary persona) that reinforces their commitment.
56
 
@@ -96,15 +106,33 @@ FOLLUP_ACTION_STATE = f"""
96
  Objective: Based on the micro-action that you gave yesterday, follow up the user's progress today & share some useful knowledge, tools, or practices from the user's Legendary Persona. Avoid saying “Yesterday’s micro-action is:”—state the action naturally.
97
 
98
  User’s Context
99
- • Goal: {{}}
100
- • Day: {{}}/{{}} of their journey.
101
- • User's chosen Legendary Persona: {{}} (incorporate quotes conversationally, not as a lecture).
102
- User's chosen Legendary Persona's knowledge and expertise: {{}} (Pick one or more to be brought up in the conversation, based on relevance to the user's goal and challenges)
103
-
 
 
 
 
 
 
 
 
 
 
104
  The Order of Your Conversation Flow (Do these step-by-step instructions):
105
- Step 1. Follow Up: Give a question about yesterday's micro-action. Ask the experience that the user had while doing the micro action.
106
- Step 2. After the user have shared their experience, share a list of Knowledge/Resource/Tips: Based on the user’s chosen legendary persona's knowledge and expertise, provide a useful knowledge, tools, or practices that suits the user’s experience or challenges! Give your knowledge as if you are the Legendary Persona themselves. Ask what do they think about it.
107
- Step 3. After the user replies, immediately call the end_conversation(). Conclude with a strong and motivational statement (by chanelling the energy, mindset, and knowledge of the user's legendary persona) that reinforces their commitment.
 
 
 
 
 
 
 
 
108
 
109
  ** Principles for Quality Interaction **
110
  1. **Personalization:** Align suggestions with the user’s legendary persona and recent actions.
@@ -342,7 +370,7 @@ Based on the above, coach and engage the user in a succinct and brief conversati
342
 
343
  FINAL_SUMMARY_STATE = f"""
344
  ## ** The Final Day of the Growth Coaching Session ** (PLEASE READ CAREFULLY)
345
- Objective: To summarize the user's progress and achievements during the coaching journey. And, give the user the option to either continue the coaching plan for another 2 weeks or create a new plan.
346
 
347
  Users Goal: {{}}
348
  The user is currently on day {{}}/{{}} of their journey.
@@ -369,21 +397,47 @@ User's Legendary Persona: {{}} (No need to use double quote when giving a quote)
369
 
370
  ## Example of Quality Interaction:
371
 
372
- - Congratulate the user for completing the growth plan until the last day
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
- - Highlight the user's progress and achievements in bullet points
 
 
375
 
376
- - Ask the user whether they have **accomplished** their goal. If they have accomplished their goal, call the complete_goal() function!
377
 
378
- - Ask the user whether they want to continue the plan for another 2 weeks, or create a new goal. If they want to create a new goal, call the change_goal() function!
379
 
380
- - Finally, suggest to the user to speak to a Growth Guide for deeper support by booking a Growth Guide schedule through ourcoach web app. Try to "upsell" the Growth Guide here!
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
  ** DO NOT ASK ANY OTHER QUESTION IN THIS STATE **
383
 
384
  Based on the above, coach and engage the user in a succinct and brief conversation to help them make progress towards achieving their goal!
385
 
386
-
387
  ** PRIORITIZE THESE INSTRUCTIONS BELOW AND IGNORE OVERLAPPING INSTRUCTIONS FROM THE SYSTEM PROMPT **
388
 
389
  ** IMPORTANT **: Do not explicitly state the function name that you are calling in the response
 
44
  Objective: Build momentum toward the user’s goal with small, actionable tasks that feel achievable immediately. Avoid saying “Today’s micro-action is:”—state the action naturally.
45
 
46
  User’s Context
47
+ • Goal:
48
+ {{}}
49
+
50
+ • Day:
51
+ {{}}/{{}} of their journey.
52
+
53
+ • User's Legendary Persona:
54
+ {{}} (incorporate quotes conversationally, not as a lecture).
55
+
56
+ • User's Upcoming (postponed) Micro-Actions:
57
+ {{}}
58
 
59
  The Order of Your Conversation Flow (Do these step-by-step instructions):
60
+ Step 0. If the user has postponed micro-actions, remind them of the postponed micro-actions and ask if they are ready to do it today. If the user says yes, then do the following steps with the postponed micro-action. If the user says no, then proceed with proposing a new micro-action.
61
  Step 1. Micro-Action Suggestion: Propose one clear, actionable task for the day. Ensure it is relevant, easy to begin, and framed positively. Avoid repeating previous actions.
62
  Step 2. If the user has decided to try out the micro action, let the user **try** the micro action and get back when they have done it. Don't ask any question, yet
63
+ Step 2.5. If the user says they can only do it on a later date, send the user an understanding yet encouraging message and create a reminder for them (by making a function call like: process_reminder('content': '⏰ **Reminder**: Hey <users' name>! 🌟 Just a friendly reminder to do <postponed microaction>', 'timestamp': '<relevant timestamp based on users' message>', 'recurrence': 'postponed', 'action': 'set'). Ensure that you set the 'recurrence' to 'postponed'. Then, end the conversation (do step 4.)
64
  Step 3. After the user has said that they've done the micro action, you ask one **creative** and engaging follow-up question to ask their experience.
65
  Step 4. End Gracefully: After the user's reply, immediately call the end_conversation(). Conclude with a strong and motivational statement (by chanelling the energy, mindset, and knowledge of the user's legendary persona) that reinforces their commitment.
66
 
 
106
  Objective: Based on the micro-action that you gave yesterday, follow up the user's progress today & share some useful knowledge, tools, or practices from the user's Legendary Persona. Avoid saying “Yesterday’s micro-action is:”—state the action naturally.
107
 
108
  User’s Context
109
+ • Goal:
110
+ {{}}
111
+
112
+ Day:
113
+ {{}}/{{}} of their journey.
114
+
115
+ • User's chosen Legendary Persona:
116
+ {{}} (incorporate quotes conversationally, not as a lecture).
117
+
118
+ • User's chosen Legendary Persona's knowledge and expertise:
119
+ {{}} (Pick one or more to be brought up in the conversation, based on relevance to the user's goal and challenges)
120
+
121
+ • User's Upcoming (postponed) Micro-Actions:
122
+ {{}}
123
+
124
  The Order of Your Conversation Flow (Do these step-by-step instructions):
125
+ **IF** THE USER HAS NO UPCOMING REMINDERS:
126
+ Step 1. Follow Up:
127
+ **IF** the user has completed yesterday's micro-action, ask the experience that the user had while doing the micro action.
128
+ **IF** the user hasn't completed yesterday's micro-action, ask the user if they're going to do it today (**UNLESS** if the user has specified a day to do the action, then don't ask and go to step 2).
129
+ Step 2. After the user have shared their experience, share a list of Knowledge/Resource/Tips: Based on the user’s chosen legendary persona's knowledge and expertise, provide a useful knowledge, tools, or practices that suits the user’s experience or challenges! Give your knowledge as if you are the Legendary Persona themselves. Ask what do they think about it.
130
+ Step 3. After the user replies, immediately call the end_conversation(). Conclude with a strong and motivational statement (by chanelling the energy, mindset, and knowledge of the user's legendary persona) that reinforces their commitment.
131
+ **IF** THE USER HAS AN UPCOMING REMINDER:
132
+ Step 0. Identify whether to use past/present or future tense in your messages based on the timestamp of the user's postponed micro-action and the current datetime.
133
+ Step 1. Remind the user about their upcoming micro-action in a motivating way and ask if they are ready/excited for it. Incorporate elements of the user's motivation for their goal in this message to further encourage the user.
134
+ Step 2. After the user replies, share a list of Knowledge/Resource/Tips OR Based on the user’s chosen legendary persona's knowledge and expertise, provide a useful knowledge, tools, or practices that suits the user’s experience or challenges! Give your knowledge as if you are the Legendary Persona themselves. Ask what do they think about it.
135
+ Step 3. After the user replies, immediately call the end_conversation(). Conclude with a strong and motivational statement (by chanelling the energy, mindset, and knowledge of the user's legendary persona) that reinforces their commitment.
136
 
137
  ** Principles for Quality Interaction **
138
  1. **Personalization:** Align suggestions with the user’s legendary persona and recent actions.
 
370
 
371
  FINAL_SUMMARY_STATE = f"""
372
  ## ** The Final Day of the Growth Coaching Session ** (PLEASE READ CAREFULLY)
373
+ Objective: To summarize the user's progress and achievements during the coaching journey.
374
 
375
  Users Goal: {{}}
376
  The user is currently on day {{}}/{{}} of their journey.
 
397
 
398
  ## Example of Quality Interaction:
399
 
400
+ - Congratulate the user for completing the growth plan until the last day (mention the current day, e.g. day 4)
401
+
402
+ - Highlight the user's progress and achievements in bullet points. And tell them to check out their progress in the Revelation Dashboard here: https://app.staging.ourcoach.ai/
403
+
404
+ - Ask the user whether they want to meet their Growth Guide to plan what's next! (Only ask this question first)
405
+
406
+ - Wait for the user's response.
407
+
408
+ - **IF** the user answer with a Yes, you reply with this message:
409
+
410
+ Amazing! 🎉
411
+
412
+ Connecting with a Growth Guide is a great step toward unlocking your next breakthrough. You can schedule your session here: https://app.staging.ourcoach.ai/.
413
 
414
+ Meanwhile, let me know if you want to:
415
+ • Continue your current growth plan for another 2 weeks to build on your progress.
416
+ • Set a new, exciting goal to challenge yourself even further.
417
 
418
+ Can’t wait to see what’s next for you! 🚀
419
 
420
+ - **IF** the user answer with a No, you reply with this message (could be something similar):
421
 
422
+ No worries, growth doesn’t stop here 🚀 what you’ve accomplished is just the beginning!
423
+
424
+ Here’s what you can do next:
425
+ • Extend this plan for another 2 weeks to build on your progress.
426
+ • Set a new, exciting goal to challenge yourself even further.
427
+
428
+ And remember, a Growth Guide can help you reflect, strategize, and take things to the next level.
429
+
430
+ Keep the momentum going—you’ve got this! 💥
431
+
432
+
433
+ - If they want to create a new goal, call the change_goal() function!
434
+
435
+ - If the user said that they've completed their goal, call the complete_goal() function!
436
 
437
  ** DO NOT ASK ANY OTHER QUESTION IN THIS STATE **
438
 
439
  Based on the above, coach and engage the user in a succinct and brief conversation to help them make progress towards achieving their goal!
440
 
 
441
  ** PRIORITIZE THESE INSTRUCTIONS BELOW AND IGNORE OVERLAPPING INSTRUCTIONS FROM THE SYSTEM PROMPT **
442
 
443
  ** IMPORTANT **: Do not explicitly state the function name that you are calling in the response
app/main.py CHANGED
@@ -429,7 +429,7 @@ def get_user_by_id(user_id: str, api_key: str = Security(get_api_key)):
429
  user = get_user(user_id)
430
  print_log("INFO", "Successfully retrieved user", extra={"user_id": user_id, "endpoint": "/get_user"})
431
  logger.info("Successfully retrieved user", extra={"user_id": user_id, "endpoint": "/get_user"})
432
- api_response = {"user": user.user_info, "user_messages": user.get_messages(), "general_assistant": user.conversations.assistants['general'].id, "intro_assistant": user.conversations.assistants['intro'].id}
433
 
434
  if user.goal:
435
  api_response["goal"] = user.goal
@@ -460,31 +460,6 @@ def get_user_by_id(user_id: str, api_key: str = Security(get_api_key)):
460
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
461
  detail=str(e)
462
  )
463
-
464
- # @app.get("/get_user_life_score")
465
- # def life_score_by_id(user_id: str, api_key: str = Security(get_api_key)):
466
- # print_log("INFO", "Getting user life score", extra={"user_id": user_id, "endpoint": "/get_user_life_score"})
467
- # logger.info("Getting user life score", extra={"user_id": user_id, "endpoint": "/get_user_life_score"})
468
- # try:
469
- # life_score = get_life_score(user_id)
470
- # print_log("INFO", "Successfully retrieved user life score", extra={"user_id": user_id, "endpoint": "/get_user_life_score"})
471
- # logger.info("Successfully retrieved user life score", extra={"user_id": user_id, "endpoint": "/get_user_life_score"})
472
-
473
- # return life_score
474
- # except LookupError:
475
- # print_log("ERROR", "User not found", extra={"user_id": user_id, "endpoint": "/get_user_life_score"})
476
- # logger.error("User not found", extra={"user_id": user_id, "endpoint": "/get_user_life_score"})
477
- # raise HTTPException(
478
- # status_code=status.HTTP_404_NOT_FOUND,
479
- # detail=f"User with ID {user_id} not found"
480
- # )
481
- # except Exception as e:
482
- # print_log("ERROR",f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_user_life_score"}, exc_info=True)
483
- # logger.error(f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_user_life_score"}, exc_info=True)
484
- # raise HTTPException(
485
- # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
486
- # detail=str(e)
487
- # )
488
 
489
  @app.post("/add_ai_message")
490
  def add_ai_message(request: ChatItem, api_key: str = Security(get_api_key)):
@@ -796,7 +771,8 @@ def change_date(request: ChangeDateItem, api_key: str = Security(get_api_key)):
796
  detail=f"Failed to update user pickle in S3 {user_id}"
797
  )
798
 
799
- return {"response": response}
 
800
 
801
  except ValueError as e:
802
  print_log("ERROR",f"Invalid date format for user {request.user_id}: {str(e)}", extra={"user_id": request.user_id, "endpoint": "/change_date"})
 
429
  user = get_user(user_id)
430
  print_log("INFO", "Successfully retrieved user", extra={"user_id": user_id, "endpoint": "/get_user"})
431
  logger.info("Successfully retrieved user", extra={"user_id": user_id, "endpoint": "/get_user"})
432
+ api_response = {"user": user.user_info, "user_messages": user.get_messages(show_hidden=True), "general_assistant": user.conversations.assistants['general'].id, "intro_assistant": user.conversations.assistants['intro'].id}
433
 
434
  if user.goal:
435
  api_response["goal"] = user.goal
 
460
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
461
  detail=str(e)
462
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
 
464
  @app.post("/add_ai_message")
465
  def add_ai_message(request: ChatItem, api_key: str = Security(get_api_key)):
 
771
  detail=f"Failed to update user pickle in S3 {user_id}"
772
  )
773
 
774
+ return response
775
+ # return {"response": response}
776
 
777
  except ValueError as e:
778
  print_log("ERROR",f"Invalid date format for user {request.user_id}: {str(e)}", extra={"user_id": request.user_id, "endpoint": "/change_date"})
app/user.py CHANGED
@@ -205,10 +205,6 @@ class ConversationManager:
205
 
206
  temp_thread = self.client.beta.threads.create(messages=messages)
207
  logger.info(f"Created Temp Thread: {temp_thread}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
208
-
209
- if add_to_main:
210
- logger.info(f"Adding message to main thread: {text}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
211
- self.add_message_to_thread(self.current_thread.id, "assistant", text)
212
 
213
  self.add_message_to_thread(temp_thread.id, "user", text)
214
 
@@ -220,8 +216,31 @@ class ConversationManager:
220
  self.client.beta.threads.delete(temp_thread.id)
221
  logger.info(f"Deleted Temp Thread: {temp_thread}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
222
 
 
 
 
 
223
  return response
224
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  def do_first_reflection(self):
226
  question_format = random.choice(['[Option 1] Likert-Scale Objective Question','[Option 2] Multiple-Choice Question','[Option 3] Yes-No Question'])
227
 
@@ -432,6 +451,14 @@ class User:
432
  return None
433
 
434
  def update_goal(self, goal, status, content=None):
 
 
 
 
 
 
 
 
435
  for g in self.goal:
436
  if g.content == goal:
437
  g.status = status
@@ -452,17 +479,19 @@ class User:
452
  if current_goal is None:
453
  new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
454
  self.goal.append(new_goal)
455
-
456
- if add:
457
- if current_goal:
458
- # update current_goal status to "IDLE"
459
- self.update_goal(current_goal, "IDLE")
460
- new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
461
- self.goal.append(new_goal)
462
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
463
- self.add_recent_wins(wins = "You have set your first goal!")
464
  else:
465
- self.update_goal(current_goal, "ONGOING", content=goal)
 
 
 
 
 
 
 
 
 
466
 
467
  def update_recommended_micro_action_status(self, micro_action, status):
468
  for ma in self.recommended_micro_actions:
@@ -492,7 +521,6 @@ class User:
492
  self.health_and_wellness_score = 0
493
  self.reminders = None
494
 
495
-
496
  def generate_user_interaction_guidelines(self, user_info, client):
497
  logger.info(f"Generating user interaction guidelines for user: {self.user_id}", extra={"user_id": self.user_id, "endpoint": "generate_user_interaction_guidelines"})
498
  # prompt = f"A 'profile' is a document containing rich insights on users for the purpose of \
@@ -549,9 +577,10 @@ class User:
549
  # must do current plan now
550
  action = self.growth_plan.current()
551
  logger.info(f"Current Action: {action}", extra={"user_id": self.user_id, "endpoint": "user_send_message"})
552
- response = self.do_theme(action['coachingTheme'], self.conversations.state['date'], action['day'])
553
 
554
  # add response to ai message
 
555
  self.add_ai_message(response['content'])
556
 
557
  # Move to the next action
@@ -689,7 +718,7 @@ class User:
689
  user_data_clean = json.loads(user_data['onboarding'])
690
  user_legendary_persona = user_data_clean.get('legendPersona', '')
691
 
692
- if "Steve Jobs" in user_legendary_persona:
693
  knowledge = """
694
  1. Vision and Creativity: Encouraging students to think big and innovate.
695
  2. Simplicity: Teaching the power of focus and clarity in life and work.
@@ -710,7 +739,7 @@ class User:
710
  6. Continuous Growth: Encouraging self-improvement and lifelong learning.
711
  7. Global Perspective: Instilling the importance of collaboration and understanding across cultures.
712
  """
713
- elif "Aristotle" in user_legendary_persona:
714
  knowledge = """
715
  1. The concept of eudaimonia (flourishing).
716
  2. Virtue ethics as a framework for moral living.
@@ -718,7 +747,7 @@ class User:
718
  4. The “Golden Mean” – balance as a virtue.
719
  5. Foundational ideas in logic, metaphysics, and natural sciences.
720
  """
721
- elif "Mother Teresa" in user_legendary_persona:
722
  knowledge = """
723
  1. Radical compassion for the poorest of the poor.
724
  2. Dedication to selfless service and humility.
@@ -764,6 +793,24 @@ class User:
764
 
765
  logger.info(f"Doing theme: {theme}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
766
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  if theme == "MOTIVATION_INSPIRATION_STATE":
768
  formatted_message = MOTIVATION_INSPIRATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
769
  elif theme == "PROGRESS_REFLECTION_STATE":
@@ -772,7 +819,9 @@ class User:
772
  challenge = self.challenges.pop(0)
773
  formatted_message += f"\n\n** IMPORTANT: Today, reflect on the users' challenge of: {challenge.content}, which they brought up during their growth guide session (let the user know we are bringing it up because of this) **"
774
  elif theme == "MICRO_ACTION_STATE":
775
- formatted_message = MICRO_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
 
 
776
  if len(self.recommended_micro_actions):
777
  todays_micro_action = self.recommended_micro_actions.pop(0)
778
  formatted_message += f"\n\n** IMPORTANT: Today's Micro Action is: {todays_micro_action.content}, which was recommended during their growth guide session (let the user know we are bringing it up because of this) **"
@@ -788,7 +837,8 @@ class User:
788
  elif theme == "EDUCATION_STATE":
789
  formatted_message = EDUCATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
790
  elif theme == "FOLLUP_ACTION_STATE":
791
- formatted_message = FOLLUP_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona, knowledge)
 
792
 
793
  # prompt = f"""** It is a new day: {date} **
794
  # Additional System Instruction:
@@ -802,7 +852,7 @@ class User:
802
  # {formatted_message}
803
  # """
804
 
805
- prompt = f"""** It is a new day: {date} **
806
  Additional System Instruction:
807
 
808
  To avoid a boring conversation, focus on giving more wisdom than questions by providing assertive guidance, valuable encouragement,
@@ -830,15 +880,25 @@ class User:
830
  logger.info(f"Recommended Micro Action: {micro_action}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
831
  self.micro_actions.append(micro_action)
832
 
833
- return response
834
 
835
  def change_date(self, date):
836
  logger.info(f"Changing date from {self.conversations.state['date']} to {date}",
837
  extra={"user_id": self.user_id, "endpoint": "user_change_date"})
 
 
 
838
  self.conversations.state['date'] = date
839
 
840
  action = self.growth_plan.current()
841
 
 
 
 
 
 
 
 
842
  ## ADD POINT FOR CHANGE DATE
843
  if self.growth_plan.current()['day'] == 7:
844
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
@@ -856,31 +916,23 @@ class User:
856
 
857
  # The coaching theme conditions are hardcoded for now
858
  theme = action['coachingTheme']
859
- response = self.do_theme(theme, date, action['day'])
 
 
 
 
 
 
 
 
 
 
860
 
861
  # Move to the next action
862
  self.growth_plan.next()
863
 
864
- if self.reminders is not None and len(self.reminders):
865
- # remove all reminders which 'recurrence' is 'once' or 'action' is 'delete' or the date is < today
866
- # this is to ensure that only reminders with recurrence and future reminder are kept
867
- self.reminders = [reminder for reminder in self.reminders if not (reminder['action'] == 'delete' or (reminder['recurrence'] == 'once' and reminder['timestamp'] < pd.Timestamp.now()))]
868
- logger.info(f"Active Reminders: {self.reminders}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
869
-
870
- # response['reminders'] = all reminders which date is today (so all the reminders that BE has to queue today)
871
- # convert date to YYYY-MM-DD format
872
- date = pd.to_datetime(date).date()
873
- response['reminders'] = self.get_reminders(date)
874
- if len(response['reminders']) == 0:
875
- response['reminders'] = None
876
- logger.info(f"No reminders for today {date}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
877
- logger.info(f"Reminders on {date}: {response['reminders']}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
878
- else:
879
- response['reminders'] = None
880
-
881
-
882
  logger.info(f"Date Updated: {self.conversations.state['date']}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
883
- return response
884
 
885
  def update_user_info(self, new_info):
886
  logger.info(f"Updating user info: [{self.user_info}] with: [{new_info}]", extra={"user_id": self.user_id, "endpoint": "update_user_info"})
 
205
 
206
  temp_thread = self.client.beta.threads.create(messages=messages)
207
  logger.info(f"Created Temp Thread: {temp_thread}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
 
 
 
 
208
 
209
  self.add_message_to_thread(temp_thread.id, "user", text)
210
 
 
216
  self.client.beta.threads.delete(temp_thread.id)
217
  logger.info(f"Deleted Temp Thread: {temp_thread}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
218
 
219
+ if add_to_main:
220
+ logger.info(f"Adding message to main thread: {text}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
221
+ self.add_message_to_thread(self.current_thread.id, "assistant", text)
222
+
223
  return response
224
 
225
+ def delete_hidden_messages(self, old_thread=None):
226
+ if old_thread is None:
227
+ old_thread = self.current_thread
228
+
229
+ # create a new thread
230
+ messages = [msg for msg in self._get_current_thread_history(remove_system_message=False) if not msg['content'].startswith("[hidden]")]
231
+ if len(messages) >= 29:
232
+ messages = messages[-29:]
233
+ logger.info(f"Current Thread Messages: {messages}", extra={"user_id": self.user.user_id, "endpoint": "delete_hidden_messages"})
234
+
235
+ new_thread = self.client.beta.threads.create(messages=messages)
236
+
237
+ # delete old thread
238
+ self.client.beta.threads.delete(old_thread.id)
239
+
240
+ # set current thread
241
+ self.current_thread = new_thread
242
+
243
+
244
  def do_first_reflection(self):
245
  question_format = random.choice(['[Option 1] Likert-Scale Objective Question','[Option 2] Multiple-Choice Question','[Option 3] Yes-No Question'])
246
 
 
451
  return None
452
 
453
  def update_goal(self, goal, status, content=None):
454
+ if goal is None:
455
+ # complete the current goal
456
+ current_goal = self.get_current_goal(full=True)
457
+ if current_goal:
458
+ current_goal.status = "COMPLETED"
459
+ current_goal.updated_at = pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S")
460
+ return current_goal.content
461
+
462
  for g in self.goal:
463
  if g.content == goal:
464
  g.status = status
 
479
  if current_goal is None:
480
  new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
481
  self.goal.append(new_goal)
 
 
 
 
 
 
 
482
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
483
+ self.add_recent_wins(wins = "You have set a new goal!")
484
  else:
485
+ if add:
486
+ if current_goal:
487
+ # update current_goal status to "IDLE"
488
+ self.update_goal(current_goal, "IDLE")
489
+ new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
490
+ self.goal.append(new_goal)
491
+ self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
492
+ self.add_recent_wins(wins = "You have set a new goal!")
493
+ else:
494
+ self.update_goal(current_goal, "ONGOING", content=goal)
495
 
496
  def update_recommended_micro_action_status(self, micro_action, status):
497
  for ma in self.recommended_micro_actions:
 
521
  self.health_and_wellness_score = 0
522
  self.reminders = None
523
 
 
524
  def generate_user_interaction_guidelines(self, user_info, client):
525
  logger.info(f"Generating user interaction guidelines for user: {self.user_id}", extra={"user_id": self.user_id, "endpoint": "generate_user_interaction_guidelines"})
526
  # prompt = f"A 'profile' is a document containing rich insights on users for the purpose of \
 
577
  # must do current plan now
578
  action = self.growth_plan.current()
579
  logger.info(f"Current Action: {action}", extra={"user_id": self.user_id, "endpoint": "user_send_message"})
580
+ response, prompt = self.do_theme(action['coachingTheme'], self.conversations.state['date'], action['day'])
581
 
582
  # add response to ai message
583
+ self.add_ai_message("[hidden]" + prompt)
584
  self.add_ai_message(response['content'])
585
 
586
  # Move to the next action
 
718
  user_data_clean = json.loads(user_data['onboarding'])
719
  user_legendary_persona = user_data_clean.get('legendPersona', '')
720
 
721
+ if "Coach Steve" in user_legendary_persona or "Steve Jobs" in user_legendary_persona:
722
  knowledge = """
723
  1. Vision and Creativity: Encouraging students to think big and innovate.
724
  2. Simplicity: Teaching the power of focus and clarity in life and work.
 
739
  6. Continuous Growth: Encouraging self-improvement and lifelong learning.
740
  7. Global Perspective: Instilling the importance of collaboration and understanding across cultures.
741
  """
742
+ elif "Coach Aris" in user_legendary_persona or "Aristotle" in user_legendary_persona:
743
  knowledge = """
744
  1. The concept of eudaimonia (flourishing).
745
  2. Virtue ethics as a framework for moral living.
 
747
  4. The “Golden Mean” – balance as a virtue.
748
  5. Foundational ideas in logic, metaphysics, and natural sciences.
749
  """
750
+ elif "Coach Teresa" in user_legendary_persona or "Mother Teresa" in user_legendary_persona:
751
  knowledge = """
752
  1. Radical compassion for the poorest of the poor.
753
  2. Dedication to selfless service and humility.
 
793
 
794
  logger.info(f"Doing theme: {theme}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
795
 
796
+ if self.reminders is not None and len(self.reminders):
797
+ logger.info(f"ALL Upcoming Reminders: {self.reminders}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
798
+ reminders = list(filter(lambda x : x['recurrence'] == 'postponed', self.reminders))
799
+ logger.info(f"ALL Postponed Reminders: {reminders}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
800
+ else:
801
+ reminders = []
802
+
803
+ # check if any of the posponed reminders have the date == date if yes, change the theme to "MICRO_ACTION_STATE"
804
+ if reminders:
805
+ for reminder in reminders:
806
+ if reminder['timestamp'].date() == pd.to_datetime(date).date():
807
+ logger.info(f"Reminder found for today ({pd.to_datetime(date).date()}): {reminder}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
808
+ if day != 4:
809
+ theme = "MICRO_ACTION_STATE"
810
+ break
811
+ else:
812
+ logger.info(f"No reminders found for today ({pd.to_datetime(date).date()})", extra={"user_id": self.user_id, "endpoint": "do_theme"})
813
+
814
  if theme == "MOTIVATION_INSPIRATION_STATE":
815
  formatted_message = MOTIVATION_INSPIRATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
816
  elif theme == "PROGRESS_REFLECTION_STATE":
 
819
  challenge = self.challenges.pop(0)
820
  formatted_message += f"\n\n** IMPORTANT: Today, reflect on the users' challenge of: {challenge.content}, which they brought up during their growth guide session (let the user know we are bringing it up because of this) **"
821
  elif theme == "MICRO_ACTION_STATE":
822
+ reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
823
+
824
+ formatted_message = MICRO_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona, reminder_message)
825
  if len(self.recommended_micro_actions):
826
  todays_micro_action = self.recommended_micro_actions.pop(0)
827
  formatted_message += f"\n\n** IMPORTANT: Today's Micro Action is: {todays_micro_action.content}, which was recommended during their growth guide session (let the user know we are bringing it up because of this) **"
 
837
  elif theme == "EDUCATION_STATE":
838
  formatted_message = EDUCATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
839
  elif theme == "FOLLUP_ACTION_STATE":
840
+ reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
841
+ formatted_message = FOLLUP_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona, knowledge, reminder_message)
842
 
843
  # prompt = f"""** It is a new day: {date} **
844
  # Additional System Instruction:
 
852
  # {formatted_message}
853
  # """
854
 
855
+ prompt = f"""** It is a new day: {date} ({day}) 10:00:00 **
856
  Additional System Instruction:
857
 
858
  To avoid a boring conversation, focus on giving more wisdom than questions by providing assertive guidance, valuable encouragement,
 
880
  logger.info(f"Recommended Micro Action: {micro_action}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
881
  self.micro_actions.append(micro_action)
882
 
883
+ return response, prompt
884
 
885
  def change_date(self, date):
886
  logger.info(f"Changing date from {self.conversations.state['date']} to {date}",
887
  extra={"user_id": self.user_id, "endpoint": "user_change_date"})
888
+ # delete all hidden messages prom previous day
889
+ self.conversations.delete_hidden_messages()
890
+ # update the date in the state
891
  self.conversations.state['date'] = date
892
 
893
  action = self.growth_plan.current()
894
 
895
+ # remove stale reminders
896
+ if self.reminders is not None and len(self.reminders):
897
+ # remove all reminders which 'recurrence' is 'once' or 'action' is 'delete' or the date is < today
898
+ # this is to ensure that only reminders with recurrence and future reminder are kept
899
+ self.reminders = [reminder for reminder in self.reminders if not (reminder['action'] == 'delete' or (reminder['recurrence'] in ['once', 'postponed'] and reminder['timestamp'] < pd.Timestamp.now()))]
900
+ logger.info(f"Active Reminders: {self.reminders}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
901
+
902
  ## ADD POINT FOR CHANGE DATE
903
  if self.growth_plan.current()['day'] == 7:
904
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
 
916
 
917
  # The coaching theme conditions are hardcoded for now
918
  theme = action['coachingTheme']
919
+ response, prompt = self.do_theme(theme, date, action['day'])
920
+
921
+ # add today's reminders to response to schedule
922
+ # response['reminders'] = all reminders which date is today (so all the reminders that BE has to queue today)
923
+ # convert date to YYYY-MM-DD format
924
+ date = pd.to_datetime(date).date()
925
+ response['reminders'] = self.get_reminders(date)
926
+ if response['reminders'] is None or len(response['reminders']) == 0:
927
+ response['reminders'] = None
928
+ logger.info(f"No reminders for today {date}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
929
+ logger.info(f"Reminders on {date}: {response['reminders']}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
930
 
931
  # Move to the next action
932
  self.growth_plan.next()
933
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
934
  logger.info(f"Date Updated: {self.conversations.state['date']}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
935
+ return {'response': response, 'theme_prompt': '[hidden]'+prompt}
936
 
937
  def update_user_info(self, new_info):
938
  logger.info(f"Updating user info: [{self.user_info}] with: [{new_info}]", extra={"user_id": self.user_id, "endpoint": "update_user_info"})
app/utils.py CHANGED
@@ -1093,6 +1093,29 @@ def get_growth_guide_summary(user_id, session_id):
1093
  except psycopg2.Error as e:
1094
  logger.error(f"Database error while retrieving growth guide summary for user {user_id} and session {session_id}: {e}", extra={'user_id': user_id, 'endpoint': function_name})
1095
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1096
 
1097
  def update_growth_guide_summary(user_id, session_id, ourcoach_summary):
1098
  function_name = update_growth_guide_summary.__name__
 
1093
  except psycopg2.Error as e:
1094
  logger.error(f"Database error while retrieving growth guide summary for user {user_id} and session {session_id}: {e}", extra={'user_id': user_id, 'endpoint': function_name})
1095
  return None
1096
+
1097
+ def get_all_bookings():
1098
+ function_name = get_all_bookings.__name__
1099
+ logger.info(f"Retrieving all bookings", extra={'endpoint': function_name})
1100
+ db_params = {
1101
+ 'dbname': 'ourcoach',
1102
+ 'user': 'ourcoach',
1103
+ 'password': 'hvcTL3kN3pOG5KteT17T',
1104
+ 'host': 'staging-ourcoach.cx8se8o0iaiy.ap-southeast-1.rds.amazonaws.com',
1105
+ 'port': '5432'
1106
+ }
1107
+ try:
1108
+ with psycopg2.connect(**db_params) as conn:
1109
+ with conn.cursor() as cursor:
1110
+ query = sql.SQL("SELECT id, user_id FROM {table}").format(table=sql.Identifier('public', 'booking'))
1111
+ cursor.execute(query)
1112
+ rows = cursor.fetchall()
1113
+ bookings = [{'booking_id': row[0], 'user_id': row[1]} for row in rows]
1114
+ logger.info(f"Retrieved {len(bookings)} bookings", extra={'endpoint': function_name})
1115
+ return bookings
1116
+ except psycopg2.Error as e:
1117
+ logger.error(f"Database error while retrieving bookings: {e}", extra={'endpoint': function_name})
1118
+ return []
1119
 
1120
  def update_growth_guide_summary(user_id, session_id, ourcoach_summary):
1121
  function_name = update_growth_guide_summary.__name__