Add Speaking parts.
This commit is contained in:
50
app.py
50
app.py
@@ -4,6 +4,7 @@ from functools import reduce
|
|||||||
import firebase_admin
|
import firebase_admin
|
||||||
from firebase_admin import credentials
|
from firebase_admin import credentials
|
||||||
from helper.api_messages import QuestionType, get_grading_messages, get_question_gen_messages
|
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.firebase_helper import download_firebase_file
|
||||||
from helper.speech_to_text_helper import speech_to_text
|
from helper.speech_to_text_helper import speech_to_text
|
||||||
from helper.token_counter import count_tokens
|
from helper.token_counter import count_tokens
|
||||||
@@ -25,7 +26,7 @@ cred = credentials.Certificate(os.getenv("GOOGLE_APPLICATION_CREDENTIALS"))
|
|||||||
firebase_admin.initialize_app(cred)
|
firebase_admin.initialize_app(cred)
|
||||||
|
|
||||||
GRADING_TEMPERATURE = 0.1
|
GRADING_TEMPERATURE = 0.1
|
||||||
GEN_QUESTION_TEMPERATURE = 0.7
|
GEN_QUESTION_TEMPERATURE = 0.9
|
||||||
GRADING_FIELDS = ['overall', 'comment', 'task_response']
|
GRADING_FIELDS = ['overall', 'comment', 'task_response']
|
||||||
GEN_FIELDS = ['question']
|
GEN_FIELDS = ['question']
|
||||||
|
|
||||||
@@ -76,9 +77,10 @@ def get_writing_task_2_question():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return str(e)
|
return str(e)
|
||||||
|
|
||||||
@app.route('/speaking_task', methods=['POST'])
|
@app.route('/speaking_task_1', methods=['POST'])
|
||||||
@jwt_required()
|
@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())
|
sound_file_name = AUDIO_FILES_PATH + str(uuid.uuid4())
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@@ -88,7 +90,7 @@ def grade_speaking_task():
|
|||||||
download_firebase_file(FIREBASE_BUCKET, answer_firebase_path, sound_file_name)
|
download_firebase_file(FIREBASE_BUCKET, answer_firebase_path, sound_file_name)
|
||||||
answer = speech_to_text(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'],
|
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)
|
map(lambda x: x["content"], filter(lambda x: "content" in x, messages)), 0)
|
||||||
response = make_openai_call(messages, token_count, GRADING_FIELDS, GRADING_TEMPERATURE)
|
response = make_openai_call(messages, token_count, GRADING_FIELDS, GRADING_TEMPERATURE)
|
||||||
@@ -98,11 +100,11 @@ def grade_speaking_task():
|
|||||||
os.remove(sound_file_name)
|
os.remove(sound_file_name)
|
||||||
return str(e)
|
return str(e)
|
||||||
|
|
||||||
@app.route('/speaking_task', methods=['GET'])
|
@app.route('/speaking_task_1', methods=['GET'])
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
def get_speaking_task_question():
|
def get_speaking_task_1_question():
|
||||||
try:
|
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'],
|
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)
|
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)
|
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:
|
except Exception as e:
|
||||||
return str(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__':
|
if __name__ == '__main__':
|
||||||
app.run()
|
app.run()
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ from enum import Enum
|
|||||||
class QuestionType(Enum):
|
class QuestionType(Enum):
|
||||||
WRITING_TASK_1 = "Writing Task 1"
|
WRITING_TASK_1 = "Writing Task 1"
|
||||||
WRITING_TASK_2 = "Writing Task 2"
|
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):
|
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}",
|
"content": f"Evaluate this answer according to ielts grading system: {answer}",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
elif QuestionType.SPEAKING == question_type:
|
elif QuestionType.SPEAKING_1 == question_type:
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
@@ -100,7 +101,44 @@ def get_grading_messages(question_type: QuestionType, question: str, answer: str
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "user",
|
"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",
|
"role": "user",
|
||||||
@@ -161,7 +199,7 @@ def get_question_gen_messages(question_type: QuestionType):
|
|||||||
"content": "Generate a question for IELTS exam Writing Task 2.",
|
"content": "Generate a question for IELTS exam Writing Task 2.",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
elif QuestionType.SPEAKING == question_type:
|
elif QuestionType.SPEAKING_1 == question_type:
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
@@ -169,7 +207,7 @@ def get_question_gen_messages(question_type: QuestionType):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "user",
|
"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",
|
"role": "user",
|
||||||
@@ -178,9 +216,36 @@ def get_question_gen_messages(question_type: QuestionType):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": "Example output: { 'question': 'Describe someone you know who does something well. You should say "
|
"content": "Example output: { 'question': 'Let’s talk about your home town or village. "
|
||||||
"who this person is, how do you know this person, what they do well and explain why you think this "
|
"What kind of place is it? What’s the most interesting part of your town/village? "
|
||||||
"person is so good at doing this.'}",
|
"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",
|
"role": "user",
|
||||||
|
|||||||
16
helper/file_helper.py
Normal file
16
helper/file_helper.py
Normal file
@@ -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}")
|
||||||
Reference in New Issue
Block a user