@studious-lms/server 1.13.0 → 1.15.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.
Files changed (375) hide show
  1. package/.dockerignore +28 -0
  2. package/.env.example +5 -0
  3. package/.github/workflows/deploy.yml +123 -0
  4. package/Dockerfile +54 -0
  5. package/dist/index.js +5 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/instrument.js +3 -3
  8. package/dist/instrument.js.map +1 -1
  9. package/dist/lib/config/cors.d.ts.map +1 -1
  10. package/dist/lib/config/cors.js +31 -11
  11. package/dist/lib/config/cors.js.map +1 -1
  12. package/dist/lib/config/env.d.ts +17 -0
  13. package/dist/lib/config/env.d.ts.map +1 -1
  14. package/dist/lib/config/env.js +8 -2
  15. package/dist/lib/config/env.js.map +1 -1
  16. package/dist/lib/googleCloudStorage.d.ts +10 -0
  17. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  18. package/dist/lib/googleCloudStorage.js +22 -2
  19. package/dist/lib/googleCloudStorage.js.map +1 -1
  20. package/dist/lib/inferDocumentFormat.d.ts +10 -0
  21. package/dist/lib/inferDocumentFormat.d.ts.map +1 -0
  22. package/dist/lib/inferDocumentFormat.js +48 -0
  23. package/dist/lib/inferDocumentFormat.js.map +1 -0
  24. package/dist/lib/legal.d.ts +3 -0
  25. package/dist/lib/legal.d.ts.map +1 -0
  26. package/dist/lib/legal.js +6 -0
  27. package/dist/lib/legal.js.map +1 -0
  28. package/dist/lib/prisma.d.ts +104 -5
  29. package/dist/lib/prisma.d.ts.map +1 -1
  30. package/dist/lib/prisma.js +33 -2
  31. package/dist/lib/prisma.js.map +1 -1
  32. package/dist/models/agenda.d.ts +20 -20
  33. package/dist/models/announcement.d.ts +211 -101
  34. package/dist/models/announcement.d.ts.map +1 -1
  35. package/dist/models/assignment.d.ts +560 -362
  36. package/dist/models/assignment.d.ts.map +1 -1
  37. package/dist/models/attendance.d.ts +197 -71
  38. package/dist/models/attendance.d.ts.map +1 -1
  39. package/dist/models/auth.d.ts +356 -114
  40. package/dist/models/auth.d.ts.map +1 -1
  41. package/dist/models/auth.js +4 -2
  42. package/dist/models/auth.js.map +1 -1
  43. package/dist/models/class.d.ts +327 -201
  44. package/dist/models/class.d.ts.map +1 -1
  45. package/dist/models/class.js +2 -2
  46. package/dist/models/class.js.map +1 -1
  47. package/dist/models/cloudStorage.d.ts +146 -56
  48. package/dist/models/cloudStorage.d.ts.map +1 -1
  49. package/dist/models/comment.d.ts +262 -110
  50. package/dist/models/comment.d.ts.map +1 -1
  51. package/dist/models/conversation.d.ts +206 -80
  52. package/dist/models/conversation.d.ts.map +1 -1
  53. package/dist/models/department.d.ts +196 -70
  54. package/dist/models/department.d.ts.map +1 -1
  55. package/dist/models/event.d.ts +305 -141
  56. package/dist/models/event.d.ts.map +1 -1
  57. package/dist/models/file.d.ts +422 -272
  58. package/dist/models/file.d.ts.map +1 -1
  59. package/dist/models/folder.d.ts +433 -181
  60. package/dist/models/folder.d.ts.map +1 -1
  61. package/dist/models/labChat.d.ts +205 -79
  62. package/dist/models/labChat.d.ts.map +1 -1
  63. package/dist/models/marketing.d.ts +79 -38
  64. package/dist/models/marketing.d.ts.map +1 -1
  65. package/dist/models/marketing.js +12 -4
  66. package/dist/models/marketing.js.map +1 -1
  67. package/dist/models/message.d.ts +97 -43
  68. package/dist/models/message.d.ts.map +1 -1
  69. package/dist/models/newtonChat.d.ts +84 -30
  70. package/dist/models/newtonChat.d.ts.map +1 -1
  71. package/dist/models/notification.d.ts +57 -21
  72. package/dist/models/notification.d.ts.map +1 -1
  73. package/dist/models/school.d.ts +349 -133
  74. package/dist/models/school.d.ts.map +1 -1
  75. package/dist/models/section.d.ts +216 -72
  76. package/dist/models/section.d.ts.map +1 -1
  77. package/dist/models/tag.d.ts +156 -48
  78. package/dist/models/tag.d.ts.map +1 -1
  79. package/dist/models/user.d.ts +82 -28
  80. package/dist/models/user.d.ts.map +1 -1
  81. package/dist/models/worksheet.d.ts +1032 -423
  82. package/dist/models/worksheet.d.ts.map +1 -1
  83. package/dist/models/worksheet.js +8 -2
  84. package/dist/models/worksheet.js.map +1 -1
  85. package/dist/pipelines/aiLabChat.d.ts +7 -9
  86. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  87. package/dist/pipelines/aiLabChat.js +194 -125
  88. package/dist/pipelines/aiLabChat.js.map +1 -1
  89. package/dist/pipelines/aiLabChatContract.d.ts +233 -389
  90. package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
  91. package/dist/pipelines/aiLabChatContract.js +54 -70
  92. package/dist/pipelines/aiLabChatContract.js.map +1 -1
  93. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
  94. package/dist/pipelines/aiNewtonChat.js +41 -41
  95. package/dist/pipelines/aiNewtonChat.js.map +1 -1
  96. package/dist/pipelines/gradeWorksheet.d.ts +8 -8
  97. package/dist/pipelines/labChatPrompt.d.ts +18 -11
  98. package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
  99. package/dist/pipelines/labChatPrompt.js +93 -75
  100. package/dist/pipelines/labChatPrompt.js.map +1 -1
  101. package/dist/pipelines/pdfToWorksheet.d.ts +1 -1
  102. package/dist/routers/_app.d.ts +2601 -2151
  103. package/dist/routers/_app.d.ts.map +1 -1
  104. package/dist/routers/_app.js +4 -2
  105. package/dist/routers/_app.js.map +1 -1
  106. package/dist/routers/admin.d.ts +13 -13
  107. package/dist/routers/agenda.d.ts +18 -18
  108. package/dist/routers/announcement.d.ts +60 -60
  109. package/dist/routers/assignment.d.ts +393 -383
  110. package/dist/routers/assignment.d.ts.map +1 -1
  111. package/dist/routers/attendance.d.ts +10 -10
  112. package/dist/routers/auth.d.ts +4 -2
  113. package/dist/routers/auth.d.ts.map +1 -1
  114. package/dist/routers/auth.js +15 -2
  115. package/dist/routers/auth.js.map +1 -1
  116. package/dist/routers/class.d.ts +157 -153
  117. package/dist/routers/class.d.ts.map +1 -1
  118. package/dist/routers/comment.d.ts +18 -18
  119. package/dist/routers/conversation.d.ts +11 -11
  120. package/dist/routers/department.d.ts +34 -34
  121. package/dist/routers/discussion.d.ts +68 -56
  122. package/dist/routers/discussion.d.ts.map +1 -1
  123. package/dist/routers/discussion.js +6 -2
  124. package/dist/routers/discussion.js.map +1 -1
  125. package/dist/routers/embedding.d.ts +73 -0
  126. package/dist/routers/embedding.d.ts.map +1 -0
  127. package/dist/routers/embedding.js +151 -0
  128. package/dist/routers/embedding.js.map +1 -0
  129. package/dist/routers/event.d.ts +57 -55
  130. package/dist/routers/event.d.ts.map +1 -1
  131. package/dist/routers/file.d.ts +14 -14
  132. package/dist/routers/folder.d.ts +38 -38
  133. package/dist/routers/labChat.d.ts +16 -16
  134. package/dist/routers/marketing.d.ts +1 -14
  135. package/dist/routers/marketing.d.ts.map +1 -1
  136. package/dist/routers/message.d.ts +13 -13
  137. package/dist/routers/newtonChat.d.ts +4 -4
  138. package/dist/routers/notifications.d.ts +5 -5
  139. package/dist/routers/permissions.d.ts +14 -14
  140. package/dist/routers/school.d.ts +59 -55
  141. package/dist/routers/school.d.ts.map +1 -1
  142. package/dist/routers/search.d.ts +22 -1
  143. package/dist/routers/search.d.ts.map +1 -1
  144. package/dist/routers/search.js +9 -3
  145. package/dist/routers/search.js.map +1 -1
  146. package/dist/routers/section.d.ts +4 -4
  147. package/dist/routers/studentProgress.d.ts +1 -1
  148. package/dist/routers/tag.d.ts +7 -7
  149. package/dist/routers/user.d.ts +99 -10
  150. package/dist/routers/user.d.ts.map +1 -1
  151. package/dist/routers/user.js +19 -2
  152. package/dist/routers/user.js.map +1 -1
  153. package/dist/routers/worksheet.d.ts +93 -55
  154. package/dist/routers/worksheet.d.ts.map +1 -1
  155. package/dist/routers/worksheet.js +26 -2
  156. package/dist/routers/worksheet.js.map +1 -1
  157. package/dist/routes/aiStream.d.ts +2 -0
  158. package/dist/routes/aiStream.d.ts.map +1 -0
  159. package/dist/routes/aiStream.js +726 -0
  160. package/dist/routes/aiStream.js.map +1 -0
  161. package/dist/seedDatabase.d.ts +3 -1
  162. package/dist/seedDatabase.d.ts.map +1 -1
  163. package/dist/seedDatabase.js +6 -6
  164. package/dist/seedDatabase.js.map +1 -1
  165. package/dist/services/adminInstitution.d.ts +8 -8
  166. package/dist/services/adminRunJob.d.ts +4 -4
  167. package/dist/services/agenda.d.ts +18 -18
  168. package/dist/services/aiChatStream.d.ts +88 -0
  169. package/dist/services/aiChatStream.d.ts.map +1 -0
  170. package/dist/services/aiChatStream.js +254 -0
  171. package/dist/services/aiChatStream.js.map +1 -0
  172. package/dist/services/aiModifierTools.d.ts +16 -0
  173. package/dist/services/aiModifierTools.d.ts.map +1 -0
  174. package/dist/services/aiModifierTools.js +274 -0
  175. package/dist/services/aiModifierTools.js.map +1 -0
  176. package/dist/services/aiProposalTools.d.ts +32 -0
  177. package/dist/services/aiProposalTools.d.ts.map +1 -0
  178. package/dist/services/aiProposalTools.js +207 -0
  179. package/dist/services/aiProposalTools.js.map +1 -0
  180. package/dist/services/aiStream.d.ts +22 -0
  181. package/dist/services/aiStream.d.ts.map +1 -0
  182. package/dist/services/aiStream.js +142 -0
  183. package/dist/services/aiStream.js.map +1 -0
  184. package/dist/services/aiTools.d.ts +16 -0
  185. package/dist/services/aiTools.d.ts.map +1 -0
  186. package/dist/services/aiTools.js +271 -0
  187. package/dist/services/aiTools.js.map +1 -0
  188. package/dist/services/announcement.d.ts +42 -42
  189. package/dist/services/announcement.d.ts.map +1 -1
  190. package/dist/services/announcement.js +7 -2
  191. package/dist/services/announcement.js.map +1 -1
  192. package/dist/services/assignment.d.ts +355 -345
  193. package/dist/services/assignment.d.ts.map +1 -1
  194. package/dist/services/assignment.js +30 -9
  195. package/dist/services/assignment.js.map +1 -1
  196. package/dist/services/attendance.d.ts +10 -10
  197. package/dist/services/auth.d.ts +4 -2
  198. package/dist/services/auth.d.ts.map +1 -1
  199. package/dist/services/auth.js +13 -4
  200. package/dist/services/auth.js.map +1 -1
  201. package/dist/services/class.d.ts +146 -142
  202. package/dist/services/class.d.ts.map +1 -1
  203. package/dist/services/class.js +24 -4
  204. package/dist/services/class.js.map +1 -1
  205. package/dist/services/comment.d.ts +18 -18
  206. package/dist/services/conversation.d.ts +11 -11
  207. package/dist/services/department.d.ts +33 -33
  208. package/dist/services/discussion.d.ts +58 -58
  209. package/dist/services/discussion.d.ts.map +1 -1
  210. package/dist/services/discussion.js +15 -8
  211. package/dist/services/discussion.js.map +1 -1
  212. package/dist/services/discussionSummary.d.ts +21 -0
  213. package/dist/services/discussionSummary.d.ts.map +1 -0
  214. package/dist/services/discussionSummary.js +179 -0
  215. package/dist/services/discussionSummary.js.map +1 -0
  216. package/dist/services/embedding.d.ts +85 -0
  217. package/dist/services/embedding.d.ts.map +1 -0
  218. package/dist/services/embedding.js +372 -0
  219. package/dist/services/embedding.js.map +1 -0
  220. package/dist/services/event.d.ts +55 -53
  221. package/dist/services/event.d.ts.map +1 -1
  222. package/dist/services/file.d.ts +28 -28
  223. package/dist/services/folder.d.ts +36 -36
  224. package/dist/services/folder.d.ts.map +1 -1
  225. package/dist/services/folder.js +32 -2
  226. package/dist/services/folder.js.map +1 -1
  227. package/dist/services/labChat.d.ts +16 -16
  228. package/dist/services/marketing.d.ts +2 -15
  229. package/dist/services/marketing.d.ts.map +1 -1
  230. package/dist/services/marketing.js +5 -5
  231. package/dist/services/marketing.js.map +1 -1
  232. package/dist/services/message.d.ts +11 -11
  233. package/dist/services/message.d.ts.map +1 -1
  234. package/dist/services/message.js +2 -2
  235. package/dist/services/message.js.map +1 -1
  236. package/dist/services/newtonChat.d.ts +4 -4
  237. package/dist/services/notification.d.ts +5 -5
  238. package/dist/services/notification.d.ts.map +1 -1
  239. package/dist/services/openaiDocumentGen.d.ts +37 -0
  240. package/dist/services/openaiDocumentGen.d.ts.map +1 -0
  241. package/dist/services/openaiDocumentGen.js +262 -0
  242. package/dist/services/openaiDocumentGen.js.map +1 -0
  243. package/dist/services/permissions.d.ts +14 -14
  244. package/dist/services/school.d.ts +54 -50
  245. package/dist/services/school.d.ts.map +1 -1
  246. package/dist/services/search.d.ts +13 -1
  247. package/dist/services/search.d.ts.map +1 -1
  248. package/dist/services/search.js +145 -2
  249. package/dist/services/search.js.map +1 -1
  250. package/dist/services/section.d.ts +3 -3
  251. package/dist/services/tag.d.ts +4 -4
  252. package/dist/services/user.d.ts +4 -4
  253. package/dist/services/user.d.ts.map +1 -1
  254. package/dist/services/user.js +10 -15
  255. package/dist/services/user.js.map +1 -1
  256. package/dist/services/userPrivacy.d.ts +86 -0
  257. package/dist/services/userPrivacy.d.ts.map +1 -0
  258. package/dist/services/userPrivacy.js +184 -0
  259. package/dist/services/userPrivacy.js.map +1 -0
  260. package/dist/services/worksheet.d.ts +86 -57
  261. package/dist/services/worksheet.d.ts.map +1 -1
  262. package/dist/services/worksheet.js +57 -5
  263. package/dist/services/worksheet.js.map +1 -1
  264. package/dist/utils/email.d.ts.map +1 -1
  265. package/dist/utils/email.js +7 -3
  266. package/dist/utils/email.js.map +1 -1
  267. package/dist/utils/streamBroadcast.d.ts +11 -0
  268. package/dist/utils/streamBroadcast.d.ts.map +1 -0
  269. package/dist/utils/streamBroadcast.js +32 -0
  270. package/dist/utils/streamBroadcast.js.map +1 -0
  271. package/dist/workers/connection.d.ts +15 -0
  272. package/dist/workers/connection.d.ts.map +1 -1
  273. package/dist/workers/connection.js +20 -2
  274. package/dist/workers/connection.js.map +1 -1
  275. package/dist/workers/index.js +4 -2
  276. package/dist/workers/index.js.map +1 -1
  277. package/dist/workers/processors/createRunJobWorker.d.ts.map +1 -1
  278. package/dist/workers/processors/createRunJobWorker.js +31 -8
  279. package/dist/workers/processors/createRunJobWorker.js.map +1 -1
  280. package/dist/workers/processors/embedContent.d.ts +8 -0
  281. package/dist/workers/processors/embedContent.d.ts.map +1 -0
  282. package/dist/workers/processors/embedContent.js +67 -0
  283. package/dist/workers/processors/embedContent.js.map +1 -0
  284. package/dist/workers/queues.d.ts +6 -0
  285. package/dist/workers/queues.d.ts.map +1 -1
  286. package/dist/workers/queues.js +5 -3
  287. package/dist/workers/queues.js.map +1 -1
  288. package/package.json +5 -1
  289. package/prisma/migrations/20260609215135_init/migration.sql +1412 -0
  290. package/prisma/migrations/20260610130000_add_worksheet_tags/migration.sql +13 -0
  291. package/prisma/migrations/20260610140000_expand_embedding_source_types/migration.sql +4 -0
  292. package/prisma/migrations/20260611120000_add_user_terms_acceptance/migration.sql +3 -0
  293. package/prisma/schema.prisma +44 -7
  294. package/src/index.ts +4 -0
  295. package/src/instrument.ts +1 -1
  296. package/src/lib/config/cors.ts +32 -9
  297. package/src/lib/config/env.ts +6 -0
  298. package/src/lib/googleCloudStorage.ts +21 -0
  299. package/src/lib/inferDocumentFormat.ts +58 -0
  300. package/src/lib/legal.ts +2 -0
  301. package/src/lib/prisma.ts +40 -1
  302. package/src/models/auth.ts +4 -0
  303. package/src/models/class.ts +3 -2
  304. package/src/models/marketing.ts +10 -2
  305. package/src/models/worksheet.ts +5 -2
  306. package/src/pipelines/aiLabChat.ts +245 -146
  307. package/src/pipelines/aiLabChatContract.ts +59 -69
  308. package/src/pipelines/aiNewtonChat.ts +46 -43
  309. package/src/pipelines/labChatPrompt.ts +115 -90
  310. package/src/routers/_app.ts +2 -0
  311. package/src/routers/auth.ts +13 -0
  312. package/src/routers/discussion.ts +7 -0
  313. package/src/routers/embedding.ts +164 -0
  314. package/src/routers/search.ts +10 -1
  315. package/src/routers/user.ts +26 -0
  316. package/src/routers/worksheet.ts +27 -0
  317. package/src/routes/aiStream.ts +827 -0
  318. package/src/seedDatabase.ts +4 -4
  319. package/src/services/aiChatStream.ts +341 -0
  320. package/src/services/aiModifierTools.ts +341 -0
  321. package/src/services/aiProposalTools.ts +274 -0
  322. package/src/services/aiStream.ts +175 -0
  323. package/src/services/aiTools.ts +290 -0
  324. package/src/services/announcement.ts +7 -0
  325. package/src/services/assignment.ts +31 -7
  326. package/src/services/auth.ts +14 -2
  327. package/src/services/class.ts +27 -2
  328. package/src/services/discussion.ts +22 -8
  329. package/src/services/discussionSummary.ts +226 -0
  330. package/src/services/embedding.ts +446 -0
  331. package/src/services/folder.ts +31 -0
  332. package/src/services/marketing.ts +3 -3
  333. package/src/services/message.ts +2 -2
  334. package/src/services/openaiDocumentGen.ts +319 -0
  335. package/src/services/search.ts +163 -1
  336. package/src/services/user.ts +9 -17
  337. package/src/services/userPrivacy.ts +196 -0
  338. package/src/services/worksheet.ts +77 -3
  339. package/src/utils/email.ts +5 -1
  340. package/src/utils/streamBroadcast.ts +48 -0
  341. package/src/workers/connection.ts +19 -0
  342. package/src/workers/index.ts +2 -0
  343. package/src/workers/processors/createRunJobWorker.ts +32 -6
  344. package/src/workers/processors/embedContent.ts +86 -0
  345. package/src/workers/queues.ts +5 -1
  346. package/tests/helpers/registerInput.ts +20 -0
  347. package/tests/integration/globalSetup.ts +10 -18
  348. package/tests/integration/routers/auth.test.ts +13 -18
  349. package/tests/unit/lib/inferDocumentFormat.test.ts +33 -0
  350. package/tests/unit/pipelines/aiLabChatContract.test.ts +38 -21
  351. package/tests/unit/pipelines/labChatPrompt.test.ts +23 -36
  352. package/tests/unit/services/aiChatStream.test.ts +92 -0
  353. package/tests/unit/services/openaiDocumentGen.test.ts +65 -0
  354. package/prisma/migrations/20250807062924_init/migration.sql +0 -436
  355. package/prisma/migrations/20250919063704_init2/migration.sql +0 -24
  356. package/prisma/migrations/20250920143543_add_profile_fields/migration.sql +0 -15
  357. package/prisma/migrations/20250922165147_add_chat_models/migration.sql +0 -68
  358. package/prisma/migrations/20250922165314_add_chat_roles/migration.sql +0 -5
  359. package/prisma/migrations/20250922171242_refactor_notification_system/migration.sql +0 -20
  360. package/prisma/migrations/20250922172208_add_mentions_system/migration.sql +0 -21
  361. package/prisma/migrations/20250925072732_add_lab_chat_system/migration.sql +0 -25
  362. package/prisma/migrations/20251020151505_add_upload_tracking_fields/migration.sql +0 -57
  363. package/prisma/migrations/20251109122857_annuoncements_comments/migration.sql +0 -30
  364. package/prisma/migrations/20251109135555_reactions_announcements_comments/migration.sql +0 -35
  365. package/prisma/migrations/20260410124000_add_submission_recommendation_state/migration.sql +0 -14
  366. package/prisma/migrations/20260415103500_add_session_type/migration.sql +0 -33
  367. package/prisma/migrations/20260415133000_add_ai_usage_logs/migration.sql +0 -19
  368. package/prisma/migrations/20260417083000_add_conversation_member_hidden_at/migration.sql +0 -2
  369. package/prisma/migrations/20260502230500_assignment_submission_comments/migration.sql +0 -60
  370. package/prisma/migrations/20260504071500_school_memberships/migration.sql +0 -81
  371. package/prisma/migrations/20260506162500_consolidate_run_job_and_unify_job_status/migration.sql +0 -166
  372. package/prisma/migrations/20260509220000_institution_overhaul/migration.sql +0 -184
  373. package/prisma/migrations/20260510170000_add_folder_permissions/migration.sql +0 -93
  374. package/prisma/migrations/20260510183000_add_discussions/migration.sql +0 -151
  375. /package/.github/workflows/{npm.yml → npm-publish.yml} +0 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * AI Streaming service – handles streamed AI responses with tool calls.
3
+ * Uses Vercel AI SDK for streaming and Redis for temporary chunk caching.
4
+ */
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]="1fe100cb-9555-5dad-9a73-f4e62fb469b2")}catch(e){}}();
7
+ import { streamText, stepCountIs } from 'ai';
8
+ import { openai } from '@ai-sdk/openai';
9
+ import { getRedis } from '../lib/redis.js';
10
+ import { prisma } from '../lib/prisma.js';
11
+ import { pusher, chatChannel } from '../lib/pusher.js';
12
+ import { getAIUserId, ensureAIUserExists } from '../utils/aiUser.js';
13
+ import { logger } from '../utils/logger.js';
14
+ import { JobStatus } from '@prisma/client';
15
+ import { aiTools } from './aiTools.js';
16
+ const STREAM_REDIS_TTL = 600; // 10 minutes TTL for stream keys
17
+ const STREAM_KEY_PREFIX = 'stream:';
18
+ /**
19
+ * Stream an AI response, broadcasting chunks via Pusher and caching in Redis.
20
+ * On completion, persists the full response to the database.
21
+ */
22
+ export async function streamAIResponse(options) {
23
+ const { conversationId, aiMessageId, messages, systemPrompt, classId, enableTools = true, model = 'gpt-4o-mini', temperature = 0.7, maxOutputTokens = 4096, } = options;
24
+ await ensureAIUserExists();
25
+ const redis = getRedis();
26
+ const streamKey = `${STREAM_KEY_PREFIX}${aiMessageId}`;
27
+ // Initialize Redis stream cache
28
+ if (redis) {
29
+ await redis.set(streamKey, '', 'EX', STREAM_REDIS_TTL);
30
+ }
31
+ let fullContent = '';
32
+ const toolCallResults = [];
33
+ try {
34
+ const toolSet = enableTools && classId ? aiTools(classId) : undefined;
35
+ const result = streamText({
36
+ model: openai(model),
37
+ system: systemPrompt,
38
+ messages,
39
+ tools: toolSet,
40
+ temperature,
41
+ maxOutputTokens,
42
+ stopWhen: stepCountIs(5),
43
+ onStepFinish: async ({ toolCalls, toolResults }) => {
44
+ if (toolCalls && toolCalls.length > 0) {
45
+ for (const tc of toolCalls) {
46
+ try {
47
+ await pusher.trigger(chatChannel(conversationId), 'message-tool-call', {
48
+ messageId: aiMessageId,
49
+ toolName: tc.toolName,
50
+ status: 'executing',
51
+ });
52
+ }
53
+ catch (err) {
54
+ logger.error('Failed to broadcast tool call event', { err, aiMessageId });
55
+ }
56
+ }
57
+ }
58
+ if (toolResults) {
59
+ for (const tr of toolResults) {
60
+ toolCallResults.push({
61
+ toolName: tr.toolName,
62
+ result: tr.output,
63
+ });
64
+ }
65
+ }
66
+ },
67
+ });
68
+ // Stream text chunks to client
69
+ for await (const chunk of result.textStream) {
70
+ fullContent += chunk;
71
+ // Cache in Redis
72
+ if (redis) {
73
+ await redis.append(streamKey, chunk);
74
+ }
75
+ // Broadcast chunk to client via Pusher
76
+ try {
77
+ await pusher.trigger(chatChannel(conversationId), 'message-stream-chunk', {
78
+ messageId: aiMessageId,
79
+ chunk,
80
+ accumulated: fullContent.length,
81
+ });
82
+ }
83
+ catch (err) {
84
+ logger.warn('Failed to broadcast stream chunk', { err, aiMessageId });
85
+ }
86
+ }
87
+ // Stream complete — persist to DB
88
+ const meta = toolCallResults.length > 0
89
+ ? JSON.stringify({ toolCalls: toolCallResults })
90
+ : undefined;
91
+ await prisma.message.update({
92
+ where: { id: aiMessageId },
93
+ data: {
94
+ content: fullContent,
95
+ status: JobStatus.COMPLETED,
96
+ ...(meta ? { meta: JSON.parse(meta) } : {}),
97
+ },
98
+ });
99
+ // Broadcast final message-updated event
100
+ await pusher.trigger(chatChannel(conversationId), 'message-updated', {
101
+ id: aiMessageId,
102
+ content: fullContent,
103
+ senderId: getAIUserId(),
104
+ conversationId,
105
+ status: JobStatus.COMPLETED,
106
+ sender: {
107
+ id: getAIUserId(),
108
+ username: 'Newton_AI',
109
+ profile: { displayName: 'Newton AI', profilePicture: null },
110
+ },
111
+ mentionedUserIds: [],
112
+ meta: meta ? JSON.parse(meta) : {},
113
+ attachments: [],
114
+ });
115
+ logger.info('AI stream completed', {
116
+ aiMessageId,
117
+ conversationId,
118
+ contentLength: fullContent.length,
119
+ toolCalls: toolCallResults.length,
120
+ });
121
+ }
122
+ catch (error) {
123
+ logger.error('AI stream failed', { error, aiMessageId, conversationId });
124
+ await prisma.message.update({
125
+ where: { id: aiMessageId },
126
+ data: { status: JobStatus.FAILED },
127
+ });
128
+ await pusher.trigger(chatChannel(conversationId), 'message-updated', {
129
+ id: aiMessageId,
130
+ conversationId,
131
+ status: JobStatus.FAILED,
132
+ });
133
+ throw error;
134
+ }
135
+ finally {
136
+ if (redis) {
137
+ await redis.del(streamKey);
138
+ }
139
+ }
140
+ }
141
+ //# sourceMappingURL=aiStream.js.map
142
+ //# debugId=1fe100cb-9555-5dad-9a73-f4e62fb469b2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiStream.js","sources":["services/aiStream.ts"],"sourceRoot":"/","sourcesContent":["/**\n * AI Streaming service – handles streamed AI responses with tool calls.\n * Uses Vercel AI SDK for streaming and Redis for temporary chunk caching.\n */\nimport { streamText, stepCountIs, type ModelMessage } from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { getRedis } from '../lib/redis.js';\nimport { prisma } from '../lib/prisma.js';\nimport { pusher, chatChannel } from '../lib/pusher.js';\nimport { getAIUserId, ensureAIUserExists } from '../utils/aiUser.js';\nimport { logger } from '../utils/logger.js';\nimport { JobStatus } from '@prisma/client';\nimport { aiTools, type ToolCallResult } from './aiTools.js';\n\nconst STREAM_REDIS_TTL = 600; // 10 minutes TTL for stream keys\nconst STREAM_KEY_PREFIX = 'stream:';\n\nexport interface StreamOptions {\n conversationId: string;\n aiMessageId: string;\n messages: ModelMessage[];\n systemPrompt: string;\n classId?: string;\n enableTools?: boolean;\n model?: string;\n temperature?: number;\n maxOutputTokens?: number;\n}\n\n/**\n * Stream an AI response, broadcasting chunks via Pusher and caching in Redis.\n * On completion, persists the full response to the database.\n */\nexport async function streamAIResponse(options: StreamOptions): Promise<void> {\n const {\n conversationId,\n aiMessageId,\n messages,\n systemPrompt,\n classId,\n enableTools = true,\n model = 'gpt-4o-mini',\n temperature = 0.7,\n maxOutputTokens = 4096,\n } = options;\n\n await ensureAIUserExists();\n\n const redis = getRedis();\n const streamKey = `${STREAM_KEY_PREFIX}${aiMessageId}`;\n\n // Initialize Redis stream cache\n if (redis) {\n await redis.set(streamKey, '', 'EX', STREAM_REDIS_TTL);\n }\n\n let fullContent = '';\n const toolCallResults: ToolCallResult[] = [];\n\n try {\n const toolSet = enableTools && classId ? aiTools(classId) : undefined;\n\n const result = streamText({\n model: openai(model),\n system: systemPrompt,\n messages,\n tools: toolSet,\n temperature,\n maxOutputTokens,\n stopWhen: stepCountIs(5),\n onStepFinish: async ({ toolCalls, toolResults }) => {\n if (toolCalls && toolCalls.length > 0) {\n for (const tc of toolCalls) {\n try {\n await pusher.trigger(chatChannel(conversationId), 'message-tool-call', {\n messageId: aiMessageId,\n toolName: tc.toolName,\n status: 'executing',\n });\n } catch (err) {\n logger.error('Failed to broadcast tool call event', { err, aiMessageId });\n }\n }\n }\n\n if (toolResults) {\n for (const tr of toolResults) {\n toolCallResults.push({\n toolName: tr.toolName,\n result: tr.output,\n });\n }\n }\n },\n });\n\n // Stream text chunks to client\n for await (const chunk of result.textStream) {\n fullContent += chunk;\n\n // Cache in Redis\n if (redis) {\n await redis.append(streamKey, chunk);\n }\n\n // Broadcast chunk to client via Pusher\n try {\n await pusher.trigger(chatChannel(conversationId), 'message-stream-chunk', {\n messageId: aiMessageId,\n chunk,\n accumulated: fullContent.length,\n });\n } catch (err) {\n logger.warn('Failed to broadcast stream chunk', { err, aiMessageId });\n }\n }\n\n // Stream complete — persist to DB\n const meta = toolCallResults.length > 0\n ? JSON.stringify({ toolCalls: toolCallResults })\n : undefined;\n\n await prisma.message.update({\n where: { id: aiMessageId },\n data: {\n content: fullContent,\n status: JobStatus.COMPLETED,\n ...(meta ? { meta: JSON.parse(meta) } : {}),\n },\n });\n\n // Broadcast final message-updated event\n await pusher.trigger(chatChannel(conversationId), 'message-updated', {\n id: aiMessageId,\n content: fullContent,\n senderId: getAIUserId(),\n conversationId,\n status: JobStatus.COMPLETED,\n sender: {\n id: getAIUserId(),\n username: 'Newton_AI',\n profile: { displayName: 'Newton AI', profilePicture: null },\n },\n mentionedUserIds: [],\n meta: meta ? JSON.parse(meta) : {},\n attachments: [],\n });\n\n logger.info('AI stream completed', {\n aiMessageId,\n conversationId,\n contentLength: fullContent.length,\n toolCalls: toolCallResults.length,\n });\n } catch (error) {\n logger.error('AI stream failed', { error, aiMessageId, conversationId });\n\n await prisma.message.update({\n where: { id: aiMessageId },\n data: { status: JobStatus.FAILED },\n });\n\n await pusher.trigger(chatChannel(conversationId), 'message-updated', {\n id: aiMessageId,\n conversationId,\n status: JobStatus.FAILED,\n });\n\n throw error;\n } finally {\n if (redis) {\n await redis.del(streamKey);\n }\n }\n}\n"],"names":[],"mappings":"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","debug_id":"1fe100cb-9555-5dad-9a73-f4e62fb469b2"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * AI Tool definitions and execution for agent tool calls.
3
+ * Provides RAG search, assignment lookup, worksheet creation, student progress,
4
+ * and web search tools.
5
+ */
6
+ import { type ToolSet } from 'ai';
7
+ export interface ToolCallResult {
8
+ toolName: string;
9
+ result: unknown;
10
+ }
11
+ /**
12
+ * Build the tool set for a given class context.
13
+ * Tools are scoped to a specific class for security.
14
+ */
15
+ export declare function aiTools(classId: string): ToolSet;
16
+ //# sourceMappingURL=aiTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiTools.d.ts","sourceRoot":"/","sources":["services/aiTools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,IAAI,CAAC;AAM9C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA6QhD"}
@@ -0,0 +1,271 @@
1
+ /**
2
+ * AI Tool definitions and execution for agent tool calls.
3
+ * Provides RAG search, assignment lookup, worksheet creation, student progress,
4
+ * and web search tools.
5
+ */
6
+
7
+ !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]="92c04dcc-74fa-5dc9-90cb-1ee91299efe7")}catch(e){}}();
8
+ import { jsonSchema } from 'ai';
9
+ import OpenAI from 'openai';
10
+ import { searchEmbeddings } from './embedding.js';
11
+ import { prisma } from '../lib/prisma.js';
12
+ import { logger } from '../utils/logger.js';
13
+ /**
14
+ * Build the tool set for a given class context.
15
+ * Tools are scoped to a specific class for security.
16
+ */
17
+ export function aiTools(classId) {
18
+ return {
19
+ search_materials: {
20
+ description: 'Search class materials, files, assignments, worksheets, and announcements using semantic similarity. Use this to find relevant context for answering questions.',
21
+ inputSchema: jsonSchema({
22
+ type: 'object',
23
+ properties: {
24
+ query: { type: 'string', description: 'The search query to find relevant materials' },
25
+ limit: { type: 'number', description: 'Maximum results (1-10)', default: 5 },
26
+ },
27
+ required: ['query'],
28
+ }),
29
+ execute: async ({ query, limit = 5 }) => {
30
+ try {
31
+ const results = await searchEmbeddings({ classId, query, limit });
32
+ return results.map((r) => ({
33
+ sourceType: r.sourceType,
34
+ sourceId: r.sourceId,
35
+ content: r.content.slice(0, 500),
36
+ similarity: Math.round(r.similarity * 100) / 100,
37
+ metadata: r.metadata,
38
+ }));
39
+ }
40
+ catch (error) {
41
+ logger.error('search_materials tool failed', { error, classId, query });
42
+ return [];
43
+ }
44
+ },
45
+ },
46
+ search_assignments: {
47
+ description: 'Search existing assignments by title, instructions, or type. Returns assignment details including due dates and submission types.',
48
+ inputSchema: jsonSchema({
49
+ type: 'object',
50
+ properties: {
51
+ query: { type: 'string', description: 'Search query for assignments' },
52
+ limit: { type: 'number', description: 'Maximum results (1-10)', default: 5 },
53
+ },
54
+ required: ['query'],
55
+ }),
56
+ execute: async ({ query, limit = 5 }) => {
57
+ try {
58
+ const results = await searchEmbeddings({
59
+ classId,
60
+ query,
61
+ limit,
62
+ sourceTypes: ['ASSIGNMENT'],
63
+ });
64
+ const assignmentIds = results.map((r) => r.sourceId);
65
+ const assignments = await prisma.assignment.findMany({
66
+ where: { id: { in: assignmentIds } },
67
+ select: {
68
+ id: true,
69
+ title: true,
70
+ instructions: true,
71
+ type: true,
72
+ dueDate: true,
73
+ maxGrade: true,
74
+ section: { select: { name: true } },
75
+ },
76
+ });
77
+ return assignments.map((a) => ({
78
+ id: a.id,
79
+ title: a.title,
80
+ instructions: a.instructions?.slice(0, 200) ?? null,
81
+ type: a.type,
82
+ dueDate: a.dueDate.toISOString(),
83
+ maxGrade: a.maxGrade,
84
+ section: a.section?.name ?? null,
85
+ }));
86
+ }
87
+ catch (error) {
88
+ logger.error('search_assignments tool failed', { error, classId, query });
89
+ return [];
90
+ }
91
+ },
92
+ },
93
+ get_student_progress: {
94
+ description: 'Get progress summary for a specific student in this class, including grades and submission status.',
95
+ inputSchema: jsonSchema({
96
+ type: 'object',
97
+ properties: {
98
+ studentId: { type: 'string', description: 'The student user ID' },
99
+ },
100
+ required: ['studentId'],
101
+ }),
102
+ execute: async ({ studentId }) => {
103
+ try {
104
+ const submissions = await prisma.submission.findMany({
105
+ where: {
106
+ studentId,
107
+ assignment: { classId },
108
+ },
109
+ select: {
110
+ id: true,
111
+ gradeReceived: true,
112
+ submittedAt: true,
113
+ assignment: {
114
+ select: {
115
+ title: true,
116
+ maxGrade: true,
117
+ type: true,
118
+ },
119
+ },
120
+ },
121
+ orderBy: { submittedAt: 'desc' },
122
+ take: 20,
123
+ });
124
+ const totalAssignments = await prisma.assignment.count({
125
+ where: { classId },
126
+ });
127
+ const submitted = submissions.filter((s) => s.submittedAt);
128
+ const graded = submissions.filter((s) => s.gradeReceived !== null);
129
+ const avgGrade = graded.length > 0
130
+ ? graded.reduce((sum, s) => sum + (s.gradeReceived || 0), 0) / graded.length
131
+ : null;
132
+ return {
133
+ studentId,
134
+ totalAssignments,
135
+ submittedCount: submitted.length,
136
+ gradedCount: graded.length,
137
+ averageGrade: avgGrade ? Math.round(avgGrade * 10) / 10 : null,
138
+ recentSubmissions: submissions.slice(0, 5).map((s) => ({
139
+ assignment: s.assignment.title,
140
+ gradeReceived: s.gradeReceived,
141
+ maxGrade: s.assignment.maxGrade,
142
+ submittedAt: s.submittedAt?.toISOString() ?? null,
143
+ })),
144
+ };
145
+ }
146
+ catch (error) {
147
+ logger.error('get_student_progress tool failed', { error, classId, studentId });
148
+ return { error: 'Failed to fetch student progress' };
149
+ }
150
+ },
151
+ },
152
+ get_worksheet_details: {
153
+ description: 'Get full details of a worksheet including all questions, answers, and mark schemes.',
154
+ inputSchema: jsonSchema({
155
+ type: 'object',
156
+ properties: {
157
+ worksheetId: { type: 'string', description: 'The worksheet ID to look up' },
158
+ },
159
+ required: ['worksheetId'],
160
+ }),
161
+ execute: async ({ worksheetId }) => {
162
+ try {
163
+ const worksheet = await prisma.worksheet.findUnique({
164
+ where: { id: worksheetId, classId },
165
+ include: {
166
+ questions: {
167
+ orderBy: { order: 'asc' },
168
+ },
169
+ },
170
+ });
171
+ if (!worksheet) {
172
+ return { error: 'Worksheet not found' };
173
+ }
174
+ return {
175
+ id: worksheet.id,
176
+ name: worksheet.name,
177
+ questions: worksheet.questions.map((q) => ({
178
+ id: q.id,
179
+ type: q.type,
180
+ question: q.question,
181
+ answer: q.answer,
182
+ options: q.options,
183
+ points: q.points,
184
+ order: q.order,
185
+ })),
186
+ };
187
+ }
188
+ catch (error) {
189
+ logger.error('get_worksheet_details tool failed', { error, classId, worksheetId });
190
+ return { error: 'Failed to fetch worksheet' };
191
+ }
192
+ },
193
+ },
194
+ get_mark_scheme: {
195
+ description: 'Get a mark scheme (rubric) by ID, including criteria and point allocations.',
196
+ inputSchema: jsonSchema({
197
+ type: 'object',
198
+ properties: {
199
+ markSchemeId: { type: 'string', description: 'The mark scheme ID' },
200
+ },
201
+ required: ['markSchemeId'],
202
+ }),
203
+ execute: async ({ markSchemeId }) => {
204
+ try {
205
+ const markScheme = await prisma.markScheme.findUnique({
206
+ where: { id: markSchemeId },
207
+ select: { id: true, name: true, structured: true },
208
+ });
209
+ if (!markScheme) {
210
+ return { error: 'Mark scheme not found' };
211
+ }
212
+ let parsed = {};
213
+ try {
214
+ parsed = JSON.parse(markScheme.structured || '{}');
215
+ }
216
+ catch { /* noop */ }
217
+ return {
218
+ id: markScheme.id,
219
+ name: markScheme.name,
220
+ structured: parsed,
221
+ };
222
+ }
223
+ catch (error) {
224
+ logger.error('get_mark_scheme tool failed', { error, classId, markSchemeId });
225
+ return { error: 'Failed to fetch mark scheme' };
226
+ }
227
+ },
228
+ },
229
+ web_search: {
230
+ description: 'Search the web for current information on a topic. Use this when you need up-to-date facts, definitions, explanations, or context beyond the class materials.',
231
+ inputSchema: jsonSchema({
232
+ type: 'object',
233
+ properties: {
234
+ query: { type: 'string', description: 'The search query to look up on the web' },
235
+ },
236
+ required: ['query'],
237
+ }),
238
+ execute: async ({ query }) => {
239
+ try {
240
+ const client = new OpenAI();
241
+ const response = await client.responses.create({
242
+ model: 'gpt-4o-mini',
243
+ tools: [{ type: 'web_search' }],
244
+ input: query,
245
+ });
246
+ const messageOutput = response.output.find((item) => item.type === 'message');
247
+ if (!messageOutput || messageOutput.type !== 'message') {
248
+ return { summary: 'No results found.', sources: [] };
249
+ }
250
+ const textContent = messageOutput.content.find((c) => c.type === 'output_text');
251
+ if (!textContent || textContent.type !== 'output_text') {
252
+ return { summary: 'No results found.', sources: [] };
253
+ }
254
+ const sources = textContent.annotations
255
+ .filter((a) => a.type === 'url_citation')
256
+ .map((a) => ({ url: a.url, title: a.title }));
257
+ return {
258
+ summary: textContent.text.slice(0, 2000),
259
+ sources: sources.slice(0, 5),
260
+ };
261
+ }
262
+ catch (error) {
263
+ logger.error('web_search tool failed', { error, query });
264
+ return { summary: 'Web search failed. Please try again.', sources: [] };
265
+ }
266
+ },
267
+ },
268
+ };
269
+ }
270
+ //# sourceMappingURL=aiTools.js.map
271
+ //# debugId=92c04dcc-74fa-5dc9-90cb-1ee91299efe7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiTools.js","sources":["services/aiTools.ts"],"sourceRoot":"/","sourcesContent":["/**\n * AI Tool definitions and execution for agent tool calls.\n * Provides RAG search, assignment lookup, worksheet creation, student progress,\n * and web search tools.\n */\nimport { jsonSchema, type ToolSet } from 'ai';\nimport OpenAI from 'openai';\nimport { searchEmbeddings } from './embedding.js';\nimport { prisma } from '../lib/prisma.js';\nimport { logger } from '../utils/logger.js';\n\nexport interface ToolCallResult {\n toolName: string;\n result: unknown;\n}\n\n/**\n * Build the tool set for a given class context.\n * Tools are scoped to a specific class for security.\n */\nexport function aiTools(classId: string): ToolSet {\n return {\n search_materials: {\n description: 'Search class materials, files, assignments, worksheets, and announcements using semantic similarity. Use this to find relevant context for answering questions.',\n inputSchema: jsonSchema({\n type: 'object',\n properties: {\n query: { type: 'string', description: 'The search query to find relevant materials' },\n limit: { type: 'number', description: 'Maximum results (1-10)', default: 5 },\n },\n required: ['query'],\n }),\n execute: async ({ query, limit = 5 }: { query: string; limit?: number }) => {\n try {\n const results = await searchEmbeddings({ classId, query, limit });\n return results.map((r) => ({\n sourceType: r.sourceType,\n sourceId: r.sourceId,\n content: r.content.slice(0, 500),\n similarity: Math.round(r.similarity * 100) / 100,\n metadata: r.metadata,\n }));\n } catch (error) {\n logger.error('search_materials tool failed', { error, classId, query });\n return [];\n }\n },\n },\n\n search_assignments: {\n description: 'Search existing assignments by title, instructions, or type. Returns assignment details including due dates and submission types.',\n inputSchema: jsonSchema({\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query for assignments' },\n limit: { type: 'number', description: 'Maximum results (1-10)', default: 5 },\n },\n required: ['query'],\n }),\n execute: async ({ query, limit = 5 }: { query: string; limit?: number }) => {\n try {\n const results = await searchEmbeddings({\n classId,\n query,\n limit,\n sourceTypes: ['ASSIGNMENT'],\n });\n\n const assignmentIds = results.map((r) => r.sourceId);\n const assignments = await prisma.assignment.findMany({\n where: { id: { in: assignmentIds } },\n select: {\n id: true,\n title: true,\n instructions: true,\n type: true,\n dueDate: true,\n maxGrade: true,\n section: { select: { name: true } },\n },\n });\n\n return assignments.map((a) => ({\n id: a.id,\n title: a.title,\n instructions: a.instructions?.slice(0, 200) ?? null,\n type: a.type,\n dueDate: a.dueDate.toISOString(),\n maxGrade: a.maxGrade,\n section: a.section?.name ?? null,\n }));\n } catch (error) {\n logger.error('search_assignments tool failed', { error, classId, query });\n return [];\n }\n },\n },\n\n get_student_progress: {\n description: 'Get progress summary for a specific student in this class, including grades and submission status.',\n inputSchema: jsonSchema({\n type: 'object',\n properties: {\n studentId: { type: 'string', description: 'The student user ID' },\n },\n required: ['studentId'],\n }),\n execute: async ({ studentId }: { studentId: string }) => {\n try {\n const submissions = await prisma.submission.findMany({\n where: {\n studentId,\n assignment: { classId },\n },\n select: {\n id: true,\n gradeReceived: true,\n submittedAt: true,\n assignment: {\n select: {\n title: true,\n maxGrade: true,\n type: true,\n },\n },\n },\n orderBy: { submittedAt: 'desc' },\n take: 20,\n });\n\n const totalAssignments = await prisma.assignment.count({\n where: { classId },\n });\n\n const submitted = submissions.filter((s) => s.submittedAt);\n const graded = submissions.filter((s) => s.gradeReceived !== null);\n const avgGrade = graded.length > 0\n ? graded.reduce((sum, s) => sum + (s.gradeReceived || 0), 0) / graded.length\n : null;\n\n return {\n studentId,\n totalAssignments,\n submittedCount: submitted.length,\n gradedCount: graded.length,\n averageGrade: avgGrade ? Math.round(avgGrade * 10) / 10 : null,\n recentSubmissions: submissions.slice(0, 5).map((s) => ({\n assignment: s.assignment.title,\n gradeReceived: s.gradeReceived,\n maxGrade: s.assignment.maxGrade,\n submittedAt: s.submittedAt?.toISOString() ?? null,\n })),\n };\n } catch (error) {\n logger.error('get_student_progress tool failed', { error, classId, studentId });\n return { error: 'Failed to fetch student progress' };\n }\n },\n },\n\n get_worksheet_details: {\n description: 'Get full details of a worksheet including all questions, answers, and mark schemes.',\n inputSchema: jsonSchema({\n type: 'object',\n properties: {\n worksheetId: { type: 'string', description: 'The worksheet ID to look up' },\n },\n required: ['worksheetId'],\n }),\n execute: async ({ worksheetId }: { worksheetId: string }) => {\n try {\n const worksheet = await prisma.worksheet.findUnique({\n where: { id: worksheetId, classId },\n include: {\n questions: {\n orderBy: { order: 'asc' },\n },\n },\n });\n\n if (!worksheet) {\n return { error: 'Worksheet not found' };\n }\n\n return {\n id: worksheet.id,\n name: worksheet.name,\n questions: worksheet.questions.map((q) => ({\n id: q.id,\n type: q.type,\n question: q.question,\n answer: q.answer,\n options: q.options,\n points: q.points,\n order: q.order,\n })),\n };\n } catch (error) {\n logger.error('get_worksheet_details tool failed', { error, classId, worksheetId });\n return { error: 'Failed to fetch worksheet' };\n }\n },\n },\n\n get_mark_scheme: {\n description: 'Get a mark scheme (rubric) by ID, including criteria and point allocations.',\n inputSchema: jsonSchema({\n type: 'object',\n properties: {\n markSchemeId: { type: 'string', description: 'The mark scheme ID' },\n },\n required: ['markSchemeId'],\n }),\n execute: async ({ markSchemeId }: { markSchemeId: string }) => {\n try {\n const markScheme = await prisma.markScheme.findUnique({\n where: { id: markSchemeId },\n select: { id: true, name: true, structured: true },\n });\n\n if (!markScheme) {\n return { error: 'Mark scheme not found' };\n }\n\n let parsed: unknown = {};\n try { parsed = JSON.parse(markScheme.structured || '{}'); } catch { /* noop */ }\n\n return {\n id: markScheme.id,\n name: markScheme.name,\n structured: parsed,\n };\n } catch (error) {\n logger.error('get_mark_scheme tool failed', { error, classId, markSchemeId });\n return { error: 'Failed to fetch mark scheme' };\n }\n },\n },\n\n web_search: {\n description: 'Search the web for current information on a topic. Use this when you need up-to-date facts, definitions, explanations, or context beyond the class materials.',\n inputSchema: jsonSchema({\n type: 'object',\n properties: {\n query: { type: 'string', description: 'The search query to look up on the web' },\n },\n required: ['query'],\n }),\n execute: async ({ query }: { query: string }) => {\n try {\n const client = new OpenAI();\n const response = await client.responses.create({\n model: 'gpt-4o-mini',\n tools: [{ type: 'web_search' }],\n input: query,\n });\n\n const messageOutput = response.output.find(\n (item) => item.type === 'message',\n );\n if (!messageOutput || messageOutput.type !== 'message') {\n return { summary: 'No results found.', sources: [] };\n }\n\n const textContent = messageOutput.content.find(\n (c) => c.type === 'output_text',\n );\n if (!textContent || textContent.type !== 'output_text') {\n return { summary: 'No results found.', sources: [] };\n }\n\n const sources = textContent.annotations\n .filter(\n (a): a is Extract<typeof a, { type: 'url_citation' }> =>\n a.type === 'url_citation',\n )\n .map((a) => ({ url: a.url, title: a.title }));\n\n return {\n summary: textContent.text.slice(0, 2000),\n sources: sources.slice(0, 5),\n };\n } catch (error) {\n logger.error('web_search tool failed', { error, query });\n return { summary: 'Web search failed. Please try again.', sources: [] };\n }\n },\n },\n };\n}\n"],"names":[],"mappings":"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","debug_id":"92c04dcc-74fa-5dc9-90cb-1ee91299efe7"}
@@ -5,52 +5,52 @@ export declare function getAllAnnouncements(classId: string): Promise<{
5
5
  _count: undefined;
6
6
  id: string;
7
7
  createdAt: Date;
8
- modifiedAt: Date | null;
9
8
  remarks: string;
10
- teacher: {
11
- id: string;
12
- username: string;
13
- profile: {
14
- displayName: string | null;
15
- profilePicture: string | null;
16
- profilePictureThumbnail: string | null;
17
- } | null;
18
- };
19
9
  attachments: {
20
10
  path: string;
21
11
  type: string;
22
- name: string;
23
12
  id: string;
13
+ name: string;
24
14
  size: number | null;
25
15
  uploadedAt: Date | null;
26
16
  thumbnailId: string | null;
27
17
  }[];
28
- }[];
29
- }>;
30
- export declare function getAnnouncement(id: string, classId: string): Promise<{
31
- announcement: {
32
- id: string;
33
- createdAt: Date;
34
18
  modifiedAt: Date | null;
35
- remarks: string;
36
19
  teacher: {
37
20
  id: string;
38
21
  username: string;
39
22
  profile: {
40
- displayName: string | null;
41
23
  profilePicture: string | null;
24
+ displayName: string | null;
42
25
  profilePictureThumbnail: string | null;
43
26
  } | null;
44
27
  };
28
+ }[];
29
+ }>;
30
+ export declare function getAnnouncement(id: string, classId: string): Promise<{
31
+ announcement: {
32
+ id: string;
33
+ createdAt: Date;
34
+ remarks: string;
45
35
  attachments: {
46
36
  path: string;
47
37
  type: string;
48
- name: string;
49
38
  id: string;
39
+ name: string;
50
40
  size: number | null;
51
41
  uploadedAt: Date | null;
52
42
  thumbnailId: string | null;
53
43
  }[];
44
+ modifiedAt: Date | null;
45
+ teacher: {
46
+ id: string;
47
+ username: string;
48
+ profile: {
49
+ profilePicture: string | null;
50
+ displayName: string | null;
51
+ profilePictureThumbnail: string | null;
52
+ } | null;
53
+ };
54
54
  };
55
55
  }>;
56
56
  export declare function createAnnouncementRecord(userId: string, input: {
@@ -66,26 +66,26 @@ export declare function createAnnouncementRecord(userId: string, input: {
66
66
  announcement: {
67
67
  id: string;
68
68
  createdAt: Date;
69
- modifiedAt: Date | null;
70
69
  remarks: string;
71
- teacher: {
72
- id: string;
73
- username: string;
74
- profile: {
75
- displayName: string | null;
76
- profilePicture: string | null;
77
- profilePictureThumbnail: string | null;
78
- } | null;
79
- };
80
70
  attachments: {
81
71
  path: string;
82
72
  type: string;
83
- name: string;
84
73
  id: string;
74
+ name: string;
85
75
  size: number | null;
86
76
  uploadedAt: Date | null;
87
77
  thumbnailId: string | null;
88
78
  }[];
79
+ modifiedAt: Date | null;
80
+ teacher: {
81
+ id: string;
82
+ username: string;
83
+ profile: {
84
+ profilePicture: string | null;
85
+ displayName: string | null;
86
+ profilePictureThumbnail: string | null;
87
+ } | null;
88
+ };
89
89
  };
90
90
  uploadFiles: DirectUploadFile[] | undefined;
91
91
  }>;
@@ -106,26 +106,26 @@ export declare function updateAnnouncementRecord(userId: string, input: {
106
106
  announcement: {
107
107
  id: string;
108
108
  createdAt: Date;
109
- modifiedAt: Date | null;
110
109
  remarks: string;
111
- teacher: {
112
- id: string;
113
- username: string;
114
- profile: {
115
- displayName: string | null;
116
- profilePicture: string | null;
117
- profilePictureThumbnail: string | null;
118
- } | null;
119
- };
120
110
  attachments: {
121
111
  path: string;
122
112
  type: string;
123
- name: string;
124
113
  id: string;
114
+ name: string;
125
115
  size: number | null;
126
116
  uploadedAt: Date | null;
127
117
  thumbnailId: string | null;
128
118
  }[];
119
+ modifiedAt: Date | null;
120
+ teacher: {
121
+ id: string;
122
+ username: string;
123
+ profile: {
124
+ profilePicture: string | null;
125
+ displayName: string | null;
126
+ profilePictureThumbnail: string | null;
127
+ } | null;
128
+ };
129
129
  };
130
130
  uploadFiles: DirectUploadFile[] | undefined;
131
131
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"announcement.d.ts","sourceRoot":"/","sources":["services/announcement.ts"],"names":[],"mappings":"AAcA,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAKtF,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQxD;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;GAShE;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFF;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QACvD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC/B,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;;;;;;GA2HF;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;GA0BzF"}
1
+ {"version":3,"file":"announcement.d.ts","sourceRoot":"/","sources":["services/announcement.ts"],"names":[],"mappings":"AAcA,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMtF,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQxD;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;GAShE;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFF;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QACvD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC/B,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;;;;;;GA8HF;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;GA0BzF"}