From 282594e6198936a38cea29cc6949270f8c46bafe Mon Sep 17 00:00:00 2001 From: CRI USER Date: Tue, 15 Apr 2025 15:53:52 +0200 Subject: [PATCH 1/3] feat(tutor): returns the list of syllabus by agent --- src/app/api/api_v1/endpoints/tutor.py | 8 +++----- src/app/services/tutor/agents.py | 25 +++++++++++++++++-------- src/app/services/tutor/models.py | 12 ++++++------ src/app/services/tutor/tutor.py | 19 +++++++++++-------- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/app/api/api_v1/endpoints/tutor.py b/src/app/api/api_v1/endpoints/tutor.py index 9cc9601..28c6a5b 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}, @@ -124,8 +122,8 @@ async def tutor_search( @router.post("/syllabus") async def create_syllabus(body: TutorSearchResponse) -> SyllabusResponse: - result = await tutor_manager(body) + results = await tutor_manager(body) + # 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..8830a69 100644 --- a/src/app/services/tutor/agents.py +++ b/src/app/services/tutor/agents.py @@ -13,9 +13,8 @@ from autogen_core.models import ChatCompletionClient, SystemMessage, UserMessage from src.app.services.tutor.models import ( - Message, MessageWithResources, - TaskResponse, + SyllabusResponseAgent, TutorSearchResponse, ) from src.app.services.tutor.utils import build_system_message @@ -115,6 +114,11 @@ async def handle_documents_and_themes( 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): @@ -185,10 +189,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,11 +241,13 @@ 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 @@ -256,8 +267,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..94d6b4b 100644 --- a/src/app/services/tutor/models.py +++ b/src/app/services/tutor/models.py @@ -22,16 +22,16 @@ 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" diff --git a/src/app/services/tutor/tutor.py b/src/app/services/tutor/tutor.py index 4070dd1..bb0ac5a 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,15 @@ ) -async def tutor_manager(content: TutorSearchResponse) -> Message: - queue = asyncio.Queue[TaskResponse]() +async def tutor_manager(content: TutorSearchResponse) -> list[SyllabusResponseAgent]: + queue = asyncio.Queue[SyllabusResponseAgent]() formatted_content = MessageWithResources( content=content.extracts, resources=extract_doc_info(content.documents) ) async def collect_result( - _agent: ClosureContext, message: TaskResponse, ctx: MessageContext + _agent: ClosureContext, message: SyllabusResponseAgent, ctx: MessageContext ) -> None: await queue.put(message) @@ -117,9 +116,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 + ] From e6f5b5eeb380a4e7da38d5d251e0b9795a654a85 Mon Sep 17 00:00:00 2001 From: CRI USER Date: Tue, 15 Apr 2025 20:13:58 +0200 Subject: [PATCH 2/3] makes sure themes and summary are used --- src/app/services/tutor/agents.py | 23 ++++++++--------------- src/app/services/tutor/models.py | 2 ++ src/app/services/tutor/tutor.py | 5 ++++- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/app/services/tutor/agents.py b/src/app/services/tutor/agents.py index 8830a69..ee395e4 100644 --- a/src/app/services/tutor/agents.py +++ b/src/app/services/tutor/agents.py @@ -12,11 +12,7 @@ from autogen_core.memory import ListMemory from autogen_core.models import ChatCompletionClient, SystemMessage, UserMessage -from src.app.services.tutor.models import ( - MessageWithResources, - SyllabusResponseAgent, - 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 @@ -80,15 +76,8 @@ 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}" @@ -109,7 +98,11 @@ 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, ), topic_id=TopicId(sdg_expert_topic_type, source=self.id.key), ) diff --git a/src/app/services/tutor/models.py b/src/app/services/tutor/models.py index 94d6b4b..a1a1bab 100644 --- a/src/app/services/tutor/models.py +++ b/src/app/services/tutor/models.py @@ -39,6 +39,8 @@ class MessageWithAnalysis(BaseModel): class MessageWithResources(BaseModel): 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 bb0ac5a..c36a7ff 100644 --- a/src/app/services/tutor/tutor.py +++ b/src/app/services/tutor/tutor.py @@ -45,7 +45,10 @@ async def tutor_manager(content: TutorSearchResponse) -> list[SyllabusResponseAg queue = asyncio.Queue[SyllabusResponseAgent]() formatted_content = MessageWithResources( - content=content.extracts, resources=extract_doc_info(content.documents) + 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( From ffc07722a8ee7be78f1a85cae696cad23d635271 Mon Sep 17 00:00:00 2001 From: CRI USER Date: Tue, 15 Apr 2025 20:38:30 +0200 Subject: [PATCH 3/3] handles language --- src/app/api/api_v1/endpoints/tutor.py | 6 ++++-- src/app/services/tutor/agents.py | 7 ++++--- src/app/services/tutor/models.py | 1 + src/app/services/tutor/tutor.py | 5 ++++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/app/api/api_v1/endpoints/tutor.py b/src/app/api/api_v1/endpoints/tutor.py index 28c6a5b..bbb0c99 100644 --- a/src/app/api/api_v1/endpoints/tutor.py +++ b/src/app/api/api_v1/endpoints/tutor.py @@ -121,8 +121,10 @@ async def tutor_search( @router.post("/syllabus") -async def create_syllabus(body: TutorSearchResponse) -> SyllabusResponse: - results = await tutor_manager(body) +async def create_syllabus( + body: TutorSearchResponse, lang: str = "en" +) -> SyllabusResponse: + results = await tutor_manager(body, lang) # TODO: handle errors diff --git a/src/app/services/tutor/agents.py b/src/app/services/tutor/agents.py index ee395e4..87f806b 100644 --- a/src/app/services/tutor/agents.py +++ b/src/app/services/tutor/agents.py @@ -79,7 +79,7 @@ async def handle_documents_and_themes( 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( @@ -103,6 +103,7 @@ async def handle_documents_and_themes( source=self.id.type, summary=message.summary, themes=message.themes, + lang=message.lang, ), topic_id=TopicId(sdg_expert_topic_type, source=self.id.key), ) @@ -164,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( @@ -246,7 +247,7 @@ async def handle_syllabus( 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)], diff --git a/src/app/services/tutor/models.py b/src/app/services/tutor/models.py index a1a1bab..ebc12d3 100644 --- a/src/app/services/tutor/models.py +++ b/src/app/services/tutor/models.py @@ -38,6 +38,7 @@ class MessageWithAnalysis(BaseModel): class MessageWithResources(BaseModel): + lang: str = "en" content: list[ExtractorOutput] | str themes: list[str] summary: list[str] diff --git a/src/app/services/tutor/tutor.py b/src/app/services/tutor/tutor.py index c36a7ff..9014a24 100644 --- a/src/app/services/tutor/tutor.py +++ b/src/app/services/tutor/tutor.py @@ -41,10 +41,13 @@ ) -async def tutor_manager(content: TutorSearchResponse) -> list[SyllabusResponseAgent]: +async def tutor_manager( + content: TutorSearchResponse, lang: str +) -> list[SyllabusResponseAgent]: queue = asyncio.Queue[SyllabusResponseAgent]() formatted_content = MessageWithResources( + lang=lang, content=content.extracts, resources=extract_doc_info(content.documents), themes=[theme for extract in content.extracts for theme in extract.themes],