From 68cab8085146d58a838f98c6694787210eb99e52 Mon Sep 17 00:00:00 2001 From: Carlos-Mesquita Date: Wed, 4 Dec 2024 04:18:23 +0000 Subject: [PATCH] ENCOA-256: Some more changes to level prompt and added mc to reading --- app/configs/constants.py | 1 + app/services/impl/exam/level/upload.py | 30 +++++++++++++------ app/services/impl/exam/listening/__init__.py | 5 ++-- app/services/impl/exam/reading/__init__.py | 9 +++++- .../impl/exam/reading/import_reading.py | 15 ++++++---- app/services/impl/exam/shared/__init__.py | 6 ++-- .../{listening => shared}/multiple_choice.py | 4 +-- 7 files changed, 48 insertions(+), 22 deletions(-) rename app/services/impl/exam/{listening => shared}/multiple_choice.py (89%) diff --git a/app/configs/constants.py b/app/configs/constants.py index b7be474..56b9a54 100644 --- a/app/configs/constants.py +++ b/app/configs/constants.py @@ -47,6 +47,7 @@ class ReadingExerciseType(str, Enum): trueFalse = "trueFalse" paragraphMatch = "paragraphMatch" ideaMatch = "ideaMatch" + multipleChoice = "multipleChoice" class ListeningExerciseType(str, Enum): diff --git a/app/services/impl/exam/level/upload.py b/app/services/impl/exam/level/upload.py index 2e31251..365d0bf 100644 --- a/app/services/impl/exam/level/upload.py +++ b/app/services/impl/exam/level/upload.py @@ -35,7 +35,7 @@ class UploadLevelModule: #completion: Coroutine[Any, Any, Exam] = ( # self._png_completion(path_id) if file_has_images else self._html_completion(path_id) #) - response = await self._html_completion(path_id) + response = await self._html_completion(path_id, solutions is not None) FileHelper.remove_directory(f'./tmp/{path_id}') @@ -69,16 +69,26 @@ class UploadLevelModule: ] } - async def _html_completion(self, path_id: str) -> Exam: + async def _html_completion(self, path_id: str, solutions_provided: bool) -> Exam: async with aiofiles.open(f'./tmp/{path_id}/exercises.html', 'r', encoding='utf-8') as f: html = await f.read() + solutions = [] + if solutions_provided: + async with aiofiles.open(f'./tmp/{path_id}/solutions.html', 'r', encoding='utf-8') as f: + solutions_html = await f.read() + solutions.append({ + "role": "user", + "content": f'The solutions to the question sheet are the following:\n\n{solutions_html}' + }) + return await self._llm.pydantic_prediction( [self._gpt_instructions_html(), { "role": "user", "content": html - } + }, + *solutions ], LevelMapper.map_to_exam_model, str(self._level_json_schema()) @@ -122,8 +132,10 @@ class UploadLevelModule: 'IMPORTANT: As stated earlier your job is to structure the questions into PARTS not SECTION, this means ' 'that if there is for example: Section 1, Part 1 and Part 2, Section 2, Part 1 and Part 2, you MUST ' - 'place in the parts array 4 parts NOT 2 parts with the exercises of both parts! You must strictly ' - 'adhere to this instruction, do not mistake sections for parts!\n' + 'place in the parts array 4 parts NOT 2 parts with the exercises of both parts! If there are no sections ' + 'and only Parts then group them by parts, and when I say parts I mean it in the fucking literal sense of the' + ' word Part x which is in the html. ' + 'You must strictly adhere to this instruction, do not mistake sections for parts!\n' 'The templates for the exercises are the following:\n' '- blank space multiple choice, underline multiple choice and reading passage multiple choice: ' @@ -164,17 +176,17 @@ class UploadLevelModule: "prompt": "Click a blank to select the appropriate word for it.", "text": ( "}} with 2 newlines between paragraphs>" + "ids with {{}} with 2 newlines between paragraphs>" ), "solutions": [ { - "id": "", + "id": "", "solution": "" } ], "words": [ { - "id": "", + "id": "", "options": { "A": "", "B": "", @@ -198,7 +210,7 @@ class UploadLevelModule: self._multiple_choice_png(), {"type": "blanksPassage", "text": ( "}} with 2 newlines between paragraphs>" + "ids with {{}} with 2 newlines between paragraphs>" )}, {"type": "passage", "context": ( "" diff --git a/app/services/impl/exam/listening/__init__.py b/app/services/impl/exam/listening/__init__.py index f71afde..fe319be 100644 --- a/app/services/impl/exam/listening/__init__.py +++ b/app/services/impl/exam/listening/__init__.py @@ -14,11 +14,10 @@ from app.configs.constants import ( ) from app.helpers import FileHelper from .import_listening import ImportListeningModule -from .multiple_choice import MultipleChoice from .write_blank_forms import WriteBlankForms from .write_blanks import WriteBlanks from .write_blank_notes import WriteBlankNotes -from ..shared import TrueFalse +from ..shared import TrueFalse, MultipleChoice class ListeningService(IListeningService): @@ -128,7 +127,7 @@ class ListeningService(IListeningService): if req_exercise.type == "multipleChoice" or req_exercise.type == "multipleChoice3Options": n_options = 4 if req_exercise.type == "multipleChoice" else 3 question = await self._multiple_choice.gen_multiple_choice( - dialog_type, text, req_exercise.quantity, start_id, difficulty, n_options + text, req_exercise.quantity, start_id, difficulty, n_options ) self._logger.info(f"Added multiple choice: {question}") return question diff --git a/app/services/impl/exam/reading/__init__.py b/app/services/impl/exam/reading/__init__.py index bd1d86a..57a4312 100644 --- a/app/services/impl/exam/reading/__init__.py +++ b/app/services/impl/exam/reading/__init__.py @@ -10,7 +10,7 @@ from app.services.abc import IReadingService, ILLMService from .fill_blanks import FillBlanks from .idea_match import IdeaMatch from .paragraph_match import ParagraphMatch -from ..shared import TrueFalse +from ..shared import TrueFalse, MultipleChoice from .import_reading import ImportReadingModule from .write_blanks import WriteBlanks @@ -24,6 +24,7 @@ class ReadingService(IReadingService): self._paragraph_match = ParagraphMatch(llm) self._true_false = TrueFalse(llm) self._write_blanks = WriteBlanks(llm) + self._multiple_choice = MultipleChoice(llm) self._logger = getLogger(__name__) self._import = ImportReadingModule(llm) @@ -119,6 +120,12 @@ class ReadingService(IReadingService): question["variant"] = "ideaMatch" self._logger.info(f"Added idea match: {question}") return question + elif req_exercise.type == "multipleChoice": + question = await self._multiple_choice.gen_multiple_choice( + text, req_exercise.quantity, start_id, difficulty, 4 + ) + self._logger.info(f"Added multiple choice: {question}") + return question async def generate_reading_exercises(self, dto: ReadingDTO): exercise_tasks = [] diff --git a/app/services/impl/exam/reading/import_reading.py b/app/services/impl/exam/reading/import_reading.py index 32eca34..2888e05 100644 --- a/app/services/impl/exam/reading/import_reading.py +++ b/app/services/impl/exam/reading/import_reading.py @@ -98,7 +98,11 @@ class ImportReadingModule: ] } ], - "text": "{{}}\\\\n] notice how there is a double backslash before the n -> I want an escaped newline in your output> ", + "text": ( + "{{}}\\\\n] " + "- notice how there the question number inside {{}} -> the text MUST always contain the question number in that format " + "- and notice how there is a double backslash before the n -> I want an escaped newline in your output> " + ), "type": "writeBlanks", "prompt": "" } @@ -192,13 +196,14 @@ class ImportReadingModule: + ( "Solutions were not provided - analyze the passage carefully to determine correct answers." if not solutions else - "Use the provided solutions to fill in all answer fields accurately." + "Use the provided solutions to fill in all answer fields accurately, if word answers have all letters " + "uppercase convert them to lowercase before assigning them." ) + - "Pay extra attention to fillblanks exercises the solution and option wording must match in case!" - "There can't be options in lowercase and solutions in uppercase!" + "Pay extra attention to fillblanks exercises the solution and option wording must match in case! " + "There can't be options in lowercase and solutions in uppercase! " "Also PAY ATTENTION TO SECTIONS, these most likely indicate parts, and in each section/part there " - "should be a text, if there isn't a title for it choose a reasonable one based on its contents." + "should be a text, if there isn't a title for it choose a reasonable one based on its contents. " ) return { diff --git a/app/services/impl/exam/shared/__init__.py b/app/services/impl/exam/shared/__init__.py index 5778d99..cda6463 100644 --- a/app/services/impl/exam/shared/__init__.py +++ b/app/services/impl/exam/shared/__init__.py @@ -1,5 +1,7 @@ from .true_false import TrueFalse +from .multiple_choice import MultipleChoice __all__ = [ - "TrueFalse" -] \ No newline at end of file + "TrueFalse", + "MultipleChoice" +] diff --git a/app/services/impl/exam/listening/multiple_choice.py b/app/services/impl/exam/shared/multiple_choice.py similarity index 89% rename from app/services/impl/exam/listening/multiple_choice.py rename to app/services/impl/exam/shared/multiple_choice.py index 7e3211e..5418331 100644 --- a/app/services/impl/exam/listening/multiple_choice.py +++ b/app/services/impl/exam/shared/multiple_choice.py @@ -11,7 +11,7 @@ class MultipleChoice: self._llm = llm async def gen_multiple_choice( - self, dialog_type: str, text: str, quantity: int, start_id: int, difficulty: str, n_options: int = 4 + self, text: str, quantity: int, start_id: int, difficulty: str, n_options: int = 4 ): messages = [ { @@ -27,7 +27,7 @@ class MultipleChoice: "role": "user", "content": ( f'Generate {quantity} {difficulty} difficulty multiple choice questions of {n_options} ' - f'options for this {dialog_type}:\n"' + text + '"') + f'options for this text:\n"' + text + '"') } ]