@studious-lms/server 1.5.0 → 1.7.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 (141) hide show
  1. package/dist/index.js +15 -3
  2. package/dist/index.js.map +1 -1
  3. package/dist/lib/config/cors.d.ts.map +1 -1
  4. package/dist/lib/config/cors.js +3 -2
  5. package/dist/lib/config/cors.js.map +1 -1
  6. package/dist/lib/pusher.d.ts +7 -1
  7. package/dist/lib/pusher.d.ts.map +1 -1
  8. package/dist/lib/pusher.js +9 -3
  9. package/dist/lib/pusher.js.map +1 -1
  10. package/dist/models/attendance.d.ts +16 -7
  11. package/dist/models/attendance.d.ts.map +1 -1
  12. package/dist/models/attendance.js +17 -2
  13. package/dist/models/attendance.js.map +1 -1
  14. package/dist/models/conversation.d.ts +33 -17
  15. package/dist/models/conversation.d.ts.map +1 -1
  16. package/dist/models/conversation.js +50 -2
  17. package/dist/models/conversation.js.map +1 -1
  18. package/dist/pipelines/aiLabChat.d.ts +20 -9
  19. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  20. package/dist/pipelines/aiLabChat.js +62 -74
  21. package/dist/pipelines/aiLabChat.js.map +1 -1
  22. package/dist/pipelines/aiNewtonChat.d.ts +13 -4
  23. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
  24. package/dist/pipelines/aiNewtonChat.js +39 -27
  25. package/dist/pipelines/aiNewtonChat.js.map +1 -1
  26. package/dist/pipelines/pdfToWorksheet.d.ts +31 -2
  27. package/dist/pipelines/pdfToWorksheet.d.ts.map +1 -1
  28. package/dist/pipelines/pdfToWorksheet.js +160 -28
  29. package/dist/pipelines/pdfToWorksheet.js.map +1 -1
  30. package/dist/routers/_app.d.ts +48 -4
  31. package/dist/routers/_app.d.ts.map +1 -1
  32. package/dist/routers/assignment.d.ts.map +1 -1
  33. package/dist/routers/assignment.js +64 -4
  34. package/dist/routers/assignment.js.map +1 -1
  35. package/dist/routers/conversation.d.ts +15 -0
  36. package/dist/routers/conversation.d.ts.map +1 -1
  37. package/dist/routers/conversation.js +6 -3
  38. package/dist/routers/conversation.js.map +1 -1
  39. package/dist/routers/labChat.d.ts +1 -0
  40. package/dist/routers/labChat.d.ts.map +1 -1
  41. package/dist/routers/message.d.ts +3 -0
  42. package/dist/routers/message.d.ts.map +1 -1
  43. package/dist/routers/newtonChat.d.ts +1 -0
  44. package/dist/routers/newtonChat.d.ts.map +1 -1
  45. package/dist/routers/worksheet.d.ts +4 -2
  46. package/dist/routers/worksheet.d.ts.map +1 -1
  47. package/dist/routers/worksheet.js +16 -6
  48. package/dist/routers/worksheet.js.map +1 -1
  49. package/dist/services/announcement.js +4 -4
  50. package/dist/services/announcement.js.map +1 -1
  51. package/dist/services/assignment.d.ts.map +1 -1
  52. package/dist/services/assignment.js +16 -6
  53. package/dist/services/assignment.js.map +1 -1
  54. package/dist/services/attendance.d.ts.map +1 -1
  55. package/dist/services/attendance.js +68 -4
  56. package/dist/services/attendance.js.map +1 -1
  57. package/dist/services/conversation.d.ts +16 -0
  58. package/dist/services/conversation.d.ts.map +1 -1
  59. package/dist/services/conversation.js +22 -3
  60. package/dist/services/conversation.js.map +1 -1
  61. package/dist/services/labChat.d.ts +8 -1
  62. package/dist/services/labChat.d.ts.map +1 -1
  63. package/dist/services/labChat.js +101 -42
  64. package/dist/services/labChat.js.map +1 -1
  65. package/dist/services/message.d.ts +3 -0
  66. package/dist/services/message.d.ts.map +1 -1
  67. package/dist/services/message.js +8 -2
  68. package/dist/services/message.js.map +1 -1
  69. package/dist/services/newtonChat.d.ts +1 -0
  70. package/dist/services/newtonChat.d.ts.map +1 -1
  71. package/dist/services/newtonChat.js +40 -17
  72. package/dist/services/newtonChat.js.map +1 -1
  73. package/dist/services/worksheet.d.ts +15 -6
  74. package/dist/services/worksheet.d.ts.map +1 -1
  75. package/dist/services/worksheet.js +58 -12
  76. package/dist/services/worksheet.js.map +1 -1
  77. package/dist/utils/inference.d.ts +50 -1
  78. package/dist/utils/inference.d.ts.map +1 -1
  79. package/dist/utils/inference.js +167 -2
  80. package/dist/utils/inference.js.map +1 -1
  81. package/dist/workers/index.js +13 -3
  82. package/dist/workers/index.js.map +1 -1
  83. package/dist/workers/processors/aiLabChat.d.ts +7 -0
  84. package/dist/workers/processors/aiLabChat.d.ts.map +1 -0
  85. package/dist/workers/processors/aiLabChat.js +52 -0
  86. package/dist/workers/processors/aiLabChat.js.map +1 -0
  87. package/dist/workers/processors/aiNewtonChat.d.ts +7 -0
  88. package/dist/workers/processors/aiNewtonChat.d.ts.map +1 -0
  89. package/dist/workers/processors/aiNewtonChat.js +57 -0
  90. package/dist/workers/processors/aiNewtonChat.js.map +1 -0
  91. package/dist/workers/processors/createRunJobWorker.d.ts +46 -0
  92. package/dist/workers/processors/createRunJobWorker.d.ts.map +1 -0
  93. package/dist/workers/processors/createRunJobWorker.js +118 -0
  94. package/dist/workers/processors/createRunJobWorker.js.map +1 -0
  95. package/dist/workers/processors/gradeWorksheet.d.ts +6 -0
  96. package/dist/workers/processors/gradeWorksheet.d.ts.map +1 -0
  97. package/dist/workers/processors/gradeWorksheet.js +19 -0
  98. package/dist/workers/processors/gradeWorksheet.js.map +1 -0
  99. package/dist/workers/processors/pdfToWorksheet.d.ts +5 -6
  100. package/dist/workers/processors/pdfToWorksheet.d.ts.map +1 -1
  101. package/dist/workers/processors/pdfToWorksheet.js +15 -95
  102. package/dist/workers/processors/pdfToWorksheet.js.map +1 -1
  103. package/dist/workers/processors/studentProgressChat.d.ts +8 -0
  104. package/dist/workers/processors/studentProgressChat.d.ts.map +1 -0
  105. package/dist/workers/processors/studentProgressChat.js +27 -0
  106. package/dist/workers/processors/studentProgressChat.js.map +1 -0
  107. package/dist/workers/queues.d.ts +20 -4
  108. package/dist/workers/queues.d.ts.map +1 -1
  109. package/dist/workers/queues.js +3 -3
  110. package/dist/workers/queues.js.map +1 -1
  111. package/package.json +3 -3
  112. package/src/index.ts +12 -1
  113. package/src/lib/config/cors.ts +1 -0
  114. package/src/lib/pusher.ts +7 -1
  115. package/src/models/attendance.ts +15 -0
  116. package/src/models/conversation.ts +54 -0
  117. package/src/pipelines/aiLabChat.ts +70 -78
  118. package/src/pipelines/aiNewtonChat.ts +47 -31
  119. package/src/pipelines/pdfToWorksheet.ts +224 -32
  120. package/src/routers/assignment.ts +74 -3
  121. package/src/routers/conversation.ts +7 -0
  122. package/src/routers/worksheet.ts +25 -4
  123. package/src/services/announcement.ts +2 -2
  124. package/src/services/assignment.ts +17 -4
  125. package/src/services/attendance.ts +87 -2
  126. package/src/services/conversation.ts +30 -0
  127. package/src/services/labChat.ts +106 -46
  128. package/src/services/message.ts +6 -0
  129. package/src/services/newtonChat.ts +45 -25
  130. package/src/services/worksheet.ts +71 -10
  131. package/src/utils/inference.ts +209 -1
  132. package/src/workers/index.ts +11 -1
  133. package/src/workers/processors/aiLabChat.ts +65 -0
  134. package/src/workers/processors/aiNewtonChat.ts +75 -0
  135. package/src/workers/processors/createRunJobWorker.ts +164 -0
  136. package/src/workers/processors/gradeWorksheet.ts +16 -0
  137. package/src/workers/processors/pdfToWorksheet.ts +12 -114
  138. package/src/workers/processors/studentProgressChat.ts +28 -0
  139. package/src/workers/queues.ts +20 -5
  140. package/tests/integration/routers/attendance.test.ts +60 -1
  141. package/tests/integration/routers/conversation.test.ts +121 -2
@@ -2,7 +2,7 @@
2
2
  * Conversation model – DM/group conversations, members, unread counts.
3
3
  */
4
4
 
5
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7ea2e02b-0f12-5407-9926-5fab4c199704")}catch(e){}}();
5
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="14c80725-5a2b-5e37-aa01-49cb94535659")}catch(e){}}();
6
6
  import { prisma } from "../lib/prisma.js";
7
7
  const conversationListInclude = {
8
8
  labChat: { select: { id: true, title: true } },
@@ -73,6 +73,54 @@ export function findUserByUsername(username) {
73
73
  select: { id: true, username: true },
74
74
  });
75
75
  }
76
+ /**
77
+ * Search users who share at least one active school membership with the
78
+ * requester and match the query by username or profile display name.
79
+ *
80
+ * The requester is always excluded so the result set can be used directly as
81
+ * conversation member suggestions.
82
+ */
83
+ export async function searchInstitutionConversationMembers(userId, query, limit) {
84
+ const memberships = await prisma.schoolMembership.findMany({
85
+ where: { userId, status: "ACTIVE" },
86
+ select: { schoolId: true },
87
+ });
88
+ const schoolIds = memberships.map((membership) => membership.schoolId);
89
+ if (schoolIds.length === 0)
90
+ return [];
91
+ return prisma.user.findMany({
92
+ where: {
93
+ id: { not: userId },
94
+ schoolMemberships: {
95
+ some: {
96
+ schoolId: { in: schoolIds },
97
+ status: "ACTIVE",
98
+ },
99
+ },
100
+ OR: [
101
+ { username: { contains: query, mode: "insensitive" } },
102
+ {
103
+ profile: {
104
+ is: { displayName: { contains: query, mode: "insensitive" } },
105
+ },
106
+ },
107
+ ],
108
+ },
109
+ select: {
110
+ id: true,
111
+ username: true,
112
+ profile: {
113
+ select: {
114
+ displayName: true,
115
+ profilePicture: true,
116
+ profilePictureThumbnail: true,
117
+ },
118
+ },
119
+ },
120
+ orderBy: { username: "asc" },
121
+ take: limit,
122
+ });
123
+ }
76
124
  /** @returns User's DM conversations. */
77
125
  export function findDmConversationsByUserId(userId) {
78
126
  return prisma.conversation.findMany({
@@ -183,4 +231,4 @@ export function deleteConversationMember(userId, conversationId) {
183
231
  });
184
232
  }
185
233
  //# sourceMappingURL=conversation.js.map
186
- //# debugId=7ea2e02b-0f12-5407-9926-5fab4c199704
234
+ //# debugId=14c80725-5a2b-5e37-aa01-49cb94535659
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.js","sources":["models/conversation.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Conversation model – DM/group conversations, members, unread counts.\n */\nimport { prisma } from \"../lib/prisma.js\";\n\nconst conversationListInclude = {\n labChat: { select: { id: true, title: true } },\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n },\n },\n messages: {\n orderBy: { createdAt: \"desc\" as const },\n take: 1,\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n },\n },\n};\n\n/** @returns User's conversations with members and last message. */\nexport function findConversationsByUserId(userId: string) {\n return prisma.conversation.findMany({\n where: {\n members: { some: { userId } },\n },\n include: conversationListInclude,\n orderBy: { updatedAt: \"desc\" },\n });\n}\n\n/** Count unread messages in conversation for user. */\nexport function countUnreadMessages(\n conversationId: string,\n userId: string,\n after?: Date\n) {\n return prisma.message.count({\n where: {\n conversationId,\n senderId: { not: userId },\n ...(after && { createdAt: { gt: after } }),\n },\n });\n}\n\n/** Count unread mentions for user in conversation. */\nexport function countUnreadMentions(\n conversationId: string,\n userId: string,\n after?: Date\n) {\n return prisma.mention.count({\n where: {\n userId,\n message: {\n conversationId,\n senderId: { not: userId },\n ...(after && { createdAt: { gt: after } }),\n },\n },\n });\n}\n\n/** @returns User by username (id, username only). */\nexport function findUserByUsername(username: string) {\n return prisma.user.findFirst({\n where: { username },\n select: { id: true, username: true },\n });\n}\n\n/** @returns User's DM conversations. */\nexport function findDmConversationsByUserId(userId: string) {\n return prisma.conversation.findMany({\n where: {\n type: \"DM\",\n members: { some: { userId } },\n },\n include: {\n members: { select: { userId: true } },\n },\n });\n}\n\n/** @returns Users by IDs or usernames. */\nexport function findUsersByIdsOrUsernames(identifiers: string[]) {\n if (identifiers.length === 0) return Promise.resolve([]);\n return prisma.user.findMany({\n where: {\n OR: [\n { id: { in: identifiers } },\n { username: { in: identifiers } },\n ],\n },\n select: { id: true, username: true },\n });\n}\n\n/** Create a DM or group conversation with members. */\nexport function createConversation(data: {\n type: \"DM\" | \"GROUP\";\n name?: string;\n members: { userId: string; role: string }[];\n}) {\n return prisma.conversation.create({\n data: {\n type: data.type,\n name: data.name,\n members: {\n create: data.members.map((m) => ({\n userId: m.userId,\n role: m.role as \"ADMIN\" | \"MEMBER\",\n })),\n },\n },\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n },\n },\n },\n });\n}\n\n/** @returns Conversation if user is a member. */\nexport function findConversationByIdAndMember(\n conversationId: string,\n userId: string\n) {\n return prisma.conversation.findFirst({\n where: {\n id: conversationId,\n members: { some: { userId } },\n },\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n },\n },\n },\n });\n}\n\n/** Add a member to a conversation. */\nexport function createConversationMember(data: {\n userId: string;\n conversationId: string;\n role: string;\n}) {\n return prisma.conversationMember.create({\n data: {\n userId: data.userId,\n conversationId: data.conversationId,\n role: data.role as \"ADMIN\" | \"MEMBER\",\n },\n });\n}\n\n/** Hide a conversation for a single member until new activity occurs. */\nexport function hideConversationMember(userId: string, conversationId: string) {\n return prisma.conversationMember.update({\n where: {\n userId_conversationId: { userId, conversationId },\n },\n data: {\n hiddenAt: new Date(),\n },\n });\n}\n\n/** Remove a member from a conversation. */\nexport function deleteConversationMember(userId: string, conversationId: string) {\n return prisma.conversationMember.delete({\n where: {\n userId_conversationId: { userId, conversationId },\n },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"7ea2e02b-0f12-5407-9926-5fab4c199704"}
1
+ {"version":3,"file":"conversation.js","sources":["models/conversation.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Conversation model – DM/group conversations, members, unread counts.\n */\nimport { prisma } from \"../lib/prisma.js\";\n\nconst conversationListInclude = {\n labChat: { select: { id: true, title: true } },\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n },\n },\n messages: {\n orderBy: { createdAt: \"desc\" as const },\n take: 1,\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n },\n },\n};\n\n/** @returns User's conversations with members and last message. */\nexport function findConversationsByUserId(userId: string) {\n return prisma.conversation.findMany({\n where: {\n members: { some: { userId } },\n },\n include: conversationListInclude,\n orderBy: { updatedAt: \"desc\" },\n });\n}\n\n/** Count unread messages in conversation for user. */\nexport function countUnreadMessages(\n conversationId: string,\n userId: string,\n after?: Date\n) {\n return prisma.message.count({\n where: {\n conversationId,\n senderId: { not: userId },\n ...(after && { createdAt: { gt: after } }),\n },\n });\n}\n\n/** Count unread mentions for user in conversation. */\nexport function countUnreadMentions(\n conversationId: string,\n userId: string,\n after?: Date\n) {\n return prisma.mention.count({\n where: {\n userId,\n message: {\n conversationId,\n senderId: { not: userId },\n ...(after && { createdAt: { gt: after } }),\n },\n },\n });\n}\n\n/** @returns User by username (id, username only). */\nexport function findUserByUsername(username: string) {\n return prisma.user.findFirst({\n where: { username },\n select: { id: true, username: true },\n });\n}\n\n/**\n * Search users who share at least one active school membership with the\n * requester and match the query by username or profile display name.\n *\n * The requester is always excluded so the result set can be used directly as\n * conversation member suggestions.\n */\nexport async function searchInstitutionConversationMembers(\n userId: string,\n query: string,\n limit: number\n) {\n const memberships = await prisma.schoolMembership.findMany({\n where: { userId, status: \"ACTIVE\" },\n select: { schoolId: true },\n });\n const schoolIds = memberships.map((membership) => membership.schoolId);\n\n if (schoolIds.length === 0) return [];\n\n return prisma.user.findMany({\n where: {\n id: { not: userId },\n schoolMemberships: {\n some: {\n schoolId: { in: schoolIds },\n status: \"ACTIVE\",\n },\n },\n OR: [\n { username: { contains: query, mode: \"insensitive\" } },\n {\n profile: {\n is: { displayName: { contains: query, mode: \"insensitive\" } },\n },\n },\n ],\n },\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n orderBy: { username: \"asc\" },\n take: limit,\n });\n}\n\n/** @returns User's DM conversations. */\nexport function findDmConversationsByUserId(userId: string) {\n return prisma.conversation.findMany({\n where: {\n type: \"DM\",\n members: { some: { userId } },\n },\n include: {\n members: { select: { userId: true } },\n },\n });\n}\n\n/** @returns Users by IDs or usernames. */\nexport function findUsersByIdsOrUsernames(identifiers: string[]) {\n if (identifiers.length === 0) return Promise.resolve([]);\n return prisma.user.findMany({\n where: {\n OR: [\n { id: { in: identifiers } },\n { username: { in: identifiers } },\n ],\n },\n select: { id: true, username: true },\n });\n}\n\n/** Create a DM or group conversation with members. */\nexport function createConversation(data: {\n type: \"DM\" | \"GROUP\";\n name?: string;\n members: { userId: string; role: string }[];\n}) {\n return prisma.conversation.create({\n data: {\n type: data.type,\n name: data.name,\n members: {\n create: data.members.map((m) => ({\n userId: m.userId,\n role: m.role as \"ADMIN\" | \"MEMBER\",\n })),\n },\n },\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n },\n },\n },\n });\n}\n\n/** @returns Conversation if user is a member. */\nexport function findConversationByIdAndMember(\n conversationId: string,\n userId: string\n) {\n return prisma.conversation.findFirst({\n where: {\n id: conversationId,\n members: { some: { userId } },\n },\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n },\n },\n },\n });\n}\n\n/** Add a member to a conversation. */\nexport function createConversationMember(data: {\n userId: string;\n conversationId: string;\n role: string;\n}) {\n return prisma.conversationMember.create({\n data: {\n userId: data.userId,\n conversationId: data.conversationId,\n role: data.role as \"ADMIN\" | \"MEMBER\",\n },\n });\n}\n\n/** Hide a conversation for a single member until new activity occurs. */\nexport function hideConversationMember(userId: string, conversationId: string) {\n return prisma.conversationMember.update({\n where: {\n userId_conversationId: { userId, conversationId },\n },\n data: {\n hiddenAt: new Date(),\n },\n });\n}\n\n/** Remove a member from a conversation. */\nexport function deleteConversationMember(userId: string, conversationId: string) {\n return prisma.conversationMember.delete({\n where: {\n userId_conversationId: { userId, conversationId },\n },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"14c80725-5a2b-5e37-aa01-49cb94535659"}
@@ -82,19 +82,30 @@ export declare const getBaseSystemPrompt: (context: Class, members: User[], assi
82
82
  * @param labChatId
83
83
  */
84
84
  /**
85
- * Generate and send AI introduction for lab chat
86
- * Uses the stored context directly from database
85
+ * Fill the AI introduction placeholder for a brand-new lab chat.
86
+ *
87
+ * The caller (service layer) must have already created a PENDING AI Message
88
+ * row — `aiMessageId` is that placeholder. This function flips it to
89
+ * COMPLETED with the intro content, or to FAILED on a hard error.
87
90
  */
88
- export declare const generateAndSendLabIntroduction: (labChatId: string, conversationId: string, _contextString: string, subject: string) => Promise<void>;
91
+ export declare const generateAndSendLabIntroduction: (labChatId: string, conversationId: string, _contextString: string, _subject: string, aiMessageId: string) => Promise<void>;
89
92
  /**
90
- * Generate and send AI response to teacher message
91
- * Uses the stored context directly from database
92
- * @param emitOptions - When provided, emits lab-response-completed/failed on teacher channel
93
- * `_teacherMessage` is retained for caller compatibility while generation is anchored by `emitOptions.messageId`.
93
+ * Fill the AI response placeholder for a teacher reply in a lab chat.
94
+ *
95
+ * `aiMessageId` is the PENDING AI Message row created by the service
96
+ * layer; the pipeline flips it to COMPLETED with content / attachments /
97
+ * meta, or FAILED on error. Teacher-channel `lab-response-{pending,
98
+ * completed,failed}` events fire alongside the chat-channel status flips
99
+ * (which `completePendingAIMessage` / `failPendingAIMessage` already
100
+ * broadcast) so the lab list "AI busy" indicator stays accurate.
101
+ *
102
+ * `_teacherMessage` is retained for caller compatibility — the actual
103
+ * conversation history is reloaded from the DB anchored on the AI
104
+ * placeholder.
94
105
  */
95
- export declare const generateAndSendLabResponse: (labChatId: string, _teacherMessage: string, emitOptions?: {
106
+ export declare const generateAndSendLabResponse: (labChatId: string, _teacherMessage: string, emitOptions: {
96
107
  classId: string;
97
- messageId: string;
108
+ aiMessageId: string;
98
109
  }) => Promise<void>;
99
110
  export {};
100
111
  //# sourceMappingURL=aiLabChat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aiLabChat.d.ts","sourceRoot":"/","sources":["pipelines/aiLabChat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aiLabChat.d.ts","sourceRoot":"/","sources":["pipelines/aiLabChat.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAsC7E,wDAAwD;AACxD,KAAK,gBAAgB,GAAG;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClD,iBAAiB,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClE,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE;YAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IAC9F,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE;YAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IAC9F,WAAW,EAAE,CAAC,UAAU,GAAG;QACzB,OAAO,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC;QACrE,UAAU,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QACnC,eAAe,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;KACzC,CAAC,EAAE,CAAC;CACN,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,OAAO,EAAE;YACP,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;SAC5B,GAAG,IAAI,CAAC;KACV,GAAG,IAAI,CAAC;CACV,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,GACrC,UAAU,oBAAoB,EAAE,EAChC,kBAAkB,MAAM,EACxB,cAAU,2BAYX,CAAC;AA4FF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,MAAM,gBAAgB,KAAG,MAsF/D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,KAAK,EACd,SAAS,IAAI,EAAE,EACf,aAAa,UAAU,EAAE,EACzB,OAAO,IAAI,EAAE,EACb,UAAU,OAAO,EAAE,KAClB,MAYF,CAAC;AAIF;;;;;;;GAOG;AAoBH;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GACvC,WAAW,MAAM,EACjB,gBAAgB,MAAM,EACtB,gBAAgB,MAAM,EACtB,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,OAAO,CAAC,IAAI,CAed,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,0BAA0B,GACrC,WAAW,MAAM,EACjB,iBAAiB,MAAM,EACvB,aAAa;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,KACpD,OAAO,CAAC,IAAI,CAwQd,CAAA"}
@@ -3,12 +3,11 @@
3
3
  * Can create worksheets, sections, assignments, and PDF docs 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]="dc9c58f1-eed2-5c54-9fdf-2725a8417b45")}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]="e57f7401-27fd-5eac-a76a-cb84f699b776")}catch(e){}}();
7
7
  import { isAIUser } from "../utils/aiUser.js";
8
8
  import { prisma } from "../lib/prisma.js";
9
- import { JobStatus } from "@prisma/client";
10
9
  import { pusher, teacherChannel } from "../lib/pusher.js";
11
- import { inference, sendAIMessage } from "../utils/inference.js";
10
+ import { inference, completePendingAIMessage, failPendingAIMessage, } from "../utils/inference.js";
12
11
  import { logger } from "../utils/logger.js";
13
12
  import { createPdf } from "../lib/jsonConversion.js";
14
13
  import { v4 } from "uuid";
@@ -248,35 +247,40 @@ export const getBaseSystemPrompt = (context, members, assignments, files, sectio
248
247
  // }
249
248
  // };
250
249
  /**
251
- * Generate and send AI introduction for lab chat
252
- * Uses the stored context directly from database
250
+ * Fill the AI introduction placeholder for a brand-new lab chat.
251
+ *
252
+ * The caller (service layer) must have already created a PENDING AI Message
253
+ * row — `aiMessageId` is that placeholder. This function flips it to
254
+ * COMPLETED with the intro content, or to FAILED on a hard error.
253
255
  */
254
- export const generateAndSendLabIntroduction = async (labChatId, conversationId, _contextString, subject) => {
256
+ 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?";
255
258
  try {
256
- 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?";
257
- await sendAIMessage(introMessage, conversationId, { subject });
258
- logger.info('AI Introduction sent', { labChatId, conversationId });
259
+ await completePendingAIMessage(aiMessageId, introMessage);
260
+ logger.info('AI Introduction sent', { labChatId, conversationId, aiMessageId });
259
261
  }
260
262
  catch (error) {
261
- logger.error('Failed to generate AI introduction:', { error, labChatId });
262
- // Send fallback introduction
263
- try {
264
- const fallbackIntro = `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?`;
265
- await sendAIMessage(fallbackIntro, conversationId, {
266
- subject,
267
- });
268
- logger.info('Fallback AI introduction sent', { labChatId });
269
- }
270
- catch (fallbackError) {
271
- logger.error('Failed to send fallback AI introduction:', { error: fallbackError, labChatId });
272
- }
263
+ logger.error('Failed to complete AI introduction placeholder:', {
264
+ error,
265
+ labChatId,
266
+ aiMessageId,
267
+ });
268
+ await failPendingAIMessage(aiMessageId, { reason: 'lab_intro_complete_failed' });
273
269
  }
274
270
  };
275
271
  /**
276
- * Generate and send AI response to teacher message
277
- * Uses the stored context directly from database
278
- * @param emitOptions - When provided, emits lab-response-completed/failed on teacher channel
279
- * `_teacherMessage` is retained for caller compatibility while generation is anchored by `emitOptions.messageId`.
272
+ * Fill the AI response placeholder for a teacher reply in a lab chat.
273
+ *
274
+ * `aiMessageId` is the PENDING AI Message row created by the service
275
+ * layer; the pipeline flips it to COMPLETED with content / attachments /
276
+ * meta, or FAILED on error. Teacher-channel `lab-response-{pending,
277
+ * completed,failed}` events fire alongside the chat-channel status flips
278
+ * (which `completePendingAIMessage` / `failPendingAIMessage` already
279
+ * broadcast) so the lab list "AI busy" indicator stays accurate.
280
+ *
281
+ * `_teacherMessage` is retained for caller compatibility — the actual
282
+ * conversation history is reloaded from the DB anchored on the AI
283
+ * placeholder.
280
284
  */
281
285
  export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emitOptions) => {
282
286
  try {
@@ -296,7 +300,11 @@ export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emi
296
300
  throw new Error('Lab chat not found');
297
301
  }
298
302
  const conversationId = fullLabChat.conversationId;
299
- const recentMessages = await loadRecentLabChatMessages(conversationId, emitOptions?.messageId);
303
+ // Anchor history on the AI placeholder — the slice will include the
304
+ // placeholder itself (empty content) at the top; drop it so the model
305
+ // does not see an empty assistant turn at the very end.
306
+ const anchoredMessages = await loadRecentLabChatMessages(conversationId, emitOptions.aiMessageId);
307
+ const recentMessages = anchoredMessages.filter((msg) => msg.id !== emitOptions.aiMessageId);
300
308
  const classData = await prisma.class.findUnique({
301
309
  where: {
302
310
  id: fullLabChat.classId,
@@ -376,7 +384,7 @@ export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emi
376
384
  labChatId,
377
385
  conversationId,
378
386
  classId: fullLabChat.classId,
379
- ...(emitOptions?.messageId ? { messageId: emitOptions.messageId } : {}),
387
+ aiMessageId: emitOptions.aiMessageId,
380
388
  },
381
389
  }), LAB_CHAT_RESPONSE_TIMEOUT_MS, "Lab chat response generation");
382
390
  if (!response) {
@@ -443,8 +451,7 @@ export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emi
443
451
  }
444
452
  }
445
453
  }
446
- // Send the text response to the conversation
447
- await sendAIMessage(jsonData.text, conversationId, {
454
+ await completePendingAIMessage(emitOptions.aiMessageId, jsonData.text, {
448
455
  attachments: {
449
456
  connect: attachmentIds.map(id => ({ id })),
450
457
  },
@@ -462,32 +469,24 @@ export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emi
462
469
  id: v4(),
463
470
  })) || null,
464
471
  },
465
- subject: fullLabChat.class?.subject || 'Lab',
466
472
  });
467
473
  }
468
474
  catch (parseError) {
469
475
  logger.error('Failed to parse AI response or generate PDF:', { error: parseError, labChatId });
470
- // Fallback: send the raw response if parsing fails
471
- await sendAIMessage(response.text, conversationId, {
472
- subject: fullLabChat.class?.subject || 'Lab',
473
- });
476
+ // Fallback: complete the placeholder with the raw text so the
477
+ // teacher still sees something useful instead of FAILED.
478
+ await completePendingAIMessage(emitOptions.aiMessageId, response.text);
474
479
  }
475
- if (emitOptions) {
476
- await prisma.message.update({
477
- where: { id: emitOptions.messageId },
478
- data: { status: JobStatus.COMPLETED },
480
+ try {
481
+ await pusher.trigger(teacherChannel(emitOptions.classId), "lab-response-completed", {
482
+ labChatId,
483
+ messageId: emitOptions.aiMessageId,
479
484
  });
480
- try {
481
- await pusher.trigger(teacherChannel(emitOptions.classId), "lab-response-completed", {
482
- labChatId,
483
- messageId: emitOptions.messageId,
484
- });
485
- }
486
- catch (broadcastError) {
487
- logger.error("Failed to broadcast lab response completed:", { error: broadcastError });
488
- }
489
485
  }
490
- logger.info('AI response sent', { labChatId, conversationId });
486
+ catch (broadcastError) {
487
+ logger.error("Failed to broadcast lab response completed:", { error: broadcastError });
488
+ }
489
+ logger.info('AI response sent', { labChatId, conversationId, aiMessageId: emitOptions.aiMessageId });
491
490
  }
492
491
  catch (error) {
493
492
  console.error('Full error object:', error);
@@ -497,35 +496,24 @@ export const generateAndSendLabResponse = async (labChatId, _teacherMessage, emi
497
496
  stack: error.stack,
498
497
  name: error.name
499
498
  } : error,
500
- labChatId
499
+ labChatId,
500
+ aiMessageId: emitOptions.aiMessageId,
501
501
  });
502
- if (emitOptions) {
503
- try {
504
- await prisma.message.update({
505
- where: { id: emitOptions.messageId },
506
- data: { status: JobStatus.FAILED },
507
- });
508
- }
509
- catch (statusError) {
510
- logger.error("Failed to set message status FAILED:", {
511
- error: statusError,
512
- labChatId,
513
- messageId: emitOptions.messageId,
514
- });
515
- }
516
- try {
517
- await pusher.trigger(teacherChannel(emitOptions.classId), "lab-response-failed", {
518
- labChatId,
519
- messageId: emitOptions.messageId,
520
- error: "AI response generation failed",
521
- });
522
- }
523
- catch (broadcastError) {
524
- logger.error("Failed to broadcast lab response failed:", { error: broadcastError });
525
- }
502
+ await failPendingAIMessage(emitOptions.aiMessageId, {
503
+ reason: 'lab_response_generation_failed',
504
+ });
505
+ try {
506
+ await pusher.trigger(teacherChannel(emitOptions.classId), "lab-response-failed", {
507
+ labChatId,
508
+ messageId: emitOptions.aiMessageId,
509
+ error: "AI response generation failed",
510
+ });
511
+ }
512
+ catch (broadcastError) {
513
+ logger.error("Failed to broadcast lab response failed:", { error: broadcastError });
526
514
  }
527
515
  throw error; // Re-throw to see the full error in the calling function
528
516
  }
529
517
  };
530
518
  //# sourceMappingURL=aiLabChat.js.map
531
- //# debugId=dc9c58f1-eed2-5c54-9fdf-2725a8417b45
519
+ //# debugId=e57f7401-27fd-5eac-a76a-cb84f699b776
@@ -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 { JobStatus } from \"@prisma/client\";\nimport { pusher, teacherChannel } from \"../lib/pusher.js\";\nimport type { Assignment, Class, File, Section, User } from \"@prisma/client\";\nimport { inference, sendAIMessage } 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 * Generate and send AI introduction for lab chat\n * Uses the stored context directly from database\n */\nexport const generateAndSendLabIntroduction = async (\n labChatId: string,\n conversationId: string,\n _contextString: string,\n subject: string\n ): Promise<void> => {\n try {\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 await sendAIMessage(introMessage, conversationId, { subject });\n \n logger.info('AI Introduction sent', { labChatId, conversationId });\n \n } catch (error) {\n logger.error('Failed to generate AI introduction:', { error, labChatId });\n \n // Send fallback introduction\n try {\n const fallbackIntro = `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 await sendAIMessage(fallbackIntro, conversationId, {\n subject,\n });\n \n logger.info('Fallback AI introduction sent', { labChatId });\n \n } catch (fallbackError) {\n logger.error('Failed to send fallback AI introduction:', { error: fallbackError, labChatId });\n }\n }\n }\n \n /**\n * Generate and send AI response to teacher message\n * Uses the stored context directly from database\n * @param emitOptions - When provided, emits lab-response-completed/failed on teacher channel\n * `_teacherMessage` is retained for caller compatibility while generation is anchored by `emitOptions.messageId`.\n */\n export const generateAndSendLabResponse = async (\n labChatId: string,\n _teacherMessage: string,\n emitOptions?: { classId: string; messageId: 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 const recentMessages = await loadRecentLabChatMessages(\n conversationId,\n emitOptions?.messageId,\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 ...(emitOptions?.messageId ? { messageId: emitOptions.messageId } : {}),\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 // Send the text response to the conversation\n await sendAIMessage(jsonData.text, conversationId, {\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 subject: fullLabChat.class?.subject || 'Lab',\n });\n } catch (parseError) {\n logger.error('Failed to parse AI response or generate PDF:', { error: parseError, labChatId });\n // Fallback: send the raw response if parsing fails\n await sendAIMessage(response.text, conversationId, {\n subject: fullLabChat.class?.subject || 'Lab',\n });\n }\n\n if (emitOptions) {\n await prisma.message.update({\n where: { id: emitOptions.messageId },\n data: { status: JobStatus.COMPLETED },\n });\n try {\n await pusher.trigger(teacherChannel(emitOptions.classId), \"lab-response-completed\", {\n labChatId,\n messageId: emitOptions.messageId,\n });\n } catch (broadcastError) {\n logger.error(\"Failed to broadcast lab response completed:\", { error: broadcastError });\n }\n }\n\n logger.info('AI response sent', { labChatId, conversationId });\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 });\n\n if (emitOptions) {\n try {\n await prisma.message.update({\n where: { id: emitOptions.messageId },\n data: { status: JobStatus.FAILED },\n });\n } catch (statusError) {\n logger.error(\"Failed to set message status FAILED:\", {\n error: statusError,\n labChatId,\n messageId: emitOptions.messageId,\n });\n }\n try {\n await pusher.trigger(teacherChannel(emitOptions.classId), \"lab-response-failed\", {\n labChatId,\n messageId: emitOptions.messageId,\n error: \"AI response generation failed\",\n });\n } catch (broadcastError) {\n logger.error(\"Failed to broadcast lab response failed:\", { error: broadcastError });\n }\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":"dc9c58f1-eed2-5c54-9fdf-2725a8417b45"}
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"}
@@ -10,11 +10,20 @@ export interface AIPolicyLevel {
10
10
  }
11
11
  export declare const AI_POLICY_LEVELS: AIPolicyLevel[];
12
12
  /**
13
- * Generate and send AI introduction for Newton chat
13
+ * Fill the Newton tutor introduction placeholder.
14
+ *
15
+ * `aiMessageId` is the PENDING AI Message row created by the service layer
16
+ * (see `getTutorConversation`). On success we flip it to COMPLETED with the
17
+ * generated intro; on failure we fall back to a hardcoded greeting before
18
+ * marking the placeholder FAILED only as a last resort.
14
19
  */
15
- export declare const generateAndSendNewtonIntroduction: (newtonChatId: string, conversationId: string, submissionId: string) => Promise<void>;
20
+ export declare const generateAndSendNewtonIntroduction: (newtonChatId: string, conversationId: string, submissionId: string, aiMessageId: string) => Promise<void>;
16
21
  /**
17
- * Generate and send AI response to student message
22
+ * Fill the Newton AI response placeholder for a student turn.
23
+ *
24
+ * `aiMessageId` is the PENDING AI Message row the service created post-tx;
25
+ * the pipeline flips it to COMPLETED with the generated content (the
26
+ * helper broadcasts `message-updated` on the chat channel) or to FAILED.
18
27
  */
19
28
  export declare const generateAndSendNewtonResponse: (newtonChatId: string, studentMessage: string, conversationId: string, submission: {
20
29
  id: string;
@@ -26,5 +35,5 @@ export declare const generateAndSendNewtonResponse: (newtonChatId: string, stude
26
35
  subject: string | null;
27
36
  };
28
37
  };
29
- }) => Promise<void>;
38
+ }, aiMessageId: string) => Promise<void>;
30
39
  //# sourceMappingURL=aiNewtonChat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aiNewtonChat.d.ts","sourceRoot":"/","sources":["pipelines/aiNewtonChat.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,EAAE,MAAM,CAAC;IACnC,yBAAyB,EAAE,MAAM,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAmD3C,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,iCAAiC,GAC1C,cAAc,MAAM,EACpB,gBAAgB,MAAM,EACtB,cAAc,MAAM,KACnB,OAAO,CAAC,IAAI,CA6Fd,CAAA;AA8BD;;GAEG;AACH,eAAO,MAAM,6BAA6B,GACxC,cAAc,MAAM,EACpB,gBAAgB,MAAM,EACtB,gBAAgB,MAAM,EACtB,YAAY;IACV,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,KAAK,EAAE;YACL,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;SACxB,CAAC;KACH,CAAC;CACH,KACA,OAAO,CAAC,IAAI,CA+Hd,CAAA"}
1
+ {"version":3,"file":"aiNewtonChat.d.ts","sourceRoot":"/","sources":["pipelines/aiNewtonChat.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,EAAE,MAAM,CAAC;IACnC,yBAAyB,EAAE,MAAM,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAmD3C,CAAC;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,GAC1C,cAAc,MAAM,EACpB,gBAAgB,MAAM,EACtB,cAAc,MAAM,EACpB,aAAa,MAAM,KAClB,OAAO,CAAC,IAAI,CAwFd,CAAA;AA8BD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GACxC,cAAc,MAAM,EACpB,gBAAgB,MAAM,EACtB,gBAAgB,MAAM,EACtB,YAAY;IACV,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,KAAK,EAAE;YACL,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;SACxB,CAAC;KACH,CAAC;CACH,EACD,aAAa,MAAM,KAClB,OAAO,CAAC,IAAI,CAuId,CAAA"}