@studious-lms/server 1.25.0 → 1.27.0
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/.env.example +3 -1
- package/.github/workflows/npm-publish.yml +1 -0
- package/AGENTS.md +84 -0
- package/dist/models/cloudStorage.d.ts +11 -0
- package/dist/models/cloudStorage.d.ts.map +1 -1
- package/dist/models/cloudStorage.js +18 -2
- package/dist/models/cloudStorage.js.map +1 -1
- package/dist/models/conversation.d.ts +1 -0
- package/dist/models/conversation.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +40 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/labChat.js +32 -2
- package/dist/models/labChat.js.map +1 -1
- package/dist/models/marketplace.d.ts +35 -0
- package/dist/models/marketplace.d.ts.map +1 -1
- package/dist/models/marketplace.js +9 -2
- package/dist/models/marketplace.js.map +1 -1
- package/dist/models/message.d.ts +3 -0
- package/dist/models/message.d.ts.map +1 -1
- package/dist/models/module.d.ts +228 -0
- package/dist/models/module.d.ts.map +1 -0
- package/dist/models/module.js +69 -0
- package/dist/models/module.js.map +1 -0
- package/dist/pipelines/aiLabChat.d.ts +2 -0
- package/dist/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.js +60 -16
- package/dist/pipelines/aiLabChat.js.map +1 -1
- package/dist/pipelines/aiLabChatContract.d.ts +170 -2
- package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
- package/dist/pipelines/aiLabChatContract.js +30 -2
- package/dist/pipelines/aiLabChatContract.js.map +1 -1
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
- package/dist/pipelines/aiNewtonChat.js +30 -5
- package/dist/pipelines/aiNewtonChat.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/pipelines/gradeWorksheet.js +4 -2
- package/dist/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/pipelines/labChatPrompt.d.ts +1 -1
- package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
- package/dist/pipelines/labChatPrompt.js +13 -14
- package/dist/pipelines/labChatPrompt.js.map +1 -1
- package/dist/routers/_app.d.ts +1185 -215
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +4 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/admin.d.ts +0 -1
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/conversation.d.ts +1 -0
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/labChat.d.ts +10 -0
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +6 -3
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/marketplace.d.ts +41 -0
- package/dist/routers/marketplace.d.ts.map +1 -1
- package/dist/routers/marketplace.js +13 -3
- package/dist/routers/marketplace.js.map +1 -1
- package/dist/routers/message.d.ts +37 -4
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +24 -7
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/module.d.ts +345 -0
- package/dist/routers/module.d.ts.map +1 -0
- package/dist/routers/module.js +82 -0
- package/dist/routers/module.js.map +1 -0
- package/dist/routers/parent.d.ts +18 -0
- package/dist/routers/parent.d.ts.map +1 -1
- package/dist/routers/parent.js +10 -3
- package/dist/routers/parent.js.map +1 -1
- package/dist/routers/studentProgress.d.ts +44 -0
- package/dist/routers/studentProgress.d.ts.map +1 -1
- package/dist/routers/studentProgress.js +12 -3
- package/dist/routers/studentProgress.js.map +1 -1
- package/dist/routes/aiStream.js +32 -2
- package/dist/routes/aiStream.js.map +1 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +107 -3
- package/dist/seedDatabase.js.map +1 -1
- package/dist/services/adminUser.d.ts +0 -1
- package/dist/services/adminUser.d.ts.map +1 -1
- package/dist/services/adminUser.js +7 -4
- package/dist/services/adminUser.js.map +1 -1
- package/dist/services/aiChatStream.d.ts +15 -0
- package/dist/services/aiChatStream.d.ts.map +1 -1
- package/dist/services/aiChatStream.js +144 -74
- package/dist/services/aiChatStream.js.map +1 -1
- package/dist/services/aiGeneration.d.ts +4 -0
- package/dist/services/aiGeneration.d.ts.map +1 -0
- package/dist/services/aiGeneration.js +88 -0
- package/dist/services/aiGeneration.js.map +1 -0
- package/dist/services/aiProposalTools.d.ts +4 -1
- package/dist/services/aiProposalTools.d.ts.map +1 -1
- package/dist/services/aiProposalTools.js +128 -3
- package/dist/services/aiProposalTools.js.map +1 -1
- package/dist/services/aiProposals.d.ts +70 -0
- package/dist/services/aiProposals.d.ts.map +1 -0
- package/dist/services/aiProposals.js +363 -0
- package/dist/services/aiProposals.js.map +1 -0
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +8 -3
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/conversation.d.ts +1 -0
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/labChat.d.ts +10 -0
- package/dist/services/labChat.d.ts.map +1 -1
- package/dist/services/labChat.js +52 -3
- package/dist/services/labChat.js.map +1 -1
- package/dist/services/marketplace.d.ts +36 -0
- package/dist/services/marketplace.d.ts.map +1 -1
- package/dist/services/marketplace.js +30 -3
- package/dist/services/marketplace.js.map +1 -1
- package/dist/services/message.d.ts +24 -8
- package/dist/services/message.d.ts.map +1 -1
- package/dist/services/message.js +170 -117
- package/dist/services/message.js.map +1 -1
- package/dist/services/module.d.ts +288 -0
- package/dist/services/module.d.ts.map +1 -0
- package/dist/services/module.js +518 -0
- package/dist/services/module.js.map +1 -0
- package/dist/services/parentPortal.d.ts +14 -0
- package/dist/services/parentPortal.d.ts.map +1 -1
- package/dist/services/parentPortal.js +72 -2
- package/dist/services/parentPortal.js.map +1 -1
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/school.js +17 -5
- package/dist/services/school.js.map +1 -1
- package/dist/services/studentProgress.d.ts +50 -0
- package/dist/services/studentProgress.d.ts.map +1 -1
- package/dist/services/studentProgress.js +107 -2
- package/dist/services/studentProgress.js.map +1 -1
- package/dist/utils/inference.d.ts +10 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +77 -2
- package/dist/utils/inference.js.map +1 -1
- package/dist/workers/processors/createRunJobWorker.js +17 -16
- package/dist/workers/processors/createRunJobWorker.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260708013000_add_modules/migration.sql +100 -0
- package/prisma/migrations/20260811130000_add_ai_message_feedback/migration.sql +7 -0
- package/prisma/schema.prisma +101 -0
- package/src/models/cloudStorage.ts +19 -0
- package/src/models/labChat.ts +34 -0
- package/src/models/marketplace.ts +16 -0
- package/src/models/module.ts +77 -0
- package/src/pipelines/aiLabChat.ts +79 -18
- package/src/pipelines/aiLabChatContract.ts +35 -0
- package/src/pipelines/aiNewtonChat.ts +34 -2
- package/src/pipelines/gradeWorksheet.ts +6 -0
- package/src/pipelines/labChatPrompt.ts +11 -12
- package/src/routers/_app.ts +2 -0
- package/src/routers/labChat.ts +5 -0
- package/src/routers/marketplace.ts +16 -0
- package/src/routers/message.ts +34 -5
- package/src/routers/module.ts +118 -0
- package/src/routers/parent.ts +11 -0
- package/src/routers/studentProgress.ts +15 -0
- package/src/routes/aiStream.ts +32 -0
- package/src/seedDatabase.ts +107 -2
- package/src/services/adminUser.ts +5 -2
- package/src/services/aiChatStream.ts +83 -7
- package/src/services/aiGeneration.ts +92 -0
- package/src/services/aiProposalTools.ts +166 -1
- package/src/services/aiProposals.ts +428 -0
- package/src/services/assignment.ts +12 -1
- package/src/services/labChat.ts +65 -0
- package/src/services/marketplace.ts +39 -0
- package/src/services/message.ts +205 -142
- package/src/services/module.ts +683 -0
- package/src/services/parentPortal.ts +85 -0
- package/src/services/school.ts +15 -3
- package/src/services/studentProgress.ts +135 -0
- package/src/utils/inference.ts +85 -0
- package/src/workers/processors/createRunJobWorker.ts +17 -17
- package/tests/unit/services/adminUser.test.ts +75 -0
- package/tests/unit/utils/adminPanelAccess.test.ts +26 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/** @returns Class if user is a teacher or school admin. */
|
|
2
|
+
export declare function findClassWithTeacher(classId: string, teacherId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
3
|
+
result: {
|
|
4
|
+
userProfile: {
|
|
5
|
+
profilePicture: () => {
|
|
6
|
+
needs: {
|
|
7
|
+
profilePicture: true;
|
|
8
|
+
};
|
|
9
|
+
compute(profile: {
|
|
10
|
+
profilePicture: string | null;
|
|
11
|
+
}): string | null;
|
|
12
|
+
};
|
|
13
|
+
profilePictureThumbnail: () => {
|
|
14
|
+
needs: {
|
|
15
|
+
profilePictureThumbnail: true;
|
|
16
|
+
};
|
|
17
|
+
compute(profile: {
|
|
18
|
+
profilePictureThumbnail: string | null;
|
|
19
|
+
}): string | null;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
model: {};
|
|
24
|
+
query: {};
|
|
25
|
+
client: {};
|
|
26
|
+
}, {}>, "Class", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
27
|
+
symbol: string | null;
|
|
28
|
+
section: string;
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
schoolId: string | null;
|
|
32
|
+
color: string | null;
|
|
33
|
+
subject: string;
|
|
34
|
+
syllabus: string | null;
|
|
35
|
+
departmentId: string | null;
|
|
36
|
+
discussionsEnabled: boolean;
|
|
37
|
+
studentsCanCreateDiscussions: boolean;
|
|
38
|
+
studentsCanReply: boolean;
|
|
39
|
+
sidebarFeatures: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
40
|
+
treatMissingAsZero: boolean;
|
|
41
|
+
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
42
|
+
} | null>;
|
|
43
|
+
export declare const moduleInclude: {
|
|
44
|
+
readonly items: {
|
|
45
|
+
readonly orderBy: {
|
|
46
|
+
readonly position: "asc";
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
readonly prerequisites: {
|
|
50
|
+
readonly select: {
|
|
51
|
+
readonly prerequisiteId: true;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/** @returns All modules of a class ordered by position. */
|
|
56
|
+
export declare function findModulesByClassId(classId: string, publishedOnly?: boolean): import("@prisma/client/runtime/library.js").PrismaPromise<({
|
|
57
|
+
items: {
|
|
58
|
+
id: string;
|
|
59
|
+
title: string | null;
|
|
60
|
+
position: number;
|
|
61
|
+
moduleId: string;
|
|
62
|
+
indent: number;
|
|
63
|
+
itemType: import(".prisma/client").$Enums.CourseModuleItemType;
|
|
64
|
+
itemId: string | null;
|
|
65
|
+
externalUrl: string | null;
|
|
66
|
+
requirementType: import(".prisma/client").$Enums.ModuleItemRequirementType | null;
|
|
67
|
+
requiredScore: number | null;
|
|
68
|
+
}[];
|
|
69
|
+
prerequisites: {
|
|
70
|
+
prerequisiteId: string;
|
|
71
|
+
}[];
|
|
72
|
+
} & {
|
|
73
|
+
id: string;
|
|
74
|
+
createdAt: Date;
|
|
75
|
+
updatedAt: Date;
|
|
76
|
+
classId: string;
|
|
77
|
+
title: string;
|
|
78
|
+
position: number;
|
|
79
|
+
published: boolean;
|
|
80
|
+
unlockAt: Date | null;
|
|
81
|
+
requireSequential: boolean;
|
|
82
|
+
})[]>;
|
|
83
|
+
/** @returns Module by ID scoped to a class. */
|
|
84
|
+
export declare function findModuleByClassAndId(id: string, classId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
85
|
+
result: {
|
|
86
|
+
userProfile: {
|
|
87
|
+
profilePicture: () => {
|
|
88
|
+
needs: {
|
|
89
|
+
profilePicture: true;
|
|
90
|
+
};
|
|
91
|
+
compute(profile: {
|
|
92
|
+
profilePicture: string | null;
|
|
93
|
+
}): string | null;
|
|
94
|
+
};
|
|
95
|
+
profilePictureThumbnail: () => {
|
|
96
|
+
needs: {
|
|
97
|
+
profilePictureThumbnail: true;
|
|
98
|
+
};
|
|
99
|
+
compute(profile: {
|
|
100
|
+
profilePictureThumbnail: string | null;
|
|
101
|
+
}): string | null;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
model: {};
|
|
106
|
+
query: {};
|
|
107
|
+
client: {};
|
|
108
|
+
}, {}>, "Module", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<({
|
|
109
|
+
items: {
|
|
110
|
+
id: string;
|
|
111
|
+
title: string | null;
|
|
112
|
+
position: number;
|
|
113
|
+
moduleId: string;
|
|
114
|
+
indent: number;
|
|
115
|
+
itemType: import(".prisma/client").$Enums.CourseModuleItemType;
|
|
116
|
+
itemId: string | null;
|
|
117
|
+
externalUrl: string | null;
|
|
118
|
+
requirementType: import(".prisma/client").$Enums.ModuleItemRequirementType | null;
|
|
119
|
+
requiredScore: number | null;
|
|
120
|
+
}[];
|
|
121
|
+
prerequisites: {
|
|
122
|
+
prerequisiteId: string;
|
|
123
|
+
}[];
|
|
124
|
+
} & {
|
|
125
|
+
id: string;
|
|
126
|
+
createdAt: Date;
|
|
127
|
+
updatedAt: Date;
|
|
128
|
+
classId: string;
|
|
129
|
+
title: string;
|
|
130
|
+
position: number;
|
|
131
|
+
published: boolean;
|
|
132
|
+
unlockAt: Date | null;
|
|
133
|
+
requireSequential: boolean;
|
|
134
|
+
}) | null>;
|
|
135
|
+
/** @returns Module item with its module, scoped to a class. */
|
|
136
|
+
export declare function findModuleItemInClass(itemId: string, classId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
137
|
+
result: {
|
|
138
|
+
userProfile: {
|
|
139
|
+
profilePicture: () => {
|
|
140
|
+
needs: {
|
|
141
|
+
profilePicture: true;
|
|
142
|
+
};
|
|
143
|
+
compute(profile: {
|
|
144
|
+
profilePicture: string | null;
|
|
145
|
+
}): string | null;
|
|
146
|
+
};
|
|
147
|
+
profilePictureThumbnail: () => {
|
|
148
|
+
needs: {
|
|
149
|
+
profilePictureThumbnail: true;
|
|
150
|
+
};
|
|
151
|
+
compute(profile: {
|
|
152
|
+
profilePictureThumbnail: string | null;
|
|
153
|
+
}): string | null;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
model: {};
|
|
158
|
+
query: {};
|
|
159
|
+
client: {};
|
|
160
|
+
}, {}>, "CourseModuleItem", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<({
|
|
161
|
+
module: {
|
|
162
|
+
id: string;
|
|
163
|
+
createdAt: Date;
|
|
164
|
+
updatedAt: Date;
|
|
165
|
+
classId: string;
|
|
166
|
+
title: string;
|
|
167
|
+
position: number;
|
|
168
|
+
published: boolean;
|
|
169
|
+
unlockAt: Date | null;
|
|
170
|
+
requireSequential: boolean;
|
|
171
|
+
};
|
|
172
|
+
} & {
|
|
173
|
+
id: string;
|
|
174
|
+
title: string | null;
|
|
175
|
+
position: number;
|
|
176
|
+
moduleId: string;
|
|
177
|
+
indent: number;
|
|
178
|
+
itemType: import(".prisma/client").$Enums.CourseModuleItemType;
|
|
179
|
+
itemId: string | null;
|
|
180
|
+
externalUrl: string | null;
|
|
181
|
+
requirementType: import(".prisma/client").$Enums.ModuleItemRequirementType | null;
|
|
182
|
+
requiredScore: number | null;
|
|
183
|
+
}) | null>;
|
|
184
|
+
/** @returns Progress rows of a student for all items of a class. */
|
|
185
|
+
export declare function findStudentProgressForClass(classId: string, studentId: string): import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
186
|
+
status: import(".prisma/client").$Enums.ModuleItemProgressStatus;
|
|
187
|
+
id: string;
|
|
188
|
+
createdAt: Date;
|
|
189
|
+
updatedAt: Date;
|
|
190
|
+
moduleItemId: string;
|
|
191
|
+
studentId: string;
|
|
192
|
+
completedAt: Date | null;
|
|
193
|
+
}[]>;
|
|
194
|
+
/** Upsert a progress row for a student and item. */
|
|
195
|
+
export declare function upsertItemProgress(moduleItemId: string, studentId: string, status: "INCOMPLETE" | "COMPLETE"): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
196
|
+
result: {
|
|
197
|
+
userProfile: {
|
|
198
|
+
profilePicture: () => {
|
|
199
|
+
needs: {
|
|
200
|
+
profilePicture: true;
|
|
201
|
+
};
|
|
202
|
+
compute(profile: {
|
|
203
|
+
profilePicture: string | null;
|
|
204
|
+
}): string | null;
|
|
205
|
+
};
|
|
206
|
+
profilePictureThumbnail: () => {
|
|
207
|
+
needs: {
|
|
208
|
+
profilePictureThumbnail: true;
|
|
209
|
+
};
|
|
210
|
+
compute(profile: {
|
|
211
|
+
profilePictureThumbnail: string | null;
|
|
212
|
+
}): string | null;
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
model: {};
|
|
217
|
+
query: {};
|
|
218
|
+
client: {};
|
|
219
|
+
}, {}>, "ModuleItemProgress", "upsert", never> & import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
220
|
+
status: import(".prisma/client").$Enums.ModuleItemProgressStatus;
|
|
221
|
+
id: string;
|
|
222
|
+
createdAt: Date;
|
|
223
|
+
updatedAt: Date;
|
|
224
|
+
moduleItemId: string;
|
|
225
|
+
studentId: string;
|
|
226
|
+
completedAt: Date | null;
|
|
227
|
+
}>;
|
|
228
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"/","sources":["models/module.ts"],"names":[],"mappings":"AAMA,2DAA2D;AAC3D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOtE;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;CAOhB,CAAC;AAEX,2DAA2D;AAC3D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,UAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;MAM1E;AAED,+CAA+C;AAC/C,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAKjE;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAKpE;AAED,oEAAoE;AACpE,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;KAI7E;AAED,oDAAoD;AACpD,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,GAAG,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAelC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module model – Canvas-style class modules, items and student progress.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
!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]="3e54b96a-f15c-5e1c-aac9-de042e3ac71c")}catch(e){}}();
|
|
6
|
+
import { prisma } from "../lib/prisma.js";
|
|
7
|
+
import { classTeacherOrSchoolAdminWhere } from "./access.js";
|
|
8
|
+
/** @returns Class if user is a teacher or school admin. */
|
|
9
|
+
export function findClassWithTeacher(classId, teacherId) {
|
|
10
|
+
return prisma.class.findFirst({
|
|
11
|
+
where: {
|
|
12
|
+
id: classId,
|
|
13
|
+
...classTeacherOrSchoolAdminWhere(teacherId),
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export const moduleInclude = {
|
|
18
|
+
items: {
|
|
19
|
+
orderBy: { position: "asc" },
|
|
20
|
+
},
|
|
21
|
+
prerequisites: {
|
|
22
|
+
select: { prerequisiteId: true },
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
/** @returns All modules of a class ordered by position. */
|
|
26
|
+
export function findModulesByClassId(classId, publishedOnly = false) {
|
|
27
|
+
return prisma.module.findMany({
|
|
28
|
+
where: { classId, ...(publishedOnly && { published: true }) },
|
|
29
|
+
orderBy: { position: "asc" },
|
|
30
|
+
include: moduleInclude,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** @returns Module by ID scoped to a class. */
|
|
34
|
+
export function findModuleByClassAndId(id, classId) {
|
|
35
|
+
return prisma.module.findFirst({
|
|
36
|
+
where: { id, classId },
|
|
37
|
+
include: moduleInclude,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** @returns Module item with its module, scoped to a class. */
|
|
41
|
+
export function findModuleItemInClass(itemId, classId) {
|
|
42
|
+
return prisma.courseModuleItem.findFirst({
|
|
43
|
+
where: { id: itemId, module: { classId } },
|
|
44
|
+
include: { module: true },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** @returns Progress rows of a student for all items of a class. */
|
|
48
|
+
export function findStudentProgressForClass(classId, studentId) {
|
|
49
|
+
return prisma.moduleItemProgress.findMany({
|
|
50
|
+
where: { studentId, moduleItem: { module: { classId } } },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Upsert a progress row for a student and item. */
|
|
54
|
+
export function upsertItemProgress(moduleItemId, studentId, status) {
|
|
55
|
+
return prisma.moduleItemProgress.upsert({
|
|
56
|
+
where: { moduleItemId_studentId: { moduleItemId, studentId } },
|
|
57
|
+
create: {
|
|
58
|
+
moduleItemId,
|
|
59
|
+
studentId,
|
|
60
|
+
status,
|
|
61
|
+
...(status === "COMPLETE" && { completedAt: new Date() }),
|
|
62
|
+
},
|
|
63
|
+
update: status === "COMPLETE"
|
|
64
|
+
? { status, completedAt: new Date() }
|
|
65
|
+
: {},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=module.js.map
|
|
69
|
+
//# debugId=3e54b96a-f15c-5e1c-aac9-de042e3ac71c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sources":["models/module.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Module model – Canvas-style class modules, items and student progress.\n */\nimport { prisma } from \"../lib/prisma.js\";\nimport { classTeacherOrSchoolAdminWhere } from \"./access.js\";\n\n/** @returns Class if user is a teacher or school admin. */\nexport function findClassWithTeacher(classId: string, teacherId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n ...classTeacherOrSchoolAdminWhere(teacherId),\n },\n });\n}\n\nexport const moduleInclude = {\n items: {\n orderBy: { position: \"asc\" as const },\n },\n prerequisites: {\n select: { prerequisiteId: true },\n },\n} as const;\n\n/** @returns All modules of a class ordered by position. */\nexport function findModulesByClassId(classId: string, publishedOnly = false) {\n return prisma.module.findMany({\n where: { classId, ...(publishedOnly && { published: true }) },\n orderBy: { position: \"asc\" },\n include: moduleInclude,\n });\n}\n\n/** @returns Module by ID scoped to a class. */\nexport function findModuleByClassAndId(id: string, classId: string) {\n return prisma.module.findFirst({\n where: { id, classId },\n include: moduleInclude,\n });\n}\n\n/** @returns Module item with its module, scoped to a class. */\nexport function findModuleItemInClass(itemId: string, classId: string) {\n return prisma.courseModuleItem.findFirst({\n where: { id: itemId, module: { classId } },\n include: { module: true },\n });\n}\n\n/** @returns Progress rows of a student for all items of a class. */\nexport function findStudentProgressForClass(classId: string, studentId: string) {\n return prisma.moduleItemProgress.findMany({\n where: { studentId, moduleItem: { module: { classId } } },\n });\n}\n\n/** Upsert a progress row for a student and item. */\nexport function upsertItemProgress(\n moduleItemId: string,\n studentId: string,\n status: \"INCOMPLETE\" | \"COMPLETE\"\n) {\n return prisma.moduleItemProgress.upsert({\n where: { moduleItemId_studentId: { moduleItemId, studentId } },\n create: {\n moduleItemId,\n studentId,\n status,\n ...(status === \"COMPLETE\" && { completedAt: new Date() }),\n },\n update:\n status === \"COMPLETE\"\n ? { status, completedAt: new Date() }\n : {},\n });\n}\n"],"names":[],"mappings":"AAAA;;GAEG;;;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAE7D,2DAA2D;AAC3D,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,SAAiB;IACrE,OAAO,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAC5B,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,GAAG,8BAA8B,CAAC,SAAS,CAAC;SAC7C;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,KAAK,EAAE;QACL,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAc,EAAE;KACtC;IACD,aAAa,EAAE;QACb,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;KACjC;CACO,CAAC;AAEX,2DAA2D;AAC3D,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,aAAa,GAAG,KAAK;IACzE,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC5B,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE;QAC7D,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC5B,OAAO,EAAE,aAAa;KACvB,CAAC,CAAC;AACL,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,sBAAsB,CAAC,EAAU,EAAE,OAAe;IAChE,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;QAC7B,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,OAAO,EAAE,aAAa;KACvB,CAAC,CAAC;AACL,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,qBAAqB,CAAC,MAAc,EAAE,OAAe;IACnE,OAAO,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC;QACvC,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE;QAC1C,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;KAC1B,CAAC,CAAC;AACL,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,2BAA2B,CAAC,OAAe,EAAE,SAAiB;IAC5E,OAAO,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QACxC,KAAK,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;KAC1D,CAAC,CAAC;AACL,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,kBAAkB,CAChC,YAAoB,EACpB,SAAiB,EACjB,MAAiC;IAEjC,OAAO,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC;QACtC,KAAK,EAAE,EAAE,sBAAsB,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE;QAC9D,MAAM,EAAE;YACN,YAAY;YACZ,SAAS;YACT,MAAM;YACN,GAAG,CAAC,MAAM,KAAK,UAAU,IAAI,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;SAC1D;QACD,MAAM,EACJ,MAAM,KAAK,UAAU;YACnB,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE;YACrC,CAAC,CAAC,EAAE;KACT,CAAC,CAAC;AACL,CAAC","debug_id":"3e54b96a-f15c-5e1c-aac9-de042e3ac71c"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Assignment, Class, File, Section, User } from "@prisma/client";
|
|
2
|
+
/** Class-files folder that collects AI-generated documents (created lazily). */
|
|
3
|
+
export declare const AI_GENERATED_FOLDER_NAME = "AI Generated";
|
|
2
4
|
/** Extended class data for AI context (schema-aware) */
|
|
3
5
|
type ClassContextData = {
|
|
4
6
|
class: Class;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiLabChat.d.ts","sourceRoot":"/","sources":["pipelines/aiLabChat.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"aiLabChat.d.ts","sourceRoot":"/","sources":["pipelines/aiLabChat.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Can create worksheets, sections, assignments, and generated files from AI output.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
!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]="
|
|
6
|
+
!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]="a0910d42-509a-534e-ad83-e5ccb2a920c0")}catch(e){}}();
|
|
7
7
|
import { isAIUser } from "../utils/aiUser.js";
|
|
8
8
|
import { prisma } from "../lib/prisma.js";
|
|
9
9
|
import { pusher, chatChannel, teacherChannel } from "../lib/pusher.js";
|
|
10
|
-
import { completePendingAIMessage, failPendingAIMessage, } from "../utils/inference.js";
|
|
10
|
+
import { cancelPendingAIMessage, completePendingAIMessage, failPendingAIMessage, } from "../utils/inference.js";
|
|
11
11
|
import { logger } from "../utils/logger.js";
|
|
12
12
|
import { v4 } from "uuid";
|
|
13
13
|
import { bucket } from "../lib/googleCloudStorage.js";
|
|
@@ -18,9 +18,13 @@ import { AI_CHAT_MODEL } from "../lib/chatAttachmentPrompt.js";
|
|
|
18
18
|
import { buildLabChatAgentMessages } from "./labChatPrompt.js";
|
|
19
19
|
import { applyResolvedFormatsToStreamParts, enrichStreamPartsWithGeneratedFiles, finalizeDeferredToolParts, streamChatResponse, } from "../services/aiChatStream.js";
|
|
20
20
|
import { aiTools } from "../services/aiTools.js";
|
|
21
|
+
import { findOrCreateChildFolderByName, findOrCreateScopeRoot, } from "../models/cloudStorage.js";
|
|
21
22
|
import { labProposalTools, createLabProposals } from "../services/aiProposalTools.js";
|
|
23
|
+
import { buildPendingCreations } from "../services/aiProposals.js";
|
|
22
24
|
/** Cap model wait time; leave headroom after inference for PDF/GCS/DB within Vercel maxDuration (500s). */
|
|
23
25
|
const LAB_CHAT_RESPONSE_TIMEOUT_MS = 400000;
|
|
26
|
+
/** Class-files folder that collects AI-generated documents (created lazily). */
|
|
27
|
+
export const AI_GENERATED_FOLDER_NAME = "AI Generated";
|
|
24
28
|
const withTimeout = async (task, timeoutMs, operationName) => {
|
|
25
29
|
let timeoutHandle;
|
|
26
30
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -197,7 +201,7 @@ CLASS: ${cls.name} | Subject: ${cls.subject} | Section: ${cls.section}
|
|
|
197
201
|
Syllabus: ${cls.syllabus ? cls.syllabus.slice(0, 200) + (cls.syllabus.length > 200 ? "…" : "") : "(none)"}
|
|
198
202
|
|
|
199
203
|
SECTIONS (use sectionId when creating assignments):
|
|
200
|
-
${sectionList || " (none -
|
|
204
|
+
${sectionList || " (none - propose a section first)"}
|
|
201
205
|
|
|
202
206
|
MARK SCHEMES (use markSchemeId when creating assignments):
|
|
203
207
|
${markSchemeList || " (none - suggest creating one or omit markSchemeId)"}
|
|
@@ -206,7 +210,7 @@ GRADING BOUNDARIES (use gradingBoundaryId when creating assignments):
|
|
|
206
210
|
${gradingBoundaryList || " (none - suggest creating one or omit gradingBoundaryId)"}
|
|
207
211
|
|
|
208
212
|
WORKSHEETS (use worksheetIds when acceptWorksheet is true):
|
|
209
|
-
${worksheetList || " (none -
|
|
213
|
+
${worksheetList || " (none - propose a worksheet or create via docs first)"}
|
|
210
214
|
|
|
211
215
|
FILES - PDFs/Documents (for assignment attachments):
|
|
212
216
|
${fileList || " (none)"}
|
|
@@ -373,7 +377,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
373
377
|
...aiTools(fullLabChat.classId),
|
|
374
378
|
...labProposalTools(proposals),
|
|
375
379
|
};
|
|
376
|
-
const { text: streamedText, parts: rawStreamParts } = await withTimeout(streamChatResponse({
|
|
380
|
+
const { text: streamedText, parts: rawStreamParts, cancelled, } = await withTimeout(streamChatResponse({
|
|
377
381
|
conversationId,
|
|
378
382
|
aiMessageId: emitOptions.aiMessageId,
|
|
379
383
|
model: AI_CHAT_MODEL,
|
|
@@ -392,6 +396,28 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
392
396
|
// Real text + tool-call events were already broadcast inline (in order)
|
|
393
397
|
// during the stream by `streamChatResponse`. Just close the stream.
|
|
394
398
|
await broadcastStreamComplete(conversationId, emitOptions.aiMessageId);
|
|
399
|
+
if (cancelled) {
|
|
400
|
+
// User stopped the generation: persist whatever streamed so far and
|
|
401
|
+
// skip document generation / proposal creation entirely.
|
|
402
|
+
const partialParts = finalizeDeferredToolParts(rawStreamParts);
|
|
403
|
+
await cancelPendingAIMessage(emitOptions.aiMessageId, streamedText, {
|
|
404
|
+
meta: {
|
|
405
|
+
streamParts: partialParts.length > 0 ? partialParts : null,
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
await pusher
|
|
409
|
+
.trigger(teacherChannel(emitOptions.classId), "lab-response-completed", {
|
|
410
|
+
labChatId,
|
|
411
|
+
messageId: emitOptions.aiMessageId,
|
|
412
|
+
})
|
|
413
|
+
.catch(() => { });
|
|
414
|
+
logger.info('AI response cancelled by user', {
|
|
415
|
+
labChatId,
|
|
416
|
+
conversationId,
|
|
417
|
+
aiMessageId: emitOptions.aiMessageId,
|
|
418
|
+
});
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
395
421
|
// `generate_document` is an inline tool that only QUEUES into `proposals.docs`
|
|
396
422
|
// during the stream. OpenAI Code Interpreter renders files here, after the
|
|
397
423
|
// stream ends, then we link them to this message.
|
|
@@ -399,6 +425,26 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
399
425
|
const attachmentIds = [];
|
|
400
426
|
const generatedDocFiles = [];
|
|
401
427
|
const resolvedDocs = [];
|
|
428
|
+
// Generated files are filed into the class cloud storage so teachers
|
|
429
|
+
// can find them in the class Files UI, not just as chat attachments.
|
|
430
|
+
let aiFolderId;
|
|
431
|
+
if (proposals.docs.length > 0) {
|
|
432
|
+
try {
|
|
433
|
+
const root = await findOrCreateScopeRoot({
|
|
434
|
+
kind: "CLASS",
|
|
435
|
+
classId: fullLabChat.classId,
|
|
436
|
+
});
|
|
437
|
+
const aiFolder = await findOrCreateChildFolderByName(root.id, AI_GENERATED_FOLDER_NAME);
|
|
438
|
+
aiFolderId = aiFolder.id;
|
|
439
|
+
}
|
|
440
|
+
catch (folderError) {
|
|
441
|
+
logger.error("Failed to resolve AI generated class folder", {
|
|
442
|
+
error: folderError,
|
|
443
|
+
labChatId,
|
|
444
|
+
classId: fullLabChat.classId,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
402
448
|
for (let i = 0; i < proposals.docs.length; i++) {
|
|
403
449
|
const doc = proposals.docs[i];
|
|
404
450
|
if (!doc.title || !doc.content?.trim()) {
|
|
@@ -455,6 +501,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
455
501
|
messageId: emitOptions.aiMessageId,
|
|
456
502
|
uploadStatus: 'COMPLETED',
|
|
457
503
|
uploadedAt: new Date(),
|
|
504
|
+
...(aiFolderId && { folderId: aiFolderId }),
|
|
458
505
|
},
|
|
459
506
|
});
|
|
460
507
|
attachmentIds.push(file.id);
|
|
@@ -463,6 +510,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
463
510
|
fileId: file.id,
|
|
464
511
|
fileName: filename,
|
|
465
512
|
format: generated.format,
|
|
513
|
+
...(aiFolderId && { folderId: aiFolderId }),
|
|
466
514
|
});
|
|
467
515
|
}
|
|
468
516
|
catch (docError) {
|
|
@@ -478,7 +526,10 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
478
526
|
});
|
|
479
527
|
}
|
|
480
528
|
}
|
|
481
|
-
|
|
529
|
+
// Defer-and-confirm: propose_* tool calls become pending creations the
|
|
530
|
+
// teacher approves or dismisses; nothing is created at generation time.
|
|
531
|
+
const { pendingCreations, parts: streamPartsWithProposals } = buildPendingCreations(proposals, finalizeDeferredToolParts(enrichStreamPartsWithGeneratedFiles(applyResolvedFormatsToStreamParts(rawStreamParts, resolvedDocs), generatedDocFiles)));
|
|
532
|
+
const streamParts = streamPartsWithProposals;
|
|
482
533
|
// While the message is still PENDING, resolve deferred tool indicators
|
|
483
534
|
// (spinner → attachment or error) before the final persist event.
|
|
484
535
|
for (const part of streamParts) {
|
|
@@ -495,6 +546,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
495
546
|
...(part.title ? { title: part.title } : {}),
|
|
496
547
|
...(part.format ? { format: part.format } : {}),
|
|
497
548
|
...(part.fileId ? { fileId: part.fileId, fileName: part.fileName } : {}),
|
|
549
|
+
...(part.folderId ? { folderId: part.folderId } : {}),
|
|
498
550
|
status: part.status,
|
|
499
551
|
})
|
|
500
552
|
.catch(() => { });
|
|
@@ -504,15 +556,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
504
556
|
attachments: { connect: attachmentIds.map((id) => ({ id })) },
|
|
505
557
|
}),
|
|
506
558
|
meta: {
|
|
507
|
-
|
|
508
|
-
? proposals.assignments.map(assignment => ({ ...assignment, id: v4() }))
|
|
509
|
-
: null,
|
|
510
|
-
worksheetsToCreate: proposals.worksheets.length > 0
|
|
511
|
-
? proposals.worksheets.map(worksheet => ({ ...worksheet, id: v4() }))
|
|
512
|
-
: null,
|
|
513
|
-
sectionsToCreate: proposals.sections.length > 0
|
|
514
|
-
? proposals.sections.map(section => ({ ...section, id: v4() }))
|
|
515
|
-
: null,
|
|
559
|
+
pendingCreations: pendingCreations.length > 0 ? pendingCreations : null,
|
|
516
560
|
streamParts: streamParts.length > 0 ? streamParts : null,
|
|
517
561
|
},
|
|
518
562
|
});
|
|
@@ -562,4 +606,4 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
562
606
|
}
|
|
563
607
|
};
|
|
564
608
|
//# sourceMappingURL=aiLabChat.js.map
|
|
565
|
-
//# debugId=
|
|
609
|
+
//# debugId=a0910d42-509a-534e-ad83-e5ccb2a920c0
|