Loading llm/schema/query.py +17 −4 Original line number Diff line number Diff line import graphene import json from typing import Optional from common_lib.utils import ensure_exists from running_exercise.models import EmailThread Loading Loading @@ -49,12 +50,21 @@ class Query(graphene.ObjectType): email_template_suggestion = graphene.Field( EmailTemplateSuggestionType, required=True, thread_id=graphene.ID(required=True), description="The email of a trainee you want to respond", thread_id=graphene.ID( required=True, description="The ID of the email thread instructor responds to.", ), participant_id=graphene.ID( required=False, description="The ID of the definition participant to impersonate (provide if multiple definition participants exist within the thread).", ), description="Returns a suggested email template (or new suggested response text) based on the email thread and learning activity.", ) @protected(User.AuthGroup.INSTRUCTOR) def resolve_email_template_suggestion(self, info, thread_id: str): def resolve_email_template_suggestion( self, info, thread_id: str, participant_id: Optional[str] = None ): thread = ensure_exists( EmailThread.objects.filter(id=int(thread_id)) .select_related("exercise") Loading @@ -67,7 +77,10 @@ class Query(graphene.ObjectType): response = Prompter.prompt_llm( EMAIL_SUGGESTION_SYSTEM, TemplateSuggestor(thread).serialize(), TemplateSuggestor( thread, participant_id=int(participant_id) if participant_id else None, ).serialize(), EMAIL_SUGGESTION_TASK, ) template_id, new_suggestion, accuracy = extract_template(response) Loading llm/serializer/llm_serializers.py +10 −2 Original line number Diff line number Diff line from rest_framework.fields import CharField from rest_framework.serializers import ( ModelSerializer, SerializerMethodField, IntegerField, ) from django.db.models import Q from running_exercise.models import EmailThread, EmailParticipant, Email from exercise_definition.models import ( Loading Loading @@ -85,6 +85,14 @@ class LLMEmailThreadSerializer(ModelSerializer): model = EmailThread fields = ["subject", "participants", "emails"] def get_participants(self, obj: EmailThread): participant: EmailParticipant = self.context.get("participant") if participant: return obj.participants.filter( Q(id=participant.id) | Q(definition_address__isnull=True) ) return obj.participants.all() class LLMLearningObjectiveSerializer(ModelSerializer): class Meta: Loading @@ -92,7 +100,7 @@ class LLMLearningObjectiveSerializer(ModelSerializer): fields = ["name", "description", "tags", "order"] class LLMLearningActivitySerialzer(ModelSerializer): class LLMLearningActivitySerializer(ModelSerializer): objective = LLMLearningObjectiveSerializer() class Meta: Loading llm/serializer/template_suggestor.py +49 −8 Original line number Diff line number Diff line import json from typing import Optional from common_lib.utils import ensure_exists from django.db.models import QuerySet from common_lib.exceptions import ExerciseOperationException from running_exercise.models import EmailThread, EmailParticipant from exercise_definition.models import LearningActivity from llm.serializer.llm_serializers import ( LLMEmailThreadSerializer, LLMLearningActivitySerialzer, LLMLearningActivitySerializer, ) class TemplateSuggestor: thread: EmailThread activity: LearningActivity participant: EmailParticipant # Definition participant impersonated by instructor def __init__(self, thread: EmailThread): def __init__( self, thread: EmailThread, participant_id: Optional[int] = None ): self.thread = thread definition_participant: EmailParticipant = ensure_exists( definition_participants = ( thread.participants.select_related( "definition_address", "definition_address__activity", Loading @@ -24,9 +30,44 @@ class TemplateSuggestor: .prefetch_related("definition_address__templates") .filter(definition_address__isnull=False) ) self.activity = definition_participant.definition_address.activity self.participant = self._get_participant( definition_participants, participant_id ) self.activity = self.participant.definition_address.activity def _get_participant( self, participants: QuerySet[EmailParticipant], participant_id: Optional[int], ) -> EmailParticipant: count = participants.count() if count == 0: raise ExerciseOperationException( "Thread contains no definition participants." ) if count == 1: participant = participants.first() return participant if participant_id is None: raise ExerciseOperationException( "Multiple definition email participants exist. " "You must specify which participant to impersonate." ) if participant := participants.filter(id=participant_id).first(): return participant raise ExerciseOperationException( "Definition participant with the given ID does not exist in this thread." ) def serialize(self): data = LLMEmailThreadSerializer(self.thread).data data.update(LLMLearningActivitySerialzer(self.activity).data) return json.dumps(data) context = {"participant": self.participant} thread_data = LLMEmailThreadSerializer( self.thread, context=context ).data activity_data = LLMLearningActivitySerializer(self.activity).data return json.dumps({**thread_data, **activity_data}) Loading
llm/schema/query.py +17 −4 Original line number Diff line number Diff line import graphene import json from typing import Optional from common_lib.utils import ensure_exists from running_exercise.models import EmailThread Loading Loading @@ -49,12 +50,21 @@ class Query(graphene.ObjectType): email_template_suggestion = graphene.Field( EmailTemplateSuggestionType, required=True, thread_id=graphene.ID(required=True), description="The email of a trainee you want to respond", thread_id=graphene.ID( required=True, description="The ID of the email thread instructor responds to.", ), participant_id=graphene.ID( required=False, description="The ID of the definition participant to impersonate (provide if multiple definition participants exist within the thread).", ), description="Returns a suggested email template (or new suggested response text) based on the email thread and learning activity.", ) @protected(User.AuthGroup.INSTRUCTOR) def resolve_email_template_suggestion(self, info, thread_id: str): def resolve_email_template_suggestion( self, info, thread_id: str, participant_id: Optional[str] = None ): thread = ensure_exists( EmailThread.objects.filter(id=int(thread_id)) .select_related("exercise") Loading @@ -67,7 +77,10 @@ class Query(graphene.ObjectType): response = Prompter.prompt_llm( EMAIL_SUGGESTION_SYSTEM, TemplateSuggestor(thread).serialize(), TemplateSuggestor( thread, participant_id=int(participant_id) if participant_id else None, ).serialize(), EMAIL_SUGGESTION_TASK, ) template_id, new_suggestion, accuracy = extract_template(response) Loading
llm/serializer/llm_serializers.py +10 −2 Original line number Diff line number Diff line from rest_framework.fields import CharField from rest_framework.serializers import ( ModelSerializer, SerializerMethodField, IntegerField, ) from django.db.models import Q from running_exercise.models import EmailThread, EmailParticipant, Email from exercise_definition.models import ( Loading Loading @@ -85,6 +85,14 @@ class LLMEmailThreadSerializer(ModelSerializer): model = EmailThread fields = ["subject", "participants", "emails"] def get_participants(self, obj: EmailThread): participant: EmailParticipant = self.context.get("participant") if participant: return obj.participants.filter( Q(id=participant.id) | Q(definition_address__isnull=True) ) return obj.participants.all() class LLMLearningObjectiveSerializer(ModelSerializer): class Meta: Loading @@ -92,7 +100,7 @@ class LLMLearningObjectiveSerializer(ModelSerializer): fields = ["name", "description", "tags", "order"] class LLMLearningActivitySerialzer(ModelSerializer): class LLMLearningActivitySerializer(ModelSerializer): objective = LLMLearningObjectiveSerializer() class Meta: Loading
llm/serializer/template_suggestor.py +49 −8 Original line number Diff line number Diff line import json from typing import Optional from common_lib.utils import ensure_exists from django.db.models import QuerySet from common_lib.exceptions import ExerciseOperationException from running_exercise.models import EmailThread, EmailParticipant from exercise_definition.models import LearningActivity from llm.serializer.llm_serializers import ( LLMEmailThreadSerializer, LLMLearningActivitySerialzer, LLMLearningActivitySerializer, ) class TemplateSuggestor: thread: EmailThread activity: LearningActivity participant: EmailParticipant # Definition participant impersonated by instructor def __init__(self, thread: EmailThread): def __init__( self, thread: EmailThread, participant_id: Optional[int] = None ): self.thread = thread definition_participant: EmailParticipant = ensure_exists( definition_participants = ( thread.participants.select_related( "definition_address", "definition_address__activity", Loading @@ -24,9 +30,44 @@ class TemplateSuggestor: .prefetch_related("definition_address__templates") .filter(definition_address__isnull=False) ) self.activity = definition_participant.definition_address.activity self.participant = self._get_participant( definition_participants, participant_id ) self.activity = self.participant.definition_address.activity def _get_participant( self, participants: QuerySet[EmailParticipant], participant_id: Optional[int], ) -> EmailParticipant: count = participants.count() if count == 0: raise ExerciseOperationException( "Thread contains no definition participants." ) if count == 1: participant = participants.first() return participant if participant_id is None: raise ExerciseOperationException( "Multiple definition email participants exist. " "You must specify which participant to impersonate." ) if participant := participants.filter(id=participant_id).first(): return participant raise ExerciseOperationException( "Definition participant with the given ID does not exist in this thread." ) def serialize(self): data = LLMEmailThreadSerializer(self.thread).data data.update(LLMLearningActivitySerialzer(self.activity).data) return json.dumps(data) context = {"participant": self.participant} thread_data = LLMEmailThreadSerializer( self.thread, context=context ).data activity_data = LLMLearningActivitySerializer(self.activity).data return json.dumps({**thread_data, **activity_data})