@studious-lms/server 1.2.49 → 1.2.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,133 @@ import { inference } from "../../utils/inference.js";
6
6
  import { getAIUserId } from "../../utils/aiUser.js";
7
7
  import { pusher } from "../../lib/pusher.js";
8
8
 
9
+
10
+ const removeAllPreviousAIComments = async (worksheetQuestionProgressId: string) => {
11
+ await prisma.comment.deleteMany({
12
+ where: {
13
+ studentQuestionProgressId: worksheetQuestionProgressId,
14
+ authorId: getAIUserId(),
15
+ },
16
+ });
17
+ };
18
+
19
+ const gradeWorksheetQuestion = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {
20
+
21
+ const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
22
+ where: { id: worksheetResponseId },
23
+ include: {
24
+ worksheet: true,
25
+ },
26
+ });
27
+
28
+ if (!worksheetResponse) {
29
+ logger.error('Worksheet response not found');
30
+ throw new Error('Worksheet response not found');
31
+ }
32
+
33
+ const studentQuestionProgress = await prisma.studentQuestionProgress.findFirst({
34
+ where: {
35
+ id: worksheetQuestionProgressId,
36
+ },
37
+ include: {
38
+ question: true,
39
+ comments: true,
40
+ },
41
+ });
42
+
43
+ if (!studentQuestionProgress) {
44
+ logger.error('Student question progress not found');
45
+ throw new Error('Student question progress not found');
46
+ }
47
+
48
+ pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-pending`, {
49
+ id: studentQuestionProgress.id,
50
+ });
51
+
52
+ const question = studentQuestionProgress.question;
53
+ const comments = studentQuestionProgress.comments;
54
+ const responseText = studentQuestionProgress.response;
55
+
56
+
57
+ try {
58
+ const apiResponse = await inference(
59
+ `Grade the following worksheet response:
60
+
61
+ Question: ${question.question}
62
+ Response: ${responseText}
63
+
64
+ Comments: ${comments.map((comment) => comment.content).join('\n')}
65
+ Mark Scheme: ${JSON.stringify(question.markScheme)}
66
+
67
+ Justify your reasoning by including comment(s) and mark the question please.
68
+ Return ONLY JSON in the following format (fill in the values as per the question):
69
+ {
70
+ "isCorrect": <boolean>,
71
+ "points": <number>,
72
+ "markschemeState": [
73
+ { "id": <string>, "correct": <boolean> }
74
+ ],
75
+ "comments": [<string>, ...]
76
+ }
77
+ `,
78
+ z.object({
79
+ isCorrect: z.boolean(),
80
+ points: z.number(),
81
+ markschemeState: z.array(z.object({
82
+ id: z.string(),
83
+ correct: z.boolean(),
84
+ })), // @note: this has to be converted to [id: string]: correct boolean
85
+ comments: z.array(z.string()),
86
+ }),
87
+ ).catch((error) => {
88
+ logger.error('Failed to grade worksheet response', { error });
89
+ throw error;
90
+ });
91
+
92
+ const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
93
+ where: { id: studentQuestionProgress.id, status: {
94
+ not: {
95
+ in: ['CANCELLED'],
96
+ },
97
+ } },
98
+ data: {
99
+ status: GenerationStatus.COMPLETED,
100
+ isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,
101
+ points: (apiResponse as { points: number }).points,
102
+ markschemeState: (apiResponse as {
103
+ markschemeState: { id: string; correct: boolean }[];
104
+ }).markschemeState.reduce((acc, curr) => {
105
+ acc["item-" + curr.id] = curr.correct;
106
+ return acc;
107
+ }, {} as Record<string, boolean>),
108
+ comments: {
109
+ create: (apiResponse as {
110
+ comments: string[];
111
+ }).comments.map((commentContent) => ({
112
+ content: commentContent,
113
+ authorId: getAIUserId(),
114
+ })),
115
+ },
116
+ },
117
+ });
118
+ pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-completed`, {
119
+ id: updatedStudentQuestionProgress.id,
120
+ });
121
+
122
+ return updatedStudentQuestionProgress;
123
+ } catch (error) {
124
+ logger.error('Failed to grade worksheet response', { error, worksheetResponseId });
125
+ pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-failed`, {
126
+ id: studentQuestionProgress.id,
127
+ });
128
+ await prisma.studentQuestionProgress.update({
129
+ where: { id: studentQuestionProgress.id },
130
+ data: { status: GenerationStatus.FAILED },
131
+ });
132
+ throw error;
133
+ }
134
+ }
135
+
9
136
  /**
10
137
  * Grades and regrades worksheet (can fixed failed responses)
11
138
  * @param worksheetResponseId worksheet response id
@@ -51,9 +178,6 @@ export const gradeWorksheetPipeline = async (worksheetResponseId: string) => {
51
178
  // Use for...of instead of forEach to properly handle async operations
52
179
  for (const response of worksheetResponse.responses) {
53
180
  logger.info('Grading question', { questionId: response.questionId });
54
- const question = response.question;
55
- const comments = response.comments;
56
- const responseText = response.response;
57
181
 
58
182
  const studentQuestionProgress = await prisma.studentQuestionProgress.update({
59
183
  where: { id: response.id, status: {
@@ -68,84 +192,84 @@ export const gradeWorksheetPipeline = async (worksheetResponseId: string) => {
68
192
  return;
69
193
  }
70
194
 
71
- try {
72
- const apiResponse = await inference(
73
- `Grade the following worksheet response:
74
-
75
- Question: ${question.question}
76
- Response: ${responseText}
77
-
78
- Comments: ${comments.map((comment) => comment.content).join('\n')}
79
- Mark Scheme: ${JSON.stringify(question.markScheme)}
80
-
81
- Justify your reasoning by including comment(s) and mark the question please.
82
- Return ONLY JSON in the following format (fill in the values as per the question):
83
- {
84
- "isCorrect": <boolean>,
85
- "points": <number>,
86
- "markschemeState": [
87
- { "id": <string>, "correct": <boolean> }
88
- ],
89
- "comments": [<string>, ...]
90
- }
91
- `,
92
- z.object({
93
- isCorrect: z.boolean(),
94
- points: z.number(),
95
- markschemeState: z.array(z.object({
96
- id: z.string(),
97
- correct: z.boolean(),
98
- })), // @note: this has to be converted to [id: string]: correct boolean
99
- comments: z.array(z.string()),
100
- }),
101
- ).catch((error) => {
102
- logger.error('Failed to grade worksheet response', { error });
103
- throw error;
104
- });
105
-
106
- console.log(apiResponse);
107
-
108
- const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
109
- where: { id: studentQuestionProgress.id, status: {
110
- not: {
111
- in: ['CANCELLED'],
112
- },
113
- } },
114
- data: {
115
- status: GenerationStatus.COMPLETED,
116
- isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,
117
- points: (apiResponse as { points: number }).points,
118
- markschemeState: (apiResponse as {
119
- markschemeState: { id: string; correct: boolean }[];
120
- }).markschemeState.reduce((acc, curr) => {
121
- acc["item-" + curr.id] = curr.correct;
122
- return acc;
123
- }, {} as Record<string, boolean>),
124
- comments: {
125
- create: (apiResponse as {
126
- comments: string[];
127
- }).comments.map((commentContent) => ({
128
- content: commentContent,
129
- authorId: getAIUserId(),
130
- })),
131
- },
132
- },
133
- });
134
- pusher.trigger(`class-${worksheetResponse.worksheet.classId}`, `ai-worksheet-updated-${worksheetResponse.id}`, {
135
- success: true,
136
- });
137
-
138
- return updatedStudentQuestionProgress;
139
- } catch (error) {
140
- logger.error('Failed to grade worksheet response', { error, worksheetResponseId });
141
- pusher.trigger(`class-${worksheetResponse.worksheet.classId}`, `ai-worksheet-updated-${worksheetResponse.id}`, {
142
- success: false,
143
- });
144
- await prisma.studentQuestionProgress.update({
145
- where: { id: studentQuestionProgress.id },
146
- data: { status: GenerationStatus.FAILED },
147
- });
148
- throw error;
149
- }
195
+ gradeWorksheetQuestion(worksheetResponseId, response.id);
196
+
150
197
  };
151
- };
198
+ };
199
+
200
+ export const cancelGradePipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {
201
+ logger.info('Cancelling auto grading', { worksheetResponseId, worksheetQuestionProgressId });
202
+
203
+ const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
204
+ where: { id: worksheetResponseId },
205
+ include: {
206
+ worksheet: true,
207
+ },
208
+ });
209
+ if (!worksheetResponse) {
210
+ logger.error('Worksheet response not found');
211
+ throw new Error('Worksheet response not found');
212
+ }
213
+ const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
214
+ where: { id: worksheetQuestionProgressId },
215
+ data: { status: GenerationStatus.CANCELLED },
216
+ });
217
+
218
+ await removeAllPreviousAIComments(worksheetQuestionProgressId);
219
+
220
+ pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-cancelled`, {
221
+ id: updatedStudentQuestionProgress.id,
222
+ });
223
+
224
+ return updatedStudentQuestionProgress;
225
+ };
226
+
227
+ export const regradeWorksheetPipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {
228
+ logger.info('Regrading worksheet response', { worksheetResponseId, worksheetQuestionProgressId });
229
+ try {
230
+ const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
231
+ where: { id: worksheetResponseId, },
232
+ include: {
233
+ worksheet: true,
234
+ },
235
+ });
236
+
237
+ await removeAllPreviousAIComments(worksheetQuestionProgressId);
238
+
239
+ if (!worksheetResponse) {
240
+ logger.error('Worksheet response not found');
241
+ throw new Error('Worksheet response not found');
242
+ }
243
+
244
+ const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
245
+ where: { id: worksheetQuestionProgressId },
246
+ data: { status: GenerationStatus.PENDING },
247
+ });
248
+
249
+ console.log(updatedStudentQuestionProgress);
250
+
251
+ gradeWorksheetQuestion(worksheetResponseId, worksheetQuestionProgressId);
252
+
253
+ return updatedStudentQuestionProgress;
254
+ } catch (error) {
255
+ await prisma.studentQuestionProgress.update({
256
+ where: { id: worksheetQuestionProgressId },
257
+ data: { status: GenerationStatus.FAILED },
258
+ });
259
+ const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
260
+ where: { id: worksheetResponseId, },
261
+ include: {
262
+ worksheet: true,
263
+ },
264
+ });
265
+ if (!worksheetResponse) {
266
+ logger.error('Worksheet response not found');
267
+ throw new Error('Worksheet response not found');
268
+ }
269
+ pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-failed`, {
270
+ id: worksheetQuestionProgressId,
271
+ });
272
+ logger.error('Failed to regrade worksheet response', { error, worksheetResponseId, worksheetQuestionProgressId });
273
+ throw error;
274
+ }
275
+ };