Shageenderan Sapai commited on
Commit
f294e41
·
1 Parent(s): 08467ad

Allign staging

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
@@ -1,13 +1,17 @@
1
  import json
2
  import io
3
  import os
4
- from datetime import datetime
5
  import json
6
  import random
7
  from time import sleep
 
8
  import pandas as pd
9
  from dotenv import load_dotenv
10
  import logging
 
 
 
11
 
12
  from app.utils import get_growth_guide_summary, get_users_mementos, print_log
13
  from app.flows import FOLLOW_UP_STATE, GENERAL_COACHING_STATE, MICRO_ACTION_STATE, REFLECTION_STATE
@@ -279,8 +283,25 @@ class FeedbackContent:
279
  # return result
280
  return selected
281
 
282
- def get_current_datetime():
283
- return datetime.now()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
  class Assistant:
286
  def __init__(self, id, cm):
@@ -289,19 +310,31 @@ class Assistant:
289
  self.recent_run = None
290
 
291
  def cancel_run(self, run, thread):
292
- if run.status != 'completed':
293
- cancel = self.cm.client.beta.threads.runs.cancel(thread_id=thread.id, run_id=run.id)
294
- while cancel.status != 'cancelled':
295
- sleep(0.05)
296
- cancel = self.cm.client.beta.threads.runs.retrieve(
297
- thread_id=thread.id,
298
- run_id=cancel.id
299
- )
300
- logger.info(f"Cancelled run: {run.id}", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_cancel_run"})
301
- return True
302
- else:
303
- logger.info(f"Run already completed: {run.id}", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_cancel_run"})
304
- return False
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
  def process(self, thread, text):
307
  # template_search = self.cm.add_message_to_thread(thread.id, "assistant", f"Pay attention to the current state you are in and the flow template to respond to the users query:")
@@ -387,7 +420,7 @@ class Assistant:
387
  # print(f"[DATETIME]: {get_current_datetime()}")
388
  # self.cm.state['date'] = 'date': pd.Timestamp.now().strftime("%Y-%m-%d %a %H:%M:%S")
389
  # get and update the current time to self.cm.state['date'] but keep the date component
390
- current_time = get_current_datetime()
391
  # replace time component of self.cm.state['date'] with the current time
392
  self.cm.state['date'] = str(pd.to_datetime(self.cm.state['date']).replace(hour=current_time.hour, minute=current_time.minute, second=current_time.second))
393
  logger.info(f"Current datetime: {self.cm.state['date']}",
 
1
  import json
2
  import io
3
  import os
4
+ from datetime import datetime, timezone
5
  import json
6
  import random
7
  from time import sleep
8
+ import openai
9
  import pandas as pd
10
  from dotenv import load_dotenv
11
  import logging
12
+ import psycopg2
13
+ from psycopg2 import sql
14
+ import pytz
15
 
16
  from app.utils import get_growth_guide_summary, get_users_mementos, print_log
17
  from app.flows import FOLLOW_UP_STATE, GENERAL_COACHING_STATE, MICRO_ACTION_STATE, REFLECTION_STATE
 
283
  # return result
284
  return selected
285
 
286
+ def get_current_datetime(user_id):
287
+ db_params = {
288
+ 'dbname': 'ourcoach',
289
+ 'user': 'ourcoach',
290
+ 'password': 'hvcTL3kN3pOG5KteT17T',
291
+ 'host': 'staging-ourcoach.cx8se8o0iaiy.ap-southeast-1.rds.amazonaws.com',
292
+ 'port': '5432'
293
+ }
294
+ with psycopg2.connect(**db_params) as conn:
295
+ with conn.cursor() as cursor:
296
+ query = sql.SQL("SELECT * FROM {table} WHERE id = %s").format(table=sql.Identifier('public', 'users'))
297
+ cursor.execute(query, (user_id,))
298
+ row = cursor.fetchone()
299
+ if (row):
300
+ colnames = [desc[0] for desc in cursor.description]
301
+ user_data = dict(zip(colnames, row))
302
+ user_timezone = user_data['timezone']
303
+
304
+ return datetime.now().astimezone(pytz.timezone(user_timezone))
305
 
306
  class Assistant:
307
  def __init__(self, id, cm):
 
310
  self.recent_run = None
311
 
312
  def cancel_run(self, run, thread):
313
+ try:
314
+ if run.status != 'completed':
315
+ cancel = self.cm.client.beta.threads.runs.cancel(thread_id=thread.id, run_id=run.id)
316
+ while cancel.status != 'cancelled':
317
+ sleep(0.05)
318
+ cancel = self.cm.client.beta.threads.runs.retrieve(
319
+ thread_id=thread.id,
320
+ run_id=cancel.id
321
+ )
322
+ logger.info(f"Cancelled run: {run.id}", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_cancel_run"})
323
+ return True
324
+ else:
325
+ logger.info(f"Run already completed: {run.id}", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_cancel_run"})
326
+ return False
327
+ except openai.BadRequestError:
328
+ # check if run has expired. run has a field 'expires_at' like run.expires_at = 1735008568
329
+ # if expired, return True and log run already expired
330
+ if run.expires_at < get_current_datetime().timestamp():
331
+ logger.error(f"Run already expired: {run.id}", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_cancel_run"})
332
+ return True
333
+ else:
334
+ logger.error(f"Error cancelling run: {run.id}", extra={"user_id": self.cm.user.user_id, "endpoint": "assistant_cancel_run"})
335
+ return False
336
+
337
+
338
 
339
  def process(self, thread, text):
340
  # template_search = self.cm.add_message_to_thread(thread.id, "assistant", f"Pay attention to the current state you are in and the flow template to respond to the users query:")
 
420
  # print(f"[DATETIME]: {get_current_datetime()}")
421
  # self.cm.state['date'] = 'date': pd.Timestamp.now().strftime("%Y-%m-%d %a %H:%M:%S")
422
  # get and update the current time to self.cm.state['date'] but keep the date component
423
+ current_time = get_current_datetime(self.cm.user.user_id)
424
  # replace time component of self.cm.state['date'] with the current time
425
  self.cm.state['date'] = str(pd.to_datetime(self.cm.state['date']).replace(hour=current_time.hour, minute=current_time.minute, second=current_time.second))
426
  logger.info(f"Current datetime: {self.cm.state['date']}",
app/flows.py CHANGED
@@ -92,6 +92,7 @@ MICRO_ACTION_STATE = f"""
92
 
93
  - After the user's reply, immediately call the `end_conversation()` function.
94
  - Conclude with:
 
95
  - A strong motivational statement that reinforces their commitment, channeling the energy, mindset, and knowledge of the user's legendary persona.
96
  - Be concise! (use Whatsapp texting length)
97
 
@@ -148,98 +149,53 @@ MICRO_ACTION_STATE = f"""
148
  """
149
 
150
  FOLLUP_ACTION_STATE = f"""
151
- **Following Up Yesterday's Micro Action**
152
- **Objective:** Follow up on the user's progress with their micro-action from yesterday and share useful knowledge, tools, or practices from the user's Legendary Persona. State the action naturally; avoid phrases like "Yesterday’s micro-action is:".
153
 
154
- ---
155
-
156
- **User’s Context**
157
- - **Goal:** {{}}
158
- - **Day:** {{}}/{{}} of their journey.
159
- - **User's chosen Legendary Persona:** {{}} (incorporate quotes conversationally, not as a lecture).
160
- - **Legendary Persona's knowledge and expertise (select relevant points based on the user's goal and challenges; do **not** force irrelevant quotes):**
161
- {{}}
162
- - **User's Upcoming (postponed) Micro-Actions (ignore if empty):**
163
  {{}}
164
 
165
- ---
166
-
167
- **Conversation Flow**
168
-
169
- **If the user has no upcoming reminders:**
170
-
171
- 1. **Follow Up:**
172
- - **If** the user has completed yesterday's micro-action:
173
- - Ask about their experience while doing it.
174
- - Or, ask one discussion question about the micro-action (if they already shared their experience).
175
- - **If** the user hasn't completed it:
176
- - Ask if they're going to do it today.
177
- - **Unless** they've specified a different day—then proceed to Step 2.
178
-
179
- 2. **Share Knowledge/Tips:**
180
- - After they share their experience, provide a short list (max 3 bullet points) of knowledge, resources, or tips based on the Legendary Persona's expertise that suit the user's experience or challenges.
181
- - Present this as if you are the Legendary Persona themselves.
182
- - Keep your message short (like Whatsapp texting length)
183
- - Ask what they think about it.
184
-
185
- 3. **Conclude:**
186
- - After the user replies, immediately call `end_conversation()`.
187
- - Conclude with a strong, motivational statement that reinforces their commitment, channeling the energy, mindset, and knowledge of the Legendary Persona.
188
- - Keep your message short (like Whatsapp texting length)
189
-
190
- **If the user has an upcoming reminder:**
191
-
192
- 0. **Tense Identification:**
193
- - Use past, present, or future tense based on the timestamp of the postponed micro-action and the current datetime.
194
-
195
- 1. **Motivational Reminder:**
196
- - Remind the user about their upcoming micro-action in a motivating way.
197
- - Ask if they are ready or excited for it.
198
- - Incorporate elements of the user's motivation for their goal.
199
- - Keep your message short (like Whatsapp texting length)
200
-
201
- 2. **Share Knowledge/Tips:**
202
- - After they reply, provide a list (max 3 items) of knowledge, resources, or tips based on the Legendary Persona's expertise that suit the user's experience or challenges.
203
- - Present this as if you are the Legendary Persona themselves.
204
- - Keep your message short (like Whatsapp texting length)
205
- - Ask what they think about it.
206
-
207
- 3. **Conclude:**
208
- - After the user replies, immediately call `end_conversation()`.
209
- - Conclude with a strong, motivational statement that reinforces their commitment, channeling the energy, mindset, and knowledge of the Legendary Persona.
210
- - Keep your message short (like Whatsapp texting length)
211
-
212
- ---
213
-
214
- **Principles for Quality Interaction**
215
 
216
- 1. **Personalization:** Align suggestions with the user’s Legendary Persona and recent actions.
 
217
 
218
- 2. **Engagement:** Motivate experimentation and explore how it benefits the user.
 
219
 
220
- 3. **Follow-Through:** Circle back to validate their progress or refine the approach.
221
-
222
- 4. **Concise:** Be concise; use WhatsApp texting length.
223
-
224
- ---
225
-
226
- **Important Rules**
227
-
228
- - **Function Calls:** Never explicitly mention the function you are calling to the user (do it in the background).
229
 
230
- - **Question Format:**
231
- - When asking a question, encapsulate it with asterisks (e.g., *What’s one thing you can commit to today?*).
232
- - Use only one question mark per message.
 
 
 
 
 
 
 
 
 
233
 
234
- - **Avoid Over-Questioning:** Keep questions creative and sparing; avoid overwhelming the user.
 
 
 
 
235
 
236
- - **Lists:** Provide a maximum of three items in any list.
 
 
 
 
237
 
238
- - **Quoting Legendary Persona:**
239
- - Do **not** mention the name of the Legendary Persona when quoting.
240
- - Paraphrase the quote and present it as your own message.
241
- - **Bad Example:** "Hey \<user name\>! As \<Legendary Persona\> said, '\<quote\>'"
242
- - **Good Example:** "Hey \<user name\>! \<Paraphrased quote\>."
243
  """
244
 
245
  EDUCATION_STATE = f"""
@@ -383,7 +339,7 @@ User’s Context
383
  • User's chosen Legendary Persona: {{}} (incorporate quotes conversationally, not as a lecture).
384
 
385
  The Order of Your Conversation Flow (Do these step-by-step instructions):
386
- Step 1. Start with a **creative** or unexpected open question to discuss together (only ask one question).
387
  Step 2. Acknowledge their response and using the tone of the user's chosen legendary persona, give valuable/deep advice that suits the user's challenge.
388
  Step 3. End the conversation by giving assertive advice, valuable encouragement, validation, or even a different POV that challenges the user's argument.
389
 
@@ -396,7 +352,6 @@ Important Rules
396
  • Never explicitly tell the function that you are calling to the user (just do the function calling in the background)
397
  • Question Format: When you ask a question, encapsulate it with asterisks (e.g., What’s one thing you can commit to today?). Use only one question mark per message.
398
  • Avoid Over-Questioning: Keep questions creative, sparing, and avoid overwhelming the user.
399
- • Don't ask more than one question in one message
400
  • Lists: Provide a maximum of three items in any list.
401
 
402
  When you want to mention a quote from the legendary persona, you must not say the name of the legendary persona. You should paraphrase the quote and say it like it's your own message!
@@ -544,40 +499,6 @@ Based on the above, coach and engage the user in a succinct and brief conversati
544
 
545
  ** IMPORTANT **: Although asking the user for their feedback and views is good, ensure not to pose too many questions to the user. Maintain a healthy coaching conversation flow."""
546
 
547
- FUNFACT_STATE = f"""
548
- ## ** Giving Value to The User ** (PLEASE READ CAREFULLY)
549
- Objective: Provide a personalized and uplifting message that resonates with the user's unique attributes, recent actions, and surroundings, to motivate them on their journey to achieve their goal. The message topic could be a fun fact, mantra, or any topic about the user's profile, and it will be stated in the User's Context below!
550
-
551
- User’s Context:
552
- • Goal: {{}}
553
- • User's chosen Legendary Persona: {{}} (incorporate quotes conversationally, not as a lecture).
554
- • 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):
555
- {{}}
556
- • For today's message, use this user's information as the topic: {{}}
557
-
558
- The Order of Your Conversation Flow (Do these step-by-step instructions):
559
- Step 1. Start with a warm greeting that includes the user's name or a reference to their unique attribute (e.g., "Hi, Problem-Solver!").
560
- Step 2. Offer a fun fact or an encouraging motivational message that subtly incorporates a paraphrased quote from their chosen Legendary Persona, without mentioning the persona's name.
561
- Step 3. Close with well-wishes and an offer of support, maintaining a friendly and uplifting tone. Call the end_conversation() immediately!
562
-
563
- ** Principles for Quality Interaction **
564
- 1. **Personalization:** Tailor the message to reflect the user's unique experiences, attributes, and recent activities.
565
- 2. **Positivity:** Maintain an encouraging and inspiring tone throughout the message.
566
- 3. **Subtle Incorporation of Wisdom:** Weave in insights from the Legendary Persona seamlessly and organically.
567
- 4. **Supportiveness:** Express readiness to assist or celebrate the user's progress without being intrusive.
568
-
569
- Important Rules
570
- • Never explicitly tell the function that you are calling to the user (just do the function calling in the background)
571
- • Question Format: When you ask a question, encapsulate it with asterisks (e.g., What’s one thing you can commit to today?). Use only one question mark per message.
572
- • Avoid Over-Questioning: Keep questions creative, sparing, and avoid overwhelming the user.
573
- • Lists: Provide a maximum of three items in any list.
574
- • Be concise! Use Whatsapp texting length!
575
-
576
- When you want to mention a quote from the legendary persona, you must not say the name of the legendary persona. You should paraphrase the quote and say it like it's your own message!
577
- Bad Example: Hey <user name>! As <Legendary Persona> said, "<quote>"
578
- Good Example: Hey <user name>! <paraphrased quote>.
579
- """
580
-
581
  REFLECTION_STATE = """
582
  <REFLECTION FLOW TEMPLATE>
583
 
 
92
 
93
  - After the user's reply, immediately call the `end_conversation()` function.
94
  - Conclude with:
95
+ - Two short, specific, technical, and practical pieces of advice in bullet points.
96
  - A strong motivational statement that reinforces their commitment, channeling the energy, mindset, and knowledge of the user's legendary persona.
97
  - Be concise! (use Whatsapp texting length)
98
 
 
149
  """
150
 
151
  FOLLUP_ACTION_STATE = f"""
152
+ ** Following Up Yesterday's Micro Action ** (PLEASE READ CAREFULLY)
153
+ 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.
154
 
155
+ User’s Context
156
+ • Goal:
 
 
 
 
 
 
 
157
  {{}}
158
 
159
+ • Day:
160
+ {{}}/{{}} of their journey.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
+ User's chosen Legendary Persona:
163
+ {{}} (incorporate quotes conversationally, not as a lecture).
164
 
165
+ User's chosen Legendary Persona's knowledge and expertise:
166
+ {{}} (Pick one or more to be brought up in the conversation, based on relevance to the user's goal and challenges)
167
 
168
+ User's Upcoming (postponed) Micro-Actions:
169
+ {{}}
 
 
 
 
 
 
 
170
 
171
+ The Order of Your Conversation Flow (Do these step-by-step instructions):
172
+ **IF** THE USER HAS NO UPCOMING REMINDERS:
173
+ Step 1. Follow Up:
174
+ **IF** the user has completed yesterday's micro-action, ask the experience that the user had while doing the micro action.
175
+ **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).
176
+ 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.
177
+ 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.
178
+ **IF** THE USER HAS AN UPCOMING REMINDER:
179
+ 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.
180
+ 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.
181
+ 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.
182
+ 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.
183
 
184
+ ** Principles for Quality Interaction **
185
+ 1. **Personalization:** Align suggestions with the user’s legendary persona and recent actions.
186
+ 2. **Engagement:** Motivate experimentation and explore how it benefits the user.
187
+ 3. **Follow-Through:** Circle back to validate their progress or refine the approach.
188
+ 4. **Concise:** Be concise & use whatsapp texting length.
189
 
190
+ Important Rules
191
+ • Never explicitly tell the function that you are calling to the user (just do the function calling in the background)
192
+ • Question Format: When you ask a question, encapsulate it with asterisks (e.g., What’s one thing you can commit to today?). Use only one question mark per message.
193
+ • Avoid Over-Questioning: Keep questions creative, sparing, and avoid overwhelming the user.
194
+ • Lists: Provide a maximum of three items in any list.
195
 
196
+ When you want to mention a quote from the legendary persona, you must not say the name of the legendary persona. You should paraphrase the quote and say it like it's your own message!
197
+ Bad Example: Hey <user name>! As <Legendary Persona> said, "<quote>"
198
+ Good Example: Hey <user name>! <paraphrased quote>.
 
 
199
  """
200
 
201
  EDUCATION_STATE = f"""
 
339
  • User's chosen Legendary Persona: {{}} (incorporate quotes conversationally, not as a lecture).
340
 
341
  The Order of Your Conversation Flow (Do these step-by-step instructions):
342
+ Step 1. Start with a **creative** or unexpected open question to discuss together.
343
  Step 2. Acknowledge their response and using the tone of the user's chosen legendary persona, give valuable/deep advice that suits the user's challenge.
344
  Step 3. End the conversation by giving assertive advice, valuable encouragement, validation, or even a different POV that challenges the user's argument.
345
 
 
352
  • Never explicitly tell the function that you are calling to the user (just do the function calling in the background)
353
  • Question Format: When you ask a question, encapsulate it with asterisks (e.g., What’s one thing you can commit to today?). Use only one question mark per message.
354
  • Avoid Over-Questioning: Keep questions creative, sparing, and avoid overwhelming the user.
 
355
  • Lists: Provide a maximum of three items in any list.
356
 
357
  When you want to mention a quote from the legendary persona, you must not say the name of the legendary persona. You should paraphrase the quote and say it like it's your own message!
 
499
 
500
  ** IMPORTANT **: Although asking the user for their feedback and views is good, ensure not to pose too many questions to the user. Maintain a healthy coaching conversation flow."""
501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  REFLECTION_STATE = """
503
  <REFLECTION FLOW TEMPLATE>
504
 
app/main.py CHANGED
@@ -8,7 +8,7 @@ import os
8
  import logging
9
  import json
10
  import regex as re
11
- from datetime import datetime
12
  from app.user import User
13
  from typing import List, Optional, Callable
14
  from openai import OpenAI
@@ -254,10 +254,6 @@ class ChatItem(BaseModel):
254
  user_id: str
255
  message: str
256
 
257
- class ChangeAssistantItem(BaseModel):
258
- user_id: str
259
- assistant_id: str
260
-
261
  class ChangeDateItem(BaseModel):
262
  user_id: str
263
  date: str
@@ -270,7 +266,7 @@ class ErrorResponse(BaseModel):
270
  status: str = "error"
271
  code: int
272
  message: str
273
- timestamp: datetime = datetime.now()
274
 
275
  class BookingItem(BaseModel):
276
  booking_id: str
@@ -322,14 +318,12 @@ def do_micro(request: ChangeDateItem, day: int, api_key: str = Security(get_api_
322
 
323
 
324
  # endpoint to change user assistant using user.change_to_latest_assistant()
325
- @app.post("/change_assistant")
326
- def change_assistant(request: ChangeAssistantItem, api_key: str = Security(get_api_key)):
327
- user_id = request.user_id
328
- assistant_id = request.assistant_id
329
- print_log("INFO", f"Changing assistant to {assistant_id}", extra={"user_id": user_id, "endpoint": "/change_assistant"})
330
- logger.info(f"Changing assistant to {assistant_id}", extra={"user_id": user_id, "endpoint": "/change_assistant"})
331
  user = get_user(user_id)
332
- user.change_assistant(assistant_id)
333
  logger.info(f"Assistant changed to {assistant_id}", extra={"user_id": user_id, "endpoint": "/change_assistant"})
334
  return {"assistant_id": assistant_id}
335
 
@@ -359,7 +353,7 @@ def migrate_user(request: CreateUserItem, api_key: str = Security(get_api_key)):
359
  def download_file_from_s3(filename, bucket):
360
  user_id = filename.split('.')[0]
361
  function_name = download_file_from_s3.__name__
362
- logger.info(f"Downloading file {filename} from [dev] S3 bucket {bucket}", extra={'user_id': user_id, 'endpoint': function_name})
363
  file_path = os.path.join('users', 'data', filename)
364
  try:
365
  if (AWS_ACCESS_KEY and AWS_SECRET_KEY):
@@ -368,8 +362,8 @@ def migrate_user(request: CreateUserItem, api_key: str = Security(get_api_key)):
368
  session = boto3.session.Session()
369
  s3_client = session.client('s3')
370
  with open(file_path, 'wb') as f:
371
- ## Upload to dev Folder
372
- s3_client.download_fileobj(bucket, f"dev/users/{filename}", f)
373
  logger.info(f"File {filename} downloaded successfully from S3", extra={'user_id': user_id, 'endpoint': function_name})
374
  return True
375
  except Exception as e:
@@ -435,7 +429,7 @@ def get_user_by_id(user_id: str, api_key: str = Security(get_api_key)):
435
  user = get_user(user_id)
436
  print_log("INFO", "Successfully retrieved user", extra={"user_id": user_id, "endpoint": "/get_user"})
437
  logger.info("Successfully retrieved user", extra={"user_id": user_id, "endpoint": "/get_user"})
438
- 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}
439
 
440
  if user.goal:
441
  api_response["goal"] = user.goal
@@ -664,7 +658,6 @@ def chat(request: ChatItem, api_key: str = Security(get_api_key)):
664
  if recent_run:
665
  user.cancel_run(recent_run)
666
  response = user.send_message(user.get_recent_message())
667
-
668
  finally:
669
  print_log("INFO",f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
670
  logger.info(f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
@@ -950,9 +943,6 @@ def add_session_completion_point_by_user(user_id: str, api_key: str = Security(g
950
  user = get_user(user_id)
951
  user.add_point_for_completing_session()
952
  return {"response": "ok"}
953
-
954
-
955
-
956
  except Exception as e:
957
  print_log("ERROR",f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_session_completion_point"}, exc_info=True)
958
  logger.error(f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_session_completion_point"}, exc_info=True)
 
8
  import logging
9
  import json
10
  import regex as re
11
+ from datetime import datetime, timezone
12
  from app.user import User
13
  from typing import List, Optional, Callable
14
  from openai import OpenAI
 
254
  user_id: str
255
  message: str
256
 
 
 
 
 
257
  class ChangeDateItem(BaseModel):
258
  user_id: str
259
  date: str
 
266
  status: str = "error"
267
  code: int
268
  message: str
269
+ timestamp: datetime = datetime.now(timezone.utc)
270
 
271
  class BookingItem(BaseModel):
272
  booking_id: str
 
318
 
319
 
320
  # endpoint to change user assistant using user.change_to_latest_assistant()
321
+ @app.get("/change_assistant")
322
+ def change_assistant(user_id: str, api_key: str = Security(get_api_key)):
323
+ print_log("INFO", "Changing assistant", extra={"user_id": user_id, "endpoint": "/change_assistant"})
324
+ logger.info("Changing assistant", extra={"user_id": user_id, "endpoint": "/change_assistant"})
 
 
325
  user = get_user(user_id)
326
+ assistant_id = user.change_to_latest_assistant()
327
  logger.info(f"Assistant changed to {assistant_id}", extra={"user_id": user_id, "endpoint": "/change_assistant"})
328
  return {"assistant_id": assistant_id}
329
 
 
353
  def download_file_from_s3(filename, bucket):
354
  user_id = filename.split('.')[0]
355
  function_name = download_file_from_s3.__name__
356
+ logger.info(f"Downloading file {filename} from [staging] S3 bucket {bucket}", extra={'user_id': user_id, 'endpoint': function_name})
357
  file_path = os.path.join('users', 'data', filename)
358
  try:
359
  if (AWS_ACCESS_KEY and AWS_SECRET_KEY):
 
362
  session = boto3.session.Session()
363
  s3_client = session.client('s3')
364
  with open(file_path, 'wb') as f:
365
+ ## Upload to Staging Folder
366
+ s3_client.download_fileobj(bucket, f"staging/users/{filename}", f)
367
  logger.info(f"File {filename} downloaded successfully from S3", extra={'user_id': user_id, 'endpoint': function_name})
368
  return True
369
  except Exception as e:
 
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
 
658
  if recent_run:
659
  user.cancel_run(recent_run)
660
  response = user.send_message(user.get_recent_message())
 
661
  finally:
662
  print_log("INFO",f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
663
  logger.info(f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
 
943
  user = get_user(user_id)
944
  user.add_point_for_completing_session()
945
  return {"response": "ok"}
 
 
 
946
  except Exception as e:
947
  print_log("ERROR",f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_session_completion_point"}, exc_info=True)
948
  logger.error(f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_session_completion_point"}, exc_info=True)
app/user.py CHANGED
@@ -2,7 +2,7 @@ import json
2
  import io
3
  import os
4
  import pandas as pd
5
- from datetime import datetime
6
  import json
7
  from app.assistants import Assistant
8
  import glob
@@ -11,9 +11,8 @@ import random
11
  import logging
12
  import psycopg2
13
  from psycopg2 import sql
14
- import random
15
 
16
- from app.flows import FINAL_SUMMARY_STATE, FINAL_SUMMARY_STATE, MICRO_ACTION_STATE, MOTIVATION_INSPIRATION_STATE, OPEN_DISCUSSION_STATE, POST_GG_STATE, PROGRESS_REFLECTION_STATE, PROGRESS_SUMMARY_STATE, EDUCATION_STATE, FOLLUP_ACTION_STATE, FUNFACT_STATE
17
  from pydantic import BaseModel
18
  from datetime import datetime
19
 
@@ -43,7 +42,7 @@ class Index(BaseModel):
43
  logger = logging.getLogger(__name__)
44
 
45
  def get_current_datetime():
46
- return datetime.now()
47
 
48
  class ConversationManager:
49
  def __init__(self, client, user, asst_id, intro_done=False):
@@ -52,7 +51,7 @@ class ConversationManager:
52
  self.assistants = {'general': Assistant('asst_vnucWWELJlCWadfAARwyKkCW', self), 'intro': Assistant('asst_baczEK65KKvPWIUONSzdYH8j', self)}
53
 
54
  self.client = client
55
- self.state = {'date': pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S")}
56
 
57
  self.current_thread = self.create_thread()
58
  self.daily_thread = None
@@ -206,6 +205,10 @@ class ConversationManager:
206
 
207
  temp_thread = self.client.beta.threads.create(messages=messages)
208
  logger.info(f"Created Temp Thread: {temp_thread}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
 
 
 
 
209
 
210
  self.add_message_to_thread(temp_thread.id, "user", text)
211
 
@@ -217,10 +220,6 @@ class ConversationManager:
217
  self.client.beta.threads.delete(temp_thread.id)
218
  logger.info(f"Deleted Temp Thread: {temp_thread}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
219
 
220
- if add_to_main:
221
- logger.info(f"Adding message to main thread: {text}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
222
- self.add_message_to_thread(self.current_thread.id, "assistant", text)
223
-
224
  return response
225
 
226
  def delete_hidden_messages(self, old_thread=None):
@@ -401,10 +400,18 @@ class User:
401
  },
402
  {
403
  "day": 4,
404
- "coachingTheme": "FUNFACT_STATE"
405
  },
406
  {
407
  "day": 5,
 
 
 
 
 
 
 
 
408
  "coachingTheme": "FINAL_SUMMARY_STATE"
409
  }
410
  ]
@@ -417,12 +424,69 @@ class User:
417
  self.user_interaction_guidelines = self.generate_user_interaction_guidelines(user_info, client)
418
  self.conversations = ConversationManager(client, self, asst_id)
419
 
420
- def add_recent_wins(self, wins):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
  if len(self.recent_wins)<5:
422
- self.recent_wins.insert(0,wins)
423
  else:
424
  self.recent_wins.pop()
425
- self.recent_wins.insert(0,wins)
426
 
427
  def add_life_score_point(self, variable, points_added, notes):
428
  if variable == 'Personal Growth':
@@ -461,13 +525,13 @@ class User:
461
  current_goal = self.get_current_goal(full=True)
462
  if current_goal:
463
  current_goal.status = "COMPLETED"
464
- current_goal.updated_at = pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S")
465
  return current_goal.content
466
 
467
  for g in self.goal:
468
  if g.content == goal:
469
  g.status = status
470
- g.updated_at = pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S")
471
  if content:
472
  g.content = content
473
  return True
@@ -479,22 +543,22 @@ class User:
479
  if completed:
480
  self.update_goal(current_goal, "COMPLETED")
481
  self.add_life_score_point(variable = goal_area, points_added = 30, notes = "Completing a Goal")
482
- self.add_recent_wins(wins = "You have completed your goal!")
483
 
484
  if current_goal is None:
485
- 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"))
486
  self.goal.append(new_goal)
487
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
488
- self.add_recent_wins(wins = "You have set a new goal!")
489
  else:
490
  if add:
491
  if current_goal:
492
  # update current_goal status to "IDLE"
493
  self.update_goal(current_goal, "IDLE")
494
- 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"))
495
  self.goal.append(new_goal)
496
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
497
- self.add_recent_wins(wins = "You have set a new goal!")
498
  else:
499
  self.update_goal(current_goal, "ONGOING", content=goal)
500
 
@@ -502,7 +566,7 @@ class User:
502
  for ma in self.recommended_micro_actions:
503
  if ma.content == micro_action:
504
  ma.status = status
505
- ma.updated_at = pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S")
506
  return True
507
  return False
508
 
@@ -527,6 +591,7 @@ class User:
527
  self.reminders = None
528
  self.recent_wins = []
529
 
 
530
  def generate_user_interaction_guidelines(self, user_info, client):
531
  logger.info(f"Generating user interaction guidelines for user: {self.user_id}", extra={"user_id": self.user_id, "endpoint": "generate_user_interaction_guidelines"})
532
  # prompt = f"A 'profile' is a document containing rich insights on users for the purpose of \
@@ -661,6 +726,28 @@ class User:
661
  return index
662
 
663
  def set_reminder(self, reminder):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
  # generate uuid for this reminder
665
  reminder['id'] = generate_uuid()
666
  action = reminder['action']
@@ -684,6 +771,7 @@ class User:
684
  if index == -1:
685
  logger.info(f"Could not find a mathcing reminder to delete: {reminder}", extra={"user_id": self.user_id, "endpoint": "set_reminder"})
686
  else:
 
687
  self.reminders[index]['action'] = 'delete'
688
  logger.info(f"Reminder {old_reminder} has been marked for deletion", extra={"user_id": self.user_id, "endpoint": "set_reminder"})
689
  else:
@@ -724,15 +812,6 @@ class User:
724
  user_data_clean = json.loads(user_data['onboarding'])
725
  user_legendary_persona = user_data_clean.get('legendPersona', '')
726
 
727
- if "Coach Steve" in user_legendary_persona:
728
- persona_name = 'Steve Jobs'
729
- elif "Coach Aris" in user_legendary_persona:
730
- persona_name = 'Aristotle'
731
- elif "Coach Teresa" in user_legendary_persona:
732
- persona_name = 'Mother Teresa'
733
- else:
734
- persona_name = user_legendary_persona
735
-
736
  if "Coach Steve" in user_legendary_persona or "Steve Jobs" in user_legendary_persona:
737
  knowledge = """
738
  1. Vision and Creativity: Encouraging students to think big and innovate.
@@ -820,45 +899,41 @@ class User:
820
  for reminder in reminders:
821
  if reminder['timestamp'].date() == pd.to_datetime(date).date():
822
  logger.info(f"Reminder found for today ({pd.to_datetime(date).date()}): {reminder}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
823
- if day != 4:
824
  theme = "MICRO_ACTION_STATE"
825
  break
826
  else:
827
  logger.info(f"No reminders found for today ({pd.to_datetime(date).date()})", extra={"user_id": self.user_id, "endpoint": "do_theme"})
828
 
829
  if theme == "MOTIVATION_INSPIRATION_STATE":
830
- formatted_message = MOTIVATION_INSPIRATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name)
831
  elif theme == "PROGRESS_REFLECTION_STATE":
832
- formatted_message = PROGRESS_REFLECTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name)
833
  if len(self.challenges):
834
  challenge = self.challenges.pop(0)
835
  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) **"
836
  elif theme == "MICRO_ACTION_STATE":
837
  reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
838
 
839
- formatted_message = MICRO_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name, reminder_message)
840
  if len(self.recommended_micro_actions):
841
  todays_micro_action = self.recommended_micro_actions.pop(0)
842
  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) **"
843
  elif theme == "OPEN_DISCUSSION_STATE":
844
- formatted_message = OPEN_DISCUSSION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name)
845
  if len(self.other_focusses):
846
  focus = self.other_focusses.pop(0)
847
  formatted_message += f"\n\n** IMPORTANT: Today, focus the discussion on: {focus.content}, which they discussed during their growth guide session (let the user know we are bringing it up because of this) **"
848
  elif theme == "PROGRESS_SUMMARY_STATE":
849
- formatted_message = PROGRESS_SUMMARY_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name)
850
  elif theme == "FINAL_SUMMARY_STATE":
851
- formatted_message = FINAL_SUMMARY_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name)
852
  elif theme == "EDUCATION_STATE":
853
- formatted_message = EDUCATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name)
854
  elif theme == "FOLLUP_ACTION_STATE":
855
  reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
856
- formatted_message = FOLLUP_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name, knowledge, reminder_message)
857
- elif theme == "FUNFACT_STATE":
858
- topics = ["Fun Fact about the User's Goal", "How Personality Type is affecting/shaping their behaviour towards the goal", "How Love Language may impact and be relevant toward the goal"]
859
- randomized_topic = random.choice(topics)
860
- formatted_message = FUNFACT_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), persona_name, knowledge, randomized_topic)
861
-
862
  # prompt = f"""** It is a new day: {date} **
863
  # Additional System Instruction:
864
  # - Remember all of the user's personal information. Use this information to be as personalised as possible when conversing by including it in your responses where relevant.
@@ -872,9 +947,6 @@ class User:
872
  # """
873
 
874
  prompt = f"""** It is a new day: {date} ({day}) 10:00:00 **
875
-
876
- *Send a message that suits the morning time*
877
-
878
  Additional System Instruction:
879
 
880
  1. Focus on giving more wisdom than questions:
@@ -913,7 +985,7 @@ class User:
913
  if theme == "MICRO_ACTION_STATE":
914
  # check for any recommended microactions
915
  logger.info(f"Checking for recommended micro actions", extra={"user_id": self.user_id, "endpoint": "do_theme"})
916
- micro_action = UserDataItem(role="assistant", area=self.get_current_goal(full=True).area, content=response['content'], user_id=self.user_id, status="PENDING", 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"))
917
  logger.info(f"Recommended Micro Action: {micro_action}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
918
  self.micro_actions.append(micro_action)
919
 
@@ -933,16 +1005,16 @@ class User:
933
  if self.reminders is not None and len(self.reminders):
934
  # remove all reminders which 'recurrence' is 'once' or 'action' is 'delete' or the date is < today
935
  # this is to ensure that only reminders with recurrence and future reminder are kept
936
- 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()))]
937
  logger.info(f"Active Reminders: {self.reminders}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
938
 
939
  ## ADD POINT FOR CHANGE DATE
940
  if self.growth_plan.current()['day'] == 7:
941
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
942
- self.add_recent_wins(wins = "You have reached Day 7 of your growth journey!")
943
  elif self.growth_plan.current()['day'] == 14:
944
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = "Reaching Day 14")
945
- self.add_recent_wins(wins = "You have finished your growth journey!")
946
 
947
  logger.info(f"Today's action is {action}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
948
 
@@ -1053,7 +1125,7 @@ class User:
1053
  )
1054
 
1055
  try:
1056
- current_time = datetime.now().strftime("%d-%m-%Y %a %H:%M:%S")
1057
 
1058
  response = self.client.beta.chat.completions.parse(
1059
  model="gpt-4o",
@@ -1093,10 +1165,12 @@ class User:
1093
  The user has a current goal: {self.get_current_goal()}
1094
  The user provided a new goal: {goal_text}
1095
 
1096
- Determine if the new goal is the same as the current goal or if it's a new one.
1097
- If it's the same, respond with the current goal, and set same_or_not == True
1098
- If it's a new goal, respond with the new goal, and set same_or_not == False
1099
-
 
 
1100
  Your response will be the final goal. You will also need to determine the area of this
1101
  final goal by choosing one of these areas that suits the final goal:
1102
  "Personal Growth", "Career Growth", "Relationship", "Mental Well-Being", "Health and Wellness"
@@ -1107,6 +1181,40 @@ class User:
1107
  goal: str (the final goal),
1108
  area: str (the area of the goal)
1109
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1110
  """
1111
 
1112
  response = self.client.chat.completions.create(
@@ -1168,27 +1276,27 @@ class User:
1168
  def update_micro_action_status(self, completed_micro_action):
1169
  if completed_micro_action:
1170
  self.micro_actions[-1].status = "COMPLETE"
1171
- self.micro_actions[-1].updated_at = pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S")
1172
 
1173
  num_of_micro_actions_completed = sum(1 for item in self.micro_actions if item.status == 'COMPLETE')
1174
 
1175
  if (num_of_micro_actions_completed in (1,3,5)) or (num_of_micro_actions_completed % 10 == 0 and num_of_micro_actions_completed != 0):
1176
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = f"Completing the {num_of_micro_actions_completed}-th micro-action")
1177
- self.add_recent_wins(wins = "You have completed a micro action!")
1178
 
1179
  def trigger_deep_reflection_point(self, area_of_deep_reflection):
1180
  if len(area_of_deep_reflection)>0:
1181
  for area in area_of_deep_reflection:
1182
  self.add_life_score_point(variable = area, points_added = 5, notes = f"Doing a deep reflection about {area}")
1183
- self.add_recent_wins(wins = f"You have done a deep reflection about your {area}!")
1184
 
1185
  def add_point_for_booking(self):
1186
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Booking a GG session")
1187
- self.add_recent_wins(wins = "You have booked a Growth Guide session!")
1188
 
1189
  def add_point_for_completing_session(self):
1190
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 20, notes = "Completing a GG session")
1191
- self.add_recent_wins(wins = "You have completed a Growth Guide session!")
1192
 
1193
  def build_ourcoach_report(self, overview, action_plan, gg_session_notes):
1194
  logger.info(f"Building ourcoach report", extra={"user_id": self.user_id, "endpoint": "build_ourcoach_report"})
 
2
  import io
3
  import os
4
  import pandas as pd
5
+ from datetime import datetime, timezone
6
  import json
7
  from app.assistants import Assistant
8
  import glob
 
11
  import logging
12
  import psycopg2
13
  from psycopg2 import sql
 
14
 
15
+ from app.flows import FINAL_SUMMARY_STATE, FINAL_SUMMARY_STATE, MICRO_ACTION_STATE, MOTIVATION_INSPIRATION_STATE, OPEN_DISCUSSION_STATE, POST_GG_STATE, PROGRESS_REFLECTION_STATE, PROGRESS_SUMMARY_STATE, EDUCATION_STATE, FOLLUP_ACTION_STATE
16
  from pydantic import BaseModel
17
  from datetime import datetime
18
 
 
42
  logger = logging.getLogger(__name__)
43
 
44
  def get_current_datetime():
45
+ return datetime.now(timezone.utc)
46
 
47
  class ConversationManager:
48
  def __init__(self, client, user, asst_id, intro_done=False):
 
51
  self.assistants = {'general': Assistant('asst_vnucWWELJlCWadfAARwyKkCW', self), 'intro': Assistant('asst_baczEK65KKvPWIUONSzdYH8j', self)}
52
 
53
  self.client = client
54
+ self.state = {'date': pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")}
55
 
56
  self.current_thread = self.create_thread()
57
  self.daily_thread = None
 
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
  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 delete_hidden_messages(self, old_thread=None):
 
400
  },
401
  {
402
  "day": 4,
403
+ "coachingTheme": "MICRO_ACTION_STATE"
404
  },
405
  {
406
  "day": 5,
407
+ "coachingTheme": "FOLLUP_ACTION_STATE"
408
+ },
409
+ {
410
+ "day": 6,
411
+ "coachingTheme": "OPEN_DISCUSSION_STATE"
412
+ },
413
+ {
414
+ "day": 7,
415
  "coachingTheme": "FINAL_SUMMARY_STATE"
416
  }
417
  ]
 
424
  self.user_interaction_guidelines = self.generate_user_interaction_guidelines(user_info, client)
425
  self.conversations = ConversationManager(client, self, asst_id)
426
 
427
+ def add_recent_wins(self, wins, context = None):
428
+ prompt = f"""
429
+ ## Role
430
+ You are an expert in writing achievement message and progress notification. Your task is to use the user's achievement and context to formulate a short and creative achievement message/progress notification. The output must be a one sentence short message (less than 15 words) in this JSON output schema:
431
+
432
+ ```json
433
+ {{
434
+ achievement_message: str
435
+ }}
436
+ ```
437
+
438
+ Note: No need to mention the user's name. Make it concise (less than 15 words)
439
+
440
+ ## Example
441
+ User's achievement: Completing a Task
442
+ Achievement context: The user has completed a 10k run
443
+
444
+ Output:
445
+ ```
446
+ {{
447
+ achievement_message: You crushed it! Completing that 10k run is a huge milestone—way to go!
448
+ }}
449
+ ```
450
+
451
+ ## User Input
452
+
453
+ User's achievement: {wins}
454
+ Achievement context: {context}
455
+ """
456
+
457
+ response = self.client.chat.completions.create(
458
+ model="gpt-4o",
459
+ messages=[{"role": "user", "content": prompt}],
460
+ response_format = {
461
+ "type": "json_schema",
462
+ "json_schema": {
463
+ "name": "achievement_message_schema",
464
+ "strict": True,
465
+ "schema": {
466
+ "type": "object",
467
+ "properties": {
468
+ "achievement_message": {
469
+ "type": "string",
470
+ "description": "A message indicating an achievement."
471
+ }
472
+ },
473
+ "required": [
474
+ "achievement_message"
475
+ ],
476
+ "additionalProperties": False
477
+ }
478
+ }
479
+ },
480
+ temperature=1
481
+ )
482
+
483
+ achievement_message = json.loads(response.choices[0].message.content)['achievement_message']
484
+
485
  if len(self.recent_wins)<5:
486
+ self.recent_wins.insert(0,achievement_message)
487
  else:
488
  self.recent_wins.pop()
489
+ self.recent_wins.insert(0,achievement_message)
490
 
491
  def add_life_score_point(self, variable, points_added, notes):
492
  if variable == 'Personal Growth':
 
525
  current_goal = self.get_current_goal(full=True)
526
  if current_goal:
527
  current_goal.status = "COMPLETED"
528
+ current_goal.updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
529
  return current_goal.content
530
 
531
  for g in self.goal:
532
  if g.content == goal:
533
  g.status = status
534
+ g.updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
535
  if content:
536
  g.content = content
537
  return True
 
543
  if completed:
544
  self.update_goal(current_goal, "COMPLETED")
545
  self.add_life_score_point(variable = goal_area, points_added = 30, notes = "Completing a Goal")
546
+ self.add_recent_wins(wins = "You have completed your goal!", context = current_goal)
547
 
548
  if current_goal is None:
549
+ new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S"))
550
  self.goal.append(new_goal)
551
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
552
+ self.add_recent_wins(wins = "You have set your first goal!", context = new_goal.content)
553
  else:
554
  if add:
555
  if current_goal:
556
  # update current_goal status to "IDLE"
557
  self.update_goal(current_goal, "IDLE")
558
+ new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S"))
559
  self.goal.append(new_goal)
560
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
561
+ self.add_recent_wins(wins = "You have set a new goal!", context = new_goal.content)
562
  else:
563
  self.update_goal(current_goal, "ONGOING", content=goal)
564
 
 
566
  for ma in self.recommended_micro_actions:
567
  if ma.content == micro_action:
568
  ma.status = status
569
+ ma.updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
570
  return True
571
  return False
572
 
 
591
  self.reminders = None
592
  self.recent_wins = []
593
 
594
+
595
  def generate_user_interaction_guidelines(self, user_info, client):
596
  logger.info(f"Generating user interaction guidelines for user: {self.user_id}", extra={"user_id": self.user_id, "endpoint": "generate_user_interaction_guidelines"})
597
  # prompt = f"A 'profile' is a document containing rich insights on users for the purpose of \
 
726
  return index
727
 
728
  def set_reminder(self, reminder):
729
+ db_params = {
730
+ 'dbname': 'ourcoach',
731
+ 'user': 'ourcoach',
732
+ 'password': 'hvcTL3kN3pOG5KteT17T',
733
+ 'host': 'staging-ourcoach.cx8se8o0iaiy.ap-southeast-1.rds.amazonaws.com',
734
+ 'port': '5432'
735
+ }
736
+ with psycopg2.connect(**db_params) as conn:
737
+ with conn.cursor() as cursor:
738
+ query = sql.SQL("SELECT * FROM {table} WHERE id = %s").format(table=sql.Identifier('public', 'users'))
739
+ cursor.execute(query, (self.user_id,))
740
+ row = cursor.fetchone()
741
+ if (row):
742
+ colnames = [desc[0] for desc in cursor.description]
743
+ user_data = dict(zip(colnames, row))
744
+ user_timezone = user_data['timezone']
745
+
746
+ # Convert to UTC
747
+ reminder['timestamp_local'] = reminder['timestamp']
748
+ reminder['local_timezone'] = user_timezone
749
+ reminder['timestamp'] = reminder['timestamp'].tz_localize(user_timezone).tz_convert("UTC")
750
+
751
  # generate uuid for this reminder
752
  reminder['id'] = generate_uuid()
753
  action = reminder['action']
 
771
  if index == -1:
772
  logger.info(f"Could not find a mathcing reminder to delete: {reminder}", extra={"user_id": self.user_id, "endpoint": "set_reminder"})
773
  else:
774
+ old_reminder = self.reminders[index]
775
  self.reminders[index]['action'] = 'delete'
776
  logger.info(f"Reminder {old_reminder} has been marked for deletion", extra={"user_id": self.user_id, "endpoint": "set_reminder"})
777
  else:
 
812
  user_data_clean = json.loads(user_data['onboarding'])
813
  user_legendary_persona = user_data_clean.get('legendPersona', '')
814
 
 
 
 
 
 
 
 
 
 
815
  if "Coach Steve" in user_legendary_persona or "Steve Jobs" in user_legendary_persona:
816
  knowledge = """
817
  1. Vision and Creativity: Encouraging students to think big and innovate.
 
899
  for reminder in reminders:
900
  if reminder['timestamp'].date() == pd.to_datetime(date).date():
901
  logger.info(f"Reminder found for today ({pd.to_datetime(date).date()}): {reminder}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
902
+ if theme != "FINAL_SUMMARY_STATE":
903
  theme = "MICRO_ACTION_STATE"
904
  break
905
  else:
906
  logger.info(f"No reminders found for today ({pd.to_datetime(date).date()})", extra={"user_id": self.user_id, "endpoint": "do_theme"})
907
 
908
  if theme == "MOTIVATION_INSPIRATION_STATE":
909
+ formatted_message = MOTIVATION_INSPIRATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
910
  elif theme == "PROGRESS_REFLECTION_STATE":
911
+ formatted_message = PROGRESS_REFLECTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
912
  if len(self.challenges):
913
  challenge = self.challenges.pop(0)
914
  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) **"
915
  elif theme == "MICRO_ACTION_STATE":
916
  reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
917
 
918
+ formatted_message = MICRO_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona, reminder_message)
919
  if len(self.recommended_micro_actions):
920
  todays_micro_action = self.recommended_micro_actions.pop(0)
921
  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) **"
922
  elif theme == "OPEN_DISCUSSION_STATE":
923
+ formatted_message = OPEN_DISCUSSION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
924
  if len(self.other_focusses):
925
  focus = self.other_focusses.pop(0)
926
  formatted_message += f"\n\n** IMPORTANT: Today, focus the discussion on: {focus.content}, which they discussed during their growth guide session (let the user know we are bringing it up because of this) **"
927
  elif theme == "PROGRESS_SUMMARY_STATE":
928
+ formatted_message = PROGRESS_SUMMARY_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
929
  elif theme == "FINAL_SUMMARY_STATE":
930
+ formatted_message = FINAL_SUMMARY_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
931
  elif theme == "EDUCATION_STATE":
932
+ formatted_message = EDUCATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona)
933
  elif theme == "FOLLUP_ACTION_STATE":
934
  reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
935
+ formatted_message = FOLLUP_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), user_legendary_persona, knowledge, reminder_message)
936
+
 
 
 
 
937
  # prompt = f"""** It is a new day: {date} **
938
  # Additional System Instruction:
939
  # - Remember all of the user's personal information. Use this information to be as personalised as possible when conversing by including it in your responses where relevant.
 
947
  # """
948
 
949
  prompt = f"""** It is a new day: {date} ({day}) 10:00:00 **
 
 
 
950
  Additional System Instruction:
951
 
952
  1. Focus on giving more wisdom than questions:
 
985
  if theme == "MICRO_ACTION_STATE":
986
  # check for any recommended microactions
987
  logger.info(f"Checking for recommended micro actions", extra={"user_id": self.user_id, "endpoint": "do_theme"})
988
+ micro_action = UserDataItem(role="assistant", area=self.get_current_goal(full=True).area, content=response['content'], user_id=self.user_id, status="PENDING", created_at=pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S"))
989
  logger.info(f"Recommended Micro Action: {micro_action}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
990
  self.micro_actions.append(micro_action)
991
 
 
1005
  if self.reminders is not None and len(self.reminders):
1006
  # remove all reminders which 'recurrence' is 'once' or 'action' is 'delete' or the date is < today
1007
  # this is to ensure that only reminders with recurrence and future reminder are kept
1008
+ 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(tz='UTC')))]
1009
  logger.info(f"Active Reminders: {self.reminders}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
1010
 
1011
  ## ADD POINT FOR CHANGE DATE
1012
  if self.growth_plan.current()['day'] == 7:
1013
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
1014
+ self.add_recent_wins(wins = "You have reached Day 7 of your growth journey!", context = 'Growth journey is a 14-day coaching plan')
1015
  elif self.growth_plan.current()['day'] == 14:
1016
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = "Reaching Day 14")
1017
+ self.add_recent_wins(wins = "You have finished your growth journey!", context = 'Growth journey is a 14-day coaching plan')
1018
 
1019
  logger.info(f"Today's action is {action}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
1020
 
 
1125
  )
1126
 
1127
  try:
1128
+ current_time = datetime.now(timezone.utc).strftime("%d-%m-%Y %a %H:%M:%S")
1129
 
1130
  response = self.client.beta.chat.completions.parse(
1131
  model="gpt-4o",
 
1165
  The user has a current goal: {self.get_current_goal()}
1166
  The user provided a new goal: {goal_text}
1167
 
1168
+ Determine if the new goal is inside the scope of the current goal or if it's outside the scope.
1169
+ If it's inside the scope, respond **exactly** with the current goal, and set same_or_not == True
1170
+ If it's outside the scope and related, respond with a merged goal (from the current and new goal) with larger scope, and set same_or_not == False
1171
+ If it's outside the scope and not related, respond with the new goal, and set same_or_not == False
1172
+ Note: You may paraphase the merged goal to be more succinct.
1173
+
1174
  Your response will be the final goal. You will also need to determine the area of this
1175
  final goal by choosing one of these areas that suits the final goal:
1176
  "Personal Growth", "Career Growth", "Relationship", "Mental Well-Being", "Health and Wellness"
 
1181
  goal: str (the final goal),
1182
  area: str (the area of the goal)
1183
  }}
1184
+
1185
+ ## Example 1 (Inside the scope):
1186
+ The user has a current goal: to spend at least 30 minutes exercising every day
1187
+ The user provided a new goal: to do short exercise every day
1188
+ Your verdict: inside the scope
1189
+ Your output:
1190
+ {{
1191
+ same_or_not: True,
1192
+ goal: "to spend at least 30 minutes exercising every day"
1193
+ area: "Health and Wellness"
1194
+ }}
1195
+
1196
+ ## Example 2 (Outside the scope, still related):
1197
+ The user has a current goal: to spend at least 30 minutes exercising every day
1198
+ The user provided a new goal: to exercise and have a balanced meal plan
1199
+ Your verdict: outside the scope, still related
1200
+ Your output:
1201
+ {{
1202
+ same_or_not: False,
1203
+ goal: "to exercise at least 30 minutes every day, together with a balanced meal plan"
1204
+ area: "Health and Wellness"
1205
+ }}
1206
+
1207
+ ## Example 3 (Outside the scope, not related):
1208
+ The user has a current goal: to spend at least 30 minutes exercising every day
1209
+ The user provided a new goal: to have a better relationship with friends
1210
+ Your verdict: outside the scope, not related
1211
+ Your output:
1212
+ {{
1213
+ same_or_not: False,
1214
+ goal: "to have a better relationship with friends"
1215
+ area: "Relationship"
1216
+ }}
1217
+
1218
  """
1219
 
1220
  response = self.client.chat.completions.create(
 
1276
  def update_micro_action_status(self, completed_micro_action):
1277
  if completed_micro_action:
1278
  self.micro_actions[-1].status = "COMPLETE"
1279
+ self.micro_actions[-1].updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
1280
 
1281
  num_of_micro_actions_completed = sum(1 for item in self.micro_actions if item.status == 'COMPLETE')
1282
 
1283
  if (num_of_micro_actions_completed in (1,3,5)) or (num_of_micro_actions_completed % 10 == 0 and num_of_micro_actions_completed != 0):
1284
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = f"Completing the {num_of_micro_actions_completed}-th micro-action")
1285
+ self.add_recent_wins(wins = "You have completed a micro action!", context= self.micro_actions[-1]['content'])
1286
 
1287
  def trigger_deep_reflection_point(self, area_of_deep_reflection):
1288
  if len(area_of_deep_reflection)>0:
1289
  for area in area_of_deep_reflection:
1290
  self.add_life_score_point(variable = area, points_added = 5, notes = f"Doing a deep reflection about {area}")
1291
+ self.add_recent_wins(wins = f"You have done a deep reflection about your {area}!", context = 'Deep reflection')
1292
 
1293
  def add_point_for_booking(self):
1294
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Booking a GG session")
1295
+ self.add_recent_wins(wins = "You have booked a Growth Guide session!", context = "Growth Guide is a life coach")
1296
 
1297
  def add_point_for_completing_session(self):
1298
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 20, notes = "Completing a GG session")
1299
+ self.add_recent_wins(wins = "You have completed a Growth Guide session!", context = "Growth Guide is a life coach")
1300
 
1301
  def build_ourcoach_report(self, overview, action_plan, gg_session_notes):
1302
  logger.info(f"Building ourcoach report", extra={"user_id": self.user_id, "endpoint": "build_ourcoach_report"})
app/utils.py CHANGED
@@ -15,7 +15,7 @@ import psycopg2
15
  from psycopg2 import sql
16
  import os
17
  from dotenv import load_dotenv
18
- import datetime
19
  import threading
20
  import pickle # Replace dill with pickle
21
  from cachetools import TTLCache
@@ -419,19 +419,21 @@ def get_user_summary(user_id):
419
 
420
  **Objective**: Guide the user on what to discuss with the Growth Guide, providing actionable advice and highlighting key areas to focus on during their session, covering the five key areas.
421
 
 
 
422
  **Format**:
423
 
424
  Structure the brief with the following sections, and output it as a JSON object with these keys:
425
 
426
- - **reflect**: Encourage the user to consider what each focus area means to them and which aspects they want to improve.
427
 
428
- - **recall_successes**: Prompt the user to think of times when they effectively managed or improved in the five key areas, and what strategies worked for them then.
429
 
430
- - **identify_challenges**: Encourage the user to be ready to discuss any obstacles they're facing in each area, and to consider possible solutions or resources that might help.
431
 
432
  - **set_goals**: Ask the user to decide what they hope to achieve from the session and how improvements in each area will impact their life.
433
 
434
- - **additional_tips**: Provide practical advice for the session, such as choosing a quiet space, having materials ready, and being open to sharing thoughts honestly.
435
 
436
  ---
437
 
@@ -534,23 +536,23 @@ def get_user_summary(user_id):
534
  "users_growth_guide_preparation_brief": [
535
  {
536
  "key": "reflect",
537
- "value": "Consider what mental well-being means to you. Which aspects of your life—such as stress management, anxiety reduction, or emotional balance—do you want to improve?"
538
  },
539
  {
540
  "key": "recall_successes",
541
- "value": "Think of times when you effectively managed stress or maintained a positive mindset. What strategies or habits helped you during those times?"
542
  },
543
  {
544
  "key": "identify_challenges",
545
- "value": "Be ready to discuss current obstacles you're facing in managing stress and anxiety. Consider any patterns or triggers you've noticed."
546
  },
547
  {
548
  "key": "set_goals",
549
- "value": "Decide what you hope to achieve from this session. How would improving your mental well-being impact your daily life and long-term goals?"
550
  },
551
  {
552
  "key": "additional_tips",
553
- "value": "Environment: Choose a quiet, comfortable space.\nMaterials: Have a notebook and pen ready to jot down insights.\nOpenness: Be prepared to share your thoughts honestly and openly."
554
  }
555
  ],
556
  "30_minute_coaching_session_script": {
@@ -983,7 +985,7 @@ def get_user_life_status(user_id):
983
  "relationship": user.relationship_score
984
  }
985
  # Get current goal
986
- current_goal = user.goal if not user.goal else user.goal[-1].content
987
  # Get life score achievements in list
988
  recent_wins = user.recent_wins
989
  # Combine everything
@@ -1163,7 +1165,7 @@ def add_growth_guide_session(user_id, session_id, coach_id, session_started_at,
1163
  INSERT INTO {table} (booking_id, coach_id, session_started_at, user_id, updated_at, gg_report, ourcoach_summary, created_at, zoom_ai_summary)
1164
  VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
1165
  """).format(table=sql.Identifier('public', 'user_notes'))
1166
- current_time = datetime.datetime.now()
1167
  cursor.execute(query, (
1168
  session_id,
1169
  coach_id,
@@ -1224,7 +1226,7 @@ def download_file_from_s3(filename, bucket):
1224
  s3_client = session.client('s3')
1225
  with open(file_path, 'wb') as f:
1226
  ## Upload to Production Folder
1227
- s3_client.download_fileobj(bucket, f"dev/users/{filename}", f)
1228
  logger.info(f"File {filename} downloaded successfully from S3", extra={'user_id': user_id, 'endpoint': function_name})
1229
  return True
1230
  except Exception as e:
@@ -1320,7 +1322,7 @@ def upload_mementos_to_db(user_id):
1320
  data.get('location', ''),
1321
  data.get('recurrence', ''),
1322
  data.get('context', ''),
1323
- datetime.datetime.now(),
1324
  pd.to_datetime(data.get('follow_up_on', ''))
1325
  ]
1326
  cursor.execute(base_query, memento_data)
@@ -1399,7 +1401,7 @@ def print_log(level, message, **kwargs):
1399
  print_log("INFO", "User logged in", user_id=123, action="login")
1400
  """
1401
  log_entry = {
1402
- "timestamp": datetime.datetime.utcnow().isoformat() + "Z",
1403
  "level": level,
1404
  "message": message,
1405
  }
 
15
  from psycopg2 import sql
16
  import os
17
  from dotenv import load_dotenv
18
+ from datetime import datetime, timezone
19
  import threading
20
  import pickle # Replace dill with pickle
21
  from cachetools import TTLCache
 
419
 
420
  **Objective**: Guide the user on what to discuss with the Growth Guide, providing actionable advice and highlighting key areas to focus on during their session, covering the five key areas.
421
 
422
+ **ALWAYS** be succinct, valuable and personalized!
423
+
424
  **Format**:
425
 
426
  Structure the brief with the following sections, and output it as a JSON object with these keys:
427
 
428
+ - **reflect**: Give a succinct, valuable and personalized advice to the user to consider what each focus area means to them and which aspects they want to improve.
429
 
430
+ - **recall_successes**: Give a succinct, valuable and personalized prompt to the user to think of times when they effectively managed or improved in the five key areas, and what strategies worked for them then.
431
 
432
+ - **identify_challenges**: Give a succinct, valuable and personalized advice to the user to be ready to discuss any obstacles they're facing in each area, and to consider possible solutions or resources that might help.
433
 
434
  - **set_goals**: Ask the user to decide what they hope to achieve from the session and how improvements in each area will impact their life.
435
 
436
+ - **additional_tips**: Give a succinct, valuable and personalized practical advice for the session, such as choosing a quiet space, having materials ready, and being open to sharing thoughts honestly.
437
 
438
  ---
439
 
 
536
  "users_growth_guide_preparation_brief": [
537
  {
538
  "key": "reflect",
539
+ "value": "⁠Reflect on what career growth means to you and the goals you’re striving for."
540
  },
541
  {
542
  "key": "recall_successes",
543
+ "value": "⁠Recall moments when you successfully navigated challenges in your career or relationships—what worked"
544
  },
545
  {
546
  "key": "identify_challenges",
547
+ "value": "⁠Think about any current obstacles and possible ways to overcome them"
548
  },
549
  {
550
  "key": "set_goals",
551
+ "value": "⁠Define what you want to achieve from this session and how those changes could impact your life."
552
  },
553
  {
554
  "key": "additional_tips",
555
+ "value": "⁠Prepare by choosing a quiet space, having a notebook ready, and being open to sharing honestly."
556
  }
557
  ],
558
  "30_minute_coaching_session_script": {
 
985
  "relationship": user.relationship_score
986
  }
987
  # Get current goal
988
+ current_goal = '' if not user.goal else user.goal[-1].content
989
  # Get life score achievements in list
990
  recent_wins = user.recent_wins
991
  # Combine everything
 
1165
  INSERT INTO {table} (booking_id, coach_id, session_started_at, user_id, updated_at, gg_report, ourcoach_summary, created_at, zoom_ai_summary)
1166
  VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
1167
  """).format(table=sql.Identifier('public', 'user_notes'))
1168
+ current_time = datetime.now(timezone.utc)
1169
  cursor.execute(query, (
1170
  session_id,
1171
  coach_id,
 
1226
  s3_client = session.client('s3')
1227
  with open(file_path, 'wb') as f:
1228
  ## Upload to Production Folder
1229
+ s3_client.download_fileobj(bucket, f"staging/users/{filename}", f)
1230
  logger.info(f"File {filename} downloaded successfully from S3", extra={'user_id': user_id, 'endpoint': function_name})
1231
  return True
1232
  except Exception as e:
 
1322
  data.get('location', ''),
1323
  data.get('recurrence', ''),
1324
  data.get('context', ''),
1325
+ datetime.now(timezone.utc),
1326
  pd.to_datetime(data.get('follow_up_on', ''))
1327
  ]
1328
  cursor.execute(base_query, memento_data)
 
1401
  print_log("INFO", "User logged in", user_id=123, action="login")
1402
  """
1403
  log_entry = {
1404
+ "timestamp": datetime.utcnow().isoformat() + "Z",
1405
  "level": level,
1406
  "message": message,
1407
  }