Spaces:
Sleeping
Sleeping
Shageenderan Sapai commited on
Commit ·
f294e41
1
Parent(s): 08467ad
Allign staging
Browse files- app/__pycache__/assistants.cpython-312.pyc +0 -0
- app/__pycache__/flows.cpython-312.pyc +0 -0
- app/__pycache__/main.cpython-312.pyc +0 -0
- app/__pycache__/user.cpython-312.pyc +0 -0
- app/__pycache__/utils.cpython-312.pyc +0 -0
- app/assistants.py +50 -17
- app/flows.py +39 -118
- app/main.py +11 -21
- app/user.py +169 -61
- app/utils.py +17 -15
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
|
|
|
|
| 217 |
|
| 218 |
-
|
|
|
|
| 219 |
|
| 220 |
-
|
| 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 |
-
-
|
| 231 |
-
|
| 232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
|
| 234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 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
|
| 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.
|
| 326 |
-
def change_assistant(
|
| 327 |
-
|
| 328 |
-
|
| 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.
|
| 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 [
|
| 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
|
| 372 |
-
s3_client.download_fileobj(bucket, 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(
|
| 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
|
| 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": "
|
| 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,
|
| 423 |
else:
|
| 424 |
self.recent_wins.pop()
|
| 425 |
-
self.recent_wins.insert(0,
|
| 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
|
| 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
|
| 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),
|
| 831 |
elif theme == "PROGRESS_REFLECTION_STATE":
|
| 832 |
-
formatted_message = PROGRESS_REFLECTION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array),
|
| 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),
|
| 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),
|
| 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),
|
| 850 |
elif theme == "FINAL_SUMMARY_STATE":
|
| 851 |
-
formatted_message = FINAL_SUMMARY_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array),
|
| 852 |
elif theme == "EDUCATION_STATE":
|
| 853 |
-
formatted_message = EDUCATION_STATE.format(self.get_current_goal(), day, len(self.growth_plan.array),
|
| 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),
|
| 857 |
-
|
| 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
|
| 1097 |
-
If it's the
|
| 1098 |
-
If it's
|
| 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**:
|
| 427 |
|
| 428 |
-
- **recall_successes**:
|
| 429 |
|
| 430 |
-
- **identify_challenges**:
|
| 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**:
|
| 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": "
|
| 538 |
},
|
| 539 |
{
|
| 540 |
"key": "recall_successes",
|
| 541 |
-
"value": "
|
| 542 |
},
|
| 543 |
{
|
| 544 |
"key": "identify_challenges",
|
| 545 |
-
"value": "
|
| 546 |
},
|
| 547 |
{
|
| 548 |
"key": "set_goals",
|
| 549 |
-
"value": "
|
| 550 |
},
|
| 551 |
{
|
| 552 |
"key": "additional_tips",
|
| 553 |
-
"value": "
|
| 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 =
|
| 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.
|
| 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"
|
| 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.
|
| 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.
|
| 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 |
}
|