@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
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export declare const gradeWorksheetPipeline: (worksheetResponseId: string) => Promise<
|
|
1
|
+
export declare const gradeWorksheetPipeline: (worksheetResponseId: string) => Promise<void>;
|
|
2
|
+
export declare const cancelGradePipeline: (worksheetResponseId: string, worksheetQuestionProgressId: string) => Promise<{
|
|
2
3
|
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
3
4
|
id: string;
|
|
4
5
|
feedback: string | null;
|
|
@@ -11,5 +12,19 @@ export declare const gradeWorksheetPipeline: (worksheetResponseId: string) => Pr
|
|
|
11
12
|
markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
12
13
|
points: number;
|
|
13
14
|
studentWorksheetResponseId: string | null;
|
|
14
|
-
}
|
|
15
|
+
}>;
|
|
16
|
+
export declare const regradeWorksheetPipeline: (worksheetResponseId: string, worksheetQuestionProgressId: string) => Promise<{
|
|
17
|
+
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
18
|
+
id: string;
|
|
19
|
+
feedback: string | null;
|
|
20
|
+
studentId: string;
|
|
21
|
+
createdAt: Date;
|
|
22
|
+
updatedAt: Date | null;
|
|
23
|
+
questionId: string;
|
|
24
|
+
response: string;
|
|
25
|
+
isCorrect: boolean;
|
|
26
|
+
markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
27
|
+
points: number;
|
|
28
|
+
studentWorksheetResponseId: string | null;
|
|
29
|
+
}>;
|
|
15
30
|
//# sourceMappingURL=gradeWorksheet.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gradeWorksheet.d.ts","sourceRoot":"/","sources":["server/pipelines/gradeWorksheet.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gradeWorksheet.d.ts","sourceRoot":"/","sources":["server/pipelines/gradeWorksheet.ts"],"names":[],"mappings":"AA+IA,eAAO,MAAM,sBAAsB,GAAU,qBAAqB,MAAM,kBAsDvE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,qBAAqB,MAAM,EAAE,6BAA6B,MAAM;;;;;;;;;;;;;EAyBzG,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,qBAAqB,MAAM,EAAE,6BAA6B,MAAM;;;;;;;;;;;;;EAgD9G,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="dca8f58c-f587-5706-98e0-1eafd2fa5e2f")}catch(e){}}();
|
|
3
3
|
import { GenerationStatus, WorksheetQuestionType } from "@prisma/client";
|
|
4
4
|
import { prisma } from "../../lib/prisma.js";
|
|
5
5
|
import { logger } from "../../utils/logger.js";
|
|
@@ -7,6 +7,114 @@ import z from "zod";
|
|
|
7
7
|
import { inference } from "../../utils/inference.js";
|
|
8
8
|
import { getAIUserId } from "../../utils/aiUser.js";
|
|
9
9
|
import { pusher } from "../../lib/pusher.js";
|
|
10
|
+
const removeAllPreviousAIComments = async (worksheetQuestionProgressId) => {
|
|
11
|
+
await prisma.comment.deleteMany({
|
|
12
|
+
where: {
|
|
13
|
+
studentQuestionProgressId: worksheetQuestionProgressId,
|
|
14
|
+
authorId: getAIUserId(),
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
const gradeWorksheetQuestion = async (worksheetResponseId, worksheetQuestionProgressId) => {
|
|
19
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
20
|
+
where: { id: worksheetResponseId },
|
|
21
|
+
include: {
|
|
22
|
+
worksheet: true,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
if (!worksheetResponse) {
|
|
26
|
+
logger.error('Worksheet response not found');
|
|
27
|
+
throw new Error('Worksheet response not found');
|
|
28
|
+
}
|
|
29
|
+
const studentQuestionProgress = await prisma.studentQuestionProgress.findFirst({
|
|
30
|
+
where: {
|
|
31
|
+
id: worksheetQuestionProgressId,
|
|
32
|
+
},
|
|
33
|
+
include: {
|
|
34
|
+
question: true,
|
|
35
|
+
comments: true,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
if (!studentQuestionProgress) {
|
|
39
|
+
logger.error('Student question progress not found');
|
|
40
|
+
throw new Error('Student question progress not found');
|
|
41
|
+
}
|
|
42
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-pending`, {
|
|
43
|
+
id: studentQuestionProgress.id,
|
|
44
|
+
});
|
|
45
|
+
const question = studentQuestionProgress.question;
|
|
46
|
+
const comments = studentQuestionProgress.comments;
|
|
47
|
+
const responseText = studentQuestionProgress.response;
|
|
48
|
+
try {
|
|
49
|
+
const apiResponse = await inference(`Grade the following worksheet response:
|
|
50
|
+
|
|
51
|
+
Question: ${question.question}
|
|
52
|
+
Response: ${responseText}
|
|
53
|
+
|
|
54
|
+
Comments: ${comments.map((comment) => comment.content).join('\n')}
|
|
55
|
+
Mark Scheme: ${JSON.stringify(question.markScheme)}
|
|
56
|
+
|
|
57
|
+
Justify your reasoning by including comment(s) and mark the question please.
|
|
58
|
+
Return ONLY JSON in the following format (fill in the values as per the question):
|
|
59
|
+
{
|
|
60
|
+
"isCorrect": <boolean>,
|
|
61
|
+
"points": <number>,
|
|
62
|
+
"markschemeState": [
|
|
63
|
+
{ "id": <string>, "correct": <boolean> }
|
|
64
|
+
],
|
|
65
|
+
"comments": [<string>, ...]
|
|
66
|
+
}
|
|
67
|
+
`, z.object({
|
|
68
|
+
isCorrect: z.boolean(),
|
|
69
|
+
points: z.number(),
|
|
70
|
+
markschemeState: z.array(z.object({
|
|
71
|
+
id: z.string(),
|
|
72
|
+
correct: z.boolean(),
|
|
73
|
+
})), // @note: this has to be converted to [id: string]: correct boolean
|
|
74
|
+
comments: z.array(z.string()),
|
|
75
|
+
})).catch((error) => {
|
|
76
|
+
logger.error('Failed to grade worksheet response', { error });
|
|
77
|
+
throw error;
|
|
78
|
+
});
|
|
79
|
+
const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
80
|
+
where: { id: studentQuestionProgress.id, status: {
|
|
81
|
+
not: {
|
|
82
|
+
in: ['CANCELLED'],
|
|
83
|
+
},
|
|
84
|
+
} },
|
|
85
|
+
data: {
|
|
86
|
+
status: GenerationStatus.COMPLETED,
|
|
87
|
+
isCorrect: apiResponse.isCorrect,
|
|
88
|
+
points: apiResponse.points,
|
|
89
|
+
markschemeState: apiResponse.markschemeState.reduce((acc, curr) => {
|
|
90
|
+
acc["item-" + curr.id] = curr.correct;
|
|
91
|
+
return acc;
|
|
92
|
+
}, {}),
|
|
93
|
+
comments: {
|
|
94
|
+
create: apiResponse.comments.map((commentContent) => ({
|
|
95
|
+
content: commentContent,
|
|
96
|
+
authorId: getAIUserId(),
|
|
97
|
+
})),
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-completed`, {
|
|
102
|
+
id: updatedStudentQuestionProgress.id,
|
|
103
|
+
});
|
|
104
|
+
return updatedStudentQuestionProgress;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
logger.error('Failed to grade worksheet response', { error, worksheetResponseId });
|
|
108
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-failed`, {
|
|
109
|
+
id: studentQuestionProgress.id,
|
|
110
|
+
});
|
|
111
|
+
await prisma.studentQuestionProgress.update({
|
|
112
|
+
where: { id: studentQuestionProgress.id },
|
|
113
|
+
data: { status: GenerationStatus.FAILED },
|
|
114
|
+
});
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
10
118
|
/**
|
|
11
119
|
* Grades and regrades worksheet (can fixed failed responses)
|
|
12
120
|
* @param worksheetResponseId worksheet response id
|
|
@@ -48,9 +156,6 @@ export const gradeWorksheetPipeline = async (worksheetResponseId) => {
|
|
|
48
156
|
// Use for...of instead of forEach to properly handle async operations
|
|
49
157
|
for (const response of worksheetResponse.responses) {
|
|
50
158
|
logger.info('Grading question', { questionId: response.questionId });
|
|
51
|
-
const question = response.question;
|
|
52
|
-
const comments = response.comments;
|
|
53
|
-
const responseText = response.response;
|
|
54
159
|
const studentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
55
160
|
where: { id: response.id, status: {
|
|
56
161
|
not: {
|
|
@@ -62,78 +167,75 @@ export const gradeWorksheetPipeline = async (worksheetResponseId) => {
|
|
|
62
167
|
if (studentQuestionProgress.status !== GenerationStatus.PENDING) {
|
|
63
168
|
return;
|
|
64
169
|
}
|
|
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
|
-
status: GenerationStatus.COMPLETED,
|
|
105
|
-
isCorrect: apiResponse.isCorrect,
|
|
106
|
-
points: apiResponse.points,
|
|
107
|
-
markschemeState: apiResponse.markschemeState.reduce((acc, curr) => {
|
|
108
|
-
acc["item-" + curr.id] = curr.correct;
|
|
109
|
-
return acc;
|
|
110
|
-
}, {}),
|
|
111
|
-
comments: {
|
|
112
|
-
create: apiResponse.comments.map((commentContent) => ({
|
|
113
|
-
content: commentContent,
|
|
114
|
-
authorId: getAIUserId(),
|
|
115
|
-
})),
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
pusher.trigger(`class-${worksheetResponse.worksheet.classId}`, `ai-worksheet-updated-${worksheetResponse.id}`, {
|
|
120
|
-
success: true,
|
|
121
|
-
});
|
|
122
|
-
return updatedStudentQuestionProgress;
|
|
170
|
+
gradeWorksheetQuestion(worksheetResponseId, response.id);
|
|
171
|
+
}
|
|
172
|
+
;
|
|
173
|
+
};
|
|
174
|
+
export const cancelGradePipeline = async (worksheetResponseId, worksheetQuestionProgressId) => {
|
|
175
|
+
logger.info('Cancelling auto grading', { worksheetResponseId, worksheetQuestionProgressId });
|
|
176
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
177
|
+
where: { id: worksheetResponseId },
|
|
178
|
+
include: {
|
|
179
|
+
worksheet: true,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
if (!worksheetResponse) {
|
|
183
|
+
logger.error('Worksheet response not found');
|
|
184
|
+
throw new Error('Worksheet response not found');
|
|
185
|
+
}
|
|
186
|
+
const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
187
|
+
where: { id: worksheetQuestionProgressId },
|
|
188
|
+
data: { status: GenerationStatus.CANCELLED },
|
|
189
|
+
});
|
|
190
|
+
await removeAllPreviousAIComments(worksheetQuestionProgressId);
|
|
191
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-cancelled`, {
|
|
192
|
+
id: updatedStudentQuestionProgress.id,
|
|
193
|
+
});
|
|
194
|
+
return updatedStudentQuestionProgress;
|
|
195
|
+
};
|
|
196
|
+
export const regradeWorksheetPipeline = async (worksheetResponseId, worksheetQuestionProgressId) => {
|
|
197
|
+
logger.info('Regrading worksheet response', { worksheetResponseId, worksheetQuestionProgressId });
|
|
198
|
+
try {
|
|
199
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
200
|
+
where: { id: worksheetResponseId, },
|
|
201
|
+
include: {
|
|
202
|
+
worksheet: true,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
await removeAllPreviousAIComments(worksheetQuestionProgressId);
|
|
206
|
+
if (!worksheetResponse) {
|
|
207
|
+
logger.error('Worksheet response not found');
|
|
208
|
+
throw new Error('Worksheet response not found');
|
|
123
209
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
210
|
+
const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
211
|
+
where: { id: worksheetQuestionProgressId },
|
|
212
|
+
data: { status: GenerationStatus.PENDING },
|
|
213
|
+
});
|
|
214
|
+
console.log(updatedStudentQuestionProgress);
|
|
215
|
+
gradeWorksheetQuestion(worksheetResponseId, worksheetQuestionProgressId);
|
|
216
|
+
return updatedStudentQuestionProgress;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
await prisma.studentQuestionProgress.update({
|
|
220
|
+
where: { id: worksheetQuestionProgressId },
|
|
221
|
+
data: { status: GenerationStatus.FAILED },
|
|
222
|
+
});
|
|
223
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
224
|
+
where: { id: worksheetResponseId, },
|
|
225
|
+
include: {
|
|
226
|
+
worksheet: true,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
if (!worksheetResponse) {
|
|
230
|
+
logger.error('Worksheet response not found');
|
|
231
|
+
throw new Error('Worksheet response not found');
|
|
134
232
|
}
|
|
233
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-failed`, {
|
|
234
|
+
id: worksheetQuestionProgressId,
|
|
235
|
+
});
|
|
236
|
+
logger.error('Failed to regrade worksheet response', { error, worksheetResponseId, worksheetQuestionProgressId });
|
|
237
|
+
throw error;
|
|
135
238
|
}
|
|
136
|
-
;
|
|
137
239
|
};
|
|
138
240
|
//# sourceMappingURL=gradeWorksheet.js.map
|
|
139
|
-
//# debugId=
|
|
241
|
+
//# debugId=dca8f58c-f587-5706-98e0-1eafd2fa5e2f
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gradeWorksheet.js","sources":["server/pipelines/gradeWorksheet.ts"],"sourceRoot":"/","sourcesContent":["import { GenerationStatus, WorksheetQuestionType } from \"@prisma/client\";\nimport { prisma } from \"../../lib/prisma.js\";\nimport { logger } from \"../../utils/logger.js\";\nimport z from \"zod\";\nimport { inference } from \"../../utils/inference.js\";\nimport { getAIUserId } from \"../../utils/aiUser.js\";\nimport { pusher } from \"../../lib/pusher.js\";\n\n/**\n * Grades and regrades worksheet (can fixed failed responses)\n * @param worksheetResponseId worksheet response id\n * @returns updated worksheet response\n */\n\nconst DO_NOT_INFERENCE_STATUSES = [GenerationStatus.CANCELLED, GenerationStatus.PENDING, GenerationStatus.COMPLETED];\n\nexport const gradeWorksheetPipeline = async (worksheetResponseId: string) => {\n logger.info('Grading worksheet response', { worksheetResponseId });\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n responses: {\n where: {\n status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n },\n },\n question: {\n type: {\n not: {\n in: [WorksheetQuestionType.MULTIPLE_CHOICE, WorksheetQuestionType.TRUE_FALSE],\n }\n },\n },\n },\n include: {\n question: true,\n comments: true,\n },\n },\n },\n });\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n\n // Use for...of instead of forEach to properly handle async operations\n for (const response of worksheetResponse.responses) {\n logger.info('Grading question', { questionId: response.questionId });\n const question = response.question;\n const comments = response.comments;\n const responseText = response.response;\n\n const studentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: response.id, status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n }\n } },\n data: { status: GenerationStatus.PENDING },\n });\n\n if (studentQuestionProgress.status !== GenerationStatus.PENDING) {\n return;\n }\n\n try {\n const apiResponse = await inference(\n `Grade the following worksheet response:\n \n Question: ${question.question}\n Response: ${responseText}\n\n Comments: ${comments.map((comment) => comment.content).join('\\n')}\n Mark Scheme: ${JSON.stringify(question.markScheme)}\n \n Justify your reasoning by including comment(s) and mark the question please. \n Return ONLY JSON in the following format (fill in the values as per the question):\n {\n \"isCorrect\": <boolean>,\n \"points\": <number>,\n \"markschemeState\": [\n { \"id\": <string>, \"correct\": <boolean> }\n ],\n \"comments\": [<string>, ...]\n }\n `,\n z.object({\n isCorrect: z.boolean(),\n points: z.number(),\n markschemeState: z.array(z.object({\n id: z.string(),\n correct: z.boolean(),\n })), // @note: this has to be converted to [id: string]: correct boolean\n comments: z.array(z.string()),\n }),\n ).catch((error) => {\n logger.error('Failed to grade worksheet response', { error });\n throw error;\n });\n\n console.log(apiResponse);\n\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: studentQuestionProgress.id, status: {\n not: {\n in: ['CANCELLED'],\n },\n } },\n data: {\n status: GenerationStatus.COMPLETED,\n isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,\n points: (apiResponse as { points: number }).points,\n markschemeState: (apiResponse as {\n markschemeState: { id: string; correct: boolean }[];\n }).markschemeState.reduce((acc, curr) => {\n acc[\"item-\" + curr.id] = curr.correct;\n return acc;\n }, {} as Record<string, boolean>),\n comments: {\n create: (apiResponse as {\n comments: string[];\n }).comments.map((commentContent) => ({\n content: commentContent,\n authorId: getAIUserId(),\n })),\n },\n },\n });\n pusher.trigger(`class-${worksheetResponse.worksheet.classId}`, `ai-worksheet-updated-${worksheetResponse.id}`, {\n success: true,\n });\n\n return updatedStudentQuestionProgress;\n } catch (error) {\n logger.error('Failed to grade worksheet response', { error, worksheetResponseId });\n pusher.trigger(`class-${worksheetResponse.worksheet.classId}`, `ai-worksheet-updated-${worksheetResponse.id}`, {\n success: false,\n });\n await prisma.studentQuestionProgress.update({\n where: { id: studentQuestionProgress.id },\n data: { status: GenerationStatus.FAILED },\n });\n throw error;\n }\n };\n};"],"names":[],"mappings":";;AAAA,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C;;;;GAIG;AAEH,MAAM,yBAAyB,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;AAErH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,EAAE,mBAA2B,EAAE,EAAE;IACxE,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,mBAAmB,EAAE,CAAC,CAAC;IACnE,MAAM,iBAAiB,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,UAAU,CAAC;QACvE,KAAK,EAAE,EAAE,EAAE,EAAE,mBAAmB,EAAE;QAClC,OAAO,EAAE;YACL,SAAS,EAAE,IAAI;YACf,SAAS,EAAE;gBACP,KAAK,EAAE;oBACH,MAAM,EAAE;wBACJ,GAAG,EAAE;4BACD,EAAE,EAAE,yBAAyB;yBAChC;qBACJ;oBACD,QAAQ,EAAE;wBACN,IAAI,EAAE;4BACF,GAAG,EAAE;gCACD,EAAE,EAAE,CAAC,qBAAqB,CAAC,eAAe,EAAE,qBAAqB,CAAC,UAAU,CAAC;6BAChF;yBACJ;qBACJ;iBACJ;gBACD,OAAO,EAAE;oBACL,QAAQ,EAAE,IAAI;oBACd,QAAQ,EAAE,IAAI;iBACjB;aACJ;SACJ;KACJ,CAAC,CAAC;IAEH,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrB,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,sEAAsE;IACtE,KAAK,MAAM,QAAQ,IAAI,iBAAiB,CAAC,SAAS,EAAE,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACrE,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACnC,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACnC,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC;QAEvC,MAAM,uBAAuB,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC;YACxE,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE;oBAC9B,GAAG,EAAE;wBACD,EAAE,EAAE,yBAAyB;qBAChC;iBACJ,EAAE;YACH,IAAI,EAAE,EAAE,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE;SAC7C,CAAC,CAAC;QAEH,IAAI,uBAAuB,CAAC,MAAM,KAAK,gBAAgB,CAAC,OAAO,EAAE,CAAC;YAC9D,OAAO;QACX,CAAC;QAED,IAAI,CAAC;YACD,MAAM,WAAW,GAAG,MAAM,SAAS,CAC/B;;4BAEY,QAAQ,CAAC,QAAQ;4BACjB,YAAY;;4BAEZ,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;+BAClD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC;;;;;;;;;;;;iBAYjD,EACD,CAAC,CAAC,MAAM,CAAC;gBACL,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;gBACtB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;gBAClB,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;oBAC9B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;oBACd,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;iBACrB,CAAC,CAAC,EAAE,mEAAmE;gBAC1E,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aAChC,CAAC,CACL,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACd,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC9D,MAAM,KAAK,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAEzB,MAAM,8BAA8B,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC;gBAC/E,KAAK,EAAE,EAAE,EAAE,EAAE,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE;wBAC7C,GAAG,EAAE;4BACD,EAAE,EAAE,CAAC,WAAW,CAAC;yBACpB;qBACJ,EAAE;gBACH,IAAI,EAAE;oBACF,MAAM,EAAE,gBAAgB,CAAC,SAAS;oBAClC,SAAS,EAAG,WAAsC,CAAC,SAAS;oBAC5D,MAAM,EAAG,WAAkC,CAAC,MAAM;oBAClD,eAAe,EAAG,WAEhB,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;wBACpC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;wBACtC,OAAO,GAAG,CAAC;oBACf,CAAC,EAAE,EAA6B,CAAC;oBACjC,QAAQ,EAAE;wBACN,MAAM,EAAG,WAEP,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;4BACjC,OAAO,EAAE,cAAc;4BACvB,QAAQ,EAAE,WAAW,EAAE;yBAC1B,CAAC,CAAC;qBACN;iBACJ;aACJ,CAAC,CAAC;YACH,MAAM,CAAC,OAAO,CAAC,SAAS,iBAAiB,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,wBAAwB,iBAAiB,CAAC,EAAE,EAAE,EAAE;gBAC3G,OAAO,EAAE,IAAI;aAChB,CAAC,CAAC;YAEH,OAAO,8BAA8B,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YACnF,MAAM,CAAC,OAAO,CAAC,SAAS,iBAAiB,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,wBAAwB,iBAAiB,CAAC,EAAE,EAAE,EAAE;gBAC3G,OAAO,EAAE,KAAK;aACjB,CAAC,CAAC;YACH,MAAM,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC;gBACxC,KAAK,EAAE,EAAE,EAAE,EAAE,uBAAuB,CAAC,EAAE,EAAE;gBACzC,IAAI,EAAE,EAAE,MAAM,EAAE,gBAAgB,CAAC,MAAM,EAAE;aAC5C,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IAAA,CAAC;AACN,CAAC,CAAC","debug_id":"1b23ac7f-6ff8-51c0-8cc7-e0276b542625"}
|
|
1
|
+
{"version":3,"file":"gradeWorksheet.js","sources":["server/pipelines/gradeWorksheet.ts"],"sourceRoot":"/","sourcesContent":["import { GenerationStatus, WorksheetQuestionType } from \"@prisma/client\";\nimport { prisma } from \"../../lib/prisma.js\";\nimport { logger } from \"../../utils/logger.js\";\nimport z from \"zod\";\nimport { inference } from \"../../utils/inference.js\";\nimport { getAIUserId } from \"../../utils/aiUser.js\";\nimport { pusher } from \"../../lib/pusher.js\";\n\n\nconst removeAllPreviousAIComments = async (worksheetQuestionProgressId: string) => {\n await prisma.comment.deleteMany({\n where: {\n studentQuestionProgressId: worksheetQuestionProgressId,\n authorId: getAIUserId(),\n },\n });\n};\n\nconst gradeWorksheetQuestion = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n },\n });\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n } \n\n const studentQuestionProgress = await prisma.studentQuestionProgress.findFirst({\n where: {\n id: worksheetQuestionProgressId,\n },\n include: {\n question: true,\n comments: true,\n },\n });\n\n if (!studentQuestionProgress) {\n logger.error('Student question progress not found');\n throw new Error('Student question progress not found');\n }\n\n pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-pending`, {\n id: studentQuestionProgress.id,\n });\n\n const question = studentQuestionProgress.question;\n const comments = studentQuestionProgress.comments;\n const responseText = studentQuestionProgress.response;\n\n\n try {\n const apiResponse = await inference(\n `Grade the following worksheet response:\n \n Question: ${question.question}\n Response: ${responseText}\n\n Comments: ${comments.map((comment) => comment.content).join('\\n')}\n Mark Scheme: ${JSON.stringify(question.markScheme)}\n \n Justify your reasoning by including comment(s) and mark the question please. \n Return ONLY JSON in the following format (fill in the values as per the question):\n {\n \"isCorrect\": <boolean>,\n \"points\": <number>,\n \"markschemeState\": [\n { \"id\": <string>, \"correct\": <boolean> }\n ],\n \"comments\": [<string>, ...]\n }\n `,\n z.object({\n isCorrect: z.boolean(),\n points: z.number(),\n markschemeState: z.array(z.object({\n id: z.string(),\n correct: z.boolean(),\n })), // @note: this has to be converted to [id: string]: correct boolean\n comments: z.array(z.string()),\n }),\n ).catch((error) => {\n logger.error('Failed to grade worksheet response', { error });\n throw error;\n });\n\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: studentQuestionProgress.id, status: {\n not: {\n in: ['CANCELLED'],\n },\n } },\n data: {\n status: GenerationStatus.COMPLETED,\n isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,\n points: (apiResponse as { points: number }).points,\n markschemeState: (apiResponse as {\n markschemeState: { id: string; correct: boolean }[];\n }).markschemeState.reduce((acc, curr) => {\n acc[\"item-\" + curr.id] = curr.correct;\n return acc;\n }, {} as Record<string, boolean>),\n comments: {\n create: (apiResponse as {\n comments: string[];\n }).comments.map((commentContent) => ({\n content: commentContent,\n authorId: getAIUserId(),\n })),\n },\n },\n });\n pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-completed`, {\n id: updatedStudentQuestionProgress.id,\n });\n\n return updatedStudentQuestionProgress;\n } catch (error) {\n logger.error('Failed to grade worksheet response', { error, worksheetResponseId });\n pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-failed`, {\n id: studentQuestionProgress.id,\n });\n await prisma.studentQuestionProgress.update({\n where: { id: studentQuestionProgress.id },\n data: { status: GenerationStatus.FAILED },\n });\n throw error;\n }\n}\n\n/**\n * Grades and regrades worksheet (can fixed failed responses)\n * @param worksheetResponseId worksheet response id\n * @returns updated worksheet response\n */\n\nconst DO_NOT_INFERENCE_STATUSES = [GenerationStatus.CANCELLED, GenerationStatus.PENDING, GenerationStatus.COMPLETED];\n\nexport const gradeWorksheetPipeline = async (worksheetResponseId: string) => {\n logger.info('Grading worksheet response', { worksheetResponseId });\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n responses: {\n where: {\n status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n },\n },\n question: {\n type: {\n not: {\n in: [WorksheetQuestionType.MULTIPLE_CHOICE, WorksheetQuestionType.TRUE_FALSE],\n }\n },\n },\n },\n include: {\n question: true,\n comments: true,\n },\n },\n },\n });\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n\n // Use for...of instead of forEach to properly handle async operations\n for (const response of worksheetResponse.responses) {\n logger.info('Grading question', { questionId: response.questionId });\n\n const studentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: response.id, status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n }\n } },\n data: { status: GenerationStatus.PENDING },\n });\n\n if (studentQuestionProgress.status !== GenerationStatus.PENDING) {\n return;\n }\n\n gradeWorksheetQuestion(worksheetResponseId, response.id);\n\n };\n};\n\nexport const cancelGradePipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n logger.info('Cancelling auto grading', { worksheetResponseId, worksheetQuestionProgressId });\n\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n },\n });\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: worksheetQuestionProgressId },\n data: { status: GenerationStatus.CANCELLED },\n });\n\n await removeAllPreviousAIComments(worksheetQuestionProgressId);\n\n pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-cancelled`, {\n id: updatedStudentQuestionProgress.id,\n });\n\n return updatedStudentQuestionProgress;\n};\n\nexport const regradeWorksheetPipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n logger.info('Regrading worksheet response', { worksheetResponseId, worksheetQuestionProgressId });\n try {\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId, },\n include: {\n worksheet: true,\n },\n });\n \n await removeAllPreviousAIComments(worksheetQuestionProgressId);\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: worksheetQuestionProgressId },\n data: { status: GenerationStatus.PENDING },\n });\n\nconsole.log(updatedStudentQuestionProgress);\n\n gradeWorksheetQuestion(worksheetResponseId, worksheetQuestionProgressId);\n\n return updatedStudentQuestionProgress;\n } catch (error) {\n await prisma.studentQuestionProgress.update({\n where: { id: worksheetQuestionProgressId },\n data: { status: GenerationStatus.FAILED },\n });\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId, },\n include: {\n worksheet: true,\n },\n });\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-failed`, {\n id: worksheetQuestionProgressId,\n });\n logger.error('Failed to regrade worksheet response', { error, worksheetResponseId, worksheetQuestionProgressId });\n throw error;\n }\n};\n"],"names":[],"mappings":"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","debug_id":"dca8f58c-f587-5706-98e0-1eafd2fa5e2f"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studious-lms/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.51",
|
|
4
4
|
"description": "Backend server for Studious application",
|
|
5
5
|
"main": "dist/exportType.js",
|
|
6
6
|
"types": "dist/exportType.d.ts",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@prisma/client": "^6.7.0",
|
|
32
32
|
"@sentry/cli": "^2.58.2",
|
|
33
33
|
"@sentry/node": "^10.26.0",
|
|
34
|
+
"@studious-lms/server": "^1.2.49",
|
|
34
35
|
"@trpc/server": "^11.4.3",
|
|
35
36
|
"@unkey/ratelimit": "^2.1.3",
|
|
36
37
|
"bcryptjs": "^3.0.2",
|
|
@@ -314,6 +314,53 @@ export const conversationRouter = createTRPCRouter({
|
|
|
314
314
|
});
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
return conversation;
|
|
318
|
+
}),
|
|
319
|
+
addMember: protectedProcedure
|
|
320
|
+
.input(z.object({ conversationId: z.string(), memberId: z.string() }))
|
|
321
|
+
.mutation(async ({ input, ctx }) => {
|
|
322
|
+
const userId = ctx.user!.id;
|
|
323
|
+
const { conversationId, memberId } = input;
|
|
324
|
+
|
|
325
|
+
const conversation = await prisma.conversation.findFirst({
|
|
326
|
+
where: { id: conversationId, members: { some: { userId } } },
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (!conversation) {
|
|
330
|
+
throw new TRPCError({
|
|
331
|
+
code: 'NOT_FOUND',
|
|
332
|
+
message: 'Conversation not found or access denied',
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
await prisma.conversationMember.create({
|
|
337
|
+
data: { userId: memberId, conversationId, role: 'MEMBER' },
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
return conversation;
|
|
341
|
+
}),
|
|
342
|
+
|
|
343
|
+
removeMember: protectedProcedure
|
|
344
|
+
.input(z.object({ conversationId: z.string(), memberId: z.string() }))
|
|
345
|
+
.mutation(async ({ input, ctx }) => {
|
|
346
|
+
const userId = ctx.user!.id;
|
|
347
|
+
const { conversationId, memberId } = input;
|
|
348
|
+
|
|
349
|
+
const conversation = await prisma.conversation.findFirst({
|
|
350
|
+
where: { id: conversationId, members: { some: { userId } } },
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
if (!conversation) {
|
|
354
|
+
throw new TRPCError({
|
|
355
|
+
code: 'NOT_FOUND',
|
|
356
|
+
message: 'Conversation not found or access denied',
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
await prisma.conversationMember.delete({
|
|
361
|
+
where: { userId_conversationId: { userId: memberId, conversationId } },
|
|
362
|
+
});
|
|
363
|
+
|
|
317
364
|
return conversation;
|
|
318
365
|
}),
|
|
319
366
|
});
|
package/src/routers/worksheet.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
5
|
import { GenerationStatus, WorksheetQuestionType } from "@prisma/client";
|
|
6
6
|
import { commentSelect } from "./comment.js";
|
|
7
|
+
import { cancelGradePipeline, regradeWorksheetPipeline } from "../server/pipelines/gradeWorksheet.js";
|
|
7
8
|
|
|
8
9
|
type MCQOptions = {
|
|
9
10
|
id: string;
|
|
@@ -415,22 +416,24 @@ export const worksheetRouter = createTRPCRouter({
|
|
|
415
416
|
|
|
416
417
|
return updatedWorksheetResponse;
|
|
417
418
|
}),
|
|
418
|
-
|
|
419
|
+
cancelGrading: protectedProcedure
|
|
419
420
|
.input(z.object({
|
|
420
421
|
worksheetResponseId: z.string(),
|
|
421
|
-
|
|
422
|
+
progressId: z.string(),
|
|
422
423
|
}))
|
|
423
424
|
.mutation(async ({ ctx, input }) => {
|
|
424
|
-
const { worksheetResponseId,
|
|
425
|
-
|
|
426
|
-
const updatedQuestion = await prisma.studentQuestionProgress.update({
|
|
427
|
-
where: { id: questionId, studentWorksheetResponseId: worksheetResponseId },
|
|
428
|
-
data: {
|
|
429
|
-
status: GenerationStatus.CANCELLED,
|
|
430
|
-
},
|
|
431
|
-
});
|
|
425
|
+
const { worksheetResponseId, progressId } = input;
|
|
432
426
|
|
|
433
|
-
return
|
|
427
|
+
return cancelGradePipeline(worksheetResponseId, progressId);
|
|
428
|
+
}),
|
|
429
|
+
regradeQuestion: protectedProcedure
|
|
430
|
+
.input(z.object({
|
|
431
|
+
worksheetResponseId: z.string(),
|
|
432
|
+
progressId: z.string(),
|
|
433
|
+
}))
|
|
434
|
+
.mutation(async ({ ctx, input }) => {
|
|
435
|
+
const { worksheetResponseId, progressId } = input;
|
|
436
|
+
return regradeWorksheetPipeline(worksheetResponseId, progressId);
|
|
434
437
|
}),
|
|
435
438
|
// Grade a student's answer
|
|
436
439
|
gradeAnswer: protectedProcedure
|