diff --git a/src/app/api/api_v1/endpoints/tutor.py b/src/app/api/api_v1/endpoints/tutor.py index 9cc9601..bbb0c99 100644 --- a/src/app/api/api_v1/endpoints/tutor.py +++ b/src/app/api/api_v1/endpoints/tutor.py @@ -66,8 +66,6 @@ async def tutor_search( ] file_content_str = "\n\n".join(file_content_str) - print(file_content_str) - messages = [ {"role": "system", "content": extractor_prompt}, {"role": "assistant", "content": file_content_str}, @@ -123,9 +121,11 @@ async def tutor_search( @router.post("/syllabus") -async def create_syllabus(body: TutorSearchResponse) -> SyllabusResponse: - result = await tutor_manager(body) +async def create_syllabus( + body: TutorSearchResponse, lang: str = "en" +) -> SyllabusResponse: + results = await tutor_manager(body, lang) + # TODO: handle errors - # TODO: mae sure documents are used - return SyllabusResponse(syllabus=result.content, documents=body.documents) + return SyllabusResponse(syllabus=results, documents=body.documents) diff --git a/src/app/services/tutor/agents.py b/src/app/services/tutor/agents.py index 64301e5..87f806b 100644 --- a/src/app/services/tutor/agents.py +++ b/src/app/services/tutor/agents.py @@ -12,12 +12,7 @@ from autogen_core.memory import ListMemory from autogen_core.models import ChatCompletionClient, SystemMessage, UserMessage -from src.app.services.tutor.models import ( - Message, - MessageWithResources, - TaskResponse, - TutorSearchResponse, -) +from src.app.services.tutor.models import MessageWithResources, SyllabusResponseAgent from src.app.services.tutor.utils import build_system_message from src.app.utils.logger import logger as utils_logger @@ -81,17 +76,10 @@ def __init__(self, model_client: ChatCompletionClient) -> None: async def handle_documents_and_themes( self, message: MessageWithResources, ctx: MessageContext ) -> None: - contents = [] - for curr_content in message.content: - if isinstance(curr_content, TutorSearchResponse): - contents.append(curr_content.extracts) - - themes = [] - for curr_content in contents: - if isinstance(curr_content, TutorSearchResponse): - themes.extend(curr_content.extracts) + contents = "summary :".join(message.summary) + themes = ",".join(message.themes) - prompt = f"Using the content in TEXT CONTENTS, you generate a syllabus that is engaging and coherent in relation to the THEMES extracted from these contents. \n\nTEXT CONTENTS:\n{contents}\n\nTHEMES:\n{themes}" + prompt = f"Using the content in TEXT CONTENTS, you generate a syllabus that is engaging and coherent in relation to the THEMES extracted from these contents. The syllabus should be written in lang: {message.lang} \n\nTEXT CONTENTS:\n{contents}\n\nTHEMES:\n{themes}" start_time = time.time() llm_result = await self._model_client.create( @@ -110,11 +98,21 @@ async def handle_documents_and_themes( await self.publish_message( MessageWithResources( - content=response, resources=message.resources, source=self.id.type + content=response, + resources=message.resources, + source=self.id.type, + summary=message.summary, + themes=message.themes, + lang=message.lang, ), topic_id=TopicId(sdg_expert_topic_type, source=self.id.key), ) + await self.publish_message( + SyllabusResponseAgent(content=response, source=self.id.type), + task_results_topic_id, + ) + @type_subscription(topic_type=sdg_expert_topic_type) class SDGExpertAgent(RoutedAgent): @@ -167,7 +165,7 @@ async def handle_syllabus( None """ - prompt = f"Use these WeLearn documents: {message.resources} to integrate sustainability in this syllabus: {message.content}. You do not need to use ALL the information in the WeLearn documents, but ensure that sustainability integration is done in a way that is relevant and thematically linked to the discipline and the topics of the syllabus, that they are deeply embedded in the course content, and aligned with both the discipline and the broader educational goals. Add all WeLearn documents that you use in the REFERENCES section of the syllabus." + prompt = f"Use these WeLearn documents: {message.resources} to integrate sustainability in this syllabus: {message.content}. You do not need to use ALL the information in the WeLearn documents, but ensure that sustainability integration is done in a way that is relevant and thematically linked to the discipline and the topics of the syllabus, that they are deeply embedded in the course content, and aligned with both the discipline and the broader educational goals. Add all WeLearn documents that you use in the REFERENCES section of the syllabus. Keep the same language, lang: {message.lang}" try: start_time = time.time() llm_result = await self._delegate.on_messages( @@ -185,10 +183,15 @@ async def handle_syllabus( ) assert isinstance(response, str) await self.publish_message( - Message(content=response, source=self.id.type), + SyllabusResponseAgent(content=response, source=self.id.type), topic_id=TopicId(pedagogical_engineer_topic_type, source=self.id.key), ) + await self.publish_message( + SyllabusResponseAgent(content=response, source=self.id.type), + task_results_topic_id, + ) + @type_subscription(topic_type=pedagogical_engineer_topic_type) class PedagogicalEngineerAgent(RoutedAgent): @@ -232,17 +235,19 @@ def __init__(self, model_client: ChatCompletionClient, memory: ListMemory) -> No self._model_client = model_client @message_handler(match=lambda msg, ctx: msg.source.startswith("SDGExpert")) # type: ignore - async def handle_syllabus(self, message: Message, ctx: MessageContext) -> Message: + async def handle_syllabus( + self, message: SyllabusResponseAgent, ctx: MessageContext + ) -> None: """ Handles the syllabus by improving on the pedagogical aspects and ensures that the competencies cited in the EU GreenComp framework are present in a coherent manner with the discipline and course content. Args: - message (Message): The message containing the syllabus content. + message (SyllabusResponseAgent): The message containing the syllabus content. ctx (MessageContext): The context of the message. Returns: None """ - prompt = f"Ensure that the syllabus is pedagogically sound, aligns with competency-based learning, and optimizes student engagement and learning effectiveness. Ensure that the learning objectives, outcomes and the competencies and related in a logical and meaningful way, and that these overarching goals are accomplished through the course plan and activities, also ensure that the competencies cited in the EU GreenComp framework are present in the syllabus in a way that is coherent with the discipline and the course content.\n\nSYLLABUS:\n{message.content}" + prompt = f"Ensure that the syllabus is pedagogically sound, aligns with competency-based learning, and optimizes student engagement and learning effectiveness. Ensure that the learning objectives, outcomes and the competencies and related in a logical and meaningful way, and that these overarching goals are accomplished through the course plan and activities, also ensure that the competencies cited in the EU GreenComp framework are present in the syllabus in a way that is coherent with the discipline and the course content. Make sure to use the same language as the current syllabus. \n\nSYLLABUS:\n{message.content}" start_time = time.time() llm_result = await self._delegate.on_messages( [TextMessage(content=prompt, source=self.id.key)], @@ -256,8 +261,6 @@ async def handle_syllabus(self, message: Message, ctx: MessageContext) -> Messag "agent_type=%s response_time=%s", self.id.type, end_time - start_time ) await self.publish_message( - TaskResponse(result=response, task_id="pedagogical"), + SyllabusResponseAgent(content=response, source=self.id.type), task_results_topic_id, ) - - return Message(content=response) diff --git a/src/app/services/tutor/models.py b/src/app/services/tutor/models.py index d08e2f6..ebc12d3 100644 --- a/src/app/services/tutor/models.py +++ b/src/app/services/tutor/models.py @@ -22,23 +22,26 @@ class TutorSearchResponse(BaseModel): documents: list[Document] -class SyllabusResponse(BaseModel): - syllabus: str - documents: list[Document] - - -class Message(BaseModel): +class SyllabusResponseAgent(BaseModel): content: str source: str = "default" +class SyllabusResponse(BaseModel): + syllabus: list[SyllabusResponseAgent] + documents: list[Document] + + class MessageWithAnalysis(BaseModel): content: Dict source: str = "default" class MessageWithResources(BaseModel): + lang: str = "en" content: list[ExtractorOutput] | str + themes: list[str] + summary: list[str] resources: List[Dict] source: str = "default" diff --git a/src/app/services/tutor/tutor.py b/src/app/services/tutor/tutor.py index 4070dd1..9014a24 100644 --- a/src/app/services/tutor/tutor.py +++ b/src/app/services/tutor/tutor.py @@ -23,9 +23,8 @@ university_teacher_topic_type, ) from src.app.services.tutor.models import ( - Message, MessageWithResources, - TaskResponse, + SyllabusResponseAgent, TutorSearchResponse, ) from src.app.services.tutor.utils import extract_doc_info @@ -42,15 +41,21 @@ ) -async def tutor_manager(content: TutorSearchResponse) -> Message: - queue = asyncio.Queue[TaskResponse]() +async def tutor_manager( + content: TutorSearchResponse, lang: str +) -> list[SyllabusResponseAgent]: + queue = asyncio.Queue[SyllabusResponseAgent]() formatted_content = MessageWithResources( - content=content.extracts, resources=extract_doc_info(content.documents) + lang=lang, + content=content.extracts, + resources=extract_doc_info(content.documents), + themes=[theme for extract in content.extracts for theme in extract.themes], + summary=[extract.summary for extract in content.extracts], ) async def collect_result( - _agent: ClosureContext, message: TaskResponse, ctx: MessageContext + _agent: ClosureContext, message: SyllabusResponseAgent, ctx: MessageContext ) -> None: await queue.put(message) @@ -117,9 +122,13 @@ async def collect_result( ) await runtime.stop_when_idle() - response: TaskResponse = TaskResponse(task_id="", result="") - if not queue.empty(): + responses = [] + while not queue.empty(): response = await queue.get() + responses.append(response) await runtime.close() - return Message(content=response.result) + return [ + SyllabusResponseAgent(content=response.content, source=response.source) + for response in responses + ]