@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.
- package/dist/routers/_app.d.ts +104 -2
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/conversation.d.ts +30 -0
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/conversation.js +40 -2
- package/dist/routers/conversation.js.map +1 -1
- package/dist/routers/worksheet.d.ts +22 -1
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +16 -12
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/server/pipelines/gradeWorksheet.d.ts +17 -2
- package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/server/pipelines/gradeWorksheet.js +176 -74
- package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
- package/package.json +2 -1
- package/src/routers/conversation.ts +47 -0
- package/src/routers/worksheet.ts +14 -11
- package/src/server/pipelines/gradeWorksheet.ts +207 -83
|
@@ -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
|
-
|
|
72
|
-
|
|
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
|
+
};
|