ENCOA-311

This commit is contained in:
Carlos-Mesquita
2025-01-13 01:13:28 +00:00
parent 8550b520e1
commit b32e38156c
27 changed files with 126 additions and 62 deletions

View File

@@ -13,6 +13,7 @@ from ielts_be.services import (
ILevelService, ILLMService, IReadingService,
IWritingService, IListeningService, ISpeakingService
)
from ielts_be.utils import pick_difficulty
from .exercises import MultipleChoice, BlankSpace, PassageUtas, FillBlanks
from .full_exams import CustomLevelModule, LevelUtas
from .upload import UploadLevelModule
@@ -50,19 +51,22 @@ class LevelService(ILevelService):
async def upload_level(self, upload: UploadFile, solutions: Optional[UploadFile] = None) -> Dict:
return await self._upload_module.generate_level_from_file(upload, solutions)
async def _generate_exercise(self, req_exercise, start_id):
async def _generate_exercise(self, req_exercise, start_id, difficulties):
difficulty = pick_difficulty(req_exercise.difficulty, difficulties)
if req_exercise.type == "mcBlank":
questions = await self._mc.gen_multiple_choice("blank_space", req_exercise.quantity, start_id)
questions = await self._mc.gen_multiple_choice("blank_space", req_exercise.quantity, difficulty, start_id)
questions["variant"] = "mcBlank"
questions["type"] = "multipleChoice"
questions["prompt"] = "Choose the correct word or group of words that completes the sentences."
questions["difficulty"] = difficulty
return questions
elif req_exercise.type == "mcUnderline":
questions = await self._mc.gen_multiple_choice("underline", req_exercise.quantity, start_id)
questions = await self._mc.gen_multiple_choice("underline", req_exercise.quantity, difficulty, start_id)
questions["variant"] = "mcUnderline"
questions["type"] = "multipleChoice"
questions["prompt"] = "Choose the underlined word or group of words that is not correct."
questions["difficulty"] = difficulty
return questions
elif req_exercise.type == "passageUtas":
@@ -70,21 +74,24 @@ class LevelService(ILevelService):
exercise = await self._passage_utas.gen_reading_passage_utas(
start_id,
req_exercise.quantity,
difficulty,
topic,
req_exercise.text_size
)
exercise["prompt"] = "Read the text and answer the questions below."
exercise["difficulty"] = difficulty
return exercise
elif req_exercise.type == "fillBlanksMC":
exercise = await self._fill_blanks.gen_fill_blanks(
start_id,
req_exercise.quantity,
difficulty,
req_exercise.text_size,
req_exercise.topic
)
exercise["prompt"] = "Read the text below and choose the correct word for each space."
exercise["difficulty"] = difficulty
return exercise
async def generate_exercises(self, dto: LevelExercisesDTO):
@@ -95,7 +102,7 @@ class LevelService(ILevelService):
current_id += req_exercise.quantity
tasks = [
self._generate_exercise(req_exercise, start_id)
self._generate_exercise(req_exercise, start_id, dto.difficulty)
for req_exercise, start_id in zip(dto.exercises, start_ids)
]
questions = await gather(*tasks)
@@ -105,10 +112,12 @@ class LevelService(ILevelService):
# Just here to support other modules that I don't know if they are supposed to still be used
async def gen_multiple_choice(self, mc_variant: str, quantity: int, start_id: int = 1):
return await self._mc.gen_multiple_choice(mc_variant, quantity, start_id)
difficulty = random.choice(EducationalContent.DIFFICULTIES)
return await self._mc.gen_multiple_choice(mc_variant, quantity, difficulty, start_id)
async def gen_reading_passage_utas(self, start_id, mc_quantity: int, topic=Optional[str]): # sa_quantity: int,
return await self._passage_utas.gen_reading_passage_utas(start_id, mc_quantity, topic)
difficulty = random.choice(EducationalContent.DIFFICULTIES)
return await self._passage_utas.gen_reading_passage_utas(start_id, mc_quantity, difficulty, topic)
async def gen_blank_space_text_utas(self, quantity: int, start_id: int, size: int, topic: str):
return await self._blank_space.gen_blank_space_text_utas(quantity, start_id, size, topic)

View File

@@ -11,12 +11,10 @@ class FillBlanks:
async def gen_fill_blanks(
self, start_id: int, quantity: int, size: int = 300, topic=None
self, start_id: int, quantity: int, difficulty: str, size: int = 300, topic=None
):
if not topic:
topic = random.choice(EducationalContent.MTI_TOPICS)
print(quantity)
print(start_id)
messages = [
{
"role": "system",
@@ -34,8 +32,15 @@ class FillBlanks:
'JSON object containing: the modified text, a solutions array with each word\'s correct '
'letter (A-D), and a words array containing each id with four options where one is '
'the original word (matching the solution) and three are plausible but incorrect '
'alternatives that maintain grammatical consistency. '
'You cannot use repeated words!' #TODO: Solve this after
f'alternatives that maintain grammatical consistency and {difficulty} CEFR level complexity. '
'You cannot use repeated words!'
# TODO: Solve this after -> forgot about this TODO just saw now in
# 1/11/25 what I meant by this is gpt still sometimes returns repeated
# words even with explicit instructions to not do so, this is a general problem
# for all exercises, for more robust validation use self._llm.pydantic_prediction
# or implement a method that calls a mapper, catches validation error messages
# from that mapper and asks gpt to retry if creating a pydantic model for each
# operation proves to be unsustainable
)
}
]

View File

@@ -10,16 +10,16 @@ class MultipleChoice:
self._mc_variants = mc_variants
async def gen_multiple_choice(
self, mc_variant: str, quantity: int, start_id: int = 1
self, mc_variant: str, quantity: int, difficulty: str, start_id: int = 1
):
mc_template = self._mc_variants[mc_variant]
blank_mod = " blank space " if mc_variant == "blank_space" else " "
gen_multiple_choice_for_text: str = (
'Generate {quantity} multiple choice{blank}questions of 4 options for an english level exam, some easy '
'questions, some intermediate questions and some advanced questions. Ensure that the questions cover '
'a range of topics such as verb tense, subject-verb agreement, pronoun usage, sentence structure, and '
'punctuation. Make sure every question only has 1 correct answer.'
'Generate {quantity} multiple choice{blank}questions of 4 options for an english level exam of {difficulty} '
'CEFR level, some easy questions, some intermediate questions and some advanced questions. Ensure that '
'the questions cover a range of topics such as verb tense, subject-verb agreement, pronoun usage, sentence '
'structure, and punctuation. Make sure every question only has 1 correct answer.'
)
messages = [
@@ -31,7 +31,7 @@ class MultipleChoice:
},
{
"role": "user",
"content": gen_multiple_choice_for_text.format(quantity=str(quantity), blank=blank_mod)
"content": gen_multiple_choice_for_text.format(quantity=str(quantity), blank=blank_mod, difficulty=difficulty)
}
]

View File

@@ -13,11 +13,11 @@ class PassageUtas:
self._mc_variants = mc_variants
async def gen_reading_passage_utas(
self, start_id, mc_quantity: int, topic: Optional[str], word_size: Optional[int] # sa_quantity: int,
self, start_id, mc_quantity: int, difficulty: str, topic: Optional[str] = None, word_size: Optional[int] = None# sa_quantity: int,
):
passage = await self._reading_service.generate_reading_passage(1, topic, word_size)
mc_exercises = await self._gen_text_multiple_choice_utas(passage["text"], start_id, mc_quantity)
mc_exercises = await self._gen_text_multiple_choice_utas(passage["text"], start_id, mc_quantity, difficulty)
mc_exercises["type"] = "multipleChoice"
"""
exercises: {
@@ -61,7 +61,7 @@ class PassageUtas:
return question["questions"]
async def _gen_text_multiple_choice_utas(self, text: str, start_id: int, mc_quantity: int):
async def _gen_text_multiple_choice_utas(self, text: str, start_id: int, mc_quantity: int, difficulty: str):
json_template = self._mc_variants["text_mc_utas"]
messages = [
@@ -71,7 +71,9 @@ class PassageUtas:
},
{
"role": "user",
"content": f'Generate {mc_quantity} multiple choice questions of 4 options for this text:\n{text}'
"content": (
f'Generate {mc_quantity} multiple choice questions of 4 options, {difficulty} CEFR '
f'level difficulty, for this text:\n{text}')
},
{
"role": "user",

View File

@@ -1,6 +1,8 @@
import json
import random
import uuid
from ielts_be.configs.constants import EducationalContent
from ielts_be.services import ILLMService