Spaces:
Runtime error
Runtime error
Greg Thompson
Updated v2_conversation_manager to handle message_package from data-driven quiz
2887020
| import base64 | |
| import copy | |
| import dill | |
| import os | |
| import json | |
| import jsonpickle | |
| import pickle | |
| import random | |
| import requests | |
| import mathtext_fastapi.global_state_manager as gsm | |
| from dotenv import load_dotenv | |
| from mathtext_fastapi.nlu import evaluate_message_with_nlu | |
| from mathtext_fastapi.math_quiz_fsm import MathQuizFSM | |
| from mathtext_fastapi.math_subtraction_fsm import MathSubtractionFSM | |
| from supabase import create_client | |
| from transitions import Machine | |
| from mathactive.generators import start_interactive_math | |
| from mathactive.hints import generate_hint | |
| from mathactive.microlessons import num_one | |
| load_dotenv() | |
| SUPA = create_client( | |
| os.environ.get('SUPABASE_URL'), | |
| os.environ.get('SUPABASE_KEY') | |
| ) | |
| def pickle_and_encode_state_machine(state_machine): | |
| dump = pickle.dumps(state_machine) | |
| dump_encoded = base64.b64encode(dump).decode('utf-8') | |
| return dump_encoded | |
| def manage_math_quiz_fsm(user_message, contact_uuid, type): | |
| fsm_check = SUPA.table('state_machines').select("*").eq( | |
| "contact_uuid", | |
| contact_uuid | |
| ).execute() | |
| # This doesn't allow for when one FSM is present and the other is empty | |
| """ | |
| 1 | |
| data=[] count=None | |
| 2 | |
| data=[{'id': 29, 'contact_uuid': 'j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09', 'addition3': None, 'subtraction': None, 'addition': | |
| - but problem is there is no subtraction , but it's assuming there's a subtration | |
| Cases | |
| - make a completely new record | |
| - update an existing record with an existing FSM | |
| - update an existing record without an existing FSM | |
| """ | |
| print("MATH QUIZ FSM ACTIVITY") | |
| print("user_message") | |
| print(user_message) | |
| # Make a completely new entry | |
| if fsm_check.data == []: | |
| if type == 'addition': | |
| math_quiz_state_machine = MathQuizFSM() | |
| else: | |
| math_quiz_state_machine = MathSubtractionFSM() | |
| messages = [math_quiz_state_machine.response_text] | |
| dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine) | |
| SUPA.table('state_machines').insert({ | |
| 'contact_uuid': contact_uuid, | |
| f'{type}': dump_encoded | |
| }).execute() | |
| # Update an existing record with a new state machine | |
| elif not fsm_check.data[0][type]: | |
| if type == 'addition': | |
| math_quiz_state_machine = MathQuizFSM() | |
| else: | |
| math_quiz_state_machine = MathSubtractionFSM() | |
| messages = [math_quiz_state_machine.response_text] | |
| dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine) | |
| SUPA.table('state_machines').update({ | |
| f'{type}': dump_encoded | |
| }).eq( | |
| "contact_uuid", contact_uuid | |
| ).execute() | |
| # Update an existing record with an existing state machine | |
| elif fsm_check.data[0][type]: | |
| undump_encoded = base64.b64decode( | |
| fsm_check.data[0][type].encode('utf-8') | |
| ) | |
| math_quiz_state_machine = pickle.loads(undump_encoded) | |
| math_quiz_state_machine.student_answer = user_message | |
| math_quiz_state_machine.correct_answer = str(math_quiz_state_machine.correct_answer) | |
| messages = math_quiz_state_machine.validate_answer() | |
| dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine) | |
| SUPA.table('state_machines').update({ | |
| f'{type}': dump_encoded | |
| }).eq( | |
| "contact_uuid", contact_uuid | |
| ).execute() | |
| return messages | |
| def retrieve_microlesson_content(context_data, user_message, microlesson, contact_uuid): | |
| # TODO: This is being filtered by both the local and global states, so not changing | |
| if microlesson == 'addition': | |
| messages = manage_math_quiz_fsm(user_message, contact_uuid, 'addition') | |
| if user_message == 'exit': | |
| state_label = 'exit' | |
| else: | |
| state_label = 'addition-question-sequence' | |
| input_prompt = messages.pop() | |
| message_package = { | |
| 'messages': messages, | |
| 'input_prompt': input_prompt, | |
| 'state': state_label | |
| } | |
| elif context_data['local_state'] == 'addition2' or microlesson == 'addition2': | |
| if user_message == 'harder' or user_message == 'easier': | |
| user_message = '' | |
| message_package = num_one.process_user_message(contact_uuid, user_message) | |
| message_package['state'] = 'addition2' | |
| message_package['input_prompt'] = '?' | |
| elif context_data['local_state'] == 'subtraction-question-sequence' or \ | |
| user_message == 'subtract' or \ | |
| microlesson == 'subtraction': | |
| messages = manage_math_quiz_fsm(user_message, contact_uuid, 'subtraction') | |
| if user_message == 'exit': | |
| state_label = 'exit' | |
| else: | |
| state_label = 'subtraction-question-sequence' | |
| input_prompt = messages.pop() | |
| message_package = { | |
| 'messages': messages, | |
| 'input_prompt': input_prompt, | |
| 'state': state_label | |
| } | |
| print("MICROLESSON CONTENT RESPONSE") | |
| print(message_package) | |
| return message_package | |
| curriculum_lookup_table = { | |
| 'N1.1.1_G1': 'addition', | |
| 'N1.1.1_G2': 'addition2', | |
| 'N1.1.2_G1': 'subtraction' | |
| } | |
| def lookup_local_state(next_state): | |
| microlesson = curriculum_lookup_table[next_state] | |
| return microlesson | |
| def create_text_message(message_text, whatsapp_id): | |
| """ Fills a template with input values to send a text message to Whatsapp | |
| Inputs | |
| - message_text: str - the content that the message should display | |
| - whatsapp_id: str - the message recipient's phone number | |
| Outputs | |
| - message_data: dict - a preformatted template filled with inputs | |
| """ | |
| message_data = { | |
| "preview_url": False, | |
| "recipient_type": "individual", | |
| "to": whatsapp_id, | |
| "type": "text", | |
| "text": { | |
| "body": message_text | |
| } | |
| } | |
| return message_data | |
| def manage_conversation_response(data_json): | |
| """ Calls functions necessary to determine message and context data """ | |
| print("V2 ENDPOINT") | |
| # whatsapp_id = data_json['author_id'] | |
| message_data = data_json['message_data'] | |
| context_data = data_json['context_data'] | |
| whatsapp_id = message_data['author_id'] | |
| user_message = message_data['message_body'] | |
| print("MESSAGE DATA") | |
| print(message_data) | |
| print("CONTEXT DATA") | |
| print(context_data) | |
| print("=================") | |
| # nlu_response = evaluate_message_with_nlu(message_data) | |
| # context_data = { | |
| # 'contact_uuid': 'abcdefg', | |
| # 'current_state': 'N1.1.1_G2', | |
| # 'user_message': '1', | |
| # 'local_state': '' | |
| # } | |
| print("STEP 1") | |
| print(data_json) | |
| print(f"1: {context_data['current_state']}") | |
| if not context_data['current_state']: | |
| context_data['current_state'] = 'N1.1.1_G1' | |
| print(f"2: {context_data['current_state']}") | |
| curriculum_copy = copy.deepcopy(gsm.curriculum) | |
| curriculum_copy.state = context_data['current_state'] | |
| print("STEP 2") | |
| if user_message == 'easier': | |
| curriculum_copy.left() | |
| next_state = curriculum_copy.state | |
| elif user_message == 'harder': | |
| curriculum_copy.right() | |
| next_state = curriculum_copy.state | |
| else: | |
| next_state = context_data['current_state'] | |
| print("next_state") | |
| print(next_state) | |
| print("STEP 3") | |
| microlesson = lookup_local_state(next_state) | |
| print("microlesson") | |
| print(microlesson) | |
| microlesson_content = retrieve_microlesson_content(context_data, user_message, microlesson, context_data['contact_uuid']) | |
| headers = { | |
| 'Authorization': f"Bearer {os.environ.get('TURN_AUTHENTICATION_TOKEN')}", | |
| 'Content-Type': 'application/json' | |
| } | |
| # Send all messages for the current state before a user input prompt (text/button input request) | |
| for message in microlesson_content['messages']: | |
| data = create_text_message(message, whatsapp_id) | |
| # print("data") | |
| # print(data) | |
| r = requests.post( | |
| f'https://whatsapp.turn.io/v1/messages', | |
| data=json.dumps(data), | |
| headers=headers | |
| ) | |
| print("STEP 4") | |
| # combine microlesson content and context_data object | |
| updated_context = { | |
| "context": { | |
| "contact_id": whatsapp_id, | |
| "contact_uuid": context_data['contact_uuid'], | |
| "current_state": next_state, | |
| "local_state": microlesson_content['state'], | |
| "bot_message": microlesson_content['input_prompt'], | |
| "user_message": user_message, | |
| "type": 'ask' | |
| } | |
| } | |
| print(updated_context) | |
| return updated_context | |