Shageenderan Sapai commited on
Commit
52acf33
·
2 Parent(s): 695cf889fbf0f3

Merge branch 'new_followups' into pr/3

Browse files
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
@@ -551,15 +551,10 @@ class Assistant:
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
-
555
  tool_outputs.append({
556
  "tool_call_id": tool.id,
557
- "output": f"true"
558
  })
559
- # tool_outputs.append({
560
- # "tool_call_id": tool.id,
561
- # "output": f"Marked users' goal: {goal} as COMPLETED"
562
- # })
563
  elif tool.function.name == "process_reminder":
564
  reminder = json.loads(tool.function.arguments)["content"]
565
  timestamp = json.loads(tool.function.arguments)["timestamp"]
 
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,18 +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:
106
- **IF** the user has completed yesterday's micro-action, ask the experience that the user had while doing the micro action.
107
- **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).
108
- **DO NOT** ask more than 1 question in 1 message!
109
- 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.
110
- 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.
 
 
 
 
 
111
 
112
  ** Principles for Quality Interaction **
113
  1. **Personalization:** Align suggestions with the user’s legendary persona and recent actions.
@@ -392,7 +417,7 @@ Meanwhile, let me know if you want to:
392
 
393
  Can’t wait to see what’s next for you! 🚀
394
 
395
- - **IF** the user answer with a No, you reply with this message:
396
 
397
  No worries, growth doesn’t stop here 🚀 — what you’ve accomplished is just the beginning!
398
 
 
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.
 
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
 
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
 
@@ -433,13 +452,12 @@ class User:
433
 
434
  def update_goal(self, goal, status, content=None):
435
  if goal is None:
436
- pass
437
- # # complete the current goal
438
- # current_goal = self.get_current_goal(full=True)
439
- # if current_goal:
440
- # current_goal.status = "COMPLETED"
441
- # current_goal.updated_at = pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S")
442
- # return current_goal.content
443
 
444
  for g in self.goal:
445
  if g.content == goal:
@@ -503,7 +521,6 @@ class User:
503
  self.health_and_wellness_score = 0
504
  self.reminders = None
505
 
506
-
507
  def generate_user_interaction_guidelines(self, user_info, client):
508
  logger.info(f"Generating user interaction guidelines for user: {self.user_id}", extra={"user_id": self.user_id, "endpoint": "generate_user_interaction_guidelines"})
509
  # prompt = f"A 'profile' is a document containing rich insights on users for the purpose of \
@@ -560,9 +577,10 @@ class User:
560
  # must do current plan now
561
  action = self.growth_plan.current()
562
  logger.info(f"Current Action: {action}", extra={"user_id": self.user_id, "endpoint": "user_send_message"})
563
- response = self.do_theme(action['coachingTheme'], self.conversations.state['date'], action['day'])
564
 
565
  # add response to ai message
 
566
  self.add_ai_message(response['content'])
567
 
568
  # Move to the next action
@@ -775,6 +793,24 @@ class User:
775
 
776
  logger.info(f"Doing theme: {theme}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
777
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778
  if theme == "MOTIVATION_INSPIRATION_STATE":
779
  formatted_message = MOTIVATION_INSPIRATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
780
  elif theme == "PROGRESS_REFLECTION_STATE":
@@ -783,7 +819,9 @@ class User:
783
  challenge = self.challenges.pop(0)
784
  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) **"
785
  elif theme == "MICRO_ACTION_STATE":
786
- formatted_message = MICRO_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
 
 
787
  if len(self.recommended_micro_actions):
788
  todays_micro_action = self.recommended_micro_actions.pop(0)
789
  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) **"
@@ -799,7 +837,8 @@ class User:
799
  elif theme == "EDUCATION_STATE":
800
  formatted_message = EDUCATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
801
  elif theme == "FOLLUP_ACTION_STATE":
802
- formatted_message = FOLLUP_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona, knowledge)
 
803
 
804
  # prompt = f"""** It is a new day: {date} **
805
  # Additional System Instruction:
@@ -813,7 +852,7 @@ class User:
813
  # {formatted_message}
814
  # """
815
 
816
- prompt = f"""** It is a new day: {date} **
817
  Additional System Instruction:
818
 
819
  To avoid a boring conversation, focus on giving more wisdom than questions by providing assertive guidance, valuable encouragement,
@@ -841,15 +880,25 @@ class User:
841
  logger.info(f"Recommended Micro Action: {micro_action}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
842
  self.micro_actions.append(micro_action)
843
 
844
- return response
845
 
846
  def change_date(self, date):
847
  logger.info(f"Changing date from {self.conversations.state['date']} to {date}",
848
  extra={"user_id": self.user_id, "endpoint": "user_change_date"})
 
 
 
849
  self.conversations.state['date'] = date
850
 
851
  action = self.growth_plan.current()
852
 
 
 
 
 
 
 
 
853
  ## ADD POINT FOR CHANGE DATE
854
  if self.growth_plan.current()['day'] == 7:
855
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
@@ -867,31 +916,23 @@ class User:
867
 
868
  # The coaching theme conditions are hardcoded for now
869
  theme = action['coachingTheme']
870
- response = self.do_theme(theme, date, action['day'])
 
 
 
 
 
 
 
 
 
 
871
 
872
  # Move to the next action
873
  self.growth_plan.next()
874
 
875
- if self.reminders is not None and len(self.reminders):
876
- # remove all reminders which 'recurrence' is 'once' or 'action' is 'delete' or the date is < today
877
- # this is to ensure that only reminders with recurrence and future reminder are kept
878
- self.reminders = [reminder for reminder in self.reminders if not (reminder['action'] == 'delete' or (reminder['recurrence'] == 'once' and reminder['timestamp'] < pd.Timestamp.now()))]
879
- logger.info(f"Active Reminders: {self.reminders}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
880
-
881
- # response['reminders'] = all reminders which date is today (so all the reminders that BE has to queue today)
882
- # convert date to YYYY-MM-DD format
883
- date = pd.to_datetime(date).date()
884
- response['reminders'] = self.get_reminders(date)
885
- if len(response['reminders']) == 0:
886
- response['reminders'] = None
887
- logger.info(f"No reminders for today {date}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
888
- logger.info(f"Reminders on {date}: {response['reminders']}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
889
- else:
890
- response['reminders'] = None
891
-
892
-
893
  logger.info(f"Date Updated: {self.conversations.state['date']}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
894
- return response
895
 
896
  def update_user_info(self, new_info):
897
  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
 
 
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:
 
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
 
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__