@studious-lms/server 1.26.0 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -1
- package/.github/workflows/npm-publish.yml +1 -0
- package/AGENTS.md +84 -0
- package/dist/models/cloudStorage.d.ts +11 -0
- package/dist/models/cloudStorage.d.ts.map +1 -1
- package/dist/models/cloudStorage.js +18 -2
- package/dist/models/cloudStorage.js.map +1 -1
- package/dist/models/conversation.d.ts +1 -0
- package/dist/models/conversation.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +40 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/labChat.js +32 -2
- package/dist/models/labChat.js.map +1 -1
- package/dist/models/message.d.ts +3 -0
- package/dist/models/message.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.d.ts +2 -0
- package/dist/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.js +60 -16
- package/dist/pipelines/aiLabChat.js.map +1 -1
- package/dist/pipelines/aiLabChatContract.d.ts +170 -2
- package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
- package/dist/pipelines/aiLabChatContract.js +30 -2
- package/dist/pipelines/aiLabChatContract.js.map +1 -1
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
- package/dist/pipelines/aiNewtonChat.js +30 -5
- package/dist/pipelines/aiNewtonChat.js.map +1 -1
- package/dist/pipelines/labChatPrompt.d.ts +1 -1
- package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
- package/dist/pipelines/labChatPrompt.js +13 -14
- package/dist/pipelines/labChatPrompt.js.map +1 -1
- package/dist/routers/_app.d.ts +220 -10
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/admin.d.ts +0 -1
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/conversation.d.ts +1 -0
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/labChat.d.ts +10 -0
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +6 -3
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/message.d.ts +37 -4
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +24 -7
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/parent.d.ts +18 -0
- package/dist/routers/parent.d.ts.map +1 -1
- package/dist/routers/parent.js +10 -3
- package/dist/routers/parent.js.map +1 -1
- package/dist/routers/studentProgress.d.ts +44 -0
- package/dist/routers/studentProgress.d.ts.map +1 -1
- package/dist/routers/studentProgress.js +12 -3
- package/dist/routers/studentProgress.js.map +1 -1
- package/dist/routes/aiStream.js +32 -2
- package/dist/routes/aiStream.js.map +1 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +107 -3
- package/dist/seedDatabase.js.map +1 -1
- package/dist/services/adminUser.d.ts +0 -1
- package/dist/services/adminUser.d.ts.map +1 -1
- package/dist/services/adminUser.js +7 -4
- package/dist/services/adminUser.js.map +1 -1
- package/dist/services/aiChatStream.d.ts +15 -0
- package/dist/services/aiChatStream.d.ts.map +1 -1
- package/dist/services/aiChatStream.js +144 -74
- package/dist/services/aiChatStream.js.map +1 -1
- package/dist/services/aiGeneration.d.ts +4 -0
- package/dist/services/aiGeneration.d.ts.map +1 -0
- package/dist/services/aiGeneration.js +88 -0
- package/dist/services/aiGeneration.js.map +1 -0
- package/dist/services/aiProposalTools.d.ts +4 -1
- package/dist/services/aiProposalTools.d.ts.map +1 -1
- package/dist/services/aiProposalTools.js +128 -3
- package/dist/services/aiProposalTools.js.map +1 -1
- package/dist/services/aiProposals.d.ts +70 -0
- package/dist/services/aiProposals.d.ts.map +1 -0
- package/dist/services/aiProposals.js +363 -0
- package/dist/services/aiProposals.js.map +1 -0
- package/dist/services/conversation.d.ts +1 -0
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/labChat.d.ts +10 -0
- package/dist/services/labChat.d.ts.map +1 -1
- package/dist/services/labChat.js +52 -3
- package/dist/services/labChat.js.map +1 -1
- package/dist/services/message.d.ts +24 -8
- package/dist/services/message.d.ts.map +1 -1
- package/dist/services/message.js +170 -117
- package/dist/services/message.js.map +1 -1
- package/dist/services/parentPortal.d.ts +14 -0
- package/dist/services/parentPortal.d.ts.map +1 -1
- package/dist/services/parentPortal.js +72 -2
- package/dist/services/parentPortal.js.map +1 -1
- package/dist/services/studentProgress.d.ts +50 -0
- package/dist/services/studentProgress.d.ts.map +1 -1
- package/dist/services/studentProgress.js +107 -2
- package/dist/services/studentProgress.js.map +1 -1
- package/dist/utils/inference.d.ts +10 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +77 -2
- package/dist/utils/inference.js.map +1 -1
- package/dist/workers/processors/createRunJobWorker.js +17 -16
- package/dist/workers/processors/createRunJobWorker.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260811130000_add_ai_message_feedback/migration.sql +7 -0
- package/prisma/schema.prisma +8 -0
- package/src/models/cloudStorage.ts +19 -0
- package/src/models/labChat.ts +34 -0
- package/src/pipelines/aiLabChat.ts +79 -18
- package/src/pipelines/aiLabChatContract.ts +35 -0
- package/src/pipelines/aiNewtonChat.ts +34 -2
- package/src/pipelines/labChatPrompt.ts +11 -12
- package/src/routers/labChat.ts +5 -0
- package/src/routers/message.ts +34 -5
- package/src/routers/parent.ts +11 -0
- package/src/routers/studentProgress.ts +15 -0
- package/src/routes/aiStream.ts +32 -0
- package/src/seedDatabase.ts +107 -2
- package/src/services/adminUser.ts +5 -2
- package/src/services/aiChatStream.ts +83 -7
- package/src/services/aiGeneration.ts +92 -0
- package/src/services/aiProposalTools.ts +166 -1
- package/src/services/aiProposals.ts +428 -0
- package/src/services/labChat.ts +65 -0
- package/src/services/message.ts +205 -142
- package/src/services/parentPortal.ts +85 -0
- package/src/services/studentProgress.ts +135 -0
- package/src/utils/inference.ts +85 -0
- package/src/workers/processors/createRunJobWorker.ts +17 -17
- package/tests/unit/services/adminUser.test.ts +75 -0
- package/tests/unit/utils/adminPanelAccess.test.ts +26 -0
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Can create worksheets, sections, assignments, and generated files from AI output.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
6
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a0910d42-509a-534e-ad83-e5ccb2a920c0")}catch(e){}}();
|
|
7
7
|
import { isAIUser } from "../utils/aiUser.js";
|
|
8
8
|
import { prisma } from "../lib/prisma.js";
|
|
9
9
|
import { pusher, chatChannel, teacherChannel } from "../lib/pusher.js";
|
|
10
|
-
import { completePendingAIMessage, failPendingAIMessage, } from "../utils/inference.js";
|
|
10
|
+
import { cancelPendingAIMessage, completePendingAIMessage, failPendingAIMessage, } from "../utils/inference.js";
|
|
11
11
|
import { logger } from "../utils/logger.js";
|
|
12
12
|
import { v4 } from "uuid";
|
|
13
13
|
import { bucket } from "../lib/googleCloudStorage.js";
|
|
@@ -18,9 +18,13 @@ import { AI_CHAT_MODEL } from "../lib/chatAttachmentPrompt.js";
|
|
|
18
18
|
import { buildLabChatAgentMessages } from "./labChatPrompt.js";
|
|
19
19
|
import { applyResolvedFormatsToStreamParts, enrichStreamPartsWithGeneratedFiles, finalizeDeferredToolParts, streamChatResponse, } from "../services/aiChatStream.js";
|
|
20
20
|
import { aiTools } from "../services/aiTools.js";
|
|
21
|
+
import { findOrCreateChildFolderByName, findOrCreateScopeRoot, } from "../models/cloudStorage.js";
|
|
21
22
|
import { labProposalTools, createLabProposals } from "../services/aiProposalTools.js";
|
|
23
|
+
import { buildPendingCreations } from "../services/aiProposals.js";
|
|
22
24
|
/** Cap model wait time; leave headroom after inference for PDF/GCS/DB within Vercel maxDuration (500s). */
|
|
23
25
|
const LAB_CHAT_RESPONSE_TIMEOUT_MS = 400000;
|
|
26
|
+
/** Class-files folder that collects AI-generated documents (created lazily). */
|
|
27
|
+
export const AI_GENERATED_FOLDER_NAME = "AI Generated";
|
|
24
28
|
const withTimeout = async (task, timeoutMs, operationName) => {
|
|
25
29
|
let timeoutHandle;
|
|
26
30
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -197,7 +201,7 @@ CLASS: ${cls.name} | Subject: ${cls.subject} | Section: ${cls.section}
|
|
|
197
201
|
Syllabus: ${cls.syllabus ? cls.syllabus.slice(0, 200) + (cls.syllabus.length > 200 ? "…" : "") : "(none)"}
|
|
198
202
|
|
|
199
203
|
SECTIONS (use sectionId when creating assignments):
|
|
200
|
-
${sectionList || " (none -
|
|
204
|
+
${sectionList || " (none - propose a section first)"}
|
|
201
205
|
|
|
202
206
|
MARK SCHEMES (use markSchemeId when creating assignments):
|
|
203
207
|
${markSchemeList || " (none - suggest creating one or omit markSchemeId)"}
|
|
@@ -206,7 +210,7 @@ GRADING BOUNDARIES (use gradingBoundaryId when creating assignments):
|
|
|
206
210
|
${gradingBoundaryList || " (none - suggest creating one or omit gradingBoundaryId)"}
|
|
207
211
|
|
|
208
212
|
WORKSHEETS (use worksheetIds when acceptWorksheet is true):
|
|
209
|
-
${worksheetList || " (none -
|
|
213
|
+
${worksheetList || " (none - propose a worksheet or create via docs first)"}
|
|
210
214
|
|
|
211
215
|
FILES - PDFs/Documents (for assignment attachments):
|
|
212
216
|
${fileList || " (none)"}
|
|
@@ -373,7 +377,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
373
377
|
...aiTools(fullLabChat.classId),
|
|
374
378
|
...labProposalTools(proposals),
|
|
375
379
|
};
|
|
376
|
-
const { text: streamedText, parts: rawStreamParts } = await withTimeout(streamChatResponse({
|
|
380
|
+
const { text: streamedText, parts: rawStreamParts, cancelled, } = await withTimeout(streamChatResponse({
|
|
377
381
|
conversationId,
|
|
378
382
|
aiMessageId: emitOptions.aiMessageId,
|
|
379
383
|
model: AI_CHAT_MODEL,
|
|
@@ -392,6 +396,28 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
392
396
|
// Real text + tool-call events were already broadcast inline (in order)
|
|
393
397
|
// during the stream by `streamChatResponse`. Just close the stream.
|
|
394
398
|
await broadcastStreamComplete(conversationId, emitOptions.aiMessageId);
|
|
399
|
+
if (cancelled) {
|
|
400
|
+
// User stopped the generation: persist whatever streamed so far and
|
|
401
|
+
// skip document generation / proposal creation entirely.
|
|
402
|
+
const partialParts = finalizeDeferredToolParts(rawStreamParts);
|
|
403
|
+
await cancelPendingAIMessage(emitOptions.aiMessageId, streamedText, {
|
|
404
|
+
meta: {
|
|
405
|
+
streamParts: partialParts.length > 0 ? partialParts : null,
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
await pusher
|
|
409
|
+
.trigger(teacherChannel(emitOptions.classId), "lab-response-completed", {
|
|
410
|
+
labChatId,
|
|
411
|
+
messageId: emitOptions.aiMessageId,
|
|
412
|
+
})
|
|
413
|
+
.catch(() => { });
|
|
414
|
+
logger.info('AI response cancelled by user', {
|
|
415
|
+
labChatId,
|
|
416
|
+
conversationId,
|
|
417
|
+
aiMessageId: emitOptions.aiMessageId,
|
|
418
|
+
});
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
395
421
|
// `generate_document` is an inline tool that only QUEUES into `proposals.docs`
|
|
396
422
|
// during the stream. OpenAI Code Interpreter renders files here, after the
|
|
397
423
|
// stream ends, then we link them to this message.
|
|
@@ -399,6 +425,26 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
399
425
|
const attachmentIds = [];
|
|
400
426
|
const generatedDocFiles = [];
|
|
401
427
|
const resolvedDocs = [];
|
|
428
|
+
// Generated files are filed into the class cloud storage so teachers
|
|
429
|
+
// can find them in the class Files UI, not just as chat attachments.
|
|
430
|
+
let aiFolderId;
|
|
431
|
+
if (proposals.docs.length > 0) {
|
|
432
|
+
try {
|
|
433
|
+
const root = await findOrCreateScopeRoot({
|
|
434
|
+
kind: "CLASS",
|
|
435
|
+
classId: fullLabChat.classId,
|
|
436
|
+
});
|
|
437
|
+
const aiFolder = await findOrCreateChildFolderByName(root.id, AI_GENERATED_FOLDER_NAME);
|
|
438
|
+
aiFolderId = aiFolder.id;
|
|
439
|
+
}
|
|
440
|
+
catch (folderError) {
|
|
441
|
+
logger.error("Failed to resolve AI generated class folder", {
|
|
442
|
+
error: folderError,
|
|
443
|
+
labChatId,
|
|
444
|
+
classId: fullLabChat.classId,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
402
448
|
for (let i = 0; i < proposals.docs.length; i++) {
|
|
403
449
|
const doc = proposals.docs[i];
|
|
404
450
|
if (!doc.title || !doc.content?.trim()) {
|
|
@@ -455,6 +501,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
455
501
|
messageId: emitOptions.aiMessageId,
|
|
456
502
|
uploadStatus: 'COMPLETED',
|
|
457
503
|
uploadedAt: new Date(),
|
|
504
|
+
...(aiFolderId && { folderId: aiFolderId }),
|
|
458
505
|
},
|
|
459
506
|
});
|
|
460
507
|
attachmentIds.push(file.id);
|
|
@@ -463,6 +510,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
463
510
|
fileId: file.id,
|
|
464
511
|
fileName: filename,
|
|
465
512
|
format: generated.format,
|
|
513
|
+
...(aiFolderId && { folderId: aiFolderId }),
|
|
466
514
|
});
|
|
467
515
|
}
|
|
468
516
|
catch (docError) {
|
|
@@ -478,7 +526,10 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
478
526
|
});
|
|
479
527
|
}
|
|
480
528
|
}
|
|
481
|
-
|
|
529
|
+
// Defer-and-confirm: propose_* tool calls become pending creations the
|
|
530
|
+
// teacher approves or dismisses; nothing is created at generation time.
|
|
531
|
+
const { pendingCreations, parts: streamPartsWithProposals } = buildPendingCreations(proposals, finalizeDeferredToolParts(enrichStreamPartsWithGeneratedFiles(applyResolvedFormatsToStreamParts(rawStreamParts, resolvedDocs), generatedDocFiles)));
|
|
532
|
+
const streamParts = streamPartsWithProposals;
|
|
482
533
|
// While the message is still PENDING, resolve deferred tool indicators
|
|
483
534
|
// (spinner → attachment or error) before the final persist event.
|
|
484
535
|
for (const part of streamParts) {
|
|
@@ -495,6 +546,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
495
546
|
...(part.title ? { title: part.title } : {}),
|
|
496
547
|
...(part.format ? { format: part.format } : {}),
|
|
497
548
|
...(part.fileId ? { fileId: part.fileId, fileName: part.fileName } : {}),
|
|
549
|
+
...(part.folderId ? { folderId: part.folderId } : {}),
|
|
498
550
|
status: part.status,
|
|
499
551
|
})
|
|
500
552
|
.catch(() => { });
|
|
@@ -504,15 +556,7 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
504
556
|
attachments: { connect: attachmentIds.map((id) => ({ id })) },
|
|
505
557
|
}),
|
|
506
558
|
meta: {
|
|
507
|
-
|
|
508
|
-
? proposals.assignments.map(assignment => ({ ...assignment, id: v4() }))
|
|
509
|
-
: null,
|
|
510
|
-
worksheetsToCreate: proposals.worksheets.length > 0
|
|
511
|
-
? proposals.worksheets.map(worksheet => ({ ...worksheet, id: v4() }))
|
|
512
|
-
: null,
|
|
513
|
-
sectionsToCreate: proposals.sections.length > 0
|
|
514
|
-
? proposals.sections.map(section => ({ ...section, id: v4() }))
|
|
515
|
-
: null,
|
|
559
|
+
pendingCreations: pendingCreations.length > 0 ? pendingCreations : null,
|
|
516
560
|
streamParts: streamParts.length > 0 ? streamParts : null,
|
|
517
561
|
},
|
|
518
562
|
});
|
|
@@ -562,4 +606,4 @@ export const generateAndSendLabResponse = async (labChatId, teacherMessage, emit
|
|
|
562
606
|
}
|
|
563
607
|
};
|
|
564
608
|
//# sourceMappingURL=aiLabChat.js.map
|
|
565
|
-
//# debugId=
|
|
609
|
+
//# debugId=a0910d42-509a-534e-ad83-e5ccb2a920c0
|
|
@@ -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 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 } 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 { AI_CHAT_MODEL } from \"../lib/chatAttachmentPrompt.js\";\nimport { buildLabChatAgentMessages } 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 * 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; userId?: string | null }\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 { system, messages } = await buildLabChatAgentMessages({\n context: fullLabChat.context,\n classContext,\n recentMessages: recentMessages.reverse(),\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 model: AI_CHAT_MODEL,\n system,\n messages,\n usageReason: 'lab_chat_response',\n attribution: {\n userId: emitOptions.userId ?? fullLabChat.createdById,\n classId: emitOptions.classId,\n },\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":"AAAA;;;GAGG;;;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EACL,iCAAiC,EACjC,mCAAmC,EACnC,yBAAyB,EACzB,kBAAkB,GAGnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEtF,2GAA2G;AAC3G,MAAM,4BAA4B,GAAG,MAAO,CAAC;AAE7C,MAAM,WAAW,GAAG,KAAK,EACvB,IAAgB,EAChB,SAAiB,EACjB,aAAqB,EACT,EAAE;IACd,IAAI,aAAyC,CAAC;IAC9C,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACtD,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,aAAa,oBAAoB,SAAS,IAAI,CAAC,CAAC,CAAC;QACvE,CAAC,EAAE,SAAS,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IACpD,CAAC;YAAS,CAAC;QACT,IAAI,aAAa,EAAE,CAAC;YAClB,YAAY,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAuCF;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,QAAgC,EAChC,eAAwB,EACxB,KAAK,GAAG,EAAE,EACV,EAAE;IACF,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC;IACpF,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,GAAG,KAAK,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,KAAK,EACrC,cAAsB,EACtB,eAAwB,EACS,EAAE;IACnC,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,MAAM,SAAS,GAAG;QAChB,cAAc;KACf,CAAC;IACF,MAAM,OAAO,GAAG;QACd,MAAM,EAAE;YACN,MAAM,EAAE;gBACN,EAAE,EAAE,IAAI;gBACR,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE;oBACP,MAAM,EAAE;wBACN,WAAW,EAAE,IAAI;qBAClB;iBACF;aACF;SACF;QACD,WAAW,EAAE;YACX,MAAM,EAAE;gBACN,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,IAAI;aACX;SACF;KACF,CAAC;IAEF,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;QACnD,KAAK,EAAE,SAAS;QAChB,OAAO;QACP,OAAO,EAAE;YACP,SAAS,EAAE,MAAM;SAClB;QACD,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IAEH,MAAM,gBAAgB,GAAG,0BAA0B,CAAC,cAAc,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;IAC5F,IAAI,CAAC,eAAe,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,eAAe,CAAC,EAAE,CAAC;QAC3F,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;QACpD,KAAK,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE;QAC9B,OAAO;KACR,CAAC,CAAC;IAEH,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,kBAAkB,eAAe,YAAY,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,aAAa,CAAC,cAAc,KAAK,cAAc,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,kBAAkB,eAAe,oCAAoC,cAAc,EAAE,CAAC,CAAC;IACzG,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;QAClD,KAAK,EAAE;YACL,cAAc;YACd,SAAS,EAAE;gBACT,GAAG,EAAE,aAAa,CAAC,SAAS;aAC7B;SACF;QACD,OAAO;QACP,OAAO,EAAE;YACP,SAAS,EAAE,MAAM;SAClB;QACD,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,IAAI,GAAG,EAAgC,CAAC;IAChE,eAAe,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAErD,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAChC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;YACrC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;SACxC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACvE,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,IAAI,CAAC,EAAE,KAAK,aAAa,CAAC,EAAE,EAAE,CAAC;YACjC,OAAO,CAAC,CAAC,CAAC;QACZ,CAAC;QACD,IAAI,KAAK,CAAC,EAAE,KAAK,aAAa,CAAC,EAAE,EAAE,CAAC;YAClC,OAAO,CAAC,CAAC;QACX,CAAC;QAED,OAAO,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,IAAsB,EAAU,EAAE;IACvE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAEhH,MAAM,WAAW,GAAG,QAAQ;SACzB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC;SAClF,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,cAAc,GAAG,WAAW;SAC/B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACV,IAAI,OAAO,GAAG,mBAAmB,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC;YACjD,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;QAClF,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;QACD,OAAO,WAAW,EAAE,CAAC,EAAE,MAAM,OAAO,EAAE,CAAC;IACzC,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,mBAAmB,GAAG,iBAAiB;SAC1C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACV,IAAI,OAAO,GAAG,eAAe,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC;YACjD,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC;QACjF,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;QACD,OAAO,WAAW,EAAE,CAAC,EAAE,MAAM,OAAO,EAAE,CAAC;IACzC,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,aAAa,GAAG,UAAU;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,aAAa,EAAE,CAAC;SACjF,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,QAAQ,GAAG,KAAK;SACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,iBAAiB,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;SAC3E,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC;SACnE,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,iBAAiB,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACtG,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM;QACrC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzE,CAAC,CAAC,UAAU,CAAC;IAEf,MAAM,WAAW,GAAG,QAAQ;SACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,QAAQ,mBAAmB,CAAC,CAAC,OAAO,EAAE,WAAW,IAAI,GAAG,EAAE,CAAC;SACvG,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,iBAAiB,GAAG,WAAW;SAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,WAAW,GAAG,CAAC,CAAC,OAAO,EAAE,IAAI,IAAI,GAAG,CAAC;QAC3C,OAAO,WAAW,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,IAAI,gBAAgB,WAAW,YAAY,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC9I,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;SACA,GAAG,CAAC,IAAI,eAAe,GAAG,CAAC,OAAO,eAAe,GAAG,CAAC,OAAO;YACzD,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ;;;EAGvG,WAAW,IAAI,2CAA2C;;;EAG1D,cAAc,IAAI,sDAAsD;;;EAGxE,mBAAmB,IAAI,2DAA2D;;;EAGlF,aAAa,IAAI,4DAA4D;;;EAG7E,QAAQ,IAAI,UAAU;;;EAGtB,aAAa;;;EAGb,WAAW,IAAI,UAAU;;;EAGzB,iBAAiB,IAAI,UAAU;CAChC,CAAC,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAAc,EACd,OAAe,EACf,WAAyB,EACzB,KAAa,EACb,QAAmB,EACX,EAAE;IACV,OAAO,sBAAsB,CAAC;QAC5B,KAAK,EAAE,OAAO;QACd,QAAQ;QACR,WAAW,EAAE,EAAE;QACf,iBAAiB,EAAE,EAAE;QACrB,UAAU,EAAE,EAAE;QACd,KAAK;QACL,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,EAAE;QACZ,WAAW;KACZ,CAAC,CAAC;AACL,CAAC,CAAC;AAIF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,KAAK,EAC/C,SAAiB,EACjB,cAAsB,EACtB,cAAsB,EACtB,QAAgB,EAChB,WAAmB,EACJ,EAAE;IACjB,MAAM,YAAY,GAChB,2VAA2V,CAAC;IAE9V,IAAI,CAAC;QACH,MAAM,mBAAmB,CAAC,cAAc,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;QACrE,MAAM,wBAAwB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;IAClF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,iDAAiD,EAAE;YAC9D,KAAK;YACL,SAAS;YACT,WAAW;SACZ,CAAC,CAAC;QACH,MAAM,oBAAoB,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC,CAAC;IACnF,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,EAC7C,SAAiB,EACjB,cAAsB,EACtB,WAA6E,EAC9D,EAAE;IACjB,IAAI,CAAC;QACH,gCAAgC;QAChC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YAClD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,OAAO,EAAE;gBACP,KAAK,EAAE;oBACL,MAAM,EAAE;wBACN,IAAI,EAAE,IAAI;wBACV,OAAO,EAAE,IAAI;qBACd;iBACF;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,cAAc,GAAG,WAAW,CAAC,cAAc,CAAC;QAElD,oEAAoE;QACpE,sEAAsE;QACtE,wDAAwD;QACxD,MAAM,gBAAgB,GAAG,MAAM,yBAAyB,CACtD,cAAc,EACd,WAAW,CAAC,WAAW,CACxB,CAAC;QACF,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAC5C,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,WAAW,CAAC,WAAW,CAC5C,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;YAC9C,KAAK,EAAE;gBACL,EAAE,EAAE,WAAW,CAAC,OAAO;aACxB;YACD,OAAO,EAAE;gBACP,WAAW,EAAE;oBACX,OAAO,EAAE;wBACP,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;wBAC1D,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;wBACpC,eAAe,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;qBAC1C;iBACF;gBACD,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE;gBACvD,iBAAiB,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE;gBAC7D,UAAU,EAAE;oBACV,MAAM,EAAE;wBACN,EAAE,EAAE,IAAI;wBACR,IAAI,EAAE,IAAI;wBACV,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;qBACxC;iBACF;gBACD,QAAQ,EAAE;oBACR,MAAM,EAAE;wBACN,EAAE,EAAE,IAAI;wBACR,QAAQ,EAAE,IAAI;wBACd,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;qBAC3C;iBACF;gBACD,QAAQ,EAAE;oBACR,MAAM,EAAE;wBACN,EAAE,EAAE,IAAI;wBACR,QAAQ,EAAE,IAAI;wBACd,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;qBAC3C;iBACF;gBACD,UAAU,EAAE;oBACV,OAAO,EAAE;wBACP,KAAK,EAAE,IAAI;qBACZ;iBACF;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACrC,CAAC;QAED,MAAM,YAAY,GAAG,sBAAsB,CAAC;YAC1C,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;YAC9C,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC3C,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS;aAClC,CAAC,CAAC;YACH,KAAK,EAAE,SAAS,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxC,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,WAAW,EAAE,SAAS,CAAC,WAAW;SACnC,CAAC,CAAC;QAEH,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,yBAAyB,CAAC;YAC3D,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,YAAY;YACZ,cAAc,EAAE,cAAc,CAAC,OAAO,EAAE;YACxC,QAAQ;SACT,CAAC,CAAC;QAEH,yEAAyE;QACzE,yEAAyE;QACzE,oEAAoE;QACpE,uEAAuE;QACvE,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG;YACZ,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;YAC/B,GAAG,gBAAgB,CAAC,SAAS,CAAC;SAC/B,CAAC;QAEF,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,WAAW,CACrE,kBAAkB,CAAC;YACjB,cAAc;YACd,WAAW,EAAE,WAAW,CAAC,WAAW;YACpC,KAAK,EAAE,aAAa;YACpB,MAAM;YACN,QAAQ;YACR,WAAW,EAAE,mBAAmB;YAChC,WAAW,EAAE;gBACX,MAAM,EAAE,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,WAAW;gBACrD,OAAO,EAAE,WAAW,CAAC,OAAO;aAC7B;YACD,KAAK;YACL,WAAW,EAAE,GAAG;YAChB,eAAe,EAAE,IAAI;YACrB,QAAQ,EAAE,CAAC;SACZ,CAAC,EACF,4BAA4B,EAC5B,8BAA8B,CAC/B,CAAC;QAEF,wEAAwE;QACxE,oEAAoE;QACpE,MAAM,uBAAuB,CAAC,cAAc,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;QAEvE,+EAA+E;QAC/E,2EAA2E;QAC3E,kDAAkD;QAClD,IAAI,CAAC;YACH,MAAM,aAAa,GAAa,EAAE,CAAC;YACnC,MAAM,iBAAiB,GAA2B,EAAE,CAAC;YACrD,MAAM,YAAY,GAA6C,EAAE,CAAC;YAElE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC/C,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAC9B,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;oBACvC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,8BAA8B,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;oBAClF,SAAS;gBACX,CAAC;gBAED,MAAM,MAAM,GAAG,mBAAmB,CAChC,GAAG,CAAC,KAAK,EACT,GAAG,CAAC,OAAO,EACX,GAAG,CAAC,MAAM,EACV,cAAc,CACf,CAAC;gBACF,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;gBAEhD,IAAI,MAAM,KAAK,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnD,MAAM,CAAC,IAAI,CAAC,+CAA+C,EAAE;wBAC3D,SAAS;wBACT,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,WAAW,EAAE,GAAG,CAAC,MAAM;wBACvB,cAAc,EAAE,MAAM;qBACvB,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,MAAM,0BAA0B,CAAC;wBACjD,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,OAAO,EAAE,GAAG,CAAC,OAAO;wBACpB,MAAM;qBACP,CAAC,CAAC;oBACH,IAAI,CAAC,SAAS,EAAE,CAAC;wBACf,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,2BAA2B,EAAE;4BACzD,SAAS;4BACT,KAAK,EAAE,GAAG,CAAC,KAAK;4BAChB,MAAM;yBACP,CAAC,CAAC;wBACH,SAAS;oBACX,CAAC;oBAED,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK;yBAC7B,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC;yBACjC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;yBACpB,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACpB,MAAM,QAAQ,GAAG,GAAG,cAAc,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;oBACjF,MAAM,QAAQ,GAAG,mBAAmB,WAAW,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC;oBAEtE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,yBAAyB,EAAE;wBACtD,SAAS;wBACT,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,MAAM,EAAE,SAAS,CAAC,MAAM;qBACzB,CAAC,CAAC;oBAEH,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;wBAChD,QAAQ,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,QAAQ,EAAE;qBAC9C,CAAC,CAAC;oBAEH,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,wBAAwB,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;oBAEhF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;wBACpC,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,SAAS,CAAC,QAAQ;4BACxB,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM;4BAC5B,MAAM,EAAE,WAAW,CAAC,WAAW;4BAC/B,cAAc;4BACd,SAAS,EAAE,WAAW,CAAC,WAAW;4BAClC,YAAY,EAAE,WAAW;4BACzB,UAAU,EAAE,IAAI,IAAI,EAAE;yBACvB;qBACF,CAAC,CAAC;oBAEH,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBAC5B,iBAAiB,CAAC,IAAI,CAAC;wBACrB,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,QAAQ,EAAE,QAAQ;wBAClB,MAAM,EAAE,SAAS,CAAC,MAAM;qBACzB,CAAC,CAAC;gBACL,CAAC;gBAAC,OAAO,QAAQ,EAAE,CAAC;oBAClB,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,GAAG,CAAC,GAAG,EAAE;wBAC5D,KAAK,EAAE,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC;4BACjC,OAAO,EAAE,QAAQ,CAAC,OAAO;4BACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;4BACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;yBACpB,CAAC,CAAC,CAAC,QAAQ;wBACZ,SAAS;wBACT,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,MAAM,EAAE,GAAG,CAAC,MAAM;qBACnB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,MAAM,WAAW,GAAiB,yBAAyB,CACzD,mCAAmC,CACjC,iCAAiC,CAAC,cAAc,EAAE,YAAY,CAAC,EAC/D,iBAAiB,CAClB,CACF,CAAC;YAEF,uEAAuE;YACvE,kEAAkE;YAClE,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;gBAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,mBAAmB,EAAE,CAAC;oBAClE,SAAS;gBACX,CAAC;gBACD,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW;oBAAE,SAAS;gBAC1C,MAAM,MAAM;qBACT,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,mBAAmB,EAAE;oBACzD,SAAS,EAAE,WAAW,CAAC,WAAW;oBAClC,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC5C,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/C,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACxE,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC;qBACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACrB,CAAC;YAED,MAAM,wBAAwB,CAAC,WAAW,CAAC,WAAW,EAAE,YAAY,EAAE;gBACpE,GAAG,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI;oBAC9B,WAAW,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;iBAC9D,CAAC;gBACF,IAAI,EAAE;oBACJ,mBAAmB,EAAE,SAAS,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;wBACnD,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;wBACxE,CAAC,CAAC,IAAI;oBACR,kBAAkB,EAAE,SAAS,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;wBACjD,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;wBACrE,CAAC,CAAC,IAAI;oBACR,gBAAgB,EAAE,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;wBAC7C,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;wBAC/D,CAAC,CAAC,IAAI;oBACR,WAAW,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;iBACzD;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACtB,MAAM,CAAC,KAAK,CAAC,gDAAgD,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC;YACnG,8DAA8D;YAC9D,yDAAyD;YACzD,MAAM,wBAAwB,CAAC,WAAW,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,wBAAwB,EAAE;gBAClF,SAAS;gBACT,SAAS,EAAE,WAAW,CAAC,WAAW;aACnC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,cAAc,EAAE,CAAC;YACxB,MAAM,CAAC,KAAK,CAAC,6CAA6C,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAEvG,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE;YAC9C,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC;gBAC9B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,IAAI,EAAE,KAAK,CAAC,IAAI;aACjB,CAAC,CAAC,CAAC,KAAK;YACT,SAAS;YACT,WAAW,EAAE,WAAW,CAAC,WAAW;SACrC,CAAC,CAAC;QAEH,MAAM,oBAAoB,CAAC,WAAW,CAAC,WAAW,EAAE;YAClD,MAAM,EAAE,gCAAgC;SACzC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,qBAAqB,EAAE;gBAC/E,SAAS;gBACT,SAAS,EAAE,WAAW,CAAC,WAAW;gBAClC,KAAK,EAAE,+BAA+B;aACvC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,cAAc,EAAE,CAAC;YACxB,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACtF,CAAC;QAED,MAAM,KAAK,CAAC,CAAC,yDAAyD;IACxE,CAAC;AACH,CAAC,CAAA","debug_id":"3084d592-a0a1-5867-bf8b-f4f57ced0b40"}
|
|
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 cancelPendingAIMessage,\n completePendingAIMessage,\n failPendingAIMessage,\n} from \"../utils/inference.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { v4 } from \"uuid\";\nimport { bucket } 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 { AI_CHAT_MODEL } from \"../lib/chatAttachmentPrompt.js\";\nimport { buildLabChatAgentMessages } 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 {\n findOrCreateChildFolderByName,\n findOrCreateScopeRoot,\n} from \"../models/cloudStorage.js\";\nimport { labProposalTools, createLabProposals } from \"../services/aiProposalTools.js\";\nimport { buildPendingCreations } from \"../services/aiProposals.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\n/** Class-files folder that collects AI-generated documents (created lazily). */\nexport const AI_GENERATED_FOLDER_NAME = \"AI Generated\";\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 * 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 - propose a section 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 - propose a worksheet 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; userId?: string | null }\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 { system, messages } = await buildLabChatAgentMessages({\n context: fullLabChat.context,\n classContext,\n recentMessages: recentMessages.reverse(),\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 {\n text: streamedText,\n parts: rawStreamParts,\n cancelled,\n } = await withTimeout(\n streamChatResponse({\n conversationId,\n aiMessageId: emitOptions.aiMessageId,\n model: AI_CHAT_MODEL,\n system,\n messages,\n usageReason: 'lab_chat_response',\n attribution: {\n userId: emitOptions.userId ?? fullLabChat.createdById,\n classId: emitOptions.classId,\n },\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 if (cancelled) {\n // User stopped the generation: persist whatever streamed so far and\n // skip document generation / proposal creation entirely.\n const partialParts = finalizeDeferredToolParts(rawStreamParts);\n await cancelPendingAIMessage(emitOptions.aiMessageId, streamedText, {\n meta: {\n streamParts: partialParts.length > 0 ? partialParts : null,\n },\n });\n await pusher\n .trigger(teacherChannel(emitOptions.classId), \"lab-response-completed\", {\n labChatId,\n messageId: emitOptions.aiMessageId,\n })\n .catch(() => {});\n logger.info('AI response cancelled by user', {\n labChatId,\n conversationId,\n aiMessageId: emitOptions.aiMessageId,\n });\n return;\n }\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 // Generated files are filed into the class cloud storage so teachers\n // can find them in the class Files UI, not just as chat attachments.\n let aiFolderId: string | undefined;\n if (proposals.docs.length > 0) {\n try {\n const root = await findOrCreateScopeRoot({\n kind: \"CLASS\",\n classId: fullLabChat.classId,\n });\n const aiFolder = await findOrCreateChildFolderByName(\n root.id,\n AI_GENERATED_FOLDER_NAME,\n );\n aiFolderId = aiFolder.id;\n } catch (folderError) {\n logger.error(\"Failed to resolve AI generated class folder\", {\n error: folderError,\n labChatId,\n classId: fullLabChat.classId,\n });\n }\n }\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 ...(aiFolderId && { folderId: aiFolderId }),\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 ...(aiFolderId && { folderId: aiFolderId }),\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 // Defer-and-confirm: propose_* tool calls become pending creations the\n // teacher approves or dismisses; nothing is created at generation time.\n const { pendingCreations, parts: streamPartsWithProposals } =\n buildPendingCreations(\n proposals,\n finalizeDeferredToolParts(\n enrichStreamPartsWithGeneratedFiles(\n applyResolvedFormatsToStreamParts(rawStreamParts, resolvedDocs),\n generatedDocFiles,\n ),\n ),\n );\n const streamParts: StreamPart[] = streamPartsWithProposals;\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 ...(part.folderId ? { folderId: part.folderId } : {}),\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 pendingCreations: pendingCreations.length > 0 ? pendingCreations : 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":"a0910d42-509a-534e-ad83-e5ccb2a920c0"}
|
|
@@ -244,14 +244,182 @@ export declare const docToCreateSchema: z.ZodObject<{
|
|
|
244
244
|
}, "strip", z.ZodTypeAny, {
|
|
245
245
|
title: string;
|
|
246
246
|
content: string;
|
|
247
|
-
format: "jpg" | "jpeg" | "png" | "gif" | "html" | "
|
|
247
|
+
format: "jpg" | "jpeg" | "png" | "gif" | "html" | "json" | "pdf" | "docx" | "doc" | "xlsx" | "pptx" | "csv" | "md" | "txt" | "xml" | "tex" | "zip" | "tar" | "py" | "js" | "ts" | "css" | "sh";
|
|
248
248
|
}, {
|
|
249
249
|
title: string;
|
|
250
250
|
content: string;
|
|
251
|
-
format?: "jpg" | "jpeg" | "png" | "gif" | "html" | "
|
|
251
|
+
format?: "jpg" | "jpeg" | "png" | "gif" | "html" | "json" | "pdf" | "docx" | "doc" | "xlsx" | "pptx" | "csv" | "md" | "txt" | "xml" | "tex" | "zip" | "tar" | "py" | "js" | "ts" | "css" | "sh" | undefined;
|
|
252
|
+
}>;
|
|
253
|
+
export declare const assignmentUpdateSchema: z.ZodObject<{
|
|
254
|
+
id: z.ZodString;
|
|
255
|
+
targetTitle: z.ZodString;
|
|
256
|
+
title: z.ZodOptional<z.ZodString>;
|
|
257
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
258
|
+
dueDate: z.ZodOptional<z.ZodString>;
|
|
259
|
+
type: z.ZodOptional<z.ZodEnum<["HOMEWORK", "QUIZ", "TEST", "PROJECT", "ESSAY", "DISCUSSION", "PRESENTATION", "LAB", "OTHER"]>>;
|
|
260
|
+
maxGrade: z.ZodOptional<z.ZodNumber>;
|
|
261
|
+
acceptFiles: z.ZodOptional<z.ZodBoolean>;
|
|
262
|
+
acceptExtendedResponse: z.ZodOptional<z.ZodBoolean>;
|
|
263
|
+
acceptWorksheet: z.ZodOptional<z.ZodBoolean>;
|
|
264
|
+
sectionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
265
|
+
}, "strip", z.ZodTypeAny, {
|
|
266
|
+
id: string;
|
|
267
|
+
targetTitle: string;
|
|
268
|
+
type?: "DISCUSSION" | "HOMEWORK" | "QUIZ" | "TEST" | "PROJECT" | "ESSAY" | "PRESENTATION" | "LAB" | "OTHER" | undefined;
|
|
269
|
+
title?: string | undefined;
|
|
270
|
+
dueDate?: string | undefined;
|
|
271
|
+
maxGrade?: number | undefined;
|
|
272
|
+
instructions?: string | undefined;
|
|
273
|
+
acceptFiles?: boolean | undefined;
|
|
274
|
+
acceptExtendedResponse?: boolean | undefined;
|
|
275
|
+
acceptWorksheet?: boolean | undefined;
|
|
276
|
+
sectionId?: string | null | undefined;
|
|
277
|
+
}, {
|
|
278
|
+
id: string;
|
|
279
|
+
targetTitle: string;
|
|
280
|
+
type?: "DISCUSSION" | "HOMEWORK" | "QUIZ" | "TEST" | "PROJECT" | "ESSAY" | "PRESENTATION" | "LAB" | "OTHER" | undefined;
|
|
281
|
+
title?: string | undefined;
|
|
282
|
+
dueDate?: string | undefined;
|
|
283
|
+
maxGrade?: number | undefined;
|
|
284
|
+
instructions?: string | undefined;
|
|
285
|
+
acceptFiles?: boolean | undefined;
|
|
286
|
+
acceptExtendedResponse?: boolean | undefined;
|
|
287
|
+
acceptWorksheet?: boolean | undefined;
|
|
288
|
+
sectionId?: string | null | undefined;
|
|
289
|
+
}>;
|
|
290
|
+
export declare const worksheetUpdateSchema: z.ZodObject<{
|
|
291
|
+
id: z.ZodString;
|
|
292
|
+
targetTitle: z.ZodString;
|
|
293
|
+
name: z.ZodOptional<z.ZodString>;
|
|
294
|
+
addQuestions: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
295
|
+
type: z.ZodEnum<["MULTIPLE_CHOICE", "TRUE_FALSE", "SHORT_ANSWER", "LONG_ANSWER", "MATH_EXPRESSION", "ESSAY"]>;
|
|
296
|
+
question: z.ZodString;
|
|
297
|
+
answer: z.ZodString;
|
|
298
|
+
options: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
299
|
+
id: z.ZodString;
|
|
300
|
+
text: z.ZodString;
|
|
301
|
+
isCorrect: z.ZodBoolean;
|
|
302
|
+
}, "strip", z.ZodTypeAny, {
|
|
303
|
+
id: string;
|
|
304
|
+
text: string;
|
|
305
|
+
isCorrect: boolean;
|
|
306
|
+
}, {
|
|
307
|
+
id: string;
|
|
308
|
+
text: string;
|
|
309
|
+
isCorrect: boolean;
|
|
310
|
+
}>, "many">>>;
|
|
311
|
+
markScheme: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
312
|
+
id: z.ZodString;
|
|
313
|
+
points: z.ZodNumber;
|
|
314
|
+
description: z.ZodString;
|
|
315
|
+
}, "strip", z.ZodTypeAny, {
|
|
316
|
+
id: string;
|
|
317
|
+
description: string;
|
|
318
|
+
points: number;
|
|
319
|
+
}, {
|
|
320
|
+
id: string;
|
|
321
|
+
description: string;
|
|
322
|
+
points: number;
|
|
323
|
+
}>, "many">>>;
|
|
324
|
+
points: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
325
|
+
order: z.ZodNumber;
|
|
326
|
+
}, "strip", z.ZodTypeAny, {
|
|
327
|
+
type: "ESSAY" | "MULTIPLE_CHOICE" | "TRUE_FALSE" | "SHORT_ANSWER" | "LONG_ANSWER" | "MATH_EXPRESSION";
|
|
328
|
+
options: {
|
|
329
|
+
id: string;
|
|
330
|
+
text: string;
|
|
331
|
+
isCorrect: boolean;
|
|
332
|
+
}[];
|
|
333
|
+
markScheme: {
|
|
334
|
+
id: string;
|
|
335
|
+
description: string;
|
|
336
|
+
points: number;
|
|
337
|
+
}[];
|
|
338
|
+
points: number;
|
|
339
|
+
question: string;
|
|
340
|
+
order: number;
|
|
341
|
+
answer: string;
|
|
342
|
+
}, {
|
|
343
|
+
type: "ESSAY" | "MULTIPLE_CHOICE" | "TRUE_FALSE" | "SHORT_ANSWER" | "LONG_ANSWER" | "MATH_EXPRESSION";
|
|
344
|
+
question: string;
|
|
345
|
+
order: number;
|
|
346
|
+
answer: string;
|
|
347
|
+
options?: {
|
|
348
|
+
id: string;
|
|
349
|
+
text: string;
|
|
350
|
+
isCorrect: boolean;
|
|
351
|
+
}[] | undefined;
|
|
352
|
+
markScheme?: {
|
|
353
|
+
id: string;
|
|
354
|
+
description: string;
|
|
355
|
+
points: number;
|
|
356
|
+
}[] | undefined;
|
|
357
|
+
points?: number | undefined;
|
|
358
|
+
}>, "many">>>;
|
|
359
|
+
}, "strip", z.ZodTypeAny, {
|
|
360
|
+
id: string;
|
|
361
|
+
targetTitle: string;
|
|
362
|
+
addQuestions: {
|
|
363
|
+
type: "ESSAY" | "MULTIPLE_CHOICE" | "TRUE_FALSE" | "SHORT_ANSWER" | "LONG_ANSWER" | "MATH_EXPRESSION";
|
|
364
|
+
options: {
|
|
365
|
+
id: string;
|
|
366
|
+
text: string;
|
|
367
|
+
isCorrect: boolean;
|
|
368
|
+
}[];
|
|
369
|
+
markScheme: {
|
|
370
|
+
id: string;
|
|
371
|
+
description: string;
|
|
372
|
+
points: number;
|
|
373
|
+
}[];
|
|
374
|
+
points: number;
|
|
375
|
+
question: string;
|
|
376
|
+
order: number;
|
|
377
|
+
answer: string;
|
|
378
|
+
}[];
|
|
379
|
+
name?: string | undefined;
|
|
380
|
+
}, {
|
|
381
|
+
id: string;
|
|
382
|
+
targetTitle: string;
|
|
383
|
+
name?: string | undefined;
|
|
384
|
+
addQuestions?: {
|
|
385
|
+
type: "ESSAY" | "MULTIPLE_CHOICE" | "TRUE_FALSE" | "SHORT_ANSWER" | "LONG_ANSWER" | "MATH_EXPRESSION";
|
|
386
|
+
question: string;
|
|
387
|
+
order: number;
|
|
388
|
+
answer: string;
|
|
389
|
+
options?: {
|
|
390
|
+
id: string;
|
|
391
|
+
text: string;
|
|
392
|
+
isCorrect: boolean;
|
|
393
|
+
}[] | undefined;
|
|
394
|
+
markScheme?: {
|
|
395
|
+
id: string;
|
|
396
|
+
description: string;
|
|
397
|
+
points: number;
|
|
398
|
+
}[] | undefined;
|
|
399
|
+
points?: number | undefined;
|
|
400
|
+
}[] | undefined;
|
|
401
|
+
}>;
|
|
402
|
+
export declare const sectionUpdateSchema: z.ZodObject<{
|
|
403
|
+
id: z.ZodString;
|
|
404
|
+
targetTitle: z.ZodString;
|
|
405
|
+
name: z.ZodString;
|
|
406
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
407
|
+
}, "strip", z.ZodTypeAny, {
|
|
408
|
+
id: string;
|
|
409
|
+
name: string;
|
|
410
|
+
targetTitle: string;
|
|
411
|
+
color?: string | null | undefined;
|
|
412
|
+
}, {
|
|
413
|
+
id: string;
|
|
414
|
+
name: string;
|
|
415
|
+
targetTitle: string;
|
|
416
|
+
color?: string | null | undefined;
|
|
252
417
|
}>;
|
|
253
418
|
export type WorksheetToCreate = z.infer<typeof worksheetToCreateSchema>;
|
|
254
419
|
export type SectionToCreate = z.infer<typeof sectionToCreateSchema>;
|
|
255
420
|
export type AssignmentToCreate = z.infer<typeof assignmentToCreateSchema>;
|
|
256
421
|
export type DocToCreate = z.infer<typeof docToCreateSchema>;
|
|
422
|
+
export type AssignmentUpdate = z.infer<typeof assignmentUpdateSchema>;
|
|
423
|
+
export type WorksheetUpdate = z.infer<typeof worksheetUpdateSchema>;
|
|
424
|
+
export type SectionUpdate = z.infer<typeof sectionUpdateSchema>;
|
|
257
425
|
//# sourceMappingURL=aiLabChatContract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiLabChatContract.d.ts","sourceRoot":"/","sources":["pipelines/aiLabChatContract.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBlC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGlC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;EAGhC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBnC,CAAC;AAEH,eAAO,MAAM,oBAAoB,+KAI/B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;EAI5B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACxE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"aiLabChatContract.d.ts","sourceRoot":"/","sources":["pipelines/aiLabChatContract.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBlC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGlC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;EAGhC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBnC,CAAC;AAEH,eAAO,MAAM,oBAAoB,+KAI/B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;EAI5B,CAAC;AAMH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYjC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKhC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACxE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c00c6112-86d0-5fea-b720-24d9bd40bfdc")}catch(e){}}();
|
|
3
3
|
import z from "zod";
|
|
4
4
|
export const worksheetQuestionSchema = z.object({
|
|
5
5
|
type: z.enum(["MULTIPLE_CHOICE", "TRUE_FALSE", "SHORT_ANSWER", "LONG_ANSWER", "MATH_EXPRESSION", "ESSAY"]),
|
|
@@ -54,5 +54,33 @@ export const docToCreateSchema = z.object({
|
|
|
54
54
|
content: z.string().min(1),
|
|
55
55
|
format: documentFormatSchema.optional().default('pdf'),
|
|
56
56
|
});
|
|
57
|
+
const assignmentTypeSchema = z.enum(["HOMEWORK", "QUIZ", "TEST", "PROJECT", "ESSAY", "DISCUSSION", "PRESENTATION", "LAB", "OTHER"]);
|
|
58
|
+
// Update payloads: `id` targets an existing entity; `targetTitle` is the
|
|
59
|
+
// entity's current name for the approval card. Only provided fields change.
|
|
60
|
+
export const assignmentUpdateSchema = z.object({
|
|
61
|
+
id: z.string(),
|
|
62
|
+
targetTitle: z.string(),
|
|
63
|
+
title: z.string().optional(),
|
|
64
|
+
instructions: z.string().optional(),
|
|
65
|
+
dueDate: z.string().datetime().optional(),
|
|
66
|
+
type: assignmentTypeSchema.optional(),
|
|
67
|
+
maxGrade: z.number().optional(),
|
|
68
|
+
acceptFiles: z.boolean().optional(),
|
|
69
|
+
acceptExtendedResponse: z.boolean().optional(),
|
|
70
|
+
acceptWorksheet: z.boolean().optional(),
|
|
71
|
+
sectionId: z.string().nullable().optional(),
|
|
72
|
+
});
|
|
73
|
+
export const worksheetUpdateSchema = z.object({
|
|
74
|
+
id: z.string(),
|
|
75
|
+
targetTitle: z.string(),
|
|
76
|
+
name: z.string().optional(),
|
|
77
|
+
addQuestions: z.array(worksheetQuestionSchema).optional().default([]),
|
|
78
|
+
});
|
|
79
|
+
export const sectionUpdateSchema = z.object({
|
|
80
|
+
id: z.string(),
|
|
81
|
+
targetTitle: z.string(),
|
|
82
|
+
name: z.string(),
|
|
83
|
+
color: z.string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/).nullable().optional(),
|
|
84
|
+
});
|
|
57
85
|
//# sourceMappingURL=aiLabChatContract.js.map
|
|
58
|
-
//# debugId=
|
|
86
|
+
//# debugId=c00c6112-86d0-5fea-b720-24d9bd40bfdc
|