Hobson/mathtext-fastapi
1
1"""FastAPI endpoint2To run locally use 'uvicorn app:app --host localhost --port 7860'3"""4import ast5import scripts.quiz.generators as generators6import scripts.quiz.hints as hints7import scripts.quiz.questions as questions8import scripts.quiz.utils as utils9import sentry_sdk10 11from fastapi import FastAPI, Request12from fastapi.responses import JSONResponse13from fastapi.staticfiles import StaticFiles14from fastapi.templating import Jinja2Templates15from mathtext.sentiment import sentiment16from mathtext.text2int import text2int17from pydantic import BaseModel18 19from mathtext_fastapi.logging import prepare_message_data_for_logging20from mathtext_fastapi.conversation_manager import manage_conversation_response21from mathtext_fastapi.nlu import evaluate_message_with_nlu22from mathtext_fastapi.nlu import run_intent_classification23 24sentry_sdk.init(25 dsn="https://143c9ac3f429452eb036deda0e4d5aef@o1297809.ingest.sentry.io/4504896688881664",26 27 # Set traces_sample_rate to 1.0 to capture 100%28 # of transactions for performance monitoring.29 # We recommend adjusting this value in production,30 traces_sample_rate=0.20,31)32 33app = FastAPI()34 35app.mount("/static", StaticFiles(directory="static"), name="static")36 37templates = Jinja2Templates(directory="templates")38 39 40class Text(BaseModel):41 content: str = ""42 43 44@app.get("/")45def home(request: Request):46 return templates.TemplateResponse("home.html", {"request": request})47 48 49@app.get("/sentry-debug")50async def trigger_error():51 division_by_zero = 1 / 052 53 54@app.post("/hello")55def hello(content: Text = None):56 content = {"message": f"Hello {content.content}!"}57 return JSONResponse(content=content)58 59 60@app.post("/sentiment-analysis")61def sentiment_analysis_ep(content: Text = None):62 ml_response = sentiment(content.content)63 content = {"message": ml_response}64 return JSONResponse(content=content)65 66 67@app.post("/text2int")68def text2int_ep(content: Text = None):69 ml_response = text2int(content.content)70 content = {"message": ml_response}71 return JSONResponse(content=content)72 73 74@app.post("/manager")75async def programmatic_message_manager(request: Request):76 """77 Calls conversation management function to determine the next state78 79 Input80 request.body: dict - message data for the most recent user response81 {82 "author_id": "+47897891",83 "contact_uuid": "j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09",84 "author_type": "OWNER",85 "message_body": "a test message",86 "message_direction": "inbound",87 "message_id": "ABJAK64jlk3-agjkl2QHFAFH",88 "message_inserted_at": "2022-07-05T04:00:34.03352Z",89 "message_updated_at": "2023-02-14T03:54:19.342950Z",90 }91 92 Output93 context: dict - the information for the current state94 {95 "user": "47897891",96 "state": "welcome-message-state",97 "bot_message": "Welcome to Rori!",98 "user_message": "",99 "type": "ask"100 }101 """102 data_dict = await request.json()103 context = manage_conversation_response(data_dict)104 return JSONResponse(context)105 106 107@app.post("/intent-classification")108def intent_classification_ep(content: Text = None):109 ml_response = run_intent_classification(content.content)110 content = {"message": ml_response}111 return JSONResponse(content=content)112 113 114@app.post("/nlu")115async def evaluate_user_message_with_nlu_api(request: Request):116 """ Calls nlu evaluation and returns the nlu_response117 118 Input119 - request.body: json - message data for the most recent user response120 121 Output122 - int_data_dict or sent_data_dict: dict - the type of NLU run and result123 {'type':'integer', 'data': '8', 'confidence': 0}124 {'type':'sentiment', 'data': 'negative', 'confidence': 0.99}125 """126 data_dict = await request.json()127 message_data = data_dict.get('message_data', '')128 nlu_response = evaluate_message_with_nlu(message_data)129 return JSONResponse(content=nlu_response)130 131 132@app.post("/question")133async def ask_math_question(request: Request):134 """Generate a question and return it as response along with question data135 136 Input137 request.body: json - amount of correct and incorrect answers in the account138 {139 'number_correct': 0,140 'number_incorrect': 0,141 'level': 'easy'142 }143 144 Output145 context: dict - the information for the current state146 {147 'text': 'What is 1+2?',148 'question_numbers': [1,2,3], #3 numbers - current number, ordinal number, times149 'right_answer': 3,150 'number_correct': 0,151 'number_incorrect': 0,152 'hints_used': 0153 }154 """155 data_dict = await request.json()156 message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))157 right_answers = message_data['number_correct']158 wrong_answers = message_data['number_incorrect']159 level = message_data['level']160 161 return JSONResponse(generators.start_interactive_math(right_answers, wrong_answers, level))162 163 164@app.post("/hint")165async def get_hint(request: Request):166 """Generate a hint and return it as response along with hint data167 168 Input169 request.body:170 {171 'question_numbers': [1,2,3], #3 numbers - current number, ordinal number, times172 'right_answer': 3,173 'number_correct': 0,174 'number_incorrect': 0,175 'level': 'easy',176 'hints_used': 0177 }178 179 Output180 context: dict - the information for the current state181 {182 'text': 'What is 1+2?',183 'question_numbers': [1,2,3], #2 or 3 numbers184 'right_answer': 3,185 'number_correct': 0,186 'number_incorrect': 0,187 'level': 'easy',188 'hints_used': 0189 }190 """191 data_dict = await request.json()192 message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))193 question_numbers = message_data['question_numbers']194 right_answer = message_data['right_answer']195 number_correct = message_data['number_correct']196 number_incorrect = message_data['number_incorrect']197 level = message_data['level']198 hints_used = message_data['hints_used']199 200 return JSONResponse(hints.generate_hint(question_numbers, right_answer, number_correct, number_incorrect, level, hints_used))201 202 203@app.post("/generate_question")204async def generate_question(request: Request):205 """Generate a bare question and return it as response206 207 Input208 request.body: json - level209 {210 'level': 'easy'211 }212 213 Output214 context: dict - the information for the current state215 {216 "question": "Let's count up by 2s. What number is next if we start from 10?217 6 8 10 ..."218 }219 """220 data_dict = await request.json()221 message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))222 level = message_data['level']223 224 return JSONResponse(questions.generate_question_data(level)['question'])225 226 227@app.post("/numbers_by_level")228async def get_numbers_by_level(request: Request):229 """Generate three numbers and return them as response230 231 Input232 request.body: json - level233 {234 'level': 'easy'235 }236 237 Output238 context: dict - three generated numbers for specified level239 {240 "current_number": 10,241 "ordinal_number": 2,242 "times": 1243 }244 """245 data_dict = await request.json()246 message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))247 level = message_data['level']248 return JSONResponse(questions.generate_numbers_by_level(level))249 250 251@app.post("/number_sequence")252async def get_number_sequence(request: Request):253 """Generate a number sequence254 255 Input256 request.body: json - level257 {258 "current_number": 10,259 "ordinal_number": 2,260 "times": 1261 }262 263 Output264 one of following strings with (numbers differ):265 ... 1 2 3266 1 2 3 ...267 """268 data_dict = await request.json()269 message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))270 cur_num = message_data['current_number']271 ord_num = message_data['ordinal_number']272 times = message_data['times']273 return JSONResponse(questions.generate_number_sequence(cur_num, ord_num, times))274 275 276@app.post("/level")277async def get_next_level(request: Request):278 """Depending on current level and desire to level up/down return next level279 280 Input281 request.body: json - level282 {283 "current_level": "easy",284 "level_up": True285 }286 287 Output288 Literal - "easy", "medium" or "hard"289 """290 data_dict = await request.json()291 message_data = ast.literal_eval(data_dict.get('message_data', '').get('message_body', ''))292 cur_level = message_data['current_level']293 level_up = message_data['level_up']294 return JSONResponse(utils.get_next_level(cur_level, level_up))295 