Spaces:
Runtime error
Runtime error
import datetime | |
import os | |
import json | |
import gradio as gr | |
import requests | |
import firebase_admin | |
from firebase_admin import db, credentials | |
def clamp(x, minimum, maximum): | |
return max(minimum, min(x, maximum)) | |
################################################################################################################################################# | |
# API calls | |
################################################################################################################################################# | |
# read secret api key | |
API_KEY = os.environ['ApiKey'] | |
FIREBASE_API_KEY = os.environ['FirebaseSecret'] | |
FIREBASE_URL = os.environ['FirebaseURL'] | |
creds = credentials.Certificate(json.loads(FIREBASE_API_KEY)) | |
firebase_app = firebase_admin.initialize_app(creds, {'databaseURL': FIREBASE_URL}) | |
firebase_data_ref = db.reference("data") | |
firebase_current_ref = None | |
base_url = "https://skapi.polyglot-edu.com/" | |
levels = ["Primary School", "Middle School", "High School", "College", "Academy"] | |
languages = ["English", "Italian", "French", "German", "Spanish"] | |
categories = ["Factual Knowledge", "Understanding of Concepts", "Application of Skills", "Analysys And Evaluation"] | |
def get_level_mapping(level): | |
if level is None: | |
raise gr.Error("Please select a level.") | |
return levels.index(level) | |
def get_category_mapping(category): | |
if category is None: | |
raise gr.Error("Please select a level.") | |
return categories.index(category) | |
def generate_fill_gaps(original_text, level, number_of_words, number_of_gaps, number_of_distractors, temperature, language): | |
""" | |
Generate a fill-gaps question from a given text. | |
Parameters | |
---------- | |
original_text : str | |
The original text from which to generate the fill-gaps question. | |
number_of_words : int | |
The number of words of the generated text. | |
number_of_gaps : int | |
The number of gaps to generate. | |
number_of_distractors : int | |
The number of distractors to generate for each gap. | |
temperature : float | |
The temperature for the generation. | |
language : str | |
The language of the generated text. | |
Returns | |
------- | |
str | |
The fill-gaps question. | |
""" | |
match number_of_words: | |
case "β 150 Words": | |
number_of_words = 150 | |
case "β 250 Words": | |
number_of_words = 250 | |
case "β 350 Words": | |
number_of_words = 350 | |
input_json = { | |
"text": original_text, | |
"level": get_level_mapping(level), | |
"n_o_w": int(number_of_words), | |
"n_o_g": int(number_of_gaps), | |
"n_o_d": int(number_of_distractors), | |
"temperature": int(temperature) * 0.2, | |
"language": language | |
} | |
print(input_json) | |
output = "" | |
try: | |
response = requests.post( | |
base_url + "FillTheGaps/generateexercise", | |
headers={"ApiKey": API_KEY}, | |
json=input_json, | |
timeout=20 | |
) | |
if response.status_code != 200: | |
output = f"API call failed with status code {response.status_code} and message '{response.text}'" | |
raise Exception(f"API call failed with status code {response.status_code} and message '{response.text}'") | |
output = response.text | |
return output | |
finally: | |
global firebase_current_ref | |
firebase_current_ref = firebase_data_ref.push({ | |
"type": "fill_the_gaps", | |
**input_json, | |
"output": output, | |
"datetime": str(datetime.datetime.now()), | |
"like": 0, | |
"comment_text": "", | |
"flagged": False, | |
}) | |
def generate_open_question(original_text, level, temperature, language, question_type, question_category): | |
""" | |
Generate an open question from a given text. | |
Parameters | |
---------- | |
original_text : str | |
The original text from which to generate the open question. | |
temperature : float | |
The temperature for the generation. | |
language : str | |
The language of the generated text. | |
question_type : str | |
The type of the question. | |
question_category : str | |
The category of the question. | |
Returns | |
------- | |
str | |
The open question. | |
""" | |
input_json = { | |
"text": original_text, | |
"level": get_level_mapping(level), | |
"temperature": int(temperature) * 0.2, | |
"language": language, | |
"type": question_type, | |
"category": get_category_mapping(question_category), | |
} | |
print(input_json) | |
output = "" | |
try: | |
response = requests.post( | |
base_url + "QuestionExercise/generateexercise", | |
headers={"ApiKey": API_KEY}, | |
json=input_json, | |
timeout=20 | |
) | |
if response.status_code != 200: | |
output = f"API call failed with status code {response.status_code} and message '{response.text}'" | |
raise Exception(f"API call failed with status code {response.status_code} and message '{response.text}'") | |
output = response.text | |
return output | |
finally: | |
global firebase_current_ref | |
firebase_current_ref = firebase_data_ref.push({ | |
"type": "open_question", | |
**input_json, | |
"output": output, | |
"datetime": str(datetime.datetime.now()), | |
"like": 0, | |
"comment_text": "", | |
"flagged": False, | |
}) | |
def generate_multiplechoice(original_text, level, number_of_easy_distractors, number_of_distractors, temperature, language, question_category, correct_answers, exercise_type): | |
""" | |
Generate a multiple-choice question from a given text. | |
Parameters | |
---------- | |
original_text : str | |
The original text from which to generate the multiple-choice question. | |
number_of_easy_distractors : int | |
The number of easy distractors to generate for each option. | |
number_of_distractors : int | |
The number of distractors to generate for each option. | |
temperature : float | |
The temperature for the generation. | |
language : str | |
The language of the generated text. | |
question_category : str | |
The category of the question. | |
correct_answers : int | |
The number of correct answers. | |
exercise_type : bool | |
The type of the exercise (theory or practice). | |
Returns | |
------- | |
str | |
The multiple-choice question. | |
""" | |
input_json = { | |
"text": original_text, | |
"type": bool(exercise_type), | |
"level": get_level_mapping(level), | |
"category": get_category_mapping(question_category), | |
"n_o_ca": int(correct_answers), | |
"n_o_d": int(number_of_distractors), | |
"nedd": int(number_of_easy_distractors), | |
"temperature": int(temperature) * 0.2, | |
"language": language | |
} | |
print(input_json) | |
output = "" | |
try: | |
response = requests.post( | |
base_url + "QuizExercise/generateexercise", | |
headers={"ApiKey": API_KEY}, | |
json=input_json, | |
timeout=20 | |
) | |
if response.status_code != 200: | |
output = f"API call failed with status code {response.status_code} and message '{response.text}'" | |
raise Exception(f"API call failed with status code {response.status_code} and message '{response.text}'") | |
output = response.text | |
return output | |
finally: | |
global firebase_current_ref | |
firebase_current_ref = firebase_data_ref.push({ | |
"type": "open_question", | |
**input_json, | |
"output": output, | |
"datetime": str(datetime.datetime.now()), | |
"like": 0, | |
"comment_text": "", | |
"flagged": False, | |
}) | |
def like(): | |
global firebase_current_ref | |
if firebase_current_ref is not None: | |
firebase_current_ref.update({"like": 1}) | |
gr.Info("Generated text liked.") | |
else: | |
gr.Warning("No generated text to vote.") | |
def neutral_like(): | |
global firebase_current_ref | |
if firebase_current_ref is not None: | |
firebase_current_ref.update({"like": 0}) | |
gr.Info("Generated text preference removed.") | |
else: | |
gr.Warning("No generated text to vote.") | |
def dislike(): | |
global firebase_current_ref | |
if firebase_current_ref is not None: | |
firebase_current_ref.update({"like": -1}) | |
gr.Info("Generated text disliked.") | |
else: | |
gr.Warning("No generated text to vote.") | |
def comment(comment_text): | |
global firebase_current_ref | |
if firebase_current_ref is not None: | |
firebase_current_ref.update({"comment": comment_text}) | |
gr.Info("Comment added.") | |
else: | |
raise gr.Error("No generated text to comment.") | |
def flag(flag_btn): | |
global firebase_current_ref | |
if firebase_current_ref is not None: | |
firebase_current_ref.update({"flagged": not firebase_current_ref.get()["flagged"]}) | |
if firebase_current_ref.get()["flagged"]: | |
gr.Info("Generated text flagged.") | |
value = "β οΈ Unflag" | |
else: | |
gr.Info("Generated text unflagged.") | |
value = "β οΈ Flag" | |
return gr.Button(value=value) | |
else: | |
gr.Warning("No generated text to flag.") | |
return flag_btn | |
################################################################################################################################################# | |
# Interface building | |
################################################################################################################################################# | |
def build_fill_gaps_interface(): | |
""" | |
Build the fill-gaps interface. | |
""" | |
with gr.Blocks(title="Fill-gaps") as demo: | |
with gr.Row(): | |
with gr.Column(scale=5): | |
input_field = gr.TextArea(lines=10, max_lines=10, label="Input Educational resource text or URL", | |
info="Note: If the input resource is too long, the API will return an error. If the input is an URL, it must be a valid URL to webpage or a file.") | |
submit_btn = gr.Button(value="Submit") | |
with gr.Column(scale=4): | |
level = gr.Radio(levels, label="Level") | |
language = gr.Dropdown(languages, label="Language", value="English") | |
output_text_length = gr.Radio(["β 150 Words", "β 250 Words", "β 350 Words"], label="Output text length", value="β 150 Words") | |
with gr.Row(): | |
blanks = gr.Number(value=5, minimum=4, maximum=8, step=1, label="Number of blanks") | |
distractors = gr.Number(value=2, minimum=0, maximum=5, step=1, label="Number of distractors") | |
temperature = gr.Checkbox(value=False, label="Increase creativity (decreases preciseness)", visible=False) | |
def update_numeric(output_text_length, blanks, distractors): | |
if output_text_length == "β 150 Words": | |
min_, max_ = 4, 8 | |
elif output_text_length == "β 250 Words": | |
min_, max_ = 6, 10 | |
elif output_text_length == "β 350 Words": | |
min_, max_ = 8, 12 | |
return ( | |
gr.Number(value=clamp(blanks, min_, max_), minimum=min_, maximum=max_, label="Number of blanks"), | |
gr.Number(value=clamp(distractors, 0, blanks), minimum=0, maximum=blanks, label="Number of distractors") | |
) | |
def update_blanks(blanks, distractors): | |
return gr.Number(value=clamp(distractors, 0, blanks), minimum=0, maximum=blanks, label="Number of distractors") | |
blanks.change(update_blanks, [blanks, distractors], [distractors]) | |
output_text_length.change(update_numeric, [output_text_length, blanks, distractors], [blanks, distractors]) | |
with gr.Row(): | |
output = gr.TextArea(placeholder="Generated text", label="Output") | |
with gr.Row() as button_row: | |
upvote_btn = gr.Button(value="π Upvote") | |
remove_preference_btn = gr.Button(value="Remove Preference") | |
downvote_btn = gr.Button(value="π Downvote") | |
flag_btn = gr.Button(value="β οΈ Flag") | |
submit_btn.click(generate_fill_gaps, [input_field, level, output_text_length, blanks, distractors, temperature, language], [output]) | |
upvote_btn.click(like) | |
remove_preference_btn.click(neutral_like) | |
downvote_btn.click(dislike) | |
flag_btn.click(flag, [flag_btn], [flag_btn]) | |
return demo | |
def build_multiplechoice_interface(): | |
""" | |
Build the open question interface. | |
""" | |
with gr.Blocks(title="Open Question") as demo: | |
with gr.Row(): | |
with gr.Column(scale=5): | |
input_field = gr.TextArea(lines=10, max_lines=10, label="Input Educational resource text or URL", | |
info="Note: If the input resource is too long, the API will return an error. If the input is an URL, it must be a valid URL to webpage or a file.") | |
submit_btn = gr.Button(value="Submit") | |
with gr.Column(scale=4): | |
level = gr.Radio(levels, label="Level") | |
language = gr.Dropdown(languages, label="Language", value="English") | |
with gr.Row(): | |
exercise_type = gr.Radio(["Theoretical", "Practical"], label="Exercise type", value="Theoretical", type="index") | |
question_category = gr.Dropdown(categories, label="Question Category", value="Factual Knowledge") | |
with gr.Row(): | |
correct_answers = gr.Number(value=1, minimum=1, maximum=3, step=1, label="Number of correct answers") | |
easy_distractors = gr.Number(value=1, minimum=0, maximum=8, step=1, label="Number of easy distractors") | |
distractors = gr.Number(value=1, minimum=0, maximum=8, step=1, label="Number of distractors") | |
temperature = gr.Checkbox(value=False, label="Increase creativity (decreases preciseness)", visible=False) | |
def update_distractors(easy_distractors, distractors): | |
easy_distractors = clamp(easy_distractors, 0, distractors) | |
return gr.Number(value=easy_distractors, minimum=0, maximum=distractors, label="Number of easy distractors") | |
def update_exercise_type(exercise_type, question_category, number_of_correct_answers): | |
if exercise_type == 0: | |
return ( | |
gr.Dropdown(categories, label="Question Category", value="Analysys And Evaluation"), | |
gr.Number(value=number_of_correct_answers, minimum=1, maximum=3, step=1, label="Number of correct answers") | |
) | |
elif exercise_type == 1: | |
return ( | |
gr.Dropdown(["Analysys And Evaluation"], label="Question Category", value="Analysys And Evaluation"), | |
gr.Number(value=1, minimum=1, maximum=1, step=1, label="Number of correct answers") | |
) | |
distractors.change(update_distractors, [easy_distractors, distractors], [easy_distractors]) | |
exercise_type.change(update_exercise_type, [exercise_type, question_category, correct_answers], [question_category, correct_answers]) | |
with gr.Row(): | |
output = gr.TextArea(placeholder="Generated text", label="Output") | |
with gr.Row() as button_row: | |
upvote_btn = gr.Button(value="π Upvote") | |
remove_preference_btn = gr.Button(value="Remove Preference") | |
downvote_btn = gr.Button(value="π Downvote") | |
flag_btn = gr.Button(value="β οΈ Flag") | |
submit_btn.click(generate_multiplechoice, [input_field, level, easy_distractors, distractors, temperature, language, question_category, correct_answers, exercise_type], [output]) | |
upvote_btn.click(like) | |
remove_preference_btn.click(neutral_like) | |
downvote_btn.click(dislike) | |
flag_btn.click(flag, [flag_btn], [flag_btn]) | |
return demo | |
def build_open_question_interface(): | |
""" | |
Build the multiple-choice interface. | |
""" | |
with gr.Blocks(title="Multiple choice") as demo: | |
with gr.Row(): | |
with gr.Column(scale=5): | |
input_field = gr.TextArea(lines=10, max_lines=10, label="Input Educational resource text or URL", | |
info="Note: If the input resource is too long, the API will return an error. If the input is an URL, it must be a valid URL to webpage or a file.") | |
submit_btn = gr.Button(value="Submit") | |
with gr.Column(scale=4): | |
level = gr.Radio(levels, label="Level") | |
language = gr.Dropdown(languages, label="Language", value="English") | |
with gr.Row(): | |
question_type = gr.Dropdown(["Open", "ShortAnswer", "TrueFalse"], label="Question Type", value="Open", type="index") | |
question_category = gr.Dropdown(categories, label="Question Category", value="Factual Knowledge") | |
temperature = gr.Checkbox(value=False, label="Increase creativity (decreases preciseness)", visible=False) | |
with gr.Row(): | |
output = gr.TextArea(placeholder="Generated text", label="Output") | |
with gr.Row() as button_row: | |
upvote_btn = gr.Button(value="π Upvote") | |
remove_preference_btn = gr.Button(value="Remove Preference") | |
downvote_btn = gr.Button(value="π Downvote") | |
flag_btn = gr.Button(value="β οΈ Flag") | |
submit_btn.click(generate_open_question, [input_field, level, temperature, language, question_type, question_category], [output]) | |
upvote_btn.click(like) | |
remove_preference_btn.click(neutral_like) | |
downvote_btn.click(dislike) | |
flag_btn.click(flag, [flag_btn], [flag_btn]) | |
return demo | |
def build_demo(): | |
with gr.Blocks(title="Educational AI") as demo: | |
gr.Markdown("<h1 style='text-align: center; margin-bottom: 1rem'>Educational AI</h1>") | |
with gr.Row(): | |
gr.Markdown("<h4>Click on the button on the right to fill up our questionnaire!</h4>") | |
gr.Button(value="π Questionnaire", link="https://forms.gle/T8CS5CiQgPbKUdeM9", scale=0.5, interactive=True) | |
with gr.Tab("Fill-gaps"): | |
build_fill_gaps_interface() | |
with gr.Tab("Open question"): | |
build_open_question_interface() | |
with gr.Tab("Multiple-choice"): | |
build_multiplechoice_interface() | |
with gr.Blocks(): | |
with gr.Row(): | |
comment_text = gr.TextArea(placeholder="Comment", label="Comment", scale=6) | |
comment_btn = gr.Button(value="π¬ Comment", scale=2) | |
comment_btn.click(comment, [comment_text]) | |
return demo | |
if __name__ == "__main__": | |
build_demo().launch(share=False) | |