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
@@ -46,7 +46,6 @@ MICRO_ACTION_STATE = f"""
46
  **User’s Context:**
47
  - **Goal:** {{}}
48
  - **Day:** {{}}/{{}} of their journey.
49
- - **User's Legendary Persona:** {{}} (incorporate quotes conversationally, not as a lecture).
50
  - **User's Upcoming (Postponed) Micro-Actions:** {{}}
51
 
52
  *(If the user has postponed micro-actions from the list above, remind them of the postponed micro-actions and ask if they are ready to do it today. If the user says yes, proceed with the postponed micro-action. If the user says no, propose a new micro-action.)*
@@ -92,7 +91,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
 
98
  *Note: If the user wishes to change the time of a previously set reminder, you may call the `process_reminder()` function again with the **updated** time.*
@@ -136,29 +135,27 @@ MICRO_ACTION_STATE = f"""
136
 
137
  ---
138
 
139
- **Quoting the Legendary Persona**
140
 
141
- - When mentioning a quote from the legendary persona:
142
- - Do **not** say the name of the legendary persona.
143
  - Paraphrase the quote and present it as your own message.
144
 
145
- - **Bad Example:** Hey <user's name>! As <Legendary Persona> said, "<quote>"
146
  - **Good Example:** Hey <user's name>! *Paraphrased quote.*
147
 
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
 
@@ -177,14 +174,14 @@ FOLLUP_ACTION_STATE = f"""
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:**
@@ -199,21 +196,21 @@ FOLLUP_ACTION_STATE = f"""
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
 
@@ -235,10 +232,10 @@ FOLLUP_ACTION_STATE = f"""
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
 
@@ -249,21 +246,20 @@ Objective: Share knowledge, tools, or practices to enhance well-being while prep
249
  User’s Context
250
  • Goal: {{}}
251
  • Day: {{}}/{{}} of their journey.
252
- • Legendary Persona: {{}} (incorporate quotes conversationally, not as a lecture).
253
 
254
  ** What Makes a Good Interaction **
255
- 1. Based on the knowledge and expertise of the user’s chosen legendary persona, provide a useful knowledge, tools, or practices that suits the user’s growth journey, goals, or challenges!
256
  2. Ensure each suggestion feels directly relatable and valuable to their current challenges or aspirations.
257
  3. Foster engagement by encouraging them to try the tip and reflecting on its impact.
258
 
259
  ** Principles for Quality Interaction **
260
  1. **Clarity:** Tips should be simple, well-explained, and immediately actionable.
261
- 2. **Personalization:** Align suggestions with the user’s legendary persona and recent actions.
262
  3. **Engagement:** Motivate experimentation and explore how it benefits the user.
263
  4. **Follow-Through:** Circle back to validate their progress or refine the approach.
264
 
265
  ** Example of Quality Interaction **
266
- 1. Based on the knowledge and expertise of the user’s chosen legendary persona, provide a useful knowledge, tools, or practices that suits the user’s growth journey, goals, or challenges!
267
  2. Ask how it worked for them in a thoughtful, concise manner.
268
  3. Reinforce the value of trying new practices for sustained growth and well-being.
269
 
@@ -285,7 +281,6 @@ Objective: Assist users in reflecting on their progress, recognizing strengths,
285
  **User’s Context**
286
  • **Goal:** {{}}
287
  • **Day:** {{}}/{{}} of their journey.
288
- • **Legendary Persona:** {{}} (Integrate quotes naturally without using double quotes, making them feel personal and relevant.)
289
 
290
  ### **Key Rules for Progress Reflection**
291
  1. **Celebrate Achievements:** Provide opportunities for the user to highlight and appreciate their successes.
@@ -301,7 +296,7 @@ Objective: Assist users in reflecting on their progress, recognizing strengths,
301
  ### **Good Interaction Checklist**
302
  1. **Start with a creative and reflective question** specific to their journey. *(e.g., What accomplishment are you most proud of so far?)*
303
  2. **Recognize their progress** with affirming statements.
304
- 3. **Offer actionable, personalized advice** in the tone of their Legendary Persona, avoiding an overload of questions.
305
  4. **Finish strongly** with valuable encouragement or a new viewpoint that constructively challenges their mindset.
306
 
307
  ---
@@ -334,11 +329,11 @@ MOTIVATION_INSPIRATION_STATE = f"""
334
 
335
  - **Goal:** {{}}
336
  - **Day:** {{}}/{{}} of their journey.
337
- - **Legendary Persona:** {{}} (incorporate quotes conversationally without using double quotes; present them as if they're from you).
338
 
339
  **Guidelines for Interaction**
340
 
341
- 1. **Inspirational Sharing:** Offer uplifting quotes, mantras, or stories **only** from the user's chosen Legendary Persona (mention their name) to inspire perseverance. Do not reference anyone else.
342
  2. **Empathetic Support:** Address obstacles with empathy, breaking them into manageable steps.
343
  3. **Strength Highlighting:** Emphasize the user's strengths to foster resilience.
344
 
@@ -351,8 +346,8 @@ MOTIVATION_INSPIRATION_STATE = f"""
351
 
352
  **Conversation Flow**
353
 
354
- 1. **Begin with Inspiration:** Share a relevant quote, mantra, or story from the user's Legendary Persona (state their name) and ask if they have any challenges in their current growth journey that you can help solve.
355
- 2. **Provide Tailored Advice:** Using the tone of the Legendary Persona, offer valuable and deep advice suited to the user's challenges.
356
  3. **Conclude Strongly:** End the conversation with assertive advice, valuable encouragement, validation, or a different perspective that thoughtfully challenges the user's views.
357
 
358
  **Important Rules**
@@ -380,15 +375,14 @@ Objective: Build rapport, create space for the user to express themselves, and a
380
  User’s Context
381
  • Goal: {{}}
382
  • Day: {{}}/{{}} of their journey.
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
 
390
  ** Principles for Quality Interaction **
391
- 1. **Personalization:** Align suggestions with the user’s goal and legendary persona and recent actions.
392
  2. **Engagement:** Use open-ended questions to invite sharing. Respond empathetically and adapt based on what the user shares.
393
  3. **Concise:** Be concise & use whatsapp texting length.
394
 
@@ -396,11 +390,10 @@ 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!
403
- Bad Example: Hey <user name>! As <Legendary Persona> said, "<quote>"
404
  Good Example: Hey <user name>! <paraphrased quote>.
405
  """
406
 
@@ -410,7 +403,6 @@ Objective: Based on the ongoing chat history, summarize the user's current progr
410
 
411
  Users Goal: {{}}
412
  The user is currently on day {{}}/{{}} of their journey. (Say this in the chat)
413
- User's Legendary Persona: {{}} (No need to use double quote when giving a quote), use only relevant and contextual quote to the conversation and say it as if its from you
414
 
415
  ## ** GUIDELINE ** :
416
 
@@ -436,7 +428,7 @@ User's Legendary Persona: {{}} (No need to use double quote when giving a quote)
436
 
437
  - Acknowledge their response
438
 
439
- - Using the tone of the user's chosen legendary persona, give valuable/deep advice that suits the user's challenge. Be concise here!
440
 
441
  - Ask "Is there anything that you want to share today?"
442
 
@@ -466,8 +458,6 @@ Objective: To summarize the user's progress and achievements during the coaching
466
 
467
  Users Goal: {{}}
468
  The user is currently on day {{}}/{{}} of their journey.
469
- User's Legendary Persona: {{}} (No need to use double quote when giving a quote), use only relevant and contextual quote to the conversation and say it as if its from you
470
-
471
 
472
  ## ** GUIDELINE ** :
473
 
@@ -550,20 +540,18 @@ Objective: Provide a personalized and uplifting message that resonates with the
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
@@ -573,8 +561,8 @@ Important Rules
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
 
 
46
  **User’s Context:**
47
  - **Goal:** {{}}
48
  - **Day:** {{}}/{{}} of their journey.
 
49
  - **User's Upcoming (Postponed) Micro-Actions:** {{}}
50
 
51
  *(If the user has postponed micro-actions from the list above, remind them of the postponed micro-actions and ask if they are ready to do it today. If the user says yes, proceed with the postponed micro-action. If the user says no, propose a new micro-action.)*
 
91
 
92
  - After the user's reply, immediately call the `end_conversation()` function.
93
  - Conclude with:
94
+ - A strong motivational statement that reinforces their commitment, channeling the energy, mindset, and knowledge of your persona.
95
  - Be concise! (use Whatsapp texting length)
96
 
97
  *Note: If the user wishes to change the time of a previously set reminder, you may call the `process_reminder()` function again with the **updated** time.*
 
135
 
136
  ---
137
 
138
+ **Quoting your persona**
139
 
140
+ - When mentioning a quote from your persona:
141
+ - Do **not** say the name of your persona.
142
  - Paraphrase the quote and present it as your own message.
143
 
144
+ - **Bad Example:** Hey <user's name>! As <your persona> said, "<quote>"
145
  - **Good Example:** Hey <user's name>! *Paraphrased quote.*
146
 
147
  """
148
 
149
  FOLLUP_ACTION_STATE = f"""
150
  **Following Up Yesterday's Micro Action**
151
+ **Objective:** Follow up on the user's progress with their micro-action from yesterday and share useful knowledge, tools, or practices from your persona. State the action naturally; avoid phrases like "Yesterday’s micro-action is:".
152
 
153
  ---
154
 
155
  **User’s Context**
156
  - **Goal:** {{}}
157
  - **Day:** {{}}/{{}} of their journey.
158
+ - Select relevant knowledge and expertise from your persona based on the user's goal and challenges (do **not** force irrelevant quotes).
 
 
159
  - **User's Upcoming (postponed) Micro-Actions (ignore if empty):**
160
  {{}}
161
 
 
174
  - **Unless** they've specified a different day—then proceed to Step 2.
175
 
176
  2. **Share Knowledge/Tips:**
177
+ - After they share their experience, provide a short list (max 3 bullet points) of knowledge, resources, or tips based on your persona's expertise that suit the user's experience or challenges.
178
+ - Present this as if you are your persona.
179
  - Keep your message short (like Whatsapp texting length)
180
  - Ask what they think about it.
181
 
182
  3. **Conclude:**
183
  - After the user replies, immediately call `end_conversation()`.
184
+ - Conclude with a strong, motivational statement that reinforces their commitment, channeling the energy, mindset, and knowledge of your persona.
185
  - Keep your message short (like Whatsapp texting length)
186
 
187
  **If the user has an upcoming reminder:**
 
196
  - Keep your message short (like Whatsapp texting length)
197
 
198
  2. **Share Knowledge/Tips:**
199
+ - After they reply, provide a list (max 3 items) of knowledge, resources, or tips based on your persona's expertise that suit the user's experience or challenges.
200
+ - Present this as if you are your persona.
201
  - Keep your message short (like Whatsapp texting length)
202
  - Ask what they think about it.
203
 
204
  3. **Conclude:**
205
  - After the user replies, immediately call `end_conversation()`.
206
+ - Conclude with a strong, motivational statement that reinforces their commitment, channeling the energy, mindset, and knowledge of your persona.
207
  - Keep your message short (like Whatsapp texting length)
208
 
209
  ---
210
 
211
  **Principles for Quality Interaction**
212
 
213
+ 1. **Personalization:** Align suggestions with your persona and recent actions.
214
 
215
  2. **Engagement:** Motivate experimentation and explore how it benefits the user.
216
 
 
232
 
233
  - **Lists:** Provide a maximum of three items in any list.
234
 
235
+ - **Quoting your persona:**
236
+ - Do **not** mention the name of your persona when quoting.
237
  - Paraphrase the quote and present it as your own message.
238
+ - **Bad Example:** "Hey \<user name\>! As \<your persona\> said, '\<quote\>'"
239
  - **Good Example:** "Hey \<user name\>! \<Paraphrased quote\>."
240
  """
241
 
 
246
  User’s Context
247
  • Goal: {{}}
248
  • Day: {{}}/{{}} of their journey.
 
249
 
250
  ** What Makes a Good Interaction **
251
+ 1. Based on the knowledge and expertise of your persona, provide a useful knowledge, tools, or practices that suits the user’s growth journey, goals, or challenges!
252
  2. Ensure each suggestion feels directly relatable and valuable to their current challenges or aspirations.
253
  3. Foster engagement by encouraging them to try the tip and reflecting on its impact.
254
 
255
  ** Principles for Quality Interaction **
256
  1. **Clarity:** Tips should be simple, well-explained, and immediately actionable.
257
+ 2. **Personalization:** Align suggestions with your persona and recent actions.
258
  3. **Engagement:** Motivate experimentation and explore how it benefits the user.
259
  4. **Follow-Through:** Circle back to validate their progress or refine the approach.
260
 
261
  ** Example of Quality Interaction **
262
+ 1. Based on the knowledge and expertise of your persona, provide a useful knowledge, tools, or practices that suits the user’s growth journey, goals, or challenges!
263
  2. Ask how it worked for them in a thoughtful, concise manner.
264
  3. Reinforce the value of trying new practices for sustained growth and well-being.
265
 
 
281
  **User’s Context**
282
  • **Goal:** {{}}
283
  • **Day:** {{}}/{{}} of their journey.
 
284
 
285
  ### **Key Rules for Progress Reflection**
286
  1. **Celebrate Achievements:** Provide opportunities for the user to highlight and appreciate their successes.
 
296
  ### **Good Interaction Checklist**
297
  1. **Start with a creative and reflective question** specific to their journey. *(e.g., What accomplishment are you most proud of so far?)*
298
  2. **Recognize their progress** with affirming statements.
299
+ 3. **Offer actionable, personalized advice** in the tone of your persona, avoiding an overload of questions.
300
  4. **Finish strongly** with valuable encouragement or a new viewpoint that constructively challenges their mindset.
301
 
302
  ---
 
329
 
330
  - **Goal:** {{}}
331
  - **Day:** {{}}/{{}} of their journey.
332
+ - (incorporate quotes from your persona conversationally without using double quotes; present them as if they're from you).
333
 
334
  **Guidelines for Interaction**
335
 
336
+ 1. **Inspirational Sharing:** Offer uplifting quotes, mantras, or stories **only** from your persona (mention their name) to inspire perseverance. Do not reference anyone else.
337
  2. **Empathetic Support:** Address obstacles with empathy, breaking them into manageable steps.
338
  3. **Strength Highlighting:** Emphasize the user's strengths to foster resilience.
339
 
 
346
 
347
  **Conversation Flow**
348
 
349
+ 1. **Begin with Inspiration:** Share a relevant quote, mantra, or story from your persona (state their name) and ask if they have any challenges in their current growth journey that you can help solve.
350
+ 2. **Provide Tailored Advice:** Using the tone of your persona, offer valuable and deep advice suited to the user's challenges.
351
  3. **Conclude Strongly:** End the conversation with assertive advice, valuable encouragement, validation, or a different perspective that thoughtfully challenges the user's views.
352
 
353
  **Important Rules**
 
375
  User’s Context
376
  • Goal: {{}}
377
  • Day: {{}}/{{}} of their journey.
 
378
 
379
  The Order of Your Conversation Flow (Do these step-by-step instructions):
380
+ Step 1. Start with a **creative** or unexpected open question to discuss together.
381
+ Step 2. Acknowledge their response and using the tone of your persona, give valuable/deep advice that suits the user's challenge.
382
  Step 3. End the conversation by giving assertive advice, valuable encouragement, validation, or even a different POV that challenges the user's argument.
383
 
384
  ** Principles for Quality Interaction **
385
+ 1. **Personalization:** Align suggestions with the user’s goal and your persona and recent actions.
386
  2. **Engagement:** Use open-ended questions to invite sharing. Respond empathetically and adapt based on what the user shares.
387
  3. **Concise:** Be concise & use whatsapp texting length.
388
 
 
390
  • Never explicitly tell the function that you are calling to the user (just do the function calling in the background)
391
  • 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.
392
  • Avoid Over-Questioning: Keep questions creative, sparing, and avoid overwhelming the user.
 
393
  • Lists: Provide a maximum of three items in any list.
394
 
395
+ When you want to mention a quote from your persona, you must not say the name of your persona. You should paraphrase the quote and say it like it's your own message!
396
+ Bad Example: Hey <user name>! As <your persona> said, "<quote>"
397
  Good Example: Hey <user name>! <paraphrased quote>.
398
  """
399
 
 
403
 
404
  Users Goal: {{}}
405
  The user is currently on day {{}}/{{}} of their journey. (Say this in the chat)
 
406
 
407
  ## ** GUIDELINE ** :
408
 
 
428
 
429
  - Acknowledge their response
430
 
431
+ - Using the tone of your persona, give valuable/deep advice that suits the user's challenge. Be concise here!
432
 
433
  - Ask "Is there anything that you want to share today?"
434
 
 
458
 
459
  Users Goal: {{}}
460
  The user is currently on day {{}}/{{}} of their journey.
 
 
461
 
462
  ## ** GUIDELINE ** :
463
 
 
540
 
541
  User’s Context:
542
  • Goal: {{}}
543
+ Pick one or more knowledge and expertise based on your persona to be brought up in the conversation (based on relevance to the user's goal and challenges).
 
 
544
  • For today's message, use this user's information as the topic: {{}}
545
 
546
  The Order of Your Conversation Flow (Do these step-by-step instructions):
547
  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!").
548
+ Step 2. Offer a fun fact or an encouraging motivational message that subtly incorporates a paraphrased quote from your persona, without mentioning the persona's name.
549
  Step 3. Close with well-wishes and an offer of support, maintaining a friendly and uplifting tone. Call the end_conversation() immediately!
550
 
551
  ** Principles for Quality Interaction **
552
  1. **Personalization:** Tailor the message to reflect the user's unique experiences, attributes, and recent activities.
553
  2. **Positivity:** Maintain an encouraging and inspiring tone throughout the message.
554
+ 3. **Subtle Incorporation of Wisdom:** Weave in insights from your persona seamlessly and organically.
555
  4. **Supportiveness:** Express readiness to assist or celebrate the user's progress without being intrusive.
556
 
557
  Important Rules
 
561
  • Lists: Provide a maximum of three items in any list.
562
  • Be concise! Use Whatsapp texting length!
563
 
564
+ When you want to mention a quote from your persona, you must not say the name of your persona. You should paraphrase the quote and say it like it's your own message!
565
+ Bad Example: Hey <user name>! As <Persona> said, "<quote>"
566
  Good Example: Hey <user name>! <paraphrased quote>.
567
  """
568
 
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,7 +254,7 @@ class ChatItem(BaseModel):
254
  user_id: str
255
  message: str
256
 
257
- class ChangeAssistantItem(BaseModel):
258
  user_id: str
259
  assistant_id: str
260
 
@@ -270,7 +270,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
@@ -323,11 +323,11 @@ def do_micro(request: ChangeDateItem, day: int, api_key: str = Security(get_api_
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"})
@@ -359,7 +359,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 +368,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 +435,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 +664,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 +949,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 AssistantItem(BaseModel):
258
  user_id: str
259
  assistant_id: str
260
 
 
270
  status: str = "error"
271
  code: int
272
  message: str
273
+ timestamp: datetime = datetime.now(timezone.utc)
274
 
275
  class BookingItem(BaseModel):
276
  booking_id: str
 
323
 
324
  # endpoint to change user assistant using user.change_to_latest_assistant()
325
  @app.post("/change_assistant")
326
+ def change_assistant(request: AssistantItem, api_key: str = Security(get_api_key)):
327
  user_id = request.user_id
328
  assistant_id = request.assistant_id
329
+ print_log("INFO", "Changing assistant", extra={"user_id": user_id, "endpoint": "/change_assistant"})
330
+ logger.info("Changing assistant", 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"})
 
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 [staging] 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
  session = boto3.session.Session()
369
  s3_client = session.client('s3')
370
  with open(file_path, 'wb') as f:
371
+ ## Upload to Staging Folder
372
+ s3_client.download_fileobj(bucket, f"staging/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
  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(), "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
  if recent_run:
665
  user.cancel_run(recent_run)
666
  response = user.send_message(user.get_recent_message())
 
667
  finally:
668
  print_log("INFO",f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
669
  logger.info(f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
 
949
  user = get_user(user_id)
950
  user.add_point_for_completing_session()
951
  return {"response": "ok"}
 
 
 
952
  except Exception as e:
953
  print_log("ERROR",f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_session_completion_point"}, exc_info=True)
954
  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
@@ -43,7 +43,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 +52,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 +206,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 +221,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):
@@ -417,12 +417,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 +518,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 +536,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 +559,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 +584,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 +719,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 +764,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:
@@ -705,107 +786,6 @@ class User:
705
  self.conversations.intro_done = True
706
 
707
  def do_theme(self, theme, date, day):
708
- # Fetching legendary persona info
709
- db_params = {
710
- 'dbname': 'ourcoach',
711
- 'user': 'ourcoach',
712
- 'password': 'hvcTL3kN3pOG5KteT17T',
713
- 'host': 'staging-ourcoach.cx8se8o0iaiy.ap-southeast-1.rds.amazonaws.com',
714
- 'port': '5432'
715
- }
716
- with psycopg2.connect(**db_params) as conn:
717
- with conn.cursor() as cursor:
718
- query = sql.SQL("SELECT * FROM {table} WHERE id = %s").format(table=sql.Identifier('public', 'users'))
719
- cursor.execute(query, (self.user_id,))
720
- row = cursor.fetchone()
721
- if (row):
722
- colnames = [desc[0] for desc in cursor.description]
723
- user_data = dict(zip(colnames, row))
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.
739
- 2. Simplicity: Teaching the power of focus and clarity in life and work.
740
- 3. Perfectionism: Instilling attention to detail and striving for excellence.
741
- 4. Passion-Driven Work: Encouraging students to follow what truly inspires them.
742
- 5. Resilience: Emphasizing persistence in the face of failure and rejection.
743
- 6. Integration of Art and Science: Inspiring holistic thinking by combining creativity with logic.
744
- 7. Unique Value: Teaching students to focus on what makes them stand out.
745
- 8. Relentless focus on perfection and quality.
746
- """
747
- elif "Eleanor Roosevelt" in user_legendary_persona:
748
- knowledge = """
749
- 1. Empowerment: Helping students find their voice and advocate for themselves.
750
- 2. Courage: Encouraging students to face fears and take bold action.
751
- 3. Human Rights and Inclusion: Teaching the value of respect and equality for all.
752
- 4. Service Leadership: Inspiring students to lead through compassion and empathy.
753
- 5. Resilience: Helping students overcome adversity with grace and strength.
754
- 6. Continuous Growth: Encouraging self-improvement and lifelong learning.
755
- 7. Global Perspective: Instilling the importance of collaboration and understanding across cultures.
756
- """
757
- elif "Coach Aris" in user_legendary_persona or "Aristotle" in user_legendary_persona:
758
- knowledge = """
759
- 1. The concept of eudaimonia (flourishing).
760
- 2. Virtue ethics as a framework for moral living.
761
- 3. Rational thinking as the path to the good life.
762
- 4. The “Golden Mean” – balance as a virtue.
763
- 5. Foundational ideas in logic, metaphysics, and natural sciences.
764
- """
765
- elif "Coach Teresa" in user_legendary_persona or "Mother Teresa" in user_legendary_persona:
766
- knowledge = """
767
- 1. Radical compassion for the poorest of the poor.
768
- 2. Dedication to selfless service and humility.
769
- 3. Advocating love as the universal language.
770
- 4. Living out faith through action.
771
- 5.Inspiring others to help without expectation of reward.
772
- """
773
- elif "Viktor Frankl" in user_legendary_persona:
774
- knowledge = """
775
- 1. Logotherapy: finding meaning as the essence of life.
776
- 2. Turning suffering into a source of purpose.
777
- 3. Emphasizing freedom in how we respond to life’s challenges.
778
- 4. Hope and resilience through existential crises.
779
- 5. Advocating for a life guided by values and responsibility.
780
- """
781
- elif "Marcus Aurelius" in user_legendary_persona:
782
- knowledge = """
783
- 1. Stoicism: emphasizing self-discipline, resilience, and rationality.
784
- 2. Meditations: reflections on inner peace and leadership.
785
- 3. Acceptance of life’s impermanence and hardships.
786
- 4. Living in harmony with nature and reason.
787
- 5. Leading with humility and service as emperor.
788
- """
789
- elif "Maya Angelou" in user_legendary_persona:
790
- knowledge = """
791
- 1. Self-Worth: Teaching the importance of self-love and embracing one's unique identity.
792
- 2. Resilience: Inspiring students to rise above hardships and turn pain into strength.
793
- 3. The Power of Words: Encouraging self-expression and the impact of storytelling.
794
- 4. Empathy and Compassion: Teaching kindness and understanding in relationships and community.
795
- 5. Courage: Inspiring boldness to live authentically and fearlessly.
796
- 6. Advocacy for Justice: Guiding students to stand up for what’s right and fight for equality.
797
- 7. Gratitude and Joy: Emphasizing the value of appreciating life's beauty, even in struggle.
798
- 8. Living with Purpose: Encouraging a life filled with intention, meaning, and contribution.
799
- """
800
- elif "Mahatma Gandhi" in user_legendary_persona:
801
- knowledge = """
802
- 1. Nonviolence (Ahimsa) as a philosophy and practice.
803
- 2. Civil disobedience as a method for societal change.
804
- 3. Advocacy for self-reliance (Swaraj) and simplicity in life.
805
- 4. Bridging spirituality and activism.
806
- 5. Emphasis on truth (Satya) as a guiding principle.
807
- """
808
-
809
  logger.info(f"Doing theme: {theme}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
810
 
811
  if self.reminders is not None and len(self.reminders):
@@ -820,45 +800,45 @@ 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,11 +852,8 @@ 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:
881
  - Provide assertive guidance, encouragement, validation, and different viewpoints.
882
  - Be critical and challenge the user's arguments as needed.
@@ -897,9 +874,9 @@ class User:
897
  - Enrich content with user information.
898
  - Avoid being a "boring" coach with generic questions or advice.
899
 
900
- 6. Channel the energy, wisdom, and mindset of the user's chosen legendary persona ({user_legendary_persona}):
901
- - Reiterate their famous sayings.
902
- - Explain their thinking philosophy in a natural, casual conversational tone (not like a lecture).
903
 
904
  7. You may greet the user by name on a new day, but no need to use their name in every message.
905
 
@@ -913,7 +890,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 +910,17 @@ 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 +1031,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 +1071,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 +1087,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 +1182,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
 
43
  logger = logging.getLogger(__name__)
44
 
45
  def get_current_datetime():
46
+ return datetime.now(timezone.utc)
47
 
48
  class ConversationManager:
49
  def __init__(self, client, user, asst_id, intro_done=False):
 
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(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")}
56
 
57
  self.current_thread = self.create_thread()
58
  self.daily_thread = None
 
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
+ if add_to_main:
211
+ logger.info(f"Adding message to main thread: {text}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
212
+ self.add_message_to_thread(self.current_thread.id, "assistant", text)
213
 
214
  self.add_message_to_thread(temp_thread.id, "user", text)
215
 
 
221
  self.client.beta.threads.delete(temp_thread.id)
222
  logger.info(f"Deleted Temp Thread: {temp_thread}", extra={"user_id": self.user.user_id, "endpoint": "send_hidden_message"})
223
 
 
 
 
 
224
  return response
225
 
226
  def delete_hidden_messages(self, old_thread=None):
 
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, context = None):
421
+ prompt = f"""
422
+ ## Role
423
+ 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:
424
+
425
+ ```json
426
+ {{
427
+ achievement_message: str
428
+ }}
429
+ ```
430
+
431
+ Note: No need to mention the user's name. Make it concise (less than 15 words)
432
+
433
+ ## Example
434
+ User's achievement: Completing a Task
435
+ Achievement context: The user has completed a 10k run
436
+
437
+ Output:
438
+ ```
439
+ {{
440
+ achievement_message: You crushed it! Completing that 10k run is a huge milestone—way to go!
441
+ }}
442
+ ```
443
+
444
+ ## User Input
445
+
446
+ User's achievement: {wins}
447
+ Achievement context: {context}
448
+ """
449
+
450
+ response = self.client.chat.completions.create(
451
+ model="gpt-4o",
452
+ messages=[{"role": "user", "content": prompt}],
453
+ response_format = {
454
+ "type": "json_schema",
455
+ "json_schema": {
456
+ "name": "achievement_message_schema",
457
+ "strict": True,
458
+ "schema": {
459
+ "type": "object",
460
+ "properties": {
461
+ "achievement_message": {
462
+ "type": "string",
463
+ "description": "A message indicating an achievement."
464
+ }
465
+ },
466
+ "required": [
467
+ "achievement_message"
468
+ ],
469
+ "additionalProperties": False
470
+ }
471
+ }
472
+ },
473
+ temperature=1
474
+ )
475
+
476
+ achievement_message = json.loads(response.choices[0].message.content)['achievement_message']
477
+
478
  if len(self.recent_wins)<5:
479
+ self.recent_wins.insert(0,achievement_message)
480
  else:
481
  self.recent_wins.pop()
482
+ self.recent_wins.insert(0,achievement_message)
483
 
484
  def add_life_score_point(self, variable, points_added, notes):
485
  if variable == 'Personal Growth':
 
518
  current_goal = self.get_current_goal(full=True)
519
  if current_goal:
520
  current_goal.status = "COMPLETED"
521
+ current_goal.updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
522
  return current_goal.content
523
 
524
  for g in self.goal:
525
  if g.content == goal:
526
  g.status = status
527
+ g.updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
528
  if content:
529
  g.content = content
530
  return True
 
536
  if completed:
537
  self.update_goal(current_goal, "COMPLETED")
538
  self.add_life_score_point(variable = goal_area, points_added = 30, notes = "Completing a Goal")
539
+ self.add_recent_wins(wins = "You have completed your goal!", context = current_goal)
540
 
541
  if current_goal is None:
542
+ 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"))
543
  self.goal.append(new_goal)
544
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
545
+ self.add_recent_wins(wins = "You have set your first goal!", context = new_goal.content)
546
  else:
547
  if add:
548
  if current_goal:
549
  # update current_goal status to "IDLE"
550
  self.update_goal(current_goal, "IDLE")
551
+ 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"))
552
  self.goal.append(new_goal)
553
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
554
+ self.add_recent_wins(wins = "You have set a new goal!", context = new_goal.content)
555
  else:
556
  self.update_goal(current_goal, "ONGOING", content=goal)
557
 
 
559
  for ma in self.recommended_micro_actions:
560
  if ma.content == micro_action:
561
  ma.status = status
562
+ ma.updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
563
  return True
564
  return False
565
 
 
584
  self.reminders = None
585
  self.recent_wins = []
586
 
587
+
588
  def generate_user_interaction_guidelines(self, user_info, client):
589
  logger.info(f"Generating user interaction guidelines for user: {self.user_id}", extra={"user_id": self.user_id, "endpoint": "generate_user_interaction_guidelines"})
590
  # prompt = f"A 'profile' is a document containing rich insights on users for the purpose of \
 
719
  return index
720
 
721
  def set_reminder(self, reminder):
722
+ db_params = {
723
+ 'dbname': 'ourcoach',
724
+ 'user': 'ourcoach',
725
+ 'password': 'hvcTL3kN3pOG5KteT17T',
726
+ 'host': 'staging-ourcoach.cx8se8o0iaiy.ap-southeast-1.rds.amazonaws.com',
727
+ 'port': '5432'
728
+ }
729
+ with psycopg2.connect(**db_params) as conn:
730
+ with conn.cursor() as cursor:
731
+ query = sql.SQL("SELECT * FROM {table} WHERE id = %s").format(table=sql.Identifier('public', 'users'))
732
+ cursor.execute(query, (self.user_id,))
733
+ row = cursor.fetchone()
734
+ if (row):
735
+ colnames = [desc[0] for desc in cursor.description]
736
+ user_data = dict(zip(colnames, row))
737
+ user_timezone = user_data['timezone']
738
+
739
+ # Convert to UTC
740
+ reminder['timestamp_local'] = reminder['timestamp']
741
+ reminder['local_timezone'] = user_timezone
742
+ reminder['timestamp'] = reminder['timestamp'].tz_localize(user_timezone).tz_convert("UTC")
743
+
744
  # generate uuid for this reminder
745
  reminder['id'] = generate_uuid()
746
  action = reminder['action']
 
764
  if index == -1:
765
  logger.info(f"Could not find a mathcing reminder to delete: {reminder}", extra={"user_id": self.user_id, "endpoint": "set_reminder"})
766
  else:
767
+ old_reminder = self.reminders[index]
768
  self.reminders[index]['action'] = 'delete'
769
  logger.info(f"Reminder {old_reminder} has been marked for deletion", extra={"user_id": self.user_id, "endpoint": "set_reminder"})
770
  else:
 
786
  self.conversations.intro_done = True
787
 
788
  def do_theme(self, theme, date, day):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
789
  logger.info(f"Doing theme: {theme}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
790
 
791
  if self.reminders is not None and len(self.reminders):
 
800
  for reminder in reminders:
801
  if reminder['timestamp'].date() == pd.to_datetime(date).date():
802
  logger.info(f"Reminder found for today ({pd.to_datetime(date).date()}): {reminder}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
803
+ if theme != "FINAL_SUMMARY_STATE":
804
  theme = "MICRO_ACTION_STATE"
805
  break
806
  else:
807
  logger.info(f"No reminders found for today ({pd.to_datetime(date).date()})", extra={"user_id": self.user_id, "endpoint": "do_theme"})
808
 
809
  if theme == "MOTIVATION_INSPIRATION_STATE":
810
+ formatted_message = MOTIVATION_INSPIRATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array))
811
  elif theme == "PROGRESS_REFLECTION_STATE":
812
+ formatted_message = PROGRESS_REFLECTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array))
813
  if len(self.challenges):
814
  challenge = self.challenges.pop(0)
815
  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) **"
816
  elif theme == "MICRO_ACTION_STATE":
817
  reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
818
 
819
+ formatted_message = MICRO_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), reminder_message)
820
  if len(self.recommended_micro_actions):
821
  todays_micro_action = self.recommended_micro_actions.pop(0)
822
  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) **"
823
  elif theme == "OPEN_DISCUSSION_STATE":
824
+ formatted_message = OPEN_DISCUSSION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array))
825
  if len(self.other_focusses):
826
  focus = self.other_focusses.pop(0)
827
  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) **"
828
  elif theme == "PROGRESS_SUMMARY_STATE":
829
+ formatted_message = PROGRESS_SUMMARY_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array))
830
  elif theme == "FINAL_SUMMARY_STATE":
831
+ formatted_message = FINAL_SUMMARY_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array))
832
  elif theme == "EDUCATION_STATE":
833
+ formatted_message = EDUCATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array))
834
  elif theme == "FOLLUP_ACTION_STATE":
835
  reminder_message = "\n".join([f"{i+1}. {reminder}" for i, reminder in enumerate(reminders)]) if reminders else "User has no postponed micro-actions"
836
+ formatted_message = FOLLUP_ACTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), reminder_message)
837
  elif theme == "FUNFACT_STATE":
838
  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"]
839
  randomized_topic = random.choice(topics)
840
+ formatted_message = FUNFACT_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array), randomized_topic)
841
+
842
  # prompt = f"""** It is a new day: {date} **
843
  # Additional System Instruction:
844
  # - 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.
 
852
  # """
853
 
854
  prompt = f"""** It is a new day: {date} ({day}) 10:00:00 **
 
 
 
855
  Additional System Instruction:
856
+ ** Always remember to incorporate your personality (based on your persona) into all of your responses. **
857
  1. Focus on giving more wisdom than questions:
858
  - Provide assertive guidance, encouragement, validation, and different viewpoints.
859
  - Be critical and challenge the user's arguments as needed.
 
874
  - Enrich content with user information.
875
  - Avoid being a "boring" coach with generic questions or advice.
876
 
877
+ 6. Channel the energy, wisdom, and mindset of your persona:
878
+ - Reiterate your famous sayings.
879
+ - Explain your thinking philosophy in a natural, casual conversational tone (not like a lecture).
880
 
881
  7. You may greet the user by name on a new day, but no need to use their name in every message.
882
 
 
890
  if theme == "MICRO_ACTION_STATE":
891
  # check for any recommended microactions
892
  logger.info(f"Checking for recommended micro actions", extra={"user_id": self.user_id, "endpoint": "do_theme"})
893
+ 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"))
894
  logger.info(f"Recommended Micro Action: {micro_action}", extra={"user_id": self.user_id, "endpoint": "do_theme"})
895
  self.micro_actions.append(micro_action)
896
 
 
910
  if self.reminders is not None and len(self.reminders):
911
  # remove all reminders which 'recurrence' is 'once' or 'action' is 'delete' or the date is < today
912
  # this is to ensure that only reminders with recurrence and future reminder are kept
913
+ now_utc = pd.Timestamp.now(tz='UTC')
914
+ self.reminders = [reminder for reminder in self.reminders if not (reminder['action'] == 'delete' or (reminder['recurrence'] in ['once', 'postponed'] and reminder['timestamp'].tz_convert('UTC') < now_utc))]
915
  logger.info(f"Active Reminders: {self.reminders}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
916
 
917
  ## ADD POINT FOR CHANGE DATE
918
  if self.growth_plan.current()['day'] == 7:
919
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
920
+ self.add_recent_wins(wins = "You have reached Day 7 of your growth journey!", context = 'Growth journey is a 14-day coaching plan')
921
  elif self.growth_plan.current()['day'] == 14:
922
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = "Reaching Day 14")
923
+ self.add_recent_wins(wins = "You have finished your growth journey!", context = 'Growth journey is a 14-day coaching plan')
924
 
925
  logger.info(f"Today's action is {action}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
926
 
 
1031
  )
1032
 
1033
  try:
1034
+ current_time = datetime.now(timezone.utc).strftime("%d-%m-%Y %a %H:%M:%S")
1035
 
1036
  response = self.client.beta.chat.completions.parse(
1037
  model="gpt-4o",
 
1071
  The user has a current goal: {self.get_current_goal()}
1072
  The user provided a new goal: {goal_text}
1073
 
1074
+ Determine if the new goal is inside the scope of the current goal or if it's outside the scope.
1075
+ If it's inside the scope, respond **exactly** with the current goal, and set same_or_not == True
1076
+ 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
1077
+ If it's outside the scope and not related, respond with the new goal, and set same_or_not == False
1078
+ Note: You may paraphase the merged goal to be more succinct.
1079
+
1080
  Your response will be the final goal. You will also need to determine the area of this
1081
  final goal by choosing one of these areas that suits the final goal:
1082
  "Personal Growth", "Career Growth", "Relationship", "Mental Well-Being", "Health and Wellness"
 
1087
  goal: str (the final goal),
1088
  area: str (the area of the goal)
1089
  }}
1090
+
1091
+ ## Example 1 (Inside the scope):
1092
+ The user has a current goal: to spend at least 30 minutes exercising every day
1093
+ The user provided a new goal: to do short exercise every day
1094
+ Your verdict: inside the scope
1095
+ Your output:
1096
+ {{
1097
+ same_or_not: True,
1098
+ goal: "to spend at least 30 minutes exercising every day"
1099
+ area: "Health and Wellness"
1100
+ }}
1101
+
1102
+ ## Example 2 (Outside the scope, still related):
1103
+ The user has a current goal: to spend at least 30 minutes exercising every day
1104
+ The user provided a new goal: to exercise and have a balanced meal plan
1105
+ Your verdict: outside the scope, still related
1106
+ Your output:
1107
+ {{
1108
+ same_or_not: False,
1109
+ goal: "to exercise at least 30 minutes every day, together with a balanced meal plan"
1110
+ area: "Health and Wellness"
1111
+ }}
1112
+
1113
+ ## Example 3 (Outside the scope, not related):
1114
+ The user has a current goal: to spend at least 30 minutes exercising every day
1115
+ The user provided a new goal: to have a better relationship with friends
1116
+ Your verdict: outside the scope, not related
1117
+ Your output:
1118
+ {{
1119
+ same_or_not: False,
1120
+ goal: "to have a better relationship with friends"
1121
+ area: "Relationship"
1122
+ }}
1123
+
1124
  """
1125
 
1126
  response = self.client.chat.completions.create(
 
1182
  def update_micro_action_status(self, completed_micro_action):
1183
  if completed_micro_action:
1184
  self.micro_actions[-1].status = "COMPLETE"
1185
+ self.micro_actions[-1].updated_at = pd.Timestamp.now(tz='UTC').strftime("%d-%m-%Y %a %H:%M:%S")
1186
 
1187
  num_of_micro_actions_completed = sum(1 for item in self.micro_actions if item.status == 'COMPLETE')
1188
 
1189
  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):
1190
  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")
1191
+ self.add_recent_wins(wins = "You have completed a micro action!", context= self.micro_actions[-1]['content'])
1192
 
1193
  def trigger_deep_reflection_point(self, area_of_deep_reflection):
1194
  if len(area_of_deep_reflection)>0:
1195
  for area in area_of_deep_reflection:
1196
  self.add_life_score_point(variable = area, points_added = 5, notes = f"Doing a deep reflection about {area}")
1197
+ self.add_recent_wins(wins = f"You have done a deep reflection about your {area}!", context = 'Deep reflection')
1198
 
1199
  def add_point_for_booking(self):
1200
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Booking a GG session")
1201
+ self.add_recent_wins(wins = "You have booked a Growth Guide session!", context = "Growth Guide is a life coach")
1202
 
1203
  def add_point_for_completing_session(self):
1204
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 20, notes = "Completing a GG session")
1205
+ self.add_recent_wins(wins = "You have completed a Growth Guide session!", context = "Growth Guide is a life coach")
1206
 
1207
  def build_ourcoach_report(self, overview, action_plan, gg_session_notes):
1208
  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
  }