diff --git a/app.py b/app.py index cc3a05c..2ba5c86 100644 --- a/app.py +++ b/app.py @@ -4,6 +4,7 @@ from functools import reduce import firebase_admin from firebase_admin import credentials from helper.api_messages import QuestionType, get_grading_messages, get_question_gen_messages +from helper.file_helper import delete_files_older_than_one_day from helper.firebase_helper import download_firebase_file from helper.speech_to_text_helper import speech_to_text from helper.token_counter import count_tokens @@ -25,7 +26,7 @@ cred = credentials.Certificate(os.getenv("GOOGLE_APPLICATION_CREDENTIALS")) firebase_admin.initialize_app(cred) GRADING_TEMPERATURE = 0.1 -GEN_QUESTION_TEMPERATURE = 0.7 +GEN_QUESTION_TEMPERATURE = 0.9 GRADING_FIELDS = ['overall', 'comment', 'task_response'] GEN_FIELDS = ['question'] @@ -76,9 +77,10 @@ def get_writing_task_2_question(): except Exception as e: return str(e) -@app.route('/speaking_task', methods=['POST']) +@app.route('/speaking_task_1', methods=['POST']) @jwt_required() -def grade_speaking_task(): +def grade_speaking_task_1(): + delete_files_older_than_one_day(AUDIO_FILES_PATH) sound_file_name = AUDIO_FILES_PATH + str(uuid.uuid4()) try: data = request.get_json() @@ -88,7 +90,7 @@ def grade_speaking_task(): download_firebase_file(FIREBASE_BUCKET, answer_firebase_path, sound_file_name) answer = speech_to_text(sound_file_name) - messages = get_grading_messages(QuestionType.SPEAKING, question, answer) + messages = get_grading_messages(QuestionType.SPEAKING_1, question, answer) token_count = reduce(lambda count, item: count + count_tokens(item)['n_tokens'], map(lambda x: x["content"], filter(lambda x: "content" in x, messages)), 0) response = make_openai_call(messages, token_count, GRADING_FIELDS, GRADING_TEMPERATURE) @@ -98,11 +100,11 @@ def grade_speaking_task(): os.remove(sound_file_name) return str(e) -@app.route('/speaking_task', methods=['GET']) +@app.route('/speaking_task_1', methods=['GET']) @jwt_required() -def get_speaking_task_question(): +def get_speaking_task_1_question(): try: - messages = get_question_gen_messages(QuestionType.SPEAKING) + messages = get_question_gen_messages(QuestionType.SPEAKING_1) token_count = reduce(lambda count, item: count + count_tokens(item)['n_tokens'], map(lambda x: x["content"], filter(lambda x: "content" in x, messages)), 0) response = make_openai_call(messages, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE) @@ -110,6 +112,40 @@ def get_speaking_task_question(): except Exception as e: return str(e) +@app.route('/speaking_task_2', methods=['POST']) +@jwt_required() +def grade_speaking_task_2(): + delete_files_older_than_one_day(AUDIO_FILES_PATH) + sound_file_name = AUDIO_FILES_PATH + str(uuid.uuid4()) + try: + data = request.get_json() + question = data.get('question') + answer_firebase_path = data.get('answer') + + download_firebase_file(FIREBASE_BUCKET, answer_firebase_path, sound_file_name) + answer = speech_to_text(sound_file_name) + + messages = get_grading_messages(QuestionType.SPEAKING_2, question, answer) + token_count = reduce(lambda count, item: count + count_tokens(item)['n_tokens'], + map(lambda x: x["content"], filter(lambda x: "content" in x, messages)), 0) + response = make_openai_call(messages, token_count, GRADING_FIELDS, GRADING_TEMPERATURE) + os.remove(sound_file_name) + return response + except Exception as e: + os.remove(sound_file_name) + return str(e) + +@app.route('/speaking_task_2', methods=['GET']) +@jwt_required() +def get_speaking_task_2_question(): + try: + messages = get_question_gen_messages(QuestionType.SPEAKING_2) + token_count = reduce(lambda count, item: count + count_tokens(item)['n_tokens'], + map(lambda x: x["content"], filter(lambda x: "content" in x, messages)), 0) + response = make_openai_call(messages, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE) + return response + except Exception as e: + return str(e) if __name__ == '__main__': app.run() diff --git a/helper/api_messages.py b/helper/api_messages.py index 587a156..6246dd2 100644 --- a/helper/api_messages.py +++ b/helper/api_messages.py @@ -4,7 +4,8 @@ from enum import Enum class QuestionType(Enum): WRITING_TASK_1 = "Writing Task 1" WRITING_TASK_2 = "Writing Task 2" - SPEAKING = "Speaking Task" + SPEAKING_1 = "Speaking Task Part 1" + SPEAKING_2 = "Speaking Task Part 2" def get_grading_messages(question_type: QuestionType, question: str, answer: str, context: str = None): @@ -92,7 +93,7 @@ def get_grading_messages(question_type: QuestionType, question: str, answer: str "content": f"Evaluate this answer according to ielts grading system: {answer}", }, ] - elif QuestionType.SPEAKING == question_type: + elif QuestionType.SPEAKING_1 == question_type: return [ { "role": "user", @@ -100,7 +101,44 @@ def get_grading_messages(question_type: QuestionType, question: str, answer: str }, { "role": "user", - "content": f"The question you have to grade is of type Speaking and is the following: {question}", + "content": f"The question you have to grade is of type Speaking Task Part 1 and is the following: {question}", + }, + { + "role": "user", + "content": "It is mandatory for you to provide your response with the overall grade and breakdown grades, " + "with just the following json format: {'comment': 'comment about answer quality', 'overall': 7.0, " + "'task_response': {'Fluency and Coherence': 8.0, 'Lexical Resource': 6.5, " + "'Grammatical Range and Accuracy': 7.5, 'Pronunciation': 6.0}}", + }, + { + "role": "user", + "content": "Example output: { 'comment': 'The candidate has provided a clear response to the question and has " + "given examples of how they spend their weekends. However, there are some issues with grammar and " + "pronunciation that affect the overall score. In terms of fluency and coherence, the candidate speaks " + "clearly and smoothly with only minor hesitations. They have also provided a well-organized response " + "that is easy to follow. Regarding lexical resource, the candidate has used a range of vocabulary " + "related to weekend activities but there are some errors in word choice that affect the meaning of " + "their sentences. In terms of grammatical range and accuracy, the candidate has used a mix of simple " + "and complex sentence structures but there are some errors in subject-verb agreement and preposition " + "use. Finally, regarding pronunciation, the candidate's speech is generally clear but there are some " + "issues with stress and intonation that make it difficult to understand at times.', 'overall': 6.5, " + "'task_response': {'Fluency and Coherence': 7.0, 'Lexical Resource': 6.5, 'Grammatical Range and Accuracy': 7.0," + " 'Pronunciation': 6.0}}", + }, + { + "role": "user", + "content": f"Evaluate this answer according to ielts grading system: {answer}", + }, + ] + elif QuestionType.SPEAKING_2 == question_type: + return [ + { + "role": "user", + "content": "You are a IELTS examiner.", + }, + { + "role": "user", + "content": f"The question you have to grade is of type Speaking Task Part 2 and is the following: {question}", }, { "role": "user", @@ -161,7 +199,7 @@ def get_question_gen_messages(question_type: QuestionType): "content": "Generate a question for IELTS exam Writing Task 2.", }, ] - elif QuestionType.SPEAKING == question_type: + elif QuestionType.SPEAKING_1 == question_type: return [ { "role": "user", @@ -169,7 +207,7 @@ def get_question_gen_messages(question_type: QuestionType): }, { "role": "user", - "content": "The question you have to generate is of type Speaking Task.", + "content": "The question you have to generate is of type Speaking Task Part 1.", }, { "role": "user", @@ -178,9 +216,36 @@ def get_question_gen_messages(question_type: QuestionType): }, { "role": "user", - "content": "Example output: { 'question': 'Describe someone you know who does something well. You should say " - "who this person is, how do you know this person, what they do well and explain why you think this " - "person is so good at doing this.'}", + "content": "Example output: { 'question': 'Let’s talk about your home town or village. " + "What kind of place is it? What’s the most interesting part of your town/village? " + "What kind of jobs do the people in your town/village do? " + "Would you say it’s a good place to live? (Why?)'}", + }, + { + "role": "user", + "content": "Generate a question for IELTS exam Speaking Task.", + }, + ] + elif QuestionType.SPEAKING_2 == question_type: + return [ + { + "role": "user", + "content": "You are a IELTS program that generates questions for the exams.", + }, + { + "role": "user", + "content": "The question you have to generate is of type Speaking Task Part 2.", + }, + { + "role": "user", + "content": "It is mandatory for you to provide your response with the question " + "just with the following json format: {'question': 'question'}", + }, + { + "role": "user", + "content": "Example output: { 'question': 'Describe something you own which is very important to you. " + "You should say: where you got it from how long you have had it what you use it for and " + "explain why it is important to you.'}", }, { "role": "user", diff --git a/helper/file_helper.py b/helper/file_helper.py new file mode 100644 index 0000000..2e9ba7a --- /dev/null +++ b/helper/file_helper.py @@ -0,0 +1,16 @@ +import os +import datetime +from pathlib import Path + + +def delete_files_older_than_one_day(directory): + current_time = datetime.datetime.now() + + for entry in os.scandir(directory): + if entry.is_file(): + file_path = Path(entry) + file_modified_time = datetime.datetime.fromtimestamp(file_path.stat().st_mtime) + time_difference = current_time - file_modified_time + if time_difference.days > 1: + file_path.unlink() + print(f"Deleted file: {file_path}") \ No newline at end of file