@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
@@ -1,19 +1,23 @@
1
1
  /**
2
2
  * AI lab chat pipeline – generates lab introductions and responses.
3
- * Can create worksheets, sections, assignments, and PDF docs from AI output.
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]="e57f7401-27fd-5eac-a76a-cb84f699b776")}catch(e){}}();
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]="976e04f3-b470-5bcb-afeb-1df2af403427")}catch(e){}}();
7
7
  import { isAIUser } from "../utils/aiUser.js";
8
8
  import { prisma } from "../lib/prisma.js";
9
- import { pusher, teacherChannel } from "../lib/pusher.js";
10
- import { inference, completePendingAIMessage, failPendingAIMessage, } from "../utils/inference.js";
9
+ import { pusher, chatChannel, teacherChannel } from "../lib/pusher.js";
10
+ import { completePendingAIMessage, failPendingAIMessage, } from "../utils/inference.js";
11
11
  import { logger } from "../utils/logger.js";
12
- import { createPdf } from "../lib/jsonConversion.js";
13
12
  import { v4 } from "uuid";
14
- import { bucket } from "../lib/googleCloudStorage.js";
15
- import { labChatResponseSchema } from "./aiLabChatContract.js";
16
- import { buildLabChatResponseMessages } from "./labChatPrompt.js";
13
+ import { bucket, getSignedUrl } from "../lib/googleCloudStorage.js";
14
+ import { generateDocumentWithOpenAI, normalizeDocumentFormat, } from "../services/openaiDocumentGen.js";
15
+ import { inferDocumentFormat } from "../lib/inferDocumentFormat.js";
16
+ import { broadcastTextChunks, broadcastStreamComplete } from "../utils/streamBroadcast.js";
17
+ import { buildLabChatAgentMessages } from "./labChatPrompt.js";
18
+ import { applyResolvedFormatsToStreamParts, enrichStreamPartsWithGeneratedFiles, finalizeDeferredToolParts, streamChatResponse, } from "../services/aiChatStream.js";
19
+ import { aiTools } from "../services/aiTools.js";
20
+ import { labProposalTools, createLabProposals } from "../services/aiProposalTools.js";
17
21
  /** Cap model wait time; leave headroom after inference for PDF/GCS/DB within Vercel maxDuration (500s). */
18
22
  const LAB_CHAT_RESPONSE_TIMEOUT_MS = 400000;
19
23
  const withTimeout = async (task, timeoutMs, operationName) => {
@@ -64,6 +68,14 @@ const loadRecentLabChatMessages = async (conversationId, anchorMessageId) => {
64
68
  },
65
69
  },
66
70
  },
71
+ attachments: {
72
+ select: {
73
+ id: true,
74
+ name: true,
75
+ type: true,
76
+ path: true,
77
+ },
78
+ },
67
79
  };
68
80
  const newestMessages = await prisma.message.findMany({
69
81
  where: baseQuery,
@@ -123,6 +135,34 @@ const loadRecentLabChatMessages = async (conversationId, anchorMessageId) => {
123
135
  })
124
136
  .slice(0, limit);
125
137
  };
138
+ /**
139
+ * Resolves signed URLs for image attachments in messages so the LLM can see them.
140
+ * Non-image files are passed as text descriptions only.
141
+ */
142
+ async function resolveAttachmentUrls(messages) {
143
+ const resolved = [];
144
+ for (const msg of messages) {
145
+ const attachments = msg.attachments ?? [];
146
+ if (attachments.length === 0) {
147
+ resolved.push({ ...msg, attachments: [] });
148
+ continue;
149
+ }
150
+ const withUrls = await Promise.all(attachments.map(async (a) => {
151
+ if (a.type.startsWith('image/')) {
152
+ try {
153
+ const url = await getSignedUrl(a.path);
154
+ return { id: a.id, name: a.name, type: a.type, url };
155
+ }
156
+ catch {
157
+ return { id: a.id, name: a.name, type: a.type };
158
+ }
159
+ }
160
+ return { id: a.id, name: a.name, type: a.type };
161
+ }));
162
+ resolved.push({ ...msg, attachments: withUrls });
163
+ }
164
+ return resolved;
165
+ }
126
166
  /**
127
167
  * Builds schema-aware context for the AI from class data.
128
168
  * Formats entities with IDs so the model can reference them when creating assignments.
@@ -224,28 +264,6 @@ export const getBaseSystemPrompt = (context, members, assignments, files, sectio
224
264
  assignments,
225
265
  });
226
266
  };
227
- /**
228
- * Generate labchat responses
229
- * Allow for the generation of the following:
230
- * - Assignment(s) either individual or bulk as an lesson / course plan.
231
- * - Worksheet(s) either individual or bulk as an lesson / course plan.
232
- * - Files (PDFs)
233
- * @param labChatId
234
- */
235
- // export const sendAiLabChatResponsePipeline = async (labChatId: string) => {
236
- // const message = await prisma?.message.create({
237
- // data: {
238
- // content: "GENERATING_CONTENT",
239
- // senderId: getAIUserId(),
240
- // conversationId: labChatId,
241
- // status: JobStatus.PENDING,
242
- // },
243
- // });
244
- // try {
245
- // inference(`
246
- // `)
247
- // }
248
- // };
249
267
  /**
250
268
  * Fill the AI introduction placeholder for a brand-new lab chat.
251
269
  *
@@ -254,8 +272,9 @@ export const getBaseSystemPrompt = (context, members, assignments, files, sectio
254
272
  * COMPLETED with the intro content, or to FAILED on a hard error.
255
273
  */
256
274
  export const generateAndSendLabIntroduction = async (labChatId, conversationId, _contextString, _subject, aiMessageId) => {
257
- const introMessage = "Hello teaching team! I'm your AI assistant for course material development. I'll help you create educational content - when you have a clear request, I'll produce it directly. I only ask questions when I need to clarify the topic or scope. What would you like to work on?";
275
+ const introMessage = "Hello teaching team! I'm your AI assistant for course material development. I can create worksheets, assignments, and downloadable files — PDFs, Word docs, PowerPoints, spreadsheets, and more. When you have a clear request, I'll produce it directly. I only ask questions when I need to clarify the topic or scope. What would you like to work on?";
258
276
  try {
277
+ await broadcastTextChunks(conversationId, aiMessageId, introMessage);
259
278
  await completePendingAIMessage(aiMessageId, introMessage);
260
279
  logger.info('AI Introduction sent', { labChatId, conversationId, aiMessageId });
261
280
  }
@@ -282,7 +301,7 @@ export const generateAndSendLabIntroduction = async (labChatId, conversationId,
282
301
  * conversation history is reloaded from the DB anchored on the AI
283
302
  * placeholder.
284
303
  */
285
- export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emitOptions) => {
304
+ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emitOptions) => {
286
305
  try {
287
306
  // Get lab context from database
288
307
  const fullLabChat = await prisma.labChat.findUnique({
@@ -366,116 +385,166 @@ export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emi
366
385
  teachers: classData.teachers,
367
386
  assignments: classData.assignments,
368
387
  });
369
- const messages = buildLabChatResponseMessages({
388
+ // Resolve signed URLs for image attachments so the LLM can see them
389
+ const messagesWithAttachments = await resolveAttachmentUrls(recentMessages.reverse());
390
+ const { system, messages } = buildLabChatAgentMessages({
370
391
  context: fullLabChat.context,
371
392
  classContext,
372
- recentMessages: recentMessages.reverse(),
393
+ recentMessages: messagesWithAttachments,
373
394
  isAIUser,
374
395
  });
375
- // const completion = await inferenceClient.chat.completions.create({
376
- // model: 'command-a-03-2025',
377
- // messages,
378
- // temperature: 0.7,
379
- // response_format: zodTextFormat(labChatResponseSchema, "lab_chat_response_format"),
380
- // });
381
- const response = await withTimeout(inference(messages, labChatResponseSchema, {
382
- reason: 'lab_chat_response',
383
- metadata: {
384
- labChatId,
385
- conversationId,
386
- classId: fullLabChat.classId,
387
- aiMessageId: emitOptions.aiMessageId,
388
- },
396
+ // Genuine agentic generation: the model streams prose token-by-token and
397
+ // calls real tools inline — read-only RAG lookups plus "propose_*" tools
398
+ // that record create-actions into `proposals`. `streamChatResponse`
399
+ // broadcasts the real text chunks and tool-call events as they happen.
400
+ const proposals = createLabProposals();
401
+ const tools = {
402
+ ...aiTools(fullLabChat.classId),
403
+ ...labProposalTools(proposals),
404
+ };
405
+ const { text: streamedText, parts: rawStreamParts } = await withTimeout(streamChatResponse({
406
+ conversationId,
407
+ aiMessageId: emitOptions.aiMessageId,
408
+ system,
409
+ messages,
410
+ tools,
411
+ temperature: 0.7,
412
+ maxOutputTokens: 4096,
413
+ maxSteps: 8,
389
414
  }), LAB_CHAT_RESPONSE_TIMEOUT_MS, "Lab chat response generation");
390
- if (!response) {
391
- throw new Error('No response generated from inference API');
392
- }
393
- // Parse the JSON response and generate PDF if docs are provided
415
+ // Real text + tool-call events were already broadcast inline (in order)
416
+ // during the stream by `streamChatResponse`. Just close the stream.
417
+ await broadcastStreamComplete(conversationId, emitOptions.aiMessageId);
418
+ // `generate_document` is an inline tool that only QUEUES into `proposals.docs`
419
+ // during the stream. OpenAI Code Interpreter renders files here, after the
420
+ // stream ends, then we link them to this message.
394
421
  try {
395
- const jsonData = response;
396
422
  const attachmentIds = [];
397
- // Generate PDFs if docs are provided
398
- if (jsonData.docs && Array.isArray(jsonData.docs)) {
399
- for (let i = 0; i < jsonData.docs.length; i++) {
400
- const doc = jsonData.docs[i];
401
- if (!doc.title || !doc.blocks || !Array.isArray(doc.blocks)) {
402
- logger.error(`Document ${i + 1} is missing title or blocks`);
403
- continue;
404
- }
405
- try {
406
- let pdfBytes = await createPdf(doc.blocks);
407
- if (pdfBytes) {
408
- // Sanitize filename - remove special characters and limit length
409
- const sanitizedTitle = doc.title
410
- .replace(/[^a-zA-Z0-9\s\-_]/g, '')
411
- .replace(/\s+/g, '_')
412
- .substring(0, 50);
413
- const filename = `${sanitizedTitle}_${v4().substring(0, 8)}.pdf`;
414
- const filePath = `class/generated/${fullLabChat.classId}/${filename}`;
415
- logger.info(`PDF ${i + 1} generated successfully`, { labChatId, title: doc.title });
416
- // Upload directly to Google Cloud Storage
417
- const gcsFile = bucket.file(filePath);
418
- await gcsFile.save(Buffer.from(pdfBytes), {
419
- metadata: {
420
- contentType: 'application/pdf',
421
- }
422
- });
423
- logger.info(`PDF ${i + 1} uploaded successfully`, { labChatId, filename });
424
- const file = await prisma.file.create({
425
- data: {
426
- name: filename,
427
- path: filePath,
428
- type: 'application/pdf',
429
- size: pdfBytes.length,
430
- userId: fullLabChat.createdById,
431
- uploadStatus: 'COMPLETED',
432
- uploadedAt: new Date(),
433
- },
434
- });
435
- attachmentIds.push(file.id);
436
- }
437
- else {
438
- logger.error(`PDF ${i + 1} creation returned undefined/null`, { labChatId, title: doc.title });
439
- }
440
- }
441
- catch (pdfError) {
442
- logger.error(`PDF creation threw an error for document ${i + 1}:`, {
443
- error: pdfError instanceof Error ? {
444
- message: pdfError.message,
445
- stack: pdfError.stack,
446
- name: pdfError.name
447
- } : pdfError,
423
+ const generatedDocFiles = [];
424
+ const resolvedDocs = [];
425
+ for (let i = 0; i < proposals.docs.length; i++) {
426
+ const doc = proposals.docs[i];
427
+ if (!doc.title || !doc.content?.trim()) {
428
+ logger.error(`Document ${i + 1} is missing title or content`, { labChatId, doc });
429
+ continue;
430
+ }
431
+ const format = inferDocumentFormat(doc.title, doc.content, doc.format, teacherMessage);
432
+ resolvedDocs.push({ title: doc.title, format });
433
+ if (format !== normalizeDocumentFormat(doc.format)) {
434
+ logger.info('Document format overridden from model default', {
435
+ labChatId,
436
+ title: doc.title,
437
+ modelFormat: doc.format,
438
+ resolvedFormat: format,
439
+ });
440
+ }
441
+ try {
442
+ const generated = await generateDocumentWithOpenAI({
443
+ title: doc.title,
444
+ content: doc.content,
445
+ format,
446
+ });
447
+ if (!generated) {
448
+ logger.error(`Document ${i + 1} generation returned null`, {
448
449
  labChatId,
449
- title: doc.title
450
+ title: doc.title,
451
+ format,
450
452
  });
453
+ continue;
451
454
  }
455
+ const sanitizedTitle = doc.title
456
+ .replace(/[^a-zA-Z0-9\s\-_]/g, '')
457
+ .replace(/\s+/g, '_')
458
+ .substring(0, 50);
459
+ const filename = `${sanitizedTitle}_${v4().substring(0, 8)}.${generated.format}`;
460
+ const filePath = `class/generated/${fullLabChat.classId}/${filename}`;
461
+ logger.info(`Document ${i + 1} generated successfully`, {
462
+ labChatId,
463
+ title: doc.title,
464
+ format: generated.format,
465
+ });
466
+ await bucket.file(filePath).save(generated.bytes, {
467
+ metadata: { contentType: generated.mimeType },
468
+ });
469
+ logger.info(`Document ${i + 1} uploaded successfully`, { labChatId, filename });
470
+ const file = await prisma.file.create({
471
+ data: {
472
+ name: filename,
473
+ path: filePath,
474
+ type: generated.mimeType,
475
+ size: generated.bytes.length,
476
+ userId: fullLabChat.createdById,
477
+ conversationId,
478
+ messageId: emitOptions.aiMessageId,
479
+ uploadStatus: 'COMPLETED',
480
+ uploadedAt: new Date(),
481
+ },
482
+ });
483
+ attachmentIds.push(file.id);
484
+ generatedDocFiles.push({
485
+ title: doc.title,
486
+ fileId: file.id,
487
+ fileName: filename,
488
+ format: generated.format,
489
+ });
490
+ }
491
+ catch (docError) {
492
+ logger.error(`Document generation failed for item ${i + 1}:`, {
493
+ error: docError instanceof Error ? {
494
+ message: docError.message,
495
+ stack: docError.stack,
496
+ name: docError.name,
497
+ } : docError,
498
+ labChatId,
499
+ title: doc.title,
500
+ format: doc.format,
501
+ });
452
502
  }
453
503
  }
454
- await completePendingAIMessage(emitOptions.aiMessageId, jsonData.text, {
455
- attachments: {
456
- connect: attachmentIds.map(id => ({ id })),
457
- },
504
+ const streamParts = finalizeDeferredToolParts(enrichStreamPartsWithGeneratedFiles(applyResolvedFormatsToStreamParts(rawStreamParts, resolvedDocs), generatedDocFiles));
505
+ // While the message is still PENDING, resolve deferred tool indicators
506
+ // (spinner → attachment or error) before the final persist event.
507
+ for (const part of streamParts) {
508
+ if (part.type !== 'tool' || part.toolName !== 'generate_document') {
509
+ continue;
510
+ }
511
+ if (part.status === 'executing')
512
+ continue;
513
+ await pusher
514
+ .trigger(chatChannel(conversationId), 'message-tool-call', {
515
+ messageId: emitOptions.aiMessageId,
516
+ toolCallId: part.toolCallId,
517
+ toolName: part.toolName,
518
+ ...(part.title ? { title: part.title } : {}),
519
+ ...(part.format ? { format: part.format } : {}),
520
+ ...(part.fileId ? { fileId: part.fileId, fileName: part.fileName } : {}),
521
+ status: part.status,
522
+ })
523
+ .catch(() => { });
524
+ }
525
+ await completePendingAIMessage(emitOptions.aiMessageId, streamedText, {
526
+ ...(attachmentIds.length > 0 && {
527
+ attachments: { connect: attachmentIds.map((id) => ({ id })) },
528
+ }),
458
529
  meta: {
459
- assignmentsToCreate: jsonData.assignmentsToCreate?.map(assignment => ({
460
- ...assignment,
461
- id: v4(),
462
- })) || null,
463
- worksheetsToCreate: jsonData.worksheetsToCreate?.map(worksheet => ({
464
- ...worksheet,
465
- id: v4(),
466
- })) || null,
467
- sectionsToCreate: jsonData.sectionsToCreate?.map(section => ({
468
- ...section,
469
- id: v4(),
470
- })) || null,
530
+ assignmentsToCreate: proposals.assignments.length > 0
531
+ ? proposals.assignments.map(assignment => ({ ...assignment, id: v4() }))
532
+ : null,
533
+ worksheetsToCreate: proposals.worksheets.length > 0
534
+ ? proposals.worksheets.map(worksheet => ({ ...worksheet, id: v4() }))
535
+ : null,
536
+ sectionsToCreate: proposals.sections.length > 0
537
+ ? proposals.sections.map(section => ({ ...section, id: v4() }))
538
+ : null,
539
+ streamParts: streamParts.length > 0 ? streamParts : null,
471
540
  },
472
541
  });
473
542
  }
474
- catch (parseError) {
475
- logger.error('Failed to parse AI response or generate PDF:', { error: parseError, labChatId });
543
+ catch (persistError) {
544
+ logger.error('Failed to persist AI response or generate PDF:', { error: persistError, labChatId });
476
545
  // Fallback: complete the placeholder with the raw text so the
477
546
  // teacher still sees something useful instead of FAILED.
478
- await completePendingAIMessage(emitOptions.aiMessageId, response.text);
547
+ await completePendingAIMessage(emitOptions.aiMessageId, streamedText);
479
548
  }
480
549
  try {
481
550
  await pusher.trigger(teacherChannel(emitOptions.classId), "lab-response-completed", {
@@ -516,4 +585,4 @@ export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emi
516
585
  }
517
586
  };
518
587
  //# sourceMappingURL=aiLabChat.js.map
519
- //# debugId=e57f7401-27fd-5eac-a76a-cb84f699b776
588
+ //# debugId=976e04f3-b470-5bcb-afeb-1df2af403427
@@ -1 +1 @@
1
- {"version":3,"file":"aiLabChat.js","sources":["pipelines/aiLabChat.ts"],"sourceRoot":"/","sourcesContent":["/**\n * AI lab chat pipeline – generates lab introductions and responses.\n * Can create worksheets, sections, assignments, and PDF docs from AI output.\n */\nimport { isAIUser } from \"../utils/aiUser.js\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { pusher, teacherChannel } from \"../lib/pusher.js\";\nimport type { Assignment, Class, File, Section, User } from \"@prisma/client\";\nimport {\n inference,\n completePendingAIMessage,\n failPendingAIMessage,\n} from \"../utils/inference.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { createPdf } from \"../lib/jsonConversion.js\";\nimport { v4 } from \"uuid\";\nimport { bucket } from \"../lib/googleCloudStorage.js\";\nimport { DocumentBlock } from \"../lib/jsonStyles.js\";\nimport { type LabChatResponse, labChatResponseSchema } from \"./aiLabChatContract.js\";\nimport { buildLabChatResponseMessages } from \"./labChatPrompt.js\";\n\n/** Cap model wait time; leave headroom after inference for PDF/GCS/DB within Vercel maxDuration (500s). */\nconst LAB_CHAT_RESPONSE_TIMEOUT_MS = 400_000;\n\nconst withTimeout = async <T>(\n task: Promise<T>,\n timeoutMs: number,\n operationName: string,\n): Promise<T> => {\n let timeoutHandle: NodeJS.Timeout | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n reject(new Error(`${operationName} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([task, timeoutPromise]);\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n};\n\n/** Extended class data for AI context (schema-aware) */\ntype ClassContextData = {\n class: Class;\n sections: Section[];\n markSchemes: { id: string; structured: string }[];\n gradingBoundaries: { id: string; structured: string }[];\n worksheets: { id: string; name: string; questionCount: number }[];\n files: File[];\n students: { id: string; username: string; profile?: { displayName: string | null } | null }[];\n teachers: { id: string; username: string; profile?: { displayName: string | null } | null }[];\n assignments: (Assignment & {\n section?: { id: string; name: string; order?: number | null } | null;\n markScheme?: { id: string } | null;\n gradingBoundary?: { id: string } | null;\n })[];\n};\n\ntype RecentLabChatMessage = {\n id: string;\n content: string;\n senderId: string;\n createdAt: Date;\n sender: {\n id: string;\n username: string | null;\n profile: {\n displayName: string | null;\n } | null;\n } | null;\n};\n\n/**\n * `messages` must be ordered newest-first.\n * When `anchorMessageId` is provided, `sliceMessagesThroughAnchor` returns the\n * anchor message and older messages only, capped to `limit`.\n */\nexport const sliceMessagesThroughAnchor = (\n messages: RecentLabChatMessage[],\n anchorMessageId?: string,\n limit = 10,\n) => {\n if (!anchorMessageId) {\n return messages.slice(0, limit);\n }\n\n const anchorIndex = messages.findIndex((message) => message.id === anchorMessageId);\n if (anchorIndex === -1) {\n return messages.slice(0, limit);\n }\n\n return messages.slice(anchorIndex, anchorIndex + limit);\n};\n\nconst loadRecentLabChatMessages = async (\n conversationId: string,\n anchorMessageId?: string,\n): Promise<RecentLabChatMessage[]> => {\n const limit = 10;\n const baseQuery = {\n conversationId,\n };\n const include = {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n };\n\n const newestMessages = await prisma.message.findMany({\n where: baseQuery,\n include,\n orderBy: {\n createdAt: 'desc',\n },\n take: 25,\n });\n\n const anchoredMessages = sliceMessagesThroughAnchor(newestMessages, anchorMessageId, limit);\n if (!anchorMessageId || anchoredMessages.some((message) => message.id === anchorMessageId)) {\n return anchoredMessages;\n }\n\n const anchorMessage = await prisma.message.findUnique({\n where: { id: anchorMessageId },\n include,\n });\n\n if (!anchorMessage) {\n throw new Error(`Anchor message ${anchorMessageId} not found`);\n }\n\n if (anchorMessage.conversationId !== conversationId) {\n throw new Error(`Anchor message ${anchorMessageId} does not belong to conversation ${conversationId}`);\n }\n\n const olderMessages = await prisma.message.findMany({\n where: {\n conversationId,\n createdAt: {\n lte: anchorMessage.createdAt,\n },\n },\n include,\n orderBy: {\n createdAt: 'desc',\n },\n take: limit,\n });\n\n const dedupedMessages = new Map<string, RecentLabChatMessage>();\n dedupedMessages.set(anchorMessage.id, anchorMessage);\n\n olderMessages.forEach((message) => {\n if (!dedupedMessages.has(message.id)) {\n dedupedMessages.set(message.id, message);\n }\n });\n\n return Array.from(dedupedMessages.values())\n .sort((left, right) => {\n const timeDelta = right.createdAt.getTime() - left.createdAt.getTime();\n if (timeDelta !== 0) {\n return timeDelta;\n }\n if (left.id === anchorMessage.id) {\n return -1;\n }\n if (right.id === anchorMessage.id) {\n return 1;\n }\n\n return right.id.localeCompare(left.id);\n })\n .slice(0, limit);\n};\n\n/**\n * Builds schema-aware context for the AI from class data.\n * Formats entities with IDs so the model can reference them when creating assignments.\n */\nexport const buildClassContextForAI = (data: ClassContextData): string => {\n const { class: cls, sections, markSchemes, gradingBoundaries, worksheets, files, students, assignments } = data;\n\n const sectionList = sections\n .sort((a, b) => (a.order ?? 999) - (b.order ?? 999))\n .map((s) => ` - id: ${s.id} | name: \"${s.name}\" | color: ${s.color ?? \"default\"}`)\n .join(\"\\n\");\n\n const markSchemeList = markSchemes\n .map((ms) => {\n let preview = \"structured rubric\";\n try {\n const parsed = JSON.parse(ms.structured || \"{}\");\n preview = parsed.name || Object.keys(parsed).slice(0, 2).join(\", \") || \"rubric\";\n } catch {\n /* ignore */\n }\n return ` - id: ${ms.id} | ${preview}`;\n })\n .join(\"\\n\");\n\n const gradingBoundaryList = gradingBoundaries\n .map((gb) => {\n let preview = \"grading scale\";\n try {\n const parsed = JSON.parse(gb.structured || \"{}\");\n preview = parsed.name || Object.keys(parsed).slice(0, 2).join(\", \") || \"scale\";\n } catch {\n /* ignore */\n }\n return ` - id: ${gb.id} | ${preview}`;\n })\n .join(\"\\n\");\n\n const worksheetList = worksheets\n .map((w) => ` - id: ${w.id} | name: \"${w.name}\" | questions: ${w.questionCount}`)\n .join(\"\\n\");\n\n const fileList = files\n .filter((f) => f.type === \"application/pdf\" || f.type?.includes(\"document\"))\n .map((f) => ` - id: ${f.id} | name: \"${f.name}\" | type: ${f.type}`)\n .join(\"\\n\");\n const otherFiles = files.filter((f) => f.type !== \"application/pdf\" && !f.type?.includes(\"document\"));\n const otherFileList = otherFiles.length\n ? otherFiles.map((f) => ` - id: ${f.id} | name: \"${f.name}\"`).join(\"\\n\")\n : \" (none)\";\n\n const studentList = students\n .map((u) => ` - id: ${u.id} | username: ${u.username} | displayName: ${u.profile?.displayName ?? \"—\"}`)\n .join(\"\\n\");\n\n const assignmentSummary = assignments\n .map((a) => {\n const sectionName = a.section?.name ?? \"—\";\n return ` - id: ${a.id} | title: \"${a.title}\" | type: ${a.type} | section: \"${sectionName}\" | due: ${a.dueDate.toISOString().slice(0, 10)}`;\n })\n .join(\"\\n\");\n\n return `\nCLASS: ${cls.name} | Subject: ${cls.subject} | Section: ${cls.section}\nSyllabus: ${cls.syllabus ? cls.syllabus.slice(0, 200) + (cls.syllabus.length > 200 ? \"…\" : \"\") : \"(none)\"}\n\nSECTIONS (use sectionId when creating assignments):\n${sectionList || \" (none - suggest sectionsToCreate first)\"}\n\nMARK SCHEMES (use markSchemeId when creating assignments):\n${markSchemeList || \" (none - suggest creating one or omit markSchemeId)\"}\n\nGRADING BOUNDARIES (use gradingBoundaryId when creating assignments):\n${gradingBoundaryList || \" (none - suggest creating one or omit gradingBoundaryId)\"}\n\nWORKSHEETS (use worksheetIds when acceptWorksheet is true):\n${worksheetList || \" (none - use worksheetsToCreate or create via docs first)\"}\n\nFILES - PDFs/Documents (for assignment attachments):\n${fileList || \" (none)\"}\n\nFILES - Other (for assignment attachments):\n${otherFileList}\n\nSTUDENTS (use studentIds for specific assignment; empty array = all students):\n${studentList || \" (none)\"}\n\nEXISTING ASSIGNMENTS (for reference, avoid duplicates):\n${assignmentSummary || \" (none)\"}\n`.trim();\n};\n\n/**\n * @deprecated Use buildClassContextForAI for schema-aware context. Kept for compatibility.\n */\nexport const getBaseSystemPrompt = (\n context: Class,\n members: User[],\n assignments: Assignment[],\n files: File[],\n sections: Section[]\n): string => {\n return buildClassContextForAI({\n class: context,\n sections,\n markSchemes: [],\n gradingBoundaries: [],\n worksheets: [],\n files,\n students: members,\n teachers: [],\n assignments,\n });\n};\n\n\n\n/**\n * Generate labchat responses\n * Allow for the generation of the following:\n * - Assignment(s) either individual or bulk as an lesson / course plan.\n * - Worksheet(s) either individual or bulk as an lesson / course plan.\n * - Files (PDFs)\n * @param labChatId \n */\n// export const sendAiLabChatResponsePipeline = async (labChatId: string) => {\n// const message = await prisma?.message.create({\n// data: {\n// content: \"GENERATING_CONTENT\",\n// senderId: getAIUserId(),\n// conversationId: labChatId,\n// status: JobStatus.PENDING,\n// },\n// });\n\n// try {\n\n// inference(`\n// `)\n// }\n \n// };\n\n\n/**\n * Fill the AI introduction placeholder for a brand-new lab chat.\n *\n * The caller (service layer) must have already created a PENDING AI Message\n * row — `aiMessageId` is that placeholder. This function flips it to\n * COMPLETED with the intro content, or to FAILED on a hard error.\n */\nexport const generateAndSendLabIntroduction = async (\n labChatId: string,\n conversationId: string,\n _contextString: string,\n _subject: string,\n aiMessageId: string,\n ): Promise<void> => {\n const introMessage =\n \"Hello teaching team! I'm your AI assistant for course material development. I'll help you create educational content - when you have a clear request, I'll produce it directly. I only ask questions when I need to clarify the topic or scope. What would you like to work on?\";\n\n try {\n await completePendingAIMessage(aiMessageId, introMessage);\n logger.info('AI Introduction sent', { labChatId, conversationId, aiMessageId });\n } catch (error) {\n logger.error('Failed to complete AI introduction placeholder:', {\n error,\n labChatId,\n aiMessageId,\n });\n await failPendingAIMessage(aiMessageId, { reason: 'lab_intro_complete_failed' });\n }\n }\n \n /**\n * Fill the AI response placeholder for a teacher reply in a lab chat.\n *\n * `aiMessageId` is the PENDING AI Message row created by the service\n * layer; the pipeline flips it to COMPLETED with content / attachments /\n * meta, or FAILED on error. Teacher-channel `lab-response-{pending,\n * completed,failed}` events fire alongside the chat-channel status flips\n * (which `completePendingAIMessage` / `failPendingAIMessage` already\n * broadcast) so the lab list \"AI busy\" indicator stays accurate.\n *\n * `_teacherMessage` is retained for caller compatibility — the actual\n * conversation history is reloaded from the DB anchored on the AI\n * placeholder.\n */\n export const generateAndSendLabResponse = async (\n labChatId: string,\n _teacherMessage: string,\n emitOptions: { classId: string; aiMessageId: string }\n ): Promise<void> => {\n try {\n // Get lab context from database\n const fullLabChat = await prisma.labChat.findUnique({\n where: { id: labChatId },\n include: {\n class: {\n select: {\n name: true,\n subject: true,\n },\n },\n },\n });\n \n if (!fullLabChat) {\n throw new Error('Lab chat not found');\n }\n \n const conversationId = fullLabChat.conversationId;\n \n // Anchor history on the AI placeholder — the slice will include the\n // placeholder itself (empty content) at the top; drop it so the model\n // does not see an empty assistant turn at the very end.\n const anchoredMessages = await loadRecentLabChatMessages(\n conversationId,\n emitOptions.aiMessageId,\n );\n const recentMessages = anchoredMessages.filter(\n (msg) => msg.id !== emitOptions.aiMessageId,\n );\n \n const classData = await prisma.class.findUnique({\n where: {\n id: fullLabChat.classId,\n },\n include: {\n assignments: {\n include: {\n section: { select: { id: true, name: true, order: true } },\n markScheme: { select: { id: true } },\n gradingBoundary: { select: { id: true } },\n },\n },\n sections: true,\n markSchemes: { select: { id: true, structured: true } },\n gradingBoundaries: { select: { id: true, structured: true } },\n worksheets: {\n select: {\n id: true,\n name: true,\n _count: { select: { questions: true } },\n },\n },\n students: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n teachers: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n classFiles: {\n include: {\n files: true,\n },\n },\n },\n });\n\n if (!classData) {\n throw new Error('Class not found');\n }\n\n const classContext = buildClassContextForAI({\n class: classData,\n sections: classData.sections,\n markSchemes: classData.markSchemes,\n gradingBoundaries: classData.gradingBoundaries,\n worksheets: classData.worksheets.map((w) => ({\n id: w.id,\n name: w.name,\n questionCount: w._count.questions,\n })),\n files: classData.classFiles?.files ?? [],\n students: classData.students,\n teachers: classData.teachers,\n assignments: classData.assignments,\n });\n\n const messages = buildLabChatResponseMessages({\n context: fullLabChat.context,\n classContext,\n recentMessages: recentMessages.reverse(),\n isAIUser,\n });\n \n \n // const completion = await inferenceClient.chat.completions.create({\n // model: 'command-a-03-2025',\n // messages,\n // temperature: 0.7,\n // response_format: zodTextFormat(labChatResponseSchema, \"lab_chat_response_format\"),\n // });\n\n const response = await withTimeout(\n inference<LabChatResponse>(messages, labChatResponseSchema, {\n reason: 'lab_chat_response',\n metadata: {\n labChatId,\n conversationId,\n classId: fullLabChat.classId,\n aiMessageId: emitOptions.aiMessageId,\n },\n }),\n LAB_CHAT_RESPONSE_TIMEOUT_MS,\n \"Lab chat response generation\",\n );\n \n if (!response) {\n throw new Error('No response generated from inference API');\n }\n // Parse the JSON response and generate PDF if docs are provided\n try {\n const jsonData = response;\n \n \n const attachmentIds: string[] = [];\n // Generate PDFs if docs are provided\n if (jsonData.docs && Array.isArray(jsonData.docs)) {\n \n \n for (let i = 0; i < jsonData.docs.length; i++) {\n const doc = jsonData.docs[i];\n if (!doc.title || !doc.blocks || !Array.isArray(doc.blocks)) {\n logger.error(`Document ${i + 1} is missing title or blocks`);\n continue;\n } \n \n \n try {\n let pdfBytes = await createPdf(doc.blocks as DocumentBlock[]); \n if (pdfBytes) {\n // Sanitize filename - remove special characters and limit length\n const sanitizedTitle = doc.title\n .replace(/[^a-zA-Z0-9\\s\\-_]/g, '')\n .replace(/\\s+/g, '_')\n .substring(0, 50);\n \n const filename = `${sanitizedTitle}_${v4().substring(0, 8)}.pdf`;\n const filePath = `class/generated/${fullLabChat.classId}/${filename}`;\n \n logger.info(`PDF ${i + 1} generated successfully`, { labChatId, title: doc.title });\n \n // Upload directly to Google Cloud Storage\n const gcsFile = bucket.file(filePath);\n await gcsFile.save(Buffer.from(pdfBytes), {\n metadata: {\n contentType: 'application/pdf',\n }\n });\n \n logger.info(`PDF ${i + 1} uploaded successfully`, { labChatId, filename });\n \n const file = await prisma.file.create({\n data: {\n name: filename,\n path: filePath,\n type: 'application/pdf',\n size: pdfBytes.length,\n userId: fullLabChat.createdById,\n uploadStatus: 'COMPLETED',\n uploadedAt: new Date(),\n },\n });\n attachmentIds.push(file.id);\n } else {\n logger.error(`PDF ${i + 1} creation returned undefined/null`, { labChatId, title: doc.title });\n }\n } catch (pdfError) {\n logger.error(`PDF creation threw an error for document ${i + 1}:`, { \n error: pdfError instanceof Error ? {\n message: pdfError.message,\n stack: pdfError.stack,\n name: pdfError.name\n } : pdfError, \n labChatId,\n title: doc.title\n });\n }\n }\n }\n \n await completePendingAIMessage(emitOptions.aiMessageId, jsonData.text, {\n attachments: {\n connect: attachmentIds.map(id => ({ id })),\n },\n meta: {\n assignmentsToCreate: jsonData.assignmentsToCreate?.map(assignment => ({\n ...assignment,\n id: v4(),\n })) || null,\n worksheetsToCreate: jsonData.worksheetsToCreate?.map(worksheet => ({\n ...worksheet,\n id: v4(),\n })) || null,\n sectionsToCreate: jsonData.sectionsToCreate?.map(section => ({\n ...section,\n id: v4(),\n })) || null,\n },\n });\n } catch (parseError) {\n logger.error('Failed to parse AI response or generate PDF:', { error: parseError, labChatId });\n // Fallback: complete the placeholder with the raw text so the\n // teacher still sees something useful instead of FAILED.\n await completePendingAIMessage(emitOptions.aiMessageId, response.text);\n }\n\n try {\n await pusher.trigger(teacherChannel(emitOptions.classId), \"lab-response-completed\", {\n labChatId,\n messageId: emitOptions.aiMessageId,\n });\n } catch (broadcastError) {\n logger.error(\"Failed to broadcast lab response completed:\", { error: broadcastError });\n }\n\n logger.info('AI response sent', { labChatId, conversationId, aiMessageId: emitOptions.aiMessageId });\n\n } catch (error) {\n console.error('Full error object:', error);\n logger.error('Failed to generate AI response:', {\n error: error instanceof Error ? {\n message: error.message,\n stack: error.stack,\n name: error.name\n } : error,\n labChatId,\n aiMessageId: emitOptions.aiMessageId,\n });\n\n await failPendingAIMessage(emitOptions.aiMessageId, {\n reason: 'lab_response_generation_failed',\n });\n\n try {\n await pusher.trigger(teacherChannel(emitOptions.classId), \"lab-response-failed\", {\n labChatId,\n messageId: emitOptions.aiMessageId,\n error: \"AI response generation failed\",\n });\n } catch (broadcastError) {\n logger.error(\"Failed to broadcast lab response failed:\", { error: broadcastError });\n }\n\n throw error; // Re-throw to see the full error in the calling function\n }\n }\n"],"names":[],"mappings":"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","debug_id":"e57f7401-27fd-5eac-a76a-cb84f699b776"}
1
+ {"version":3,"file":"aiLabChat.js","sources":["pipelines/aiLabChat.ts"],"sourceRoot":"/","sourcesContent":["/**\n * AI lab chat pipeline – generates lab introductions and responses.\n * Can create worksheets, sections, assignments, and generated files from AI output.\n */\nimport { isAIUser } from \"../utils/aiUser.js\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { pusher, chatChannel, teacherChannel } from \"../lib/pusher.js\";\nimport type { Assignment, Class, File, Section, User } from \"@prisma/client\";\nimport {\n completePendingAIMessage,\n failPendingAIMessage,\n} from \"../utils/inference.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { v4 } from \"uuid\";\nimport { bucket, getSignedUrl } from \"../lib/googleCloudStorage.js\";\nimport {\n generateDocumentWithOpenAI,\n normalizeDocumentFormat,\n} from \"../services/openaiDocumentGen.js\";\nimport { inferDocumentFormat } from \"../lib/inferDocumentFormat.js\";\nimport { broadcastTextChunks, broadcastStreamComplete } from \"../utils/streamBroadcast.js\";\nimport { buildLabChatAgentMessages } from \"./labChatPrompt.js\";\nimport type { ConversationMessage } from \"./labChatPrompt.js\";\nimport {\n applyResolvedFormatsToStreamParts,\n enrichStreamPartsWithGeneratedFiles,\n finalizeDeferredToolParts,\n streamChatResponse,\n type GeneratedDocFileLink,\n type StreamPart,\n} from \"../services/aiChatStream.js\";\nimport { aiTools } from \"../services/aiTools.js\";\nimport { labProposalTools, createLabProposals } from \"../services/aiProposalTools.js\";\n\n/** Cap model wait time; leave headroom after inference for PDF/GCS/DB within Vercel maxDuration (500s). */\nconst LAB_CHAT_RESPONSE_TIMEOUT_MS = 400_000;\n\nconst withTimeout = async <T>(\n task: Promise<T>,\n timeoutMs: number,\n operationName: string,\n): Promise<T> => {\n let timeoutHandle: NodeJS.Timeout | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n reject(new Error(`${operationName} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([task, timeoutPromise]);\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n};\n\n/** Extended class data for AI context (schema-aware) */\ntype ClassContextData = {\n class: Class;\n sections: Section[];\n markSchemes: { id: string; structured: string }[];\n gradingBoundaries: { id: string; structured: string }[];\n worksheets: { id: string; name: string; questionCount: number }[];\n files: File[];\n students: { id: string; username: string; profile?: { displayName: string | null } | null }[];\n teachers: { id: string; username: string; profile?: { displayName: string | null } | null }[];\n assignments: (Assignment & {\n section?: { id: string; name: string; order?: number | null } | null;\n markScheme?: { id: string } | null;\n gradingBoundary?: { id: string } | null;\n })[];\n};\n\ntype RecentLabChatMessage = {\n id: string;\n content: string;\n senderId: string;\n createdAt: Date;\n sender: {\n id: string;\n username: string | null;\n profile: {\n displayName: string | null;\n } | null;\n } | null;\n attachments: {\n id: string;\n name: string;\n type: string;\n path: string;\n }[];\n};\n\n/**\n * `messages` must be ordered newest-first.\n * When `anchorMessageId` is provided, `sliceMessagesThroughAnchor` returns the\n * anchor message and older messages only, capped to `limit`.\n */\nexport const sliceMessagesThroughAnchor = (\n messages: RecentLabChatMessage[],\n anchorMessageId?: string,\n limit = 10,\n) => {\n if (!anchorMessageId) {\n return messages.slice(0, limit);\n }\n\n const anchorIndex = messages.findIndex((message) => message.id === anchorMessageId);\n if (anchorIndex === -1) {\n return messages.slice(0, limit);\n }\n\n return messages.slice(anchorIndex, anchorIndex + limit);\n};\n\nconst loadRecentLabChatMessages = async (\n conversationId: string,\n anchorMessageId?: string,\n): Promise<RecentLabChatMessage[]> => {\n const limit = 10;\n const baseQuery = {\n conversationId,\n };\n const include = {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n attachments: {\n select: {\n id: true,\n name: true,\n type: true,\n path: true,\n },\n },\n };\n\n const newestMessages = await prisma.message.findMany({\n where: baseQuery,\n include,\n orderBy: {\n createdAt: 'desc',\n },\n take: 25,\n });\n\n const anchoredMessages = sliceMessagesThroughAnchor(newestMessages, anchorMessageId, limit);\n if (!anchorMessageId || anchoredMessages.some((message) => message.id === anchorMessageId)) {\n return anchoredMessages;\n }\n\n const anchorMessage = await prisma.message.findUnique({\n where: { id: anchorMessageId },\n include,\n });\n\n if (!anchorMessage) {\n throw new Error(`Anchor message ${anchorMessageId} not found`);\n }\n\n if (anchorMessage.conversationId !== conversationId) {\n throw new Error(`Anchor message ${anchorMessageId} does not belong to conversation ${conversationId}`);\n }\n\n const olderMessages = await prisma.message.findMany({\n where: {\n conversationId,\n createdAt: {\n lte: anchorMessage.createdAt,\n },\n },\n include,\n orderBy: {\n createdAt: 'desc',\n },\n take: limit,\n });\n\n const dedupedMessages = new Map<string, RecentLabChatMessage>();\n dedupedMessages.set(anchorMessage.id, anchorMessage);\n\n olderMessages.forEach((message) => {\n if (!dedupedMessages.has(message.id)) {\n dedupedMessages.set(message.id, message);\n }\n });\n\n return Array.from(dedupedMessages.values())\n .sort((left, right) => {\n const timeDelta = right.createdAt.getTime() - left.createdAt.getTime();\n if (timeDelta !== 0) {\n return timeDelta;\n }\n if (left.id === anchorMessage.id) {\n return -1;\n }\n if (right.id === anchorMessage.id) {\n return 1;\n }\n\n return right.id.localeCompare(left.id);\n })\n .slice(0, limit);\n};\n\n/**\n * Resolves signed URLs for image attachments in messages so the LLM can see them.\n * Non-image files are passed as text descriptions only.\n */\nasync function resolveAttachmentUrls(\n messages: RecentLabChatMessage[],\n): Promise<ConversationMessage[]> {\n const resolved: ConversationMessage[] = [];\n\n for (const msg of messages) {\n const attachments = msg.attachments ?? [];\n if (attachments.length === 0) {\n resolved.push({ ...msg, attachments: [] });\n continue;\n }\n\n const withUrls = await Promise.all(\n attachments.map(async (a) => {\n if (a.type.startsWith('image/')) {\n try {\n const url = await getSignedUrl(a.path);\n return { id: a.id, name: a.name, type: a.type, url };\n } catch {\n return { id: a.id, name: a.name, type: a.type };\n }\n }\n return { id: a.id, name: a.name, type: a.type };\n }),\n );\n\n resolved.push({ ...msg, attachments: withUrls });\n }\n\n return resolved;\n}\n\n/**\n * Builds schema-aware context for the AI from class data.\n * Formats entities with IDs so the model can reference them when creating assignments.\n */\nexport const buildClassContextForAI = (data: ClassContextData): string => {\n const { class: cls, sections, markSchemes, gradingBoundaries, worksheets, files, students, assignments } = data;\n\n const sectionList = sections\n .sort((a, b) => (a.order ?? 999) - (b.order ?? 999))\n .map((s) => ` - id: ${s.id} | name: \"${s.name}\" | color: ${s.color ?? \"default\"}`)\n .join(\"\\n\");\n\n const markSchemeList = markSchemes\n .map((ms) => {\n let preview = \"structured rubric\";\n try {\n const parsed = JSON.parse(ms.structured || \"{}\");\n preview = parsed.name || Object.keys(parsed).slice(0, 2).join(\", \") || \"rubric\";\n } catch {\n /* ignore */\n }\n return ` - id: ${ms.id} | ${preview}`;\n })\n .join(\"\\n\");\n\n const gradingBoundaryList = gradingBoundaries\n .map((gb) => {\n let preview = \"grading scale\";\n try {\n const parsed = JSON.parse(gb.structured || \"{}\");\n preview = parsed.name || Object.keys(parsed).slice(0, 2).join(\", \") || \"scale\";\n } catch {\n /* ignore */\n }\n return ` - id: ${gb.id} | ${preview}`;\n })\n .join(\"\\n\");\n\n const worksheetList = worksheets\n .map((w) => ` - id: ${w.id} | name: \"${w.name}\" | questions: ${w.questionCount}`)\n .join(\"\\n\");\n\n const fileList = files\n .filter((f) => f.type === \"application/pdf\" || f.type?.includes(\"document\"))\n .map((f) => ` - id: ${f.id} | name: \"${f.name}\" | type: ${f.type}`)\n .join(\"\\n\");\n const otherFiles = files.filter((f) => f.type !== \"application/pdf\" && !f.type?.includes(\"document\"));\n const otherFileList = otherFiles.length\n ? otherFiles.map((f) => ` - id: ${f.id} | name: \"${f.name}\"`).join(\"\\n\")\n : \" (none)\";\n\n const studentList = students\n .map((u) => ` - id: ${u.id} | username: ${u.username} | displayName: ${u.profile?.displayName ?? \"—\"}`)\n .join(\"\\n\");\n\n const assignmentSummary = assignments\n .map((a) => {\n const sectionName = a.section?.name ?? \"—\";\n return ` - id: ${a.id} | title: \"${a.title}\" | type: ${a.type} | section: \"${sectionName}\" | due: ${a.dueDate.toISOString().slice(0, 10)}`;\n })\n .join(\"\\n\");\n\n return `\nCLASS: ${cls.name} | Subject: ${cls.subject} | Section: ${cls.section}\nSyllabus: ${cls.syllabus ? cls.syllabus.slice(0, 200) + (cls.syllabus.length > 200 ? \"…\" : \"\") : \"(none)\"}\n\nSECTIONS (use sectionId when creating assignments):\n${sectionList || \" (none - suggest sectionsToCreate first)\"}\n\nMARK SCHEMES (use markSchemeId when creating assignments):\n${markSchemeList || \" (none - suggest creating one or omit markSchemeId)\"}\n\nGRADING BOUNDARIES (use gradingBoundaryId when creating assignments):\n${gradingBoundaryList || \" (none - suggest creating one or omit gradingBoundaryId)\"}\n\nWORKSHEETS (use worksheetIds when acceptWorksheet is true):\n${worksheetList || \" (none - use worksheetsToCreate or create via docs first)\"}\n\nFILES - PDFs/Documents (for assignment attachments):\n${fileList || \" (none)\"}\n\nFILES - Other (for assignment attachments):\n${otherFileList}\n\nSTUDENTS (use studentIds for specific assignment; empty array = all students):\n${studentList || \" (none)\"}\n\nEXISTING ASSIGNMENTS (for reference, avoid duplicates):\n${assignmentSummary || \" (none)\"}\n`.trim();\n};\n\n/**\n * @deprecated Use buildClassContextForAI for schema-aware context. Kept for compatibility.\n */\nexport const getBaseSystemPrompt = (\n context: Class,\n members: User[],\n assignments: Assignment[],\n files: File[],\n sections: Section[]\n): string => {\n return buildClassContextForAI({\n class: context,\n sections,\n markSchemes: [],\n gradingBoundaries: [],\n worksheets: [],\n files,\n students: members,\n teachers: [],\n assignments,\n });\n};\n\n\n\n/**\n * Fill the AI introduction placeholder for a brand-new lab chat.\n *\n * The caller (service layer) must have already created a PENDING AI Message\n * row — `aiMessageId` is that placeholder. This function flips it to\n * COMPLETED with the intro content, or to FAILED on a hard error.\n */\nexport const generateAndSendLabIntroduction = async (\n labChatId: string,\n conversationId: string,\n _contextString: string,\n _subject: string,\n aiMessageId: string,\n ): Promise<void> => {\n const introMessage =\n \"Hello teaching team! I'm your AI assistant for course material development. I can create worksheets, assignments, and downloadable files — PDFs, Word docs, PowerPoints, spreadsheets, and more. When you have a clear request, I'll produce it directly. I only ask questions when I need to clarify the topic or scope. What would you like to work on?\";\n\n try {\n await broadcastTextChunks(conversationId, aiMessageId, introMessage);\n await completePendingAIMessage(aiMessageId, introMessage);\n logger.info('AI Introduction sent', { labChatId, conversationId, aiMessageId });\n } catch (error) {\n logger.error('Failed to complete AI introduction placeholder:', {\n error,\n labChatId,\n aiMessageId,\n });\n await failPendingAIMessage(aiMessageId, { reason: 'lab_intro_complete_failed' });\n }\n }\n \n /**\n * Fill the AI response placeholder for a teacher reply in a lab chat.\n *\n * `aiMessageId` is the PENDING AI Message row created by the service\n * layer; the pipeline flips it to COMPLETED with content / attachments /\n * meta, or FAILED on error. Teacher-channel `lab-response-{pending,\n * completed,failed}` events fire alongside the chat-channel status flips\n * (which `completePendingAIMessage` / `failPendingAIMessage` already\n * broadcast) so the lab list \"AI busy\" indicator stays accurate.\n *\n * `_teacherMessage` is retained for caller compatibility — the actual\n * conversation history is reloaded from the DB anchored on the AI\n * placeholder.\n */\n export const generateAndSendLabResponse = async (\n labChatId: string,\n teacherMessage: string,\n emitOptions: { classId: string; aiMessageId: string }\n ): Promise<void> => {\n try {\n // Get lab context from database\n const fullLabChat = await prisma.labChat.findUnique({\n where: { id: labChatId },\n include: {\n class: {\n select: {\n name: true,\n subject: true,\n },\n },\n },\n });\n \n if (!fullLabChat) {\n throw new Error('Lab chat not found');\n }\n \n const conversationId = fullLabChat.conversationId;\n \n // Anchor history on the AI placeholder — the slice will include the\n // placeholder itself (empty content) at the top; drop it so the model\n // does not see an empty assistant turn at the very end.\n const anchoredMessages = await loadRecentLabChatMessages(\n conversationId,\n emitOptions.aiMessageId,\n );\n const recentMessages = anchoredMessages.filter(\n (msg) => msg.id !== emitOptions.aiMessageId,\n );\n \n const classData = await prisma.class.findUnique({\n where: {\n id: fullLabChat.classId,\n },\n include: {\n assignments: {\n include: {\n section: { select: { id: true, name: true, order: true } },\n markScheme: { select: { id: true } },\n gradingBoundary: { select: { id: true } },\n },\n },\n sections: true,\n markSchemes: { select: { id: true, structured: true } },\n gradingBoundaries: { select: { id: true, structured: true } },\n worksheets: {\n select: {\n id: true,\n name: true,\n _count: { select: { questions: true } },\n },\n },\n students: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n teachers: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n classFiles: {\n include: {\n files: true,\n },\n },\n },\n });\n\n if (!classData) {\n throw new Error('Class not found');\n }\n\n const classContext = buildClassContextForAI({\n class: classData,\n sections: classData.sections,\n markSchemes: classData.markSchemes,\n gradingBoundaries: classData.gradingBoundaries,\n worksheets: classData.worksheets.map((w) => ({\n id: w.id,\n name: w.name,\n questionCount: w._count.questions,\n })),\n files: classData.classFiles?.files ?? [],\n students: classData.students,\n teachers: classData.teachers,\n assignments: classData.assignments,\n });\n\n // Resolve signed URLs for image attachments so the LLM can see them\n const messagesWithAttachments = await resolveAttachmentUrls(recentMessages.reverse());\n\n const { system, messages } = buildLabChatAgentMessages({\n context: fullLabChat.context,\n classContext,\n recentMessages: messagesWithAttachments,\n isAIUser,\n });\n\n // Genuine agentic generation: the model streams prose token-by-token and\n // calls real tools inline — read-only RAG lookups plus \"propose_*\" tools\n // that record create-actions into `proposals`. `streamChatResponse`\n // broadcasts the real text chunks and tool-call events as they happen.\n const proposals = createLabProposals();\n const tools = {\n ...aiTools(fullLabChat.classId),\n ...labProposalTools(proposals),\n };\n\n const { text: streamedText, parts: rawStreamParts } = await withTimeout(\n streamChatResponse({\n conversationId,\n aiMessageId: emitOptions.aiMessageId,\n system,\n messages,\n tools,\n temperature: 0.7,\n maxOutputTokens: 4096,\n maxSteps: 8,\n }),\n LAB_CHAT_RESPONSE_TIMEOUT_MS,\n \"Lab chat response generation\",\n );\n\n // Real text + tool-call events were already broadcast inline (in order)\n // during the stream by `streamChatResponse`. Just close the stream.\n await broadcastStreamComplete(conversationId, emitOptions.aiMessageId);\n\n // `generate_document` is an inline tool that only QUEUES into `proposals.docs`\n // during the stream. OpenAI Code Interpreter renders files here, after the\n // stream ends, then we link them to this message.\n try {\n const attachmentIds: string[] = [];\n const generatedDocFiles: GeneratedDocFileLink[] = [];\n const resolvedDocs: Array<{ title: string; format: string }> = [];\n\n for (let i = 0; i < proposals.docs.length; i++) {\n const doc = proposals.docs[i];\n if (!doc.title || !doc.content?.trim()) {\n logger.error(`Document ${i + 1} is missing title or content`, { labChatId, doc });\n continue;\n }\n\n const format = inferDocumentFormat(\n doc.title,\n doc.content,\n doc.format,\n teacherMessage,\n );\n resolvedDocs.push({ title: doc.title, format });\n\n if (format !== normalizeDocumentFormat(doc.format)) {\n logger.info('Document format overridden from model default', {\n labChatId,\n title: doc.title,\n modelFormat: doc.format,\n resolvedFormat: format,\n });\n }\n\n try {\n const generated = await generateDocumentWithOpenAI({\n title: doc.title,\n content: doc.content,\n format,\n });\n if (!generated) {\n logger.error(`Document ${i + 1} generation returned null`, {\n labChatId,\n title: doc.title,\n format,\n });\n continue;\n }\n\n const sanitizedTitle = doc.title\n .replace(/[^a-zA-Z0-9\\s\\-_]/g, '')\n .replace(/\\s+/g, '_')\n .substring(0, 50);\n const filename = `${sanitizedTitle}_${v4().substring(0, 8)}.${generated.format}`;\n const filePath = `class/generated/${fullLabChat.classId}/${filename}`;\n\n logger.info(`Document ${i + 1} generated successfully`, {\n labChatId,\n title: doc.title,\n format: generated.format,\n });\n\n await bucket.file(filePath).save(generated.bytes, {\n metadata: { contentType: generated.mimeType },\n });\n\n logger.info(`Document ${i + 1} uploaded successfully`, { labChatId, filename });\n\n const file = await prisma.file.create({\n data: {\n name: filename,\n path: filePath,\n type: generated.mimeType,\n size: generated.bytes.length,\n userId: fullLabChat.createdById,\n conversationId,\n messageId: emitOptions.aiMessageId,\n uploadStatus: 'COMPLETED',\n uploadedAt: new Date(),\n },\n });\n\n attachmentIds.push(file.id);\n generatedDocFiles.push({\n title: doc.title,\n fileId: file.id,\n fileName: filename,\n format: generated.format,\n });\n } catch (docError) {\n logger.error(`Document generation failed for item ${i + 1}:`, {\n error: docError instanceof Error ? {\n message: docError.message,\n stack: docError.stack,\n name: docError.name,\n } : docError,\n labChatId,\n title: doc.title,\n format: doc.format,\n });\n }\n }\n\n const streamParts: StreamPart[] = finalizeDeferredToolParts(\n enrichStreamPartsWithGeneratedFiles(\n applyResolvedFormatsToStreamParts(rawStreamParts, resolvedDocs),\n generatedDocFiles,\n ),\n );\n\n // While the message is still PENDING, resolve deferred tool indicators\n // (spinner → attachment or error) before the final persist event.\n for (const part of streamParts) {\n if (part.type !== 'tool' || part.toolName !== 'generate_document') {\n continue;\n }\n if (part.status === 'executing') continue;\n await pusher\n .trigger(chatChannel(conversationId), 'message-tool-call', {\n messageId: emitOptions.aiMessageId,\n toolCallId: part.toolCallId,\n toolName: part.toolName,\n ...(part.title ? { title: part.title } : {}),\n ...(part.format ? { format: part.format } : {}),\n ...(part.fileId ? { fileId: part.fileId, fileName: part.fileName } : {}),\n status: part.status,\n })\n .catch(() => {});\n }\n\n await completePendingAIMessage(emitOptions.aiMessageId, streamedText, {\n ...(attachmentIds.length > 0 && {\n attachments: { connect: attachmentIds.map((id) => ({ id })) },\n }),\n meta: {\n assignmentsToCreate: proposals.assignments.length > 0\n ? proposals.assignments.map(assignment => ({ ...assignment, id: v4() }))\n : null,\n worksheetsToCreate: proposals.worksheets.length > 0\n ? proposals.worksheets.map(worksheet => ({ ...worksheet, id: v4() }))\n : null,\n sectionsToCreate: proposals.sections.length > 0\n ? proposals.sections.map(section => ({ ...section, id: v4() }))\n : null,\n streamParts: streamParts.length > 0 ? streamParts : null,\n },\n });\n } catch (persistError) {\n logger.error('Failed to persist AI response or generate PDF:', { error: persistError, labChatId });\n // Fallback: complete the placeholder with the raw text so the\n // teacher still sees something useful instead of FAILED.\n await completePendingAIMessage(emitOptions.aiMessageId, streamedText);\n }\n\n try {\n await pusher.trigger(teacherChannel(emitOptions.classId), \"lab-response-completed\", {\n labChatId,\n messageId: emitOptions.aiMessageId,\n });\n } catch (broadcastError) {\n logger.error(\"Failed to broadcast lab response completed:\", { error: broadcastError });\n }\n\n logger.info('AI response sent', { labChatId, conversationId, aiMessageId: emitOptions.aiMessageId });\n\n } catch (error) {\n console.error('Full error object:', error);\n logger.error('Failed to generate AI response:', {\n error: error instanceof Error ? {\n message: error.message,\n stack: error.stack,\n name: error.name\n } : error,\n labChatId,\n aiMessageId: emitOptions.aiMessageId,\n });\n\n await failPendingAIMessage(emitOptions.aiMessageId, {\n reason: 'lab_response_generation_failed',\n });\n\n try {\n await pusher.trigger(teacherChannel(emitOptions.classId), \"lab-response-failed\", {\n labChatId,\n messageId: emitOptions.aiMessageId,\n error: \"AI response generation failed\",\n });\n } catch (broadcastError) {\n logger.error(\"Failed to broadcast lab response failed:\", { error: broadcastError });\n }\n\n throw error; // Re-throw to see the full error in the calling function\n }\n }\n"],"names":[],"mappings":"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","debug_id":"976e04f3-b470-5bcb-afeb-1df2af403427"}