Spaces:
Build error
Build error
Merge branch 'feature-wormhole' into 'vlad'
Browse files- README.md +1 -1
- app.py +45 -3
- mathtext_fastapi/curriculum_mapper.py +183 -0
- mathtext_fastapi/data/Rori_Framework_v1.xlsx +0 -0
- mathtext_fastapi/data/curriculum_framework_for_tests.xlsx +0 -0
- mathtext_fastapi/data/intent_classification_model.joblib +3 -0
- mathtext_fastapi/data/labeled_data.csv +144 -0
- mathtext_fastapi/data/text2int_results.csv +104 -91
- mathtext_fastapi/global_state_manager.py +23 -0
- mathtext_fastapi/intent_classification.py +52 -0
- mathtext_fastapi/nlu.py +96 -15
- mathtext_fastapi/v2_conversation_manager.py +257 -0
- requirements.txt +10 -7
- scripts/make_request.py +23 -15
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: Mathtext
|
| 3 |
emoji: 🐨
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: red
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Mathtext Wormhole
|
| 3 |
emoji: 🐨
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: red
|
app.py
CHANGED
|
@@ -15,7 +15,9 @@ from pydantic import BaseModel
|
|
| 15 |
|
| 16 |
from mathtext_fastapi.logging import prepare_message_data_for_logging
|
| 17 |
from mathtext_fastapi.conversation_manager import manage_conversation_response
|
|
|
|
| 18 |
from mathtext_fastapi.nlu import evaluate_message_with_nlu
|
|
|
|
| 19 |
|
| 20 |
app = FastAPI()
|
| 21 |
|
|
@@ -53,7 +55,7 @@ def text2int_ep(content: Text = None):
|
|
| 53 |
return JSONResponse(content=content)
|
| 54 |
|
| 55 |
|
| 56 |
-
@app.post("/manager")
|
| 57 |
async def programmatic_message_manager(request: Request):
|
| 58 |
"""
|
| 59 |
Calls conversation management function to determine the next state
|
|
@@ -86,6 +88,46 @@ async def programmatic_message_manager(request: Request):
|
|
| 86 |
return JSONResponse(context)
|
| 87 |
|
| 88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
@app.post("/nlu")
|
| 90 |
async def evaluate_user_message_with_nlu_api(request: Request):
|
| 91 |
""" Calls nlu evaluation and returns the nlu_response
|
|
@@ -95,8 +137,8 @@ async def evaluate_user_message_with_nlu_api(request: Request):
|
|
| 95 |
|
| 96 |
Output
|
| 97 |
- int_data_dict or sent_data_dict: dict - the type of NLU run and result
|
| 98 |
-
{'type':'integer', 'data': '8'}
|
| 99 |
-
{'type':'sentiment', 'data': 'negative'}
|
| 100 |
"""
|
| 101 |
data_dict = await request.json()
|
| 102 |
message_data = data_dict.get('message_data', '')
|
|
|
|
| 15 |
|
| 16 |
from mathtext_fastapi.logging import prepare_message_data_for_logging
|
| 17 |
from mathtext_fastapi.conversation_manager import manage_conversation_response
|
| 18 |
+
from mathtext_fastapi.v2_conversation_manager import manage_conversation_response
|
| 19 |
from mathtext_fastapi.nlu import evaluate_message_with_nlu
|
| 20 |
+
from mathtext_fastapi.nlu import run_intent_classification
|
| 21 |
|
| 22 |
app = FastAPI()
|
| 23 |
|
|
|
|
| 55 |
return JSONResponse(content=content)
|
| 56 |
|
| 57 |
|
| 58 |
+
@app.post("/v1/manager")
|
| 59 |
async def programmatic_message_manager(request: Request):
|
| 60 |
"""
|
| 61 |
Calls conversation management function to determine the next state
|
|
|
|
| 88 |
return JSONResponse(context)
|
| 89 |
|
| 90 |
|
| 91 |
+
@app.post("/v2/manager")
|
| 92 |
+
async def programmatic_message_manager(request: Request):
|
| 93 |
+
"""
|
| 94 |
+
Calls conversation management function to determine the next state
|
| 95 |
+
|
| 96 |
+
Input
|
| 97 |
+
request.body: dict - message data for the most recent user response
|
| 98 |
+
{
|
| 99 |
+
"author_id": "+47897891",
|
| 100 |
+
"contact_uuid": "j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09",
|
| 101 |
+
"author_type": "OWNER",
|
| 102 |
+
"message_body": "a test message",
|
| 103 |
+
"message_direction": "inbound",
|
| 104 |
+
"message_id": "ABJAK64jlk3-agjkl2QHFAFH",
|
| 105 |
+
"message_inserted_at": "2022-07-05T04:00:34.03352Z",
|
| 106 |
+
"message_updated_at": "2023-02-14T03:54:19.342950Z",
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
Output
|
| 110 |
+
context: dict - the information for the current state
|
| 111 |
+
{
|
| 112 |
+
"user": "47897891",
|
| 113 |
+
"state": "welcome-message-state",
|
| 114 |
+
"bot_message": "Welcome to Rori!",
|
| 115 |
+
"user_message": "",
|
| 116 |
+
"type": "ask"
|
| 117 |
+
}
|
| 118 |
+
"""
|
| 119 |
+
data_dict = await request.json()
|
| 120 |
+
context = manage_conversation_response(data_dict)
|
| 121 |
+
return JSONResponse(context)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@app.post("/intent-classification")
|
| 125 |
+
def intent_classification_ep(content: Text = None):
|
| 126 |
+
ml_response = run_intent_classification(content.content)
|
| 127 |
+
content = {"message": ml_response}
|
| 128 |
+
return JSONResponse(content=content)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
@app.post("/nlu")
|
| 132 |
async def evaluate_user_message_with_nlu_api(request: Request):
|
| 133 |
""" Calls nlu evaluation and returns the nlu_response
|
|
|
|
| 137 |
|
| 138 |
Output
|
| 139 |
- int_data_dict or sent_data_dict: dict - the type of NLU run and result
|
| 140 |
+
{'type':'integer', 'data': '8', 'confidence': 0}
|
| 141 |
+
{'type':'sentiment', 'data': 'negative', 'confidence': 0.99}
|
| 142 |
"""
|
| 143 |
data_dict = await request.json()
|
| 144 |
message_data = data_dict.get('message_data', '')
|
mathtext_fastapi/curriculum_mapper.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def read_and_preprocess_spreadsheet(file_name):
|
| 9 |
+
""" Creates a pandas dataframe from the curriculum overview spreadsheet """
|
| 10 |
+
DATA_DIR = Path(__file__).parent.parent / "mathtext_fastapi" / "data" / file_name
|
| 11 |
+
script_df = pd.read_excel(DATA_DIR, engine='openpyxl')
|
| 12 |
+
# Ensures the grade level columns are integers instead of floats
|
| 13 |
+
script_df.columns = script_df.columns[:2].tolist() + script_df.columns[2:11].astype(int).astype(str).tolist() + script_df.columns[11:].tolist()
|
| 14 |
+
script_df.fillna('', inplace=True)
|
| 15 |
+
return script_df
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def extract_skill_code(skill):
|
| 19 |
+
""" Looks within a curricular skill description for its descriptive code
|
| 20 |
+
|
| 21 |
+
Input
|
| 22 |
+
- skill: str - a brief description of a curricular skill
|
| 23 |
+
|
| 24 |
+
>>> extract_skill_code('A3.3.4 - Solve inequalities')
|
| 25 |
+
'A3.3.4'
|
| 26 |
+
>>> extract_skill_code('A3.3.2 - Graph linear equations, and identify the x- and y-intercepts or the slope of a line')
|
| 27 |
+
'A3.3.2'
|
| 28 |
+
"""
|
| 29 |
+
pattern = r'[A-Z][0-9]\.\d+\.\d+'
|
| 30 |
+
result = re.search(pattern, skill)
|
| 31 |
+
return result.group()
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def build_horizontal_transitions(script_df):
|
| 35 |
+
""" Build a list of transitional relationships within a curricular skill
|
| 36 |
+
|
| 37 |
+
Inputs
|
| 38 |
+
- script_df: pandas dataframe - an overview of the curriculum skills by grade level
|
| 39 |
+
|
| 40 |
+
Output
|
| 41 |
+
- horizontal_transitions: array of arrays - transition data with label, from state, and to state
|
| 42 |
+
|
| 43 |
+
>>> script_df = read_and_preprocess_spreadsheet('curriculum_framework_for_tests.xlsx')
|
| 44 |
+
>>> build_horizontal_transitions(script_df)
|
| 45 |
+
[['right', 'N1.1.1_G1', 'N1.1.1_G2'], ['right', 'N1.1.1_G2', 'N1.1.1_G3'], ['right', 'N1.1.1_G3', 'N1.1.1_G4'], ['right', 'N1.1.1_G4', 'N1.1.1_G5'], ['right', 'N1.1.1_G5', 'N1.1.1_G6'], ['left', 'N1.1.1_G6', 'N1.1.1_G5'], ['left', 'N1.1.1_G5', 'N1.1.1_G4'], ['left', 'N1.1.1_G4', 'N1.1.1_G3'], ['left', 'N1.1.1_G3', 'N1.1.1_G2'], ['left', 'N1.1.1_G2', 'N1.1.1_G1'], ['right', 'N1.1.2_G1', 'N1.1.2_G2'], ['right', 'N1.1.2_G2', 'N1.1.2_G3'], ['right', 'N1.1.2_G3', 'N1.1.2_G4'], ['right', 'N1.1.2_G4', 'N1.1.2_G5'], ['right', 'N1.1.2_G5', 'N1.1.2_G6'], ['left', 'N1.1.2_G6', 'N1.1.2_G5'], ['left', 'N1.1.2_G5', 'N1.1.2_G4'], ['left', 'N1.1.2_G4', 'N1.1.2_G3'], ['left', 'N1.1.2_G3', 'N1.1.2_G2'], ['left', 'N1.1.2_G2', 'N1.1.2_G1']]
|
| 46 |
+
"""
|
| 47 |
+
horizontal_transitions = []
|
| 48 |
+
for index, row in script_df.iterrows():
|
| 49 |
+
skill_code = extract_skill_code(row['Knowledge or Skill'])
|
| 50 |
+
|
| 51 |
+
rightward_matches = []
|
| 52 |
+
for i in range(9):
|
| 53 |
+
# Grade column
|
| 54 |
+
current_grade = i+1
|
| 55 |
+
if row[current_grade].lower().strip() == 'x':
|
| 56 |
+
rightward_matches.append(i)
|
| 57 |
+
|
| 58 |
+
for match in rightward_matches:
|
| 59 |
+
if rightward_matches[-1] != match:
|
| 60 |
+
horizontal_transitions.append([
|
| 61 |
+
"right",
|
| 62 |
+
f"{skill_code}_G{match}",
|
| 63 |
+
f"{skill_code}_G{match+1}"
|
| 64 |
+
])
|
| 65 |
+
|
| 66 |
+
leftward_matches = []
|
| 67 |
+
for i in reversed(range(9)):
|
| 68 |
+
current_grade = i
|
| 69 |
+
if row[current_grade].lower().strip() == 'x':
|
| 70 |
+
leftward_matches.append(i)
|
| 71 |
+
|
| 72 |
+
for match in leftward_matches:
|
| 73 |
+
if leftward_matches[0] != match:
|
| 74 |
+
horizontal_transitions.append([
|
| 75 |
+
"left",
|
| 76 |
+
f"{skill_code}_G{match}",
|
| 77 |
+
f"{skill_code}_G{match-1}"
|
| 78 |
+
])
|
| 79 |
+
|
| 80 |
+
return horizontal_transitions
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def gather_all_vertical_matches(script_df):
|
| 84 |
+
""" Build a list of transitional relationships within a grade level across skills
|
| 85 |
+
|
| 86 |
+
Inputs
|
| 87 |
+
- script_df: pandas dataframe - an overview of the curriculum skills by grade level
|
| 88 |
+
|
| 89 |
+
Output
|
| 90 |
+
- all_matches: array of arrays - represents skills at each grade level
|
| 91 |
+
|
| 92 |
+
>>> script_df = read_and_preprocess_spreadsheet('curriculum_framework_for_tests.xlsx')
|
| 93 |
+
>>> gather_all_vertical_matches(script_df)
|
| 94 |
+
[['N1.1.1', '1'], ['N1.1.2', '1'], ['N1.1.1', '2'], ['N1.1.2', '2'], ['N1.1.1', '3'], ['N1.1.2', '3'], ['N1.1.1', '4'], ['N1.1.2', '4'], ['N1.1.1', '5'], ['N1.1.2', '5'], ['N1.1.1', '6'], ['N1.1.2', '6']]
|
| 95 |
+
"""
|
| 96 |
+
all_matches = []
|
| 97 |
+
columns = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
|
| 98 |
+
|
| 99 |
+
for column in columns:
|
| 100 |
+
for index, value in script_df[column].iteritems():
|
| 101 |
+
row_num = index + 1
|
| 102 |
+
if value == 'x':
|
| 103 |
+
# Extract skill code
|
| 104 |
+
skill_code = extract_skill_code(
|
| 105 |
+
script_df['Knowledge or Skill'][row_num-1]
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
all_matches.append([skill_code, column])
|
| 109 |
+
return all_matches
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def build_vertical_transitions(script_df):
|
| 113 |
+
""" Build a list of transitional relationships within a grade level across skills
|
| 114 |
+
|
| 115 |
+
Inputs
|
| 116 |
+
- script_df: pandas dataframe - an overview of the curriculum skills by grade level
|
| 117 |
+
|
| 118 |
+
Output
|
| 119 |
+
- vertical_transitions: array of arrays - transition data with label, from state, and to state
|
| 120 |
+
|
| 121 |
+
>>> script_df = read_and_preprocess_spreadsheet('curriculum_framework_for_tests.xlsx')
|
| 122 |
+
>>> build_vertical_transitions(script_df)
|
| 123 |
+
[['down', 'N1.1.1_G1', 'N1.1.2_G1'], ['down', 'N1.1.2_G1', 'N1.1.1_G1'], ['down', 'N1.1.1_G2', 'N1.1.2_G2'], ['down', 'N1.1.2_G2', 'N1.1.1_G2'], ['down', 'N1.1.1_G3', 'N1.1.2_G3'], ['down', 'N1.1.2_G3', 'N1.1.1_G3'], ['down', 'N1.1.1_G4', 'N1.1.2_G4'], ['down', 'N1.1.2_G4', 'N1.1.1_G4'], ['down', 'N1.1.1_G5', 'N1.1.2_G5'], ['down', 'N1.1.2_G5', 'N1.1.1_G5'], ['down', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G6', 'N1.1.1_G6'], ['up', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G5', 'N1.1.1_G5'], ['up', 'N1.1.1_G5', 'N1.1.2_G5'], ['up', 'N1.1.2_G4', 'N1.1.1_G4'], ['up', 'N1.1.1_G4', 'N1.1.2_G4'], ['up', 'N1.1.2_G3', 'N1.1.1_G3'], ['up', 'N1.1.1_G3', 'N1.1.2_G3'], ['up', 'N1.1.2_G2', 'N1.1.1_G2'], ['up', 'N1.1.1_G2', 'N1.1.2_G2'], ['up', 'N1.1.2_G1', 'N1.1.1_G1']]
|
| 124 |
+
"""
|
| 125 |
+
vertical_transitions = []
|
| 126 |
+
|
| 127 |
+
all_matches = gather_all_vertical_matches(script_df)
|
| 128 |
+
|
| 129 |
+
# Downward
|
| 130 |
+
for index, match in enumerate(all_matches):
|
| 131 |
+
skill = match[0]
|
| 132 |
+
row_num = match[1]
|
| 133 |
+
if all_matches[-1] != match:
|
| 134 |
+
vertical_transitions.append([
|
| 135 |
+
"down",
|
| 136 |
+
f"{skill}_G{row_num}",
|
| 137 |
+
f"{all_matches[index+1][0]}_G{row_num}"
|
| 138 |
+
])
|
| 139 |
+
|
| 140 |
+
# Upward
|
| 141 |
+
for index, match in reversed(list(enumerate(all_matches))):
|
| 142 |
+
skill = match[0]
|
| 143 |
+
row_num = match[1]
|
| 144 |
+
if all_matches[0] != match:
|
| 145 |
+
vertical_transitions.append([
|
| 146 |
+
"up",
|
| 147 |
+
f"{skill}_G{row_num}",
|
| 148 |
+
f"{all_matches[index-1][0]}_G{row_num}"
|
| 149 |
+
])
|
| 150 |
+
|
| 151 |
+
return vertical_transitions
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def build_all_states(all_transitions):
|
| 155 |
+
""" Creates an array with all state labels for the curriculum
|
| 156 |
+
|
| 157 |
+
Input
|
| 158 |
+
- all_transitions: list of lists - all possible up, down, left, or right transitions in curriculum
|
| 159 |
+
|
| 160 |
+
Output
|
| 161 |
+
- all_states: list - a collection of state labels (skill code and grade number)
|
| 162 |
+
|
| 163 |
+
>>> all_transitions = [['right', 'N1.1.1_G1', 'N1.1.1_G2'], ['right', 'N1.1.1_G2', 'N1.1.1_G3'], ['right', 'N1.1.1_G3', 'N1.1.1_G4'], ['right', 'N1.1.1_G4', 'N1.1.1_G5'], ['right', 'N1.1.1_G5', 'N1.1.1_G6'], ['left', 'N1.1.1_G6', 'N1.1.1_G5'], ['left', 'N1.1.1_G5', 'N1.1.1_G4'], ['left', 'N1.1.1_G4', 'N1.1.1_G3'], ['left', 'N1.1.1_G3', 'N1.1.1_G2'], ['left', 'N1.1.1_G2', 'N1.1.1_G1'], ['right', 'N1.1.2_G1', 'N1.1.2_G2'], ['right', 'N1.1.2_G2', 'N1.1.2_G3'], ['right', 'N1.1.2_G3', 'N1.1.2_G4'], ['right', 'N1.1.2_G4', 'N1.1.2_G5'], ['right', 'N1.1.2_G5', 'N1.1.2_G6'], ['left', 'N1.1.2_G6', 'N1.1.2_G5'], ['left', 'N1.1.2_G5', 'N1.1.2_G4'], ['left', 'N1.1.2_G4', 'N1.1.2_G3'], ['left', 'N1.1.2_G3', 'N1.1.2_G2'], ['left', 'N1.1.2_G2', 'N1.1.2_G1'], ['down', 'N1.1.1_G1', 'N1.1.2_G1'], ['down', 'N1.1.2_G1', 'N1.1.1_G1'], ['down', 'N1.1.1_G2', 'N1.1.2_G2'], ['down', 'N1.1.2_G2', 'N1.1.1_G2'], ['down', 'N1.1.1_G3', 'N1.1.2_G3'], ['down', 'N1.1.2_G3', 'N1.1.1_G3'], ['down', 'N1.1.1_G4', 'N1.1.2_G4'], ['down', 'N1.1.2_G4', 'N1.1.1_G4'], ['down', 'N1.1.1_G5', 'N1.1.2_G5'], ['down', 'N1.1.2_G5', 'N1.1.1_G5'], ['down', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G6', 'N1.1.1_G6'], ['up', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G5', 'N1.1.1_G5'], ['up', 'N1.1.1_G5', 'N1.1.2_G5'], ['up', 'N1.1.2_G4', 'N1.1.1_G4'], ['up', 'N1.1.1_G4', 'N1.1.2_G4'], ['up', 'N1.1.2_G3', 'N1.1.1_G3'], ['up', 'N1.1.1_G3', 'N1.1.2_G3'], ['up', 'N1.1.2_G2', 'N1.1.1_G2'], ['up', 'N1.1.1_G2', 'N1.1.2_G2'], ['up', 'N1.1.2_G1', 'N1.1.1_G1']]
|
| 164 |
+
>>> build_all_states(all_transitions)
|
| 165 |
+
['N1.1.1_G1', 'N1.1.1_G2', 'N1.1.1_G3', 'N1.1.1_G4', 'N1.1.1_G5', 'N1.1.1_G6', 'N1.1.2_G1', 'N1.1.2_G2', 'N1.1.2_G3', 'N1.1.2_G4', 'N1.1.2_G5', 'N1.1.2_G6']
|
| 166 |
+
"""
|
| 167 |
+
all_states = []
|
| 168 |
+
for transition in all_transitions:
|
| 169 |
+
for index, state in enumerate(transition):
|
| 170 |
+
if index == 0:
|
| 171 |
+
continue
|
| 172 |
+
if state not in all_states:
|
| 173 |
+
all_states.append(state)
|
| 174 |
+
return all_states
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def build_curriculum_logic():
|
| 178 |
+
script_df = read_and_preprocess_spreadsheet('Rori_Framework_v1.xlsx')
|
| 179 |
+
horizontal_transitions = build_horizontal_transitions(script_df)
|
| 180 |
+
vertical_transitions = build_vertical_transitions(script_df)
|
| 181 |
+
all_transitions = horizontal_transitions + vertical_transitions
|
| 182 |
+
all_states = build_all_states(all_transitions)
|
| 183 |
+
return all_states, all_transitions
|
mathtext_fastapi/data/Rori_Framework_v1.xlsx
ADDED
|
Binary file (420 kB). View file
|
|
|
mathtext_fastapi/data/curriculum_framework_for_tests.xlsx
ADDED
|
Binary file (510 kB). View file
|
|
|
mathtext_fastapi/data/intent_classification_model.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ea4954368c3b95673167ce347f2962b5508c4af295b6af58b6c11b3c1075b42e
|
| 3 |
+
size 127903
|
mathtext_fastapi/data/labeled_data.csv
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Utterance,Label
|
| 2 |
+
skip this,skip
|
| 3 |
+
this is stupid,skip
|
| 4 |
+
this is stupid,harder
|
| 5 |
+
this is stupid,feedback
|
| 6 |
+
I'm done,exit
|
| 7 |
+
quit,exit
|
| 8 |
+
I don't know,hint
|
| 9 |
+
help,hint
|
| 10 |
+
can I do something else?,main menu
|
| 11 |
+
what's going on,rapport
|
| 12 |
+
what's going on,main menu
|
| 13 |
+
tell me a joke,rapport
|
| 14 |
+
tell me a joke,main menu
|
| 15 |
+
Sorry I don't understand,do not know
|
| 16 |
+
Ten thousand,number
|
| 17 |
+
1.234,number
|
| 18 |
+
"10,000",number
|
| 19 |
+
"123, 456",numbers
|
| 20 |
+
"11, 12, 13",numbers
|
| 21 |
+
"100, 200, 300",numbers
|
| 22 |
+
"100, 200",numbers
|
| 23 |
+
Stop for a minute,wait
|
| 24 |
+
Bye bye,exit
|
| 25 |
+
Good night,exit
|
| 26 |
+
Am done,exit
|
| 27 |
+
Yes,yes
|
| 28 |
+
Help,help
|
| 29 |
+
Idiot,harder
|
| 30 |
+
Stop,exit
|
| 31 |
+
I don't get it,hint
|
| 32 |
+
Math,main menu
|
| 33 |
+
Math,math topic
|
| 34 |
+
Tomorrow let do math,wait
|
| 35 |
+
Later,wait
|
| 36 |
+
Pls i will continue pls,skip
|
| 37 |
+
Rori tell me now,help
|
| 38 |
+
harder,skip
|
| 39 |
+
Stop for now i wont to go to School,exit
|
| 40 |
+
Next,next
|
| 41 |
+
Okay,okay
|
| 42 |
+
Great,affirmation
|
| 43 |
+
Give me for example,example
|
| 44 |
+
No I want to learn algebraic expressions,algebra
|
| 45 |
+
Hi rori,greeting
|
| 46 |
+
*help*,help
|
| 47 |
+
*Next*,next
|
| 48 |
+
Okay nice,okay
|
| 49 |
+
I don't know it,hint
|
| 50 |
+
Nex,next
|
| 51 |
+
I need a help,hint
|
| 52 |
+
Please can I ask your any math questions?,faq
|
| 53 |
+
The answer is 1,answer
|
| 54 |
+
The answer is 1,number
|
| 55 |
+
But 0.8 is also same as . 8 so I was actually right,I'm right
|
| 56 |
+
What is the number system?,faq
|
| 57 |
+
Ok thanks,thanks
|
| 58 |
+
I'm going to school now,exit
|
| 59 |
+
Let's move to another topic,main menu
|
| 60 |
+
"Ummanni saba
|
| 61 |
+
Kebena bara kana galmi keenya inni guddaan bilisummaa qofa #Gabrummaan_ammaan booda_gaha namni hundi bakka jiru irraa kutatee ka,ee jira obboleewwan goototni keenya jiran haqa Kebenaaf jechaa jiru Guraandhala 29 booda walabummaa keenya labsina Dhugaa qabna Ni injifanna *** . Naannoo giddu galeessa Itoophiyaatti #Kebenaan aanaa addaati Kun murtoo ummata Kebenaa hundaati",spam
|
| 62 |
+
Yes it,yes
|
| 63 |
+
U type fast,too fast
|
| 64 |
+
I mean your typing is fast,too fast
|
| 65 |
+
Why do u type so fast,too fast
|
| 66 |
+
Ur typing is fast,too fast
|
| 67 |
+
Can we go to a real work,harder
|
| 68 |
+
I know all this,harder
|
| 69 |
+
Answer this,preamble
|
| 70 |
+
Am tired,exit
|
| 71 |
+
This is not what I asked for,main menu
|
| 72 |
+
Bye,exit
|
| 73 |
+
😱😱😂😂😂😡😰😰😰😒,spam
|
| 74 |
+
Gbxbxbcbcbbcbchcbchc,spam
|
| 75 |
+
I want to solve math,math topic
|
| 76 |
+
Pleas let start with the fraction,fractions topic
|
| 77 |
+
Okey,okay
|
| 78 |
+
i need substraction,subtraction topic
|
| 79 |
+
Can you please stop with me,exit
|
| 80 |
+
Another one,next
|
| 81 |
+
Harder or easy,main menu
|
| 82 |
+
Hard or easier,main menu
|
| 83 |
+
Jump topic,menu
|
| 84 |
+
Got it,okay
|
| 85 |
+
I didn't understand,don't know
|
| 86 |
+
Don't understand,don't know
|
| 87 |
+
Excuse me pls,hint
|
| 88 |
+
Let stop for today,exit
|
| 89 |
+
Help and stop asking me stupid questions,
|
| 90 |
+
Ykay,okay
|
| 91 |
+
Not interested in solving this,menu
|
| 92 |
+
Stpo,exit
|
| 93 |
+
Hiiiiiii,greeting
|
| 94 |
+
Hi rori,greeting
|
| 95 |
+
I've done this things before,harder
|
| 96 |
+
Which number my phone number,
|
| 97 |
+
Unit,main menu
|
| 98 |
+
No ide,don't know
|
| 99 |
+
No ide,hint
|
| 100 |
+
No idea,don't know
|
| 101 |
+
🙈🤩😇🙏,spam
|
| 102 |
+
Thank u,thanks
|
| 103 |
+
Do you know programming,faq
|
| 104 |
+
Delete my number,unsubscribe
|
| 105 |
+
See u,exit
|
| 106 |
+
Can I go for break ??,wait
|
| 107 |
+
I wanna fuck,profanity
|
| 108 |
+
Enough of this nw,exit
|
| 109 |
+
Can we move to equations,equations
|
| 110 |
+
Do you know you are an idiot,insult
|
| 111 |
+
3 digit number,number
|
| 112 |
+
3 digit number,answer
|
| 113 |
+
Three digit number,confident answer
|
| 114 |
+
Three digit number,number
|
| 115 |
+
Good evening Rori,greeting
|
| 116 |
+
89 Next,answer
|
| 117 |
+
89 Next,number
|
| 118 |
+
3 digit number,answer
|
| 119 |
+
Three digit number,answer
|
| 120 |
+
This is too simple,harder
|
| 121 |
+
Am not a kid,harder
|
| 122 |
+
Hey Miss Roribcan you ask me some question from Secondary 2,greeting
|
| 123 |
+
Hey Miss Roribcan you ask me some question from Secondary 2,faq
|
| 124 |
+
Hey Miss Roribcan you ask me some question from Secondary 2,main menu
|
| 125 |
+
don't know,hint
|
| 126 |
+
don't know,easier
|
| 127 |
+
𝑴𝒂𝒕𝒉,math
|
| 128 |
+
Rori can you help me to gat value,
|
| 129 |
+
I called but u are not picking up,
|
| 130 |
+
0.3 answer,answer
|
| 131 |
+
Sorry rori was101,answer
|
| 132 |
+
Y is it 6,answer
|
| 133 |
+
Y is it 6,number
|
| 134 |
+
0.3 answer,number
|
| 135 |
+
Why 0.5,more explanation
|
| 136 |
+
Why 0.5,number
|
| 137 |
+
6\nNext,Next
|
| 138 |
+
How is the answer is 11,more explanation
|
| 139 |
+
How comes we have 11,more explanation
|
| 140 |
+
Yes 6,answer
|
| 141 |
+
Yes 6,number
|
| 142 |
+
6\nNext,number
|
| 143 |
+
How is the answer is 11,number
|
| 144 |
+
How comes we have 11,number
|
mathtext_fastapi/data/text2int_results.csv
CHANGED
|
@@ -1,92 +1,105 @@
|
|
| 1 |
input,output,text2int,score
|
| 2 |
-
notanumber,32202,32202,True
|
| 3 |
-
this is not a number,32202,32202,True
|
| 4 |
-
fourteen,14,14,True
|
| 5 |
-
forteen,14,14,True
|
| 6 |
-
one thousand four hundred ninety two,1492,1492,True
|
| 7 |
-
one thousand ninety two,1092,1092,True
|
| 8 |
-
Fourteen Hundred Ninety-Two,1492,1492,True
|
| 9 |
-
Fourteen Hundred,1400,1400,True
|
| 10 |
-
Ninety nine,99,99,True
|
| 11 |
-
fifteen thousand five hundred-sixty,15560,15560,True
|
| 12 |
-
three hundred fifty,350,350,True
|
| 13 |
-
one nine eight five,1985,1985,True
|
| 14 |
-
nineteen eighty-five,1985,1605,False
|
| 15 |
-
oh one,1,1,True
|
| 16 |
-
six oh 1,601,601,True
|
| 17 |
-
sex,6,6,True
|
| 18 |
-
six,6,6,True
|
| 19 |
-
eight oh,80,8,False
|
| 20 |
-
eighty,80,80,True
|
| 21 |
-
ate,8,1,False
|
| 22 |
-
double eight,88,
|
| 23 |
-
eight three seven five three O nine,8375309,
|
| 24 |
-
eight three seven five three oh nine,8375309,8375309,True
|
| 25 |
-
eight three seven five three zero nine,8375309,8375309,True
|
| 26 |
-
eight three seven five three oh ni-ee-ine,8375309,
|
| 27 |
-
two eight,28,16,False
|
| 28 |
-
seven oh eleven,7011,77,False
|
| 29 |
-
seven elevens,77,77,True
|
| 30 |
-
seven eleven,711,77,False
|
| 31 |
-
ninety nine oh five,9905,149,False
|
| 32 |
-
seven 0 seven 0 seven 0 seven,7070707,7070707,True
|
| 33 |
-
123 hundred,123000,223,False
|
| 34 |
-
5 o 5,505,
|
| 35 |
-
15 o 5,1505,
|
| 36 |
-
15-o 5,1505,
|
| 37 |
-
15 o-5,1505,
|
| 38 |
-
911-thousand,911000,911000,True
|
| 39 |
-
twenty-two twenty-two,2222,44,False
|
| 40 |
-
twenty-two twenty-twos,484,44,False
|
| 41 |
-
four eighty four,484,404,False
|
| 42 |
-
four eighties,320,72,False
|
| 43 |
-
four eighties and nine nineties,1130,243,False
|
| 44 |
-
ninety nine hundred and seventy seven,9977,276,False
|
| 45 |
-
seven thousands,7000,7000,True
|
| 46 |
-
2 hundreds,200,200,True
|
| 47 |
-
99 thousands and one,99001,99001,True
|
| 48 |
-
"forty-five thousand, seven hundred and nine",45709,1161,False
|
| 49 |
-
eighty eight hundred eighty,8880,268,False
|
| 50 |
-
a hundred hundred,10000,
|
| 51 |
-
a hundred thousand,100000,
|
| 52 |
-
a hundred million,100000000,
|
| 53 |
-
nineteen ninety nine,1999,1809,False
|
| 54 |
-
forteen twenty seven,1427,307,False
|
| 55 |
-
seventeen-thousand and seventy two,17072,17072,True
|
| 56 |
-
two hundred and nine,209,209,True
|
| 57 |
-
two thousand ten,2010,2010,True
|
| 58 |
-
two thousand and ten,2010,2010,True
|
| 59 |
-
twelve million,12000000,12000000,True
|
| 60 |
-
8 billion,8000000000,8000000000,True
|
| 61 |
-
twenty ten,2010,2010,True
|
| 62 |
-
thirty-two hundred,3200,3200,True
|
| 63 |
-
nine,9,9,True
|
| 64 |
-
forty two,42,42,True
|
| 65 |
-
1 2 three,123,123,True
|
| 66 |
-
fourtean,14,14,True
|
| 67 |
-
one tousand four hundred ninty two,1492,1492,True
|
| 68 |
-
Furteen Hundrd Ninety-Too,1492,1492,True
|
| 69 |
-
forrteen,14,14,True
|
| 70 |
-
sevnteen-thosand and seventy two,17072,17072,True
|
| 71 |
-
ninety nine hundred ad seventy seven,9977,
|
| 72 |
-
seven thusands,7000,7000,True
|
| 73 |
-
2 hunreds,200,200,True
|
| 74 |
-
99 tousands and one,99001,99001,True
|
| 75 |
-
eighty ate hundred eighty,8880,261,False
|
| 76 |
-
fourteen Hundred,1400,1400,True
|
| 77 |
-
8 Bilion,8000000000,8000000,False
|
| 78 |
-
one million three thousand one,1003001,1003001,True
|
| 79 |
-
four million nine thousand seven,4009007,4009007,True
|
| 80 |
-
two million five hundred thousand,2500000,2001500,False
|
| 81 |
-
two tousand ten,2010,2010,True
|
| 82 |
-
two thousand teen,2010,2007,False
|
| 83 |
-
tvelve milion,12000000,12000000,True
|
| 84 |
-
tventy ten,2010,2010,True
|
| 85 |
-
tirty-twoo hunred,3200,3200,True
|
| 86 |
-
sevn thoosands,7000,7000,True
|
| 87 |
-
five,5,5,True
|
| 88 |
-
ten,10,10,True
|
| 89 |
-
one two three and ten,12310,51,False
|
| 90 |
-
ONE MILLion three hunded and fiv,1000305,1000305,True
|
| 91 |
-
"50,500 and six",50506,50506,True
|
| 92 |
-
one_million_and_five,1000005,1000005,True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
input,output,text2int,score
|
| 2 |
+
notanumber,32202.0,32202.0,True
|
| 3 |
+
this is not a number,32202.0,32202.0,True
|
| 4 |
+
fourteen,14.0,14.0,True
|
| 5 |
+
forteen,14.0,14.0,True
|
| 6 |
+
one thousand four hundred ninety two,1492.0,1492.0,True
|
| 7 |
+
one thousand ninety two,1092.0,1092.0,True
|
| 8 |
+
Fourteen Hundred Ninety-Two,1492.0,1492.0,True
|
| 9 |
+
Fourteen Hundred,1400.0,1400.0,True
|
| 10 |
+
Ninety nine,99.0,99.0,True
|
| 11 |
+
fifteen thousand five hundred-sixty,15560.0,15560.0,True
|
| 12 |
+
three hundred fifty,350.0,350.0,True
|
| 13 |
+
one nine eight five,1985.0,1985.0,True
|
| 14 |
+
nineteen eighty-five,1985.0,1605.0,False
|
| 15 |
+
oh one,1.0,1.0,True
|
| 16 |
+
six oh 1,601.0,601.0,True
|
| 17 |
+
sex,6.0,6.0,True
|
| 18 |
+
six,6.0,6.0,True
|
| 19 |
+
eight oh,80.0,8.0,False
|
| 20 |
+
eighty,80.0,80.0,True
|
| 21 |
+
ate,8.0,1.0,False
|
| 22 |
+
double eight,88.0,8.0,False
|
| 23 |
+
eight three seven five three O nine,8375309.0,8375319.0,False
|
| 24 |
+
eight three seven five three oh nine,8375309.0,8375309.0,True
|
| 25 |
+
eight three seven five three zero nine,8375309.0,8375309.0,True
|
| 26 |
+
eight three seven five three oh ni-ee-ine,8375309.0,837530111.0,False
|
| 27 |
+
two eight,28.0,16.0,False
|
| 28 |
+
seven oh eleven,7011.0,77.0,False
|
| 29 |
+
seven elevens,77.0,77.0,True
|
| 30 |
+
seven eleven,711.0,77.0,False
|
| 31 |
+
ninety nine oh five,9905.0,149.0,False
|
| 32 |
+
seven 0 seven 0 seven 0 seven,7070707.0,7070707.0,True
|
| 33 |
+
123 hundred,123000.0,223.0,False
|
| 34 |
+
5 o 5,505.0,515.0,False
|
| 35 |
+
15 o 5,1505.0,21.0,False
|
| 36 |
+
15-o 5,1505.0,21.0,False
|
| 37 |
+
15 o-5,1505.0,21.0,False
|
| 38 |
+
911-thousand,911000.0,911000.0,True
|
| 39 |
+
twenty-two twenty-two,2222.0,44.0,False
|
| 40 |
+
twenty-two twenty-twos,484.0,44.0,False
|
| 41 |
+
four eighty four,484.0,404.0,False
|
| 42 |
+
four eighties,320.0,72.0,False
|
| 43 |
+
four eighties and nine nineties,1130.0,243.0,False
|
| 44 |
+
ninety nine hundred and seventy seven,9977.0,276.0,False
|
| 45 |
+
seven thousands,7000.0,7000.0,True
|
| 46 |
+
2 hundreds,200.0,200.0,True
|
| 47 |
+
99 thousands and one,99001.0,99001.0,True
|
| 48 |
+
"forty-five thousand, seven hundred and nine",45709.0,1161.0,False
|
| 49 |
+
eighty eight hundred eighty,8880.0,268.0,False
|
| 50 |
+
a hundred hundred,10000.0,100.0,False
|
| 51 |
+
a hundred thousand,100000.0,100.0,False
|
| 52 |
+
a hundred million,100000000.0,100.0,False
|
| 53 |
+
nineteen ninety nine,1999.0,1809.0,False
|
| 54 |
+
forteen twenty seven,1427.0,307.0,False
|
| 55 |
+
seventeen-thousand and seventy two,17072.0,17072.0,True
|
| 56 |
+
two hundred and nine,209.0,209.0,True
|
| 57 |
+
two thousand ten,2010.0,2010.0,True
|
| 58 |
+
two thousand and ten,2010.0,2010.0,True
|
| 59 |
+
twelve million,12000000.0,12000000.0,True
|
| 60 |
+
8 billion,8000000000.0,8000000000.0,True
|
| 61 |
+
twenty ten,2010.0,2010.0,True
|
| 62 |
+
thirty-two hundred,3200.0,3200.0,True
|
| 63 |
+
nine,9.0,9.0,True
|
| 64 |
+
forty two,42.0,42.0,True
|
| 65 |
+
1 2 three,123.0,123.0,True
|
| 66 |
+
fourtean,14.0,14.0,True
|
| 67 |
+
one tousand four hundred ninty two,1492.0,1492.0,True
|
| 68 |
+
Furteen Hundrd Ninety-Too,1492.0,1492.0,True
|
| 69 |
+
forrteen,14.0,14.0,True
|
| 70 |
+
sevnteen-thosand and seventy two,17072.0,17072.0,True
|
| 71 |
+
ninety nine hundred ad seventy seven,9977.0,90.0,False
|
| 72 |
+
seven thusands,7000.0,7000.0,True
|
| 73 |
+
2 hunreds,200.0,200.0,True
|
| 74 |
+
99 tousands and one,99001.0,99001.0,True
|
| 75 |
+
eighty ate hundred eighty,8880.0,261.0,False
|
| 76 |
+
fourteen Hundred,1400.0,1400.0,True
|
| 77 |
+
8 Bilion,8000000000.0,8000000.0,False
|
| 78 |
+
one million three thousand one,1003001.0,1003001.0,True
|
| 79 |
+
four million nine thousand seven,4009007.0,4009007.0,True
|
| 80 |
+
two million five hundred thousand,2500000.0,2001500.0,False
|
| 81 |
+
two tousand ten,2010.0,2010.0,True
|
| 82 |
+
two thousand teen,2010.0,2007.0,False
|
| 83 |
+
tvelve milion,12000000.0,12000000.0,True
|
| 84 |
+
tventy ten,2010.0,2010.0,True
|
| 85 |
+
tirty-twoo hunred,3200.0,3200.0,True
|
| 86 |
+
sevn thoosands,7000.0,7000.0,True
|
| 87 |
+
five,5.0,5.0,True
|
| 88 |
+
ten,10.0,10.0,True
|
| 89 |
+
one two three and ten,12310.0,51.0,False
|
| 90 |
+
ONE MILLion three hunded and fiv,1000305.0,1000305.0,True
|
| 91 |
+
"50,500 and six",50506.0,50506.0,True
|
| 92 |
+
one_million_and_five,1000005.0,1000005.0,True
|
| 93 |
+
2.0,2.0,2.0,True
|
| 94 |
+
4.5,4.5,4.5,True
|
| 95 |
+
12345.001,12345.001,12345.001,True
|
| 96 |
+
7..0,7.0,7.0,True
|
| 97 |
+
0.06,0.06,0.06,True
|
| 98 |
+
"0,25",0.25,25.0,False
|
| 99 |
+
o.45,0.45,32202.0,False
|
| 100 |
+
0.1.2,0.12,32202.0,False
|
| 101 |
+
0.00009,9e-05,9e-05,True
|
| 102 |
+
0.01.,0.01,0.01,True
|
| 103 |
+
I don't know 8,8.0,8.0,True
|
| 104 |
+
"You're wrong it's not 20, it's 45",45.0,20.0,False
|
| 105 |
+
I don't understand why it's 19,19.0,19.0,True
|
mathtext_fastapi/global_state_manager.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transitions import Machine
|
| 2 |
+
from mathtext_fastapi.curriculum_mapper import build_curriculum_logic
|
| 3 |
+
|
| 4 |
+
all_states, all_transitions = build_curriculum_logic()
|
| 5 |
+
|
| 6 |
+
class GlobalStateManager(object):
|
| 7 |
+
states = all_states
|
| 8 |
+
|
| 9 |
+
transitions = all_transitions
|
| 10 |
+
|
| 11 |
+
def __init__(
|
| 12 |
+
self,
|
| 13 |
+
initial_state='N1.1.1_G1',
|
| 14 |
+
):
|
| 15 |
+
self.machine = Machine(
|
| 16 |
+
model=self,
|
| 17 |
+
states=GlobalStateManager.states,
|
| 18 |
+
transitions=GlobalStateManager.transitions,
|
| 19 |
+
initial=initial_state
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
curriculum = GlobalStateManager()
|
mathtext_fastapi/intent_classification.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pandas as pd
|
| 3 |
+
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from sentence_transformers import SentenceTransformer
|
| 6 |
+
from sklearn.linear_model import LogisticRegression
|
| 7 |
+
from joblib import dump, load
|
| 8 |
+
|
| 9 |
+
def pickle_model(model):
|
| 10 |
+
DATA_DIR = Path(__file__).parent.parent / "mathtext_fastapi" / "data" / "intent_classification_model.joblib"
|
| 11 |
+
dump(model, DATA_DIR)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def create_intent_classification_model():
|
| 15 |
+
encoder = SentenceTransformer('all-MiniLM-L6-v2')
|
| 16 |
+
# path = list(Path.cwd().glob('*.csv'))
|
| 17 |
+
DATA_DIR = Path(__file__).parent.parent / "mathtext_fastapi" / "data" / "labeled_data.csv"
|
| 18 |
+
|
| 19 |
+
print("DATA_DIR")
|
| 20 |
+
print(f"{DATA_DIR}")
|
| 21 |
+
|
| 22 |
+
with open(f"{DATA_DIR}",'r', newline='', encoding='utf-8') as f:
|
| 23 |
+
df = pd.read_csv(f)
|
| 24 |
+
df = df[df.columns[:2]]
|
| 25 |
+
df = df.dropna()
|
| 26 |
+
X_explore = np.array([list(encoder.encode(x)) for x in df['Utterance']])
|
| 27 |
+
X = np.array([list(encoder.encode(x)) for x in df['Utterance']])
|
| 28 |
+
y = df['Label']
|
| 29 |
+
model = LogisticRegression(class_weight='balanced')
|
| 30 |
+
model.fit(X, y, sample_weight=None)
|
| 31 |
+
|
| 32 |
+
print("MODEL")
|
| 33 |
+
print(model)
|
| 34 |
+
|
| 35 |
+
pickle_model(model)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def retrieve_intent_classification_model():
|
| 39 |
+
DATA_DIR = Path(__file__).parent.parent / "mathtext_fastapi" / "data" / "intent_classification_model.joblib"
|
| 40 |
+
model = load(DATA_DIR)
|
| 41 |
+
return model
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def predict_message_intent(message):
|
| 45 |
+
encoder = SentenceTransformer('all-MiniLM-L6-v2')
|
| 46 |
+
model = retrieve_intent_classification_model()
|
| 47 |
+
tokenized_utterance = np.array([list(encoder.encode(message))])
|
| 48 |
+
predicted_label = model.predict(tokenized_utterance)
|
| 49 |
+
predicted_probabilities = model.predict_proba(tokenized_utterance)
|
| 50 |
+
confidence_score = predicted_probabilities.max()
|
| 51 |
+
|
| 52 |
+
return {"type": "intent", "data": predicted_label[0], "confidence": confidence_score}
|
mathtext_fastapi/nlu.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
|
|
| 1 |
from mathtext_fastapi.logging import prepare_message_data_for_logging
|
| 2 |
from mathtext.sentiment import sentiment
|
| 3 |
from mathtext.text2int import text2int
|
|
|
|
| 4 |
import re
|
| 5 |
|
| 6 |
|
|
@@ -8,27 +10,41 @@ def build_nlu_response_object(type, data, confidence):
|
|
| 8 |
""" Turns nlu results into an object to send back to Turn.io
|
| 9 |
Inputs
|
| 10 |
- type: str - the type of nlu run (integer or sentiment-analysis)
|
| 11 |
-
- data: str - the student message
|
| 12 |
- confidence: - the nlu confidence score (sentiment) or '' (integer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
"""
|
| 14 |
return {'type': type, 'data': data, 'confidence': confidence}
|
| 15 |
|
| 16 |
|
| 17 |
-
def test_for_float_or_int(message_data, message_text):
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
|
| 24 |
|
| 25 |
def test_for_number_sequence(message_text_arr, message_data, message_text):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
nlu_response = {}
|
| 27 |
if all(ele.isdigit() for ele in message_text_arr):
|
| 28 |
nlu_response = build_nlu_response_object(
|
| 29 |
'integer',
|
| 30 |
','.join(message_text_arr),
|
| 31 |
-
|
| 32 |
)
|
| 33 |
prepare_message_data_for_logging(message_data, nlu_response)
|
| 34 |
return nlu_response
|
|
@@ -42,6 +58,9 @@ def run_text2int_on_each_list_item(message_text_arr):
|
|
| 42 |
|
| 43 |
Output
|
| 44 |
- student_response_arr: list - a set of integers (32202 for error code)
|
|
|
|
|
|
|
|
|
|
| 45 |
"""
|
| 46 |
student_response_arr = []
|
| 47 |
for student_response in message_text_arr:
|
|
@@ -51,12 +70,63 @@ def run_text2int_on_each_list_item(message_text_arr):
|
|
| 51 |
|
| 52 |
|
| 53 |
def run_sentiment_analysis(message_text):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
# TODO: Add intent labelling here
|
| 55 |
# TODO: Add logic to determine whether intent labeling or sentiment analysis is more appropriate (probably default to intent labeling)
|
| 56 |
return sentiment(message_text)
|
| 57 |
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
def evaluate_message_with_nlu(message_data):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
# Keeps system working with two different inputs - full and filtered @event object
|
| 61 |
try:
|
| 62 |
message_text = message_data['message_body']
|
|
@@ -73,20 +143,31 @@ def evaluate_message_with_nlu(message_data):
|
|
| 73 |
}
|
| 74 |
message_text = message_data['message_body']
|
| 75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
number_api_resp = text2int(message_text.lower())
|
| 77 |
|
| 78 |
if number_api_resp == 32202:
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
else:
|
| 86 |
nlu_response = build_nlu_response_object(
|
| 87 |
'integer',
|
| 88 |
number_api_resp,
|
| 89 |
-
|
| 90 |
)
|
| 91 |
|
| 92 |
prepare_message_data_for_logging(message_data, nlu_response)
|
|
|
|
| 1 |
+
from fuzzywuzzy import fuzz
|
| 2 |
from mathtext_fastapi.logging import prepare_message_data_for_logging
|
| 3 |
from mathtext.sentiment import sentiment
|
| 4 |
from mathtext.text2int import text2int
|
| 5 |
+
from mathtext_fastapi.intent_classification import create_intent_classification_model, retrieve_intent_classification_model, predict_message_intent
|
| 6 |
import re
|
| 7 |
|
| 8 |
|
|
|
|
| 10 |
""" Turns nlu results into an object to send back to Turn.io
|
| 11 |
Inputs
|
| 12 |
- type: str - the type of nlu run (integer or sentiment-analysis)
|
| 13 |
+
- data: str/int - the student message
|
| 14 |
- confidence: - the nlu confidence score (sentiment) or '' (integer)
|
| 15 |
+
|
| 16 |
+
>>> build_nlu_response_object('integer', 8, 0)
|
| 17 |
+
{'type': 'integer', 'data': 8, 'confidence': 0}
|
| 18 |
+
|
| 19 |
+
>>> build_nlu_response_object('sentiment', 'POSITIVE', 0.99)
|
| 20 |
+
{'type': 'sentiment', 'data': 'POSITIVE', 'confidence': 0.99}
|
| 21 |
"""
|
| 22 |
return {'type': type, 'data': data, 'confidence': confidence}
|
| 23 |
|
| 24 |
|
| 25 |
+
# def test_for_float_or_int(message_data, message_text):
|
| 26 |
+
# nlu_response = {}
|
| 27 |
+
# if type(message_text) == int or type(message_text) == float:
|
| 28 |
+
# nlu_response = build_nlu_response_object('integer', message_text, '')
|
| 29 |
+
# prepare_message_data_for_logging(message_data, nlu_response)
|
| 30 |
+
# return nlu_response
|
| 31 |
|
| 32 |
|
| 33 |
def test_for_number_sequence(message_text_arr, message_data, message_text):
|
| 34 |
+
""" Determines if the student's message is a sequence of numbers
|
| 35 |
+
|
| 36 |
+
>>> test_for_number_sequence(['1','2','3'], {"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "I am tired", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"}, '1, 2, 3')
|
| 37 |
+
{'type': 'integer', 'data': '1,2,3', 'confidence': 0}
|
| 38 |
+
|
| 39 |
+
>>> test_for_number_sequence(['a','b','c'], {"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "I am tired", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"}, 'a, b, c')
|
| 40 |
+
{}
|
| 41 |
+
"""
|
| 42 |
nlu_response = {}
|
| 43 |
if all(ele.isdigit() for ele in message_text_arr):
|
| 44 |
nlu_response = build_nlu_response_object(
|
| 45 |
'integer',
|
| 46 |
','.join(message_text_arr),
|
| 47 |
+
0
|
| 48 |
)
|
| 49 |
prepare_message_data_for_logging(message_data, nlu_response)
|
| 50 |
return nlu_response
|
|
|
|
| 58 |
|
| 59 |
Output
|
| 60 |
- student_response_arr: list - a set of integers (32202 for error code)
|
| 61 |
+
|
| 62 |
+
>>> run_text2int_on_each_list_item(['1','2','3'])
|
| 63 |
+
[1, 2, 3]
|
| 64 |
"""
|
| 65 |
student_response_arr = []
|
| 66 |
for student_response in message_text_arr:
|
|
|
|
| 70 |
|
| 71 |
|
| 72 |
def run_sentiment_analysis(message_text):
|
| 73 |
+
""" Evaluates the sentiment of a student message
|
| 74 |
+
|
| 75 |
+
>>> run_sentiment_analysis("I am tired")
|
| 76 |
+
[{'label': 'NEGATIVE', 'score': 0.9997807145118713}]
|
| 77 |
+
|
| 78 |
+
>>> run_sentiment_analysis("I am full of joy")
|
| 79 |
+
[{'label': 'POSITIVE', 'score': 0.999882698059082}]
|
| 80 |
+
"""
|
| 81 |
# TODO: Add intent labelling here
|
| 82 |
# TODO: Add logic to determine whether intent labeling or sentiment analysis is more appropriate (probably default to intent labeling)
|
| 83 |
return sentiment(message_text)
|
| 84 |
|
| 85 |
|
| 86 |
+
def run_intent_classification(message_text):
|
| 87 |
+
""" Process a student's message using basic fuzzy text comparison
|
| 88 |
+
|
| 89 |
+
>>> run_intent_classification("exit")
|
| 90 |
+
{'type': 'intent', 'data': 'exit', 'confidence': 1.0}
|
| 91 |
+
>>> run_intent_classification("exi")
|
| 92 |
+
{'type': 'intent', 'data': 'exit', 'confidence': 0.86}
|
| 93 |
+
>>> run_intent_classification("eas")
|
| 94 |
+
{'type': 'intent', 'data': '', 'confidence': 0}
|
| 95 |
+
>>> run_intent_classification("hard")
|
| 96 |
+
{'type': 'intent', 'data': '', 'confidence': 0}
|
| 97 |
+
>>> run_intent_classification("hardier")
|
| 98 |
+
{'type': 'intent', 'data': 'harder', 'confidence': 0.92}
|
| 99 |
+
"""
|
| 100 |
+
label = ''
|
| 101 |
+
ratio = 0
|
| 102 |
+
nlu_response = {'type': 'intent', 'data': label, 'confidence': ratio}
|
| 103 |
+
commands = [
|
| 104 |
+
'easier',
|
| 105 |
+
'exit',
|
| 106 |
+
'harder',
|
| 107 |
+
'hint',
|
| 108 |
+
'next',
|
| 109 |
+
'stop',
|
| 110 |
+
]
|
| 111 |
+
|
| 112 |
+
for command in commands:
|
| 113 |
+
ratio = fuzz.ratio(command, message_text.lower())
|
| 114 |
+
if ratio > 80:
|
| 115 |
+
nlu_response['data'] = command
|
| 116 |
+
nlu_response['confidence'] = ratio / 100
|
| 117 |
+
|
| 118 |
+
return nlu_response
|
| 119 |
+
|
| 120 |
+
|
| 121 |
def evaluate_message_with_nlu(message_data):
|
| 122 |
+
""" Process a student's message using NLU functions and send the result
|
| 123 |
+
|
| 124 |
+
>>> evaluate_message_with_nlu({"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "8", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"})
|
| 125 |
+
{'type': 'integer', 'data': 8, 'confidence': 0}
|
| 126 |
+
|
| 127 |
+
>>> evaluate_message_with_nlu({"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "I am tired", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"})
|
| 128 |
+
{'type': 'sentiment', 'data': 'NEGATIVE', 'confidence': 0.9997807145118713}
|
| 129 |
+
"""
|
| 130 |
# Keeps system working with two different inputs - full and filtered @event object
|
| 131 |
try:
|
| 132 |
message_text = message_data['message_body']
|
|
|
|
| 143 |
}
|
| 144 |
message_text = message_data['message_body']
|
| 145 |
|
| 146 |
+
# Run intent classification only for keywords
|
| 147 |
+
intent_api_response = run_intent_classification(message_text)
|
| 148 |
+
if intent_api_response['data']:
|
| 149 |
+
return intent_api_response
|
| 150 |
+
|
| 151 |
number_api_resp = text2int(message_text.lower())
|
| 152 |
|
| 153 |
if number_api_resp == 32202:
|
| 154 |
+
# Run intent classification with logistic regression model
|
| 155 |
+
predicted_label = predict_message_intent(message_text)
|
| 156 |
+
if predicted_label['confidence'] > 0.01:
|
| 157 |
+
nlu_response = predicted_label
|
| 158 |
+
else:
|
| 159 |
+
# Run sentiment analysis
|
| 160 |
+
sentiment_api_resp = sentiment(message_text)
|
| 161 |
+
nlu_response = build_nlu_response_object(
|
| 162 |
+
'sentiment',
|
| 163 |
+
sentiment_api_resp[0]['label'],
|
| 164 |
+
sentiment_api_resp[0]['score']
|
| 165 |
+
)
|
| 166 |
else:
|
| 167 |
nlu_response = build_nlu_response_object(
|
| 168 |
'integer',
|
| 169 |
number_api_resp,
|
| 170 |
+
0
|
| 171 |
)
|
| 172 |
|
| 173 |
prepare_message_data_for_logging(message_data, nlu_response)
|
mathtext_fastapi/v2_conversation_manager.py
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import base64
|
| 2 |
+
import copy
|
| 3 |
+
import dill
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
import jsonpickle
|
| 7 |
+
import pickle
|
| 8 |
+
import random
|
| 9 |
+
import requests
|
| 10 |
+
import mathtext_fastapi.global_state_manager as gsm
|
| 11 |
+
|
| 12 |
+
from dotenv import load_dotenv
|
| 13 |
+
from mathtext_fastapi.nlu import evaluate_message_with_nlu
|
| 14 |
+
from mathtext_fastapi.math_quiz_fsm import MathQuizFSM
|
| 15 |
+
from mathtext_fastapi.math_subtraction_fsm import MathSubtractionFSM
|
| 16 |
+
from supabase import create_client
|
| 17 |
+
from transitions import Machine
|
| 18 |
+
|
| 19 |
+
from scripts.quiz.generators import start_interactive_math
|
| 20 |
+
from scripts.quiz.hints import generate_hint
|
| 21 |
+
|
| 22 |
+
load_dotenv()
|
| 23 |
+
|
| 24 |
+
SUPA = create_client(
|
| 25 |
+
os.environ.get('SUPABASE_URL'),
|
| 26 |
+
os.environ.get('SUPABASE_KEY')
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def pickle_and_encode_state_machine(state_machine):
|
| 31 |
+
dump = pickle.dumps(state_machine)
|
| 32 |
+
dump_encoded = base64.b64encode(dump).decode('utf-8')
|
| 33 |
+
return dump_encoded
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def manage_math_quiz_fsm(user_message, contact_uuid, type):
|
| 37 |
+
fsm_check = SUPA.table('state_machines').select("*").eq(
|
| 38 |
+
"contact_uuid",
|
| 39 |
+
contact_uuid
|
| 40 |
+
).execute()
|
| 41 |
+
|
| 42 |
+
# This doesn't allow for when one FSM is present and the other is empty
|
| 43 |
+
"""
|
| 44 |
+
1
|
| 45 |
+
data=[] count=None
|
| 46 |
+
|
| 47 |
+
2
|
| 48 |
+
data=[{'id': 29, 'contact_uuid': 'j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09', 'addition3': None, 'subtraction': None, 'addition':
|
| 49 |
+
|
| 50 |
+
- but problem is there is no subtraction , but it's assuming there's a subtration
|
| 51 |
+
|
| 52 |
+
Cases
|
| 53 |
+
- make a completely new record
|
| 54 |
+
- update an existing record with an existing FSM
|
| 55 |
+
- update an existing record without an existing FSM
|
| 56 |
+
"""
|
| 57 |
+
print("MATH QUIZ FSM ACTIVITY")
|
| 58 |
+
print("user_message")
|
| 59 |
+
print(user_message)
|
| 60 |
+
# Make a completely new entry
|
| 61 |
+
if fsm_check.data == []:
|
| 62 |
+
if type == 'addition':
|
| 63 |
+
math_quiz_state_machine = MathQuizFSM()
|
| 64 |
+
else:
|
| 65 |
+
math_quiz_state_machine = MathSubtractionFSM()
|
| 66 |
+
messages = [math_quiz_state_machine.response_text]
|
| 67 |
+
dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine)
|
| 68 |
+
|
| 69 |
+
SUPA.table('state_machines').insert({
|
| 70 |
+
'contact_uuid': contact_uuid,
|
| 71 |
+
f'{type}': dump_encoded
|
| 72 |
+
}).execute()
|
| 73 |
+
# Update an existing record with a new state machine
|
| 74 |
+
elif not fsm_check.data[0][type]:
|
| 75 |
+
if type == 'addition':
|
| 76 |
+
math_quiz_state_machine = MathQuizFSM()
|
| 77 |
+
else:
|
| 78 |
+
math_quiz_state_machine = MathSubtractionFSM()
|
| 79 |
+
messages = [math_quiz_state_machine.response_text]
|
| 80 |
+
dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine)
|
| 81 |
+
|
| 82 |
+
SUPA.table('state_machines').update({
|
| 83 |
+
f'{type}': dump_encoded
|
| 84 |
+
}).eq(
|
| 85 |
+
"contact_uuid", contact_uuid
|
| 86 |
+
).execute()
|
| 87 |
+
# Update an existing record with an existing state machine
|
| 88 |
+
elif fsm_check.data[0][type]:
|
| 89 |
+
undump_encoded = base64.b64decode(
|
| 90 |
+
fsm_check.data[0][type].encode('utf-8')
|
| 91 |
+
)
|
| 92 |
+
math_quiz_state_machine = pickle.loads(undump_encoded)
|
| 93 |
+
|
| 94 |
+
math_quiz_state_machine.student_answer = user_message
|
| 95 |
+
math_quiz_state_machine.correct_answer = str(math_quiz_state_machine.correct_answer)
|
| 96 |
+
messages = math_quiz_state_machine.validate_answer()
|
| 97 |
+
dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine)
|
| 98 |
+
SUPA.table('state_machines').update({
|
| 99 |
+
f'{type}': dump_encoded
|
| 100 |
+
}).eq(
|
| 101 |
+
"contact_uuid", contact_uuid
|
| 102 |
+
).execute()
|
| 103 |
+
return messages
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def retrieve_microlesson_content(context_data, user_message, microlesson, contact_uuid):
|
| 107 |
+
if context_data['local_state'] == 'addition-question-sequence' or \
|
| 108 |
+
user_message == 'add' or \
|
| 109 |
+
microlesson == 'addition':
|
| 110 |
+
messages = manage_math_quiz_fsm(user_message, contact_uuid, 'addition')
|
| 111 |
+
|
| 112 |
+
if user_message == 'exit':
|
| 113 |
+
state_label = 'exit'
|
| 114 |
+
else:
|
| 115 |
+
state_label = 'addition-question-sequence'
|
| 116 |
+
|
| 117 |
+
input_prompt = messages.pop()
|
| 118 |
+
message_package = {
|
| 119 |
+
'messages': messages,
|
| 120 |
+
'input_prompt': input_prompt,
|
| 121 |
+
'state': state_label
|
| 122 |
+
}
|
| 123 |
+
elif context_data['local_state'] == 'subtraction-question-sequence' or \
|
| 124 |
+
user_message == 'subtract' or \
|
| 125 |
+
microlesson == 'subtraction':
|
| 126 |
+
messages = manage_math_quiz_fsm(user_message, contact_uuid, 'subtraction')
|
| 127 |
+
|
| 128 |
+
if user_message == 'exit':
|
| 129 |
+
state_label = 'exit'
|
| 130 |
+
else:
|
| 131 |
+
state_label = 'subtraction-question-sequence'
|
| 132 |
+
|
| 133 |
+
input_prompt = messages.pop()
|
| 134 |
+
|
| 135 |
+
message_package = {
|
| 136 |
+
'messages': messages,
|
| 137 |
+
'input_prompt': input_prompt,
|
| 138 |
+
'state': state_label
|
| 139 |
+
}
|
| 140 |
+
print("MICROLESSON CONTENT RESPONSE")
|
| 141 |
+
print(message_package)
|
| 142 |
+
return message_package
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
curriculum_lookup_table = {
|
| 146 |
+
'N1.1.1_G1': 'addition',
|
| 147 |
+
'N1.1.1_G2': 'subtraction'
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def lookup_local_state(next_state):
|
| 152 |
+
microlesson = curriculum_lookup_table[next_state]
|
| 153 |
+
return microlesson
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def create_text_message(message_text, whatsapp_id):
|
| 157 |
+
""" Fills a template with input values to send a text message to Whatsapp
|
| 158 |
+
|
| 159 |
+
Inputs
|
| 160 |
+
- message_text: str - the content that the message should display
|
| 161 |
+
- whatsapp_id: str - the message recipient's phone number
|
| 162 |
+
|
| 163 |
+
Outputs
|
| 164 |
+
- message_data: dict - a preformatted template filled with inputs
|
| 165 |
+
"""
|
| 166 |
+
message_data = {
|
| 167 |
+
"preview_url": False,
|
| 168 |
+
"recipient_type": "individual",
|
| 169 |
+
"to": whatsapp_id,
|
| 170 |
+
"type": "text",
|
| 171 |
+
"text": {
|
| 172 |
+
"body": message_text
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
return message_data
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def manage_conversation_response(data_json):
|
| 179 |
+
""" Calls functions necessary to determine message and context data """
|
| 180 |
+
print("V2 ENDPOINT")
|
| 181 |
+
|
| 182 |
+
user_message = ''
|
| 183 |
+
# whatsapp_id = data_json['author_id']
|
| 184 |
+
message_data = data_json['message_data']
|
| 185 |
+
context_data = data_json['context_data']
|
| 186 |
+
whatsapp_id = message_data['author_id']
|
| 187 |
+
print("MESSAGE DATA")
|
| 188 |
+
print(message_data)
|
| 189 |
+
print("CONTEXT DATA")
|
| 190 |
+
print(context_data)
|
| 191 |
+
print("=================")
|
| 192 |
+
|
| 193 |
+
# nlu_response = evaluate_message_with_nlu(message_data)
|
| 194 |
+
|
| 195 |
+
# context_data = {
|
| 196 |
+
# 'contact_uuid': 'abcdefg',
|
| 197 |
+
# 'current_state': 'N1.1.1_G2',
|
| 198 |
+
# 'user_message': '1',
|
| 199 |
+
# 'local_state': ''
|
| 200 |
+
# }
|
| 201 |
+
print("STEP 1")
|
| 202 |
+
print(data_json)
|
| 203 |
+
if not context_data['current_state']:
|
| 204 |
+
context_data['current_state'] = 'N1.1.1_G1'
|
| 205 |
+
|
| 206 |
+
curriculum_copy = copy.deepcopy(gsm.curriculum)
|
| 207 |
+
|
| 208 |
+
print("STEP 2")
|
| 209 |
+
if context_data['user_message'] == 'easier':
|
| 210 |
+
curriculum_copy.left()
|
| 211 |
+
next_state = curriculum_copy.state
|
| 212 |
+
elif context_data['user_message'] == 'harder':
|
| 213 |
+
curriculum_copy.right()
|
| 214 |
+
next_state = curriculum_copy.state
|
| 215 |
+
else:
|
| 216 |
+
next_state = context_data['current_state']
|
| 217 |
+
|
| 218 |
+
print("STEP 3")
|
| 219 |
+
microlesson = lookup_local_state(next_state)
|
| 220 |
+
|
| 221 |
+
print("microlesson")
|
| 222 |
+
print(microlesson)
|
| 223 |
+
|
| 224 |
+
microlesson_content = retrieve_microlesson_content(context_data, context_data['user_message'], microlesson, context_data['contact_uuid'])
|
| 225 |
+
|
| 226 |
+
headers = {
|
| 227 |
+
'Authorization': f"Bearer {os.environ.get('TURN_AUTHENTICATION_TOKEN')}",
|
| 228 |
+
'Content-Type': 'application/json'
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
# Send all messages for the current state before a user input prompt (text/button input request)
|
| 232 |
+
for message in microlesson_content['messages']:
|
| 233 |
+
data = create_text_message(message, whatsapp_id)
|
| 234 |
+
|
| 235 |
+
print("data")
|
| 236 |
+
print(data)
|
| 237 |
+
|
| 238 |
+
r = requests.post(
|
| 239 |
+
f'https://whatsapp.turn.io/v1/messages',
|
| 240 |
+
data=json.dumps(data),
|
| 241 |
+
headers=headers
|
| 242 |
+
)
|
| 243 |
+
|
| 244 |
+
print("STEP 4")
|
| 245 |
+
# combine microlesson content and context_data object
|
| 246 |
+
|
| 247 |
+
updated_context = {
|
| 248 |
+
"context": {
|
| 249 |
+
"contact_id": whatsapp_id,
|
| 250 |
+
"contact_uuid": context_data['contact_uuid'],
|
| 251 |
+
"state": microlesson_content['state'],
|
| 252 |
+
"bot_message": microlesson_content['input_prompt'],
|
| 253 |
+
"user_message": user_message,
|
| 254 |
+
"type": 'ask'
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
return updated_context
|
requirements.txt
CHANGED
|
@@ -1,12 +1,15 @@
|
|
| 1 |
dill
|
| 2 |
-
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1-py3-none-any.whl
|
|
|
|
| 3 |
jsonpickle
|
| 4 |
-
git+
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
pydantic
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
| 10 |
supabase
|
| 11 |
transitions
|
| 12 |
uvicorn
|
|
|
|
| 1 |
dill
|
| 2 |
+
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1-py3-none-any.whl
|
| 3 |
+
fuzzywuzzy
|
| 4 |
jsonpickle
|
| 5 |
+
mathtext @ git+https://gitlab.com/tangibleai/community/mathtext@main
|
| 6 |
+
fastapi==0.74.*
|
| 7 |
+
openpyxl
|
| 8 |
+
pydantic==1.10.*
|
| 9 |
+
python-Levenshtein
|
| 10 |
+
requests==2.27.*
|
| 11 |
+
sentencepiece==0.1.*
|
| 12 |
+
sentence-transformers
|
| 13 |
supabase
|
| 14 |
transitions
|
| 15 |
uvicorn
|
scripts/make_request.py
CHANGED
|
@@ -22,7 +22,11 @@ def add_message_text_to_sample_object(message_text):
|
|
| 22 |
message_data = '{' + f'"author_id": "+57787919091", "author_type": "OWNER", "contact_uuid": "j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09", "message_body": "{message_text}", "message_direction": "inbound", "message_id": "4kl209sd0-a7b8-2hj3-8563-3hu4a89b32", "message_inserted_at": "2023-01-10T02:37:28.477940Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"' + '}'
|
| 23 |
# context_data = '{' + '"user":"", "state":"addition-question-sequence", "bot_message":"", "user_message":"{message_text}"' + '}'
|
| 24 |
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
# context_data = '{' + '"user":"", "state":"addition-question-sequence", "bot_message":"", "user_message":"{message_text}","text": "What is 2+3?","question_numbers": [4,3],"right_answer": 7,"number_correct": 2, "number_incorrect": 0, "hints_used": 0, "level": "easy"' + '}'
|
| 28 |
|
|
@@ -44,7 +48,7 @@ def run_simulated_request(endpoint, sample_answer, context=None):
|
|
| 44 |
print(f"Case: {sample_answer}")
|
| 45 |
b_string = add_message_text_to_sample_object(sample_answer)
|
| 46 |
|
| 47 |
-
if endpoint == 'sentiment-analysis' or endpoint == 'text2int':
|
| 48 |
request = requests.post(
|
| 49 |
url=f'http://localhost:7860/{endpoint}',
|
| 50 |
json={'content': sample_answer}
|
|
@@ -58,21 +62,25 @@ def run_simulated_request(endpoint, sample_answer, context=None):
|
|
| 58 |
print(request)
|
| 59 |
|
| 60 |
|
|
|
|
|
|
|
| 61 |
# run_simulated_request('sentiment-analysis', 'I reject it')
|
| 62 |
# run_simulated_request('text2int', 'seven thousand nine hundred fifty seven')
|
| 63 |
-
run_simulated_request('nlu', 'test message')
|
| 64 |
-
run_simulated_request('nlu', 'eight')
|
| 65 |
-
run_simulated_request('nlu', 'is it 8')
|
| 66 |
-
run_simulated_request('nlu', 'can I know how its 0.5')
|
| 67 |
-
run_simulated_request('nlu', 'eight, nine, ten')
|
| 68 |
-
run_simulated_request('nlu', '8, 9, 10')
|
| 69 |
-
run_simulated_request('nlu', '8')
|
| 70 |
-
run_simulated_request('nlu', "I don't know")
|
| 71 |
-
run_simulated_request('nlu', "I don't know eight")
|
| 72 |
-
run_simulated_request('nlu', "I don't 9")
|
| 73 |
-
run_simulated_request('nlu', "0.2")
|
| 74 |
-
run_simulated_request('nlu', 'Today is a wonderful day')
|
| 75 |
-
run_simulated_request('nlu', 'IDK 5?')
|
|
|
|
|
|
|
| 76 |
# run_simulated_request('manager', '')
|
| 77 |
# run_simulated_request('manager', 'add')
|
| 78 |
# run_simulated_request('manager', 'subtract')
|
|
|
|
| 22 |
message_data = '{' + f'"author_id": "+57787919091", "author_type": "OWNER", "contact_uuid": "j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09", "message_body": "{message_text}", "message_direction": "inbound", "message_id": "4kl209sd0-a7b8-2hj3-8563-3hu4a89b32", "message_inserted_at": "2023-01-10T02:37:28.477940Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"' + '}'
|
| 23 |
# context_data = '{' + '"user":"", "state":"addition-question-sequence", "bot_message":"", "user_message":"{message_text}"' + '}'
|
| 24 |
|
| 25 |
+
# V1
|
| 26 |
+
# context_data = '{' + '"user":"", "state":"start-conversation", "bot_message":"", "user_message":"{message_text}"' + '}'
|
| 27 |
+
|
| 28 |
+
#V2
|
| 29 |
+
context_data = '{' + '"contact_uuid": "j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09", "current_state":"", "local_state": "", "user_message":""' + '}'
|
| 30 |
|
| 31 |
# context_data = '{' + '"user":"", "state":"addition-question-sequence", "bot_message":"", "user_message":"{message_text}","text": "What is 2+3?","question_numbers": [4,3],"right_answer": 7,"number_correct": 2, "number_incorrect": 0, "hints_used": 0, "level": "easy"' + '}'
|
| 32 |
|
|
|
|
| 48 |
print(f"Case: {sample_answer}")
|
| 49 |
b_string = add_message_text_to_sample_object(sample_answer)
|
| 50 |
|
| 51 |
+
if endpoint == 'sentiment-analysis' or endpoint == 'text2int' or endpoint =='intent-classification':
|
| 52 |
request = requests.post(
|
| 53 |
url=f'http://localhost:7860/{endpoint}',
|
| 54 |
json={'content': sample_answer}
|
|
|
|
| 62 |
print(request)
|
| 63 |
|
| 64 |
|
| 65 |
+
# run_simulated_request('intent-classification', 'exit')
|
| 66 |
+
# run_simulated_request('intent-classification', "I'm not sure")
|
| 67 |
# run_simulated_request('sentiment-analysis', 'I reject it')
|
| 68 |
# run_simulated_request('text2int', 'seven thousand nine hundred fifty seven')
|
| 69 |
+
# run_simulated_request('nlu', 'test message')
|
| 70 |
+
# run_simulated_request('nlu', 'eight')
|
| 71 |
+
# run_simulated_request('nlu', 'is it 8')
|
| 72 |
+
# run_simulated_request('nlu', 'can I know how its 0.5')
|
| 73 |
+
# run_simulated_request('nlu', 'eight, nine, ten')
|
| 74 |
+
# run_simulated_request('nlu', '8, 9, 10')
|
| 75 |
+
# run_simulated_request('nlu', '8')
|
| 76 |
+
# run_simulated_request('nlu', "I don't know")
|
| 77 |
+
# run_simulated_request('nlu', "I don't know eight")
|
| 78 |
+
# run_simulated_request('nlu', "I don't 9")
|
| 79 |
+
# run_simulated_request('nlu', "0.2")
|
| 80 |
+
# run_simulated_request('nlu', 'Today is a wonderful day')
|
| 81 |
+
# run_simulated_request('nlu', 'IDK 5?')
|
| 82 |
+
run_simulated_request('v2/manager', '')
|
| 83 |
+
run_simulated_request('v2/manager', '5')
|
| 84 |
# run_simulated_request('manager', '')
|
| 85 |
# run_simulated_request('manager', 'add')
|
| 86 |
# run_simulated_request('manager', 'subtract')
|