matthew.farant commited on
Commit
a5b7be2
·
1 Parent(s): 98addbb

Add life status endpoint

Browse files
Files changed (2) hide show
  1. app/main.py +25 -1
  2. app/utils.py +241 -0
app/main.py CHANGED
@@ -15,7 +15,7 @@ from openai import OpenAI
15
  import psycopg2
16
  from psycopg2 import sql
17
  import os
18
- from app.utils import get_api_key, get_user_info, print_log, update_user, upload_file_to_s3, get_user, upload_mementos_to_db, get_user_summary
19
  from dotenv import load_dotenv
20
  import logging.config
21
  import time
@@ -574,6 +574,30 @@ def get_summary_by_id(user_id: str, api_key: str = Security(get_api_key)):
574
  except Exception as e:
575
  print_log("ERROR",f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_user_summary"}, exc_info=True)
576
  logger.error(f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_user_summary"}, exc_info=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
  raise HTTPException(
578
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
579
  detail=str(e)
 
15
  import psycopg2
16
  from psycopg2 import sql
17
  import os
18
+ from app.utils import get_api_key, get_user_info, print_log, update_user, upload_file_to_s3, get_user, upload_mementos_to_db, get_user_summary, get_user_life_status
19
  from dotenv import load_dotenv
20
  import logging.config
21
  import time
 
574
  except Exception as e:
575
  print_log("ERROR",f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_user_summary"}, exc_info=True)
576
  logger.error(f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_user_summary"}, exc_info=True)
577
+ raise HTTPException(
578
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
579
+ detail=str(e)
580
+ )
581
+
582
+ @app.get("/get_life_status")
583
+ def get_life_status_by_id(user_id: str, api_key: str = Security(get_api_key)):
584
+ print_log("INFO", "Getting user's life status", extra={"user_id": user_id, "endpoint": "/get_life_status"})
585
+ logger.info("Getting user's life status", extra={"user_id": user_id, "endpoint": "/get_life_status"})
586
+ try:
587
+ life_status = get_user_life_status(user_id)
588
+ print_log("INFO", "Successfully generated life status", extra={"user_id": user_id, "endpoint": "/get_life_status"})
589
+ logger.info("Successfully generated life status", extra={"user_id": user_id, "endpoint": "/get_life_status"})
590
+ return life_status
591
+ except LookupError:
592
+ print_log("ERROR", "User not found", extra={"user_id": user_id, "endpoint": "/get_life_status"})
593
+ logger.error("User not found", extra={"user_id": user_id, "endpoint": "/get_life_status"})
594
+ raise HTTPException(
595
+ status_code=status.HTTP_404_NOT_FOUND,
596
+ detail=f"User with ID {user_id} not found"
597
+ )
598
+ except Exception as e:
599
+ print_log("ERROR",f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_life_status"}, exc_info=True)
600
+ logger.error(f"Error getting user: {str(e)}", extra={"user_id": user_id, "endpoint": "/get_life_status"}, exc_info=True)
601
  raise HTTPException(
602
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
603
  detail=str(e)
app/utils.py CHANGED
@@ -598,6 +598,247 @@ def get_user_summary(user_id):
598
  logger.info(f"User summary generated successfully for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
599
  return reports
600
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
  def get_api_key(api_key_header: str = Security(api_key_header)) -> str:
602
  if (api_key_header in api_keys):
603
  return api_key_header
 
598
  logger.info(f"User summary generated successfully for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
599
  return reports
600
 
601
+ def get_user_life_status(user_id):
602
+ function_name = get_user_life_status.__name__
603
+ logger.info(f"Generating user life status for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
604
+
605
+ # Step 1: Call get_user to get user's info
606
+ try:
607
+ user = get_user(user_id)
608
+ user_info = user.user_info
609
+ user_messages = user.get_messages()
610
+ except LookupError as e:
611
+ logger.error(f"Error fetching user data: {e}", extra={'user_id': user_id, 'endpoint': function_name})
612
+ raise e
613
+
614
+ # Step 2: Construct the Prompt
615
+ chat_history = "\n".join(
616
+ [f"{message['role'].capitalize()}: {message['content']}" for message in user_messages]
617
+ )
618
+
619
+ # Build the system prompt according to the provided instructions
620
+ system_prompt = """
621
+ You are an AI assistant that generates a personalized life status report for users based on their profile and chat history. Your task is to analyze the provided user data and produce a JSON output following the specified schema.
622
+
623
+ **Instructions:**
624
+
625
+ 1. **Life Score:**
626
+ - Calculate numerical scores for each of the following areas based on the user's profile and chat history, especially their responses to quantitative questions (e.g., "on a scale of 1 to 5, how do you feel about ..."):
627
+ - `personal_growth`
628
+ - `career_growth`
629
+ - `mental_well_being`
630
+ - `health_and_wellness`
631
+ - `relationship`
632
+ - Each score should be a float between 0 and 1. If an area hasn't been covered in the chat history, assign it a score of 0.
633
+ - Compute the `overall` score as the average of the non-zero area scores.
634
+
635
+ 2. **Mantra of the Week:**
636
+ - Create a very short encouragement quote that encapsulates the user's journey toward achieving their goals.
637
+ - The mantra should be a single sentence with fewer than 10 words.
638
+
639
+ 3. **This Week's Focus:**
640
+ - Identify the top three most important areas for the user from the five key areas:
641
+ - `personal_growth`
642
+ - `career_growth`
643
+ - `mental_well_being`
644
+ - `health_and_wellness`
645
+ - `relationship`
646
+ - For each selected area, provide a concise statement outlining the user's focus in that area.
647
+
648
+ 4. **Suggested Action Items:**
649
+ - For the same three areas identified above, suggest concrete and actionable items that the user can undertake to make progress.
650
+
651
+ **Output Format:**
652
+
653
+ Produce your response in JSON format adhering to the following schema:
654
+
655
+ ```json
656
+ {
657
+ "life_score": {
658
+ "overall": float,
659
+ "personal_growth": float,
660
+ "health_and_wellness": float,
661
+ "mental_well_being": float,
662
+ "career_growth": float,
663
+ "relationship": float
664
+ },
665
+ "mantra_of_the_week": str,
666
+ "this_week_focus": [
667
+ {"area": str, "focus": str},
668
+ {"area": str, "focus": str},
669
+ {"area": str, "focus": str}
670
+ ],
671
+ "action_items": [
672
+ {"area": str, "focus": str},
673
+ {"area": str, "focus": str},
674
+ {"area": str, "focus": str}
675
+ ]
676
+ }
677
+ ```
678
+
679
+ **Guidelines:**
680
+
681
+ - Ensure all numerical values are floats (e.g., 3.0 instead of 3).
682
+ - The `mantra_of_the_week` should be personalized, positive, and encouraging.
683
+ - Focus statements and action items should be clear, specific, and tailored to the user's context.
684
+ - Do not include any additional text or commentary outside of the JSON structure.
685
+ """
686
+
687
+ # Combine user information and chat history for context
688
+ user_context = f"""
689
+ Based on the following user profile and chat history, generate the life status!
690
+
691
+ ### USER PROFILE ###
692
+ {user_info}
693
+
694
+ ### CHAT HISTORY ###
695
+ {chat_history}
696
+ """
697
+
698
+ # Step 3: Call the OpenAI API using the specified function
699
+ client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
700
+ try:
701
+ response = client.chat.completions.create(
702
+ model="gpt-4o-mini",
703
+ messages=[
704
+ {
705
+ "role": "system",
706
+ "content": [
707
+ {
708
+ "type": "text",
709
+ "text": system_prompt
710
+ }
711
+ ]
712
+ },
713
+ {
714
+ "role": "user",
715
+ "content": [
716
+ {
717
+ "type": "text",
718
+ "text": user_context
719
+ }
720
+ ]
721
+ }
722
+ ],
723
+ response_format={
724
+ "type": "json_schema",
725
+ "json_schema": {
726
+ "name": "life_status_report",
727
+ "strict": True,
728
+ "schema": {
729
+ "type": "object",
730
+ "properties": {
731
+ "life_score": {
732
+ "type": "object",
733
+ "description": "Numerical life scores across different areas.",
734
+ "properties": {
735
+ "overall": {
736
+ "type": "number",
737
+ "description": "Overall life score, average of all area scores."
738
+ },
739
+ "personal_growth": {
740
+ "type": "number",
741
+ "description": "Life score for personal growth."
742
+ },
743
+ "health_and_wellness": {
744
+ "type": "number",
745
+ "description": "Life score for health and wellness."
746
+ },
747
+ "mental_well_being": {
748
+ "type": "number",
749
+ "description": "Life score for mental well-being."
750
+ },
751
+ "career_growth": {
752
+ "type": "number",
753
+ "description": "Life score for career growth."
754
+ },
755
+ "relationship": {
756
+ "type": "number",
757
+ "description": "Life score for relationships."
758
+ }
759
+ },
760
+ "required": [
761
+ "overall",
762
+ "personal_growth",
763
+ "health_and_wellness",
764
+ "mental_well_being",
765
+ "career_growth",
766
+ "relationship"
767
+ ],
768
+ "additionalProperties": False
769
+ },
770
+ "mantra_of_the_week": {
771
+ "type": "string",
772
+ "description": "A very short encouragement quote that encapsulates the user's journey to achieve their goals."
773
+ },
774
+ "this_week_focus": {
775
+ "type": "array",
776
+ "description": "List of the user's top 3 most important areas with focus.",
777
+ "items": {
778
+ "type": "object",
779
+ "properties": {
780
+ "area": {
781
+ "type": "string",
782
+ "description": "The area of focus (e.g., 'personal_growth')."
783
+ },
784
+ "focus": {
785
+ "type": "string",
786
+ "description": "The focus within that area."
787
+ }
788
+ },
789
+ "required": ["area", "focus"],
790
+ "additionalProperties": False
791
+ }
792
+ },
793
+ "action_items": {
794
+ "type": "array",
795
+ "description": "List of suggested concrete action items for the user.",
796
+ "items": {
797
+ "type": "object",
798
+ "properties": {
799
+ "area": {
800
+ "type": "string",
801
+ "description": "The area of action (e.g., 'personal_growth')."
802
+ },
803
+ "focus": {
804
+ "type": "string",
805
+ "description": "Concrete action items in that area."
806
+ }
807
+ },
808
+ "required": ["area", "focus"],
809
+ "additionalProperties": False
810
+ }
811
+ }
812
+ },
813
+ "required": [
814
+ "life_score",
815
+ "mantra_of_the_week",
816
+ "this_week_focus",
817
+ "action_items"
818
+ ],
819
+ "additionalProperties": False
820
+ }
821
+ }
822
+ }
823
+ ,
824
+ temperature=0.5,
825
+ max_tokens=3000,
826
+ top_p=1,
827
+ frequency_penalty=0,
828
+ presence_penalty=0
829
+ )
830
+
831
+ # Get response and convert into dictionary
832
+ reports = json.loads(response.choices[0].message.content)
833
+
834
+ except Exception as e:
835
+ logger.error(f"OpenAI API call failed: {e}", extra={'user_id': user_id, 'endpoint': function_name})
836
+ raise e
837
+
838
+ # Step 4: Return the JSON reports
839
+ logger.info(f"User life status generated successfully for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
840
+ return reports
841
+
842
  def get_api_key(api_key_header: str = Security(api_key_header)) -> str:
843
  if (api_key_header in api_keys):
844
  return api_key_header