File size: 4,211 Bytes
977aa4a
4dec878
 
9fbb706
 
 
99cd51c
013c0dc
669c0b8
013c0dc
 
 
f243d4f
 
 
6768aaa
013c0dc
 
 
f243d4f
 
977aa4a
 
 
f243d4f
 
 
013c0dc
4dec878
07bbfbd
 
 
977aa4a
 
07bbfbd
4dec878
013c0dc
1dbd98a
f243d4f
1dbd98a
f243d4f
 
1dbd98a
4dec878
9c29307
 
 
4dec878
 
9c29307
4dec878
1dbd98a
 
9c29307
f243d4f
 
4dec878
f243d4f
 
 
013c0dc
 
07bbfbd
977aa4a
07bbfbd
 
977aa4a
07bbfbd
 
977aa4a
07bbfbd
977aa4a
07bbfbd
 
99cd51c
 
 
 
977aa4a
 
9fbb706
977aa4a
 
 
 
 
 
 
9fbb706
977aa4a
99cd51c
07bbfbd
 
 
 
1dbd98a
9c29307
f243d4f
013c0dc
4dec878
f243d4f
9fbb706
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import os
import typing

from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

from django.contrib.postgres import fields
from django.db import models


class Quiz(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Question(models.Model):
    quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
    prompt = models.CharField(max_length=200)
    rubrics = models.TextField(
        blank=True, null=True, verbose_name="Grading Rubrics - For LLM-graded questions only. You can leave this empty."
    )

    def __str__(self):
        return self.prompt

    def get_answer(self) -> typing.Union["Answer", None]:
        return (
            getattr(self, "multiplechoiceanswer", None)
            or getattr(self, "freetextanswer", None)
            # or getattr(self, "llmgradedanswer", None)
            or self.llmgradedanswer  # type: ignore
        )


class Answer(models.Model):
    question = models.OneToOneField(Question, on_delete=models.CASCADE)

    class Meta:
        abstract = True

    def __str__(self) -> str:
        return (
            getattr(self, "correct_answer", None) or getattr(self, "rubrics", None) or "No answer or rubrics provided"
        )

    def is_correct(self, user_answer) -> bool:
        return user_answer == getattr(self, "correct_answer", None)


class FreeTextAnswer(Answer):
    correct_answer = models.CharField(max_length=200, default="")
    case_sensitive = models.BooleanField(default=False)

    def is_correct(self, user_answer) -> bool:
        if not self.case_sensitive:
            return user_answer.lower() == self.correct_answer.lower()
        return user_answer == self.correct_answer


class LLMGradedAnswer(Answer):
    def grade(self, user_answer) -> dict:
        """
        Grades the user's answer by calling the grading API.

        Args:
            user_answer (str): The answer provided by the user.

        Returns:
            dict: The result of the grading.
        """
        try:
            # use dotenv to load env
            from dotenv import load_dotenv

            load_dotenv()
            prompt = f"Grade the following answer based on the rubric:\nRubric: {self.question.rubrics}\nAnswer: {user_answer}"

            response = client.chat.completions.create(
                model="gpt-4o",
                messages=[
                    {"role": "system", "content": "You are a helpful assistant."},
                    {"role": "user", "content": prompt},
                ],
            )

            return {"result": "success", "message": response.choices[0].message.content}

        except Exception as e:
            print(f"An error occurred: {e}")
            return {"result": "error", "message": str(e)}


class MultipleChoiceAnswer(Answer):
    correct_answer = models.CharField(max_length=200, default="")
    choices = fields.ArrayField(models.CharField(max_length=200, blank=True))

    def __str__(self) -> str:
        return f"{self.correct_answer} from {self.choices}"


class LLMGradedAnswer(Answer):
    correct_answer = models.CharField(max_length=200, default="")

    def grade(self, user_answer) -> dict:
        """
        Grades the user's answer by calling the grading API.

        Args:
            user_answer (str): The answer provided by the user.

        Returns:
            dict: The result of the grading.
        """
        try:
            # use dotenv to load env
            from dotenv import load_dotenv

            load_dotenv()
            prompt = f"Grade the following answer based on the rubric:\nRubric: {self.question.rubrics}\nAnswer: {user_answer}"

            response = client.chat.completions.create(
                model="gpt-4o",
                messages=[
                    {"role": "system", "content": "You are a helpful assistant."},
                    {"role": "user", "content": prompt},
                ],
            )

            return {"result": "success", "message": response.choices[0].message.content}

        except Exception as e:
            print(f"An error occurred: {e}")
            return {"result": "error", "message": str(e)}