@studious-lms/server 1.2.46 → 1.2.48
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/dist/index.js +22 -18
- package/dist/index.js.map +1 -1
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +3 -2
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/security.d.ts.map +1 -1
- package/dist/middleware/security.js +4 -4
- package/dist/middleware/security.js.map +1 -1
- package/dist/routers/_app.d.ts +126 -26
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/assignment.d.ts +10 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +18 -3
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/class.d.ts +18 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +56 -2
- package/dist/routers/class.js.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 +1 -0
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +5 -321
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/message.d.ts +1 -0
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +3 -2
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/newtonChat.d.ts.map +1 -1
- package/dist/routers/newtonChat.js +6 -183
- package/dist/routers/newtonChat.js.map +1 -1
- package/dist/routers/section.d.ts +10 -0
- package/dist/routers/section.d.ts.map +1 -1
- package/dist/routers/section.js +21 -3
- package/dist/routers/section.js.map +1 -1
- package/dist/routers/worksheet.d.ts +22 -13
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +16 -3
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +34 -8
- package/dist/seedDatabase.js.map +1 -1
- package/dist/server/pipelines/aiLabChat.d.ts +12 -1
- package/dist/server/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/server/pipelines/aiLabChat.js +388 -15
- package/dist/server/pipelines/aiLabChat.js.map +1 -1
- package/dist/server/pipelines/aiNewtonChat.d.ts +30 -0
- package/dist/server/pipelines/aiNewtonChat.d.ts.map +1 -0
- package/dist/server/pipelines/aiNewtonChat.js +280 -0
- package/dist/server/pipelines/aiNewtonChat.js.map +1 -0
- package/dist/server/pipelines/gradeWorksheet.d.ts +14 -1
- package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/server/pipelines/gradeWorksheet.js +6 -5
- package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/utils/inference.d.ts +3 -1
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +34 -4
- package/dist/utils/inference.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +2 -0
- package/src/index.ts +24 -22
- package/src/middleware/auth.ts +1 -0
- package/src/middleware/security.ts +2 -2
- package/src/routers/assignment.ts +17 -2
- package/src/routers/class.ts +55 -0
- package/src/routers/labChat.ts +3 -366
- package/src/routers/message.ts +1 -1
- package/src/routers/newtonChat.ts +8 -231
- package/src/routers/section.ts +21 -1
- package/src/routers/worksheet.ts +17 -1
- package/src/seedDatabase.ts +38 -6
- package/src/server/pipelines/aiLabChat.ts +434 -19
- package/src/server/pipelines/aiNewtonChat.ts +338 -0
- package/src/server/pipelines/gradeWorksheet.ts +3 -4
- package/src/utils/inference.ts +40 -5
package/dist/routers/labChat.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
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]="933db03e-fc38-599b-93a4-890502ba8281")}catch(e){}}();
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { createTRPCRouter, protectedProcedure } from '../trpc.js';
|
|
5
5
|
import { prisma } from '../lib/prisma.js';
|
|
6
6
|
import { pusher } from '../lib/pusher.js';
|
|
7
7
|
import { TRPCError } from '@trpc/server';
|
|
8
|
-
import { inferenceClient, sendAIMessage } from '../utils/inference.js';
|
|
9
|
-
import { logger } from '../utils/logger.js';
|
|
10
8
|
import { isAIUser } from '../utils/aiUser.js';
|
|
11
|
-
import {
|
|
12
|
-
import { createPdf } from "../lib/jsonConversion.js";
|
|
13
|
-
import { v4 as uuidv4 } from "uuid";
|
|
9
|
+
import { generateAndSendLabIntroduction, generateAndSendLabResponse } from '../server/pipelines/aiLabChat.js';
|
|
14
10
|
export const labChatRouter = createTRPCRouter({
|
|
15
11
|
create: protectedProcedure
|
|
16
12
|
.input(z.object({
|
|
@@ -150,9 +146,7 @@ export const labChatRouter = createTRPCRouter({
|
|
|
150
146
|
return labChat;
|
|
151
147
|
});
|
|
152
148
|
// Generate AI introduction message in parallel (don't await - fire and forget)
|
|
153
|
-
generateAndSendLabIntroduction(result.id, result.conversationId, context, classWithTeachers.subject || 'Lab')
|
|
154
|
-
logger.error('Failed to generate AI introduction:', { error, labChatId: result.id });
|
|
155
|
-
});
|
|
149
|
+
generateAndSendLabIntroduction(result.id, result.conversationId, context, classWithTeachers.subject || 'Lab');
|
|
156
150
|
// Broadcast lab chat creation to class members
|
|
157
151
|
try {
|
|
158
152
|
await pusher.trigger(`class-${classId}`, 'lab-chat-created', {
|
|
@@ -518,9 +512,7 @@ export const labChatRouter = createTRPCRouter({
|
|
|
518
512
|
// Generate AI response in parallel (don't await - fire and forget)
|
|
519
513
|
if (!isAIUser(userId)) {
|
|
520
514
|
// Run AI response generation in background
|
|
521
|
-
generateAndSendLabResponse(labChatId, content, labChat.conversationId)
|
|
522
|
-
logger.error('Failed to generate AI response:', { error });
|
|
523
|
-
});
|
|
515
|
+
generateAndSendLabResponse(labChatId, content, labChat.conversationId);
|
|
524
516
|
}
|
|
525
517
|
return {
|
|
526
518
|
id: result.id,
|
|
@@ -595,313 +587,5 @@ export const labChatRouter = createTRPCRouter({
|
|
|
595
587
|
return { success: true };
|
|
596
588
|
}),
|
|
597
589
|
});
|
|
598
|
-
/**
|
|
599
|
-
* Generate and send AI introduction for lab chat
|
|
600
|
-
* Uses the stored context directly from database
|
|
601
|
-
*/
|
|
602
|
-
async function generateAndSendLabIntroduction(labChatId, conversationId, contextString, subject) {
|
|
603
|
-
try {
|
|
604
|
-
// Enhance the stored context with clarifying question instructions
|
|
605
|
-
const enhancedSystemPrompt = `${contextString}
|
|
606
|
-
|
|
607
|
-
IMPORTANT INSTRUCTIONS:
|
|
608
|
-
- You are helping teachers create course materials
|
|
609
|
-
- Use the context information provided above (subject, topic, difficulty, objectives, etc.) as your foundation
|
|
610
|
-
- Only ask clarifying questions about details NOT already specified in the context
|
|
611
|
-
- Focus your questions on format preferences, specific requirements, or missing details needed to create the content
|
|
612
|
-
- Only output final course materials when you have sufficient details beyond what's in the context
|
|
613
|
-
- Do not use markdown formatting in your responses - use plain text only
|
|
614
|
-
- When creating content, make it clear and well-structured without markdown`;
|
|
615
|
-
const completion = await inferenceClient.chat.completions.create({
|
|
616
|
-
model: 'command-a-03-2025',
|
|
617
|
-
messages: [
|
|
618
|
-
{ role: 'system', content: enhancedSystemPrompt },
|
|
619
|
-
{
|
|
620
|
-
role: 'user',
|
|
621
|
-
content: 'Please introduce yourself to the teaching team. Explain that you will help create course materials by first asking clarifying questions based on the context provided, and only output final content when you have enough information.'
|
|
622
|
-
},
|
|
623
|
-
],
|
|
624
|
-
max_tokens: 300,
|
|
625
|
-
temperature: 0.8,
|
|
626
|
-
});
|
|
627
|
-
const response = completion.choices[0]?.message?.content;
|
|
628
|
-
if (!response) {
|
|
629
|
-
throw new Error('No response generated from inference API');
|
|
630
|
-
}
|
|
631
|
-
// Send AI introduction using centralized sender
|
|
632
|
-
await sendAIMessage(response, conversationId, {
|
|
633
|
-
subject,
|
|
634
|
-
});
|
|
635
|
-
logger.info('AI Introduction sent', { labChatId, conversationId });
|
|
636
|
-
}
|
|
637
|
-
catch (error) {
|
|
638
|
-
logger.error('Failed to generate AI introduction:', { error, labChatId });
|
|
639
|
-
// Send fallback introduction
|
|
640
|
-
try {
|
|
641
|
-
const fallbackIntro = `Hello teaching team! I'm your AI assistant for course material development. I will help you create educational content by first asking clarifying questions based on the provided context, then outputting final materials when I have sufficient information. I won't use markdown formatting in my responses. What would you like to work on?`;
|
|
642
|
-
await sendAIMessage(fallbackIntro, conversationId, {
|
|
643
|
-
subject,
|
|
644
|
-
});
|
|
645
|
-
logger.info('Fallback AI introduction sent', { labChatId });
|
|
646
|
-
}
|
|
647
|
-
catch (fallbackError) {
|
|
648
|
-
logger.error('Failed to send fallback AI introduction:', { error: fallbackError, labChatId });
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Generate and send AI response to teacher message
|
|
654
|
-
* Uses the stored context directly from database
|
|
655
|
-
*/
|
|
656
|
-
async function generateAndSendLabResponse(labChatId, teacherMessage, conversationId) {
|
|
657
|
-
try {
|
|
658
|
-
// Get lab context from database
|
|
659
|
-
const fullLabChat = await prisma.labChat.findUnique({
|
|
660
|
-
where: { id: labChatId },
|
|
661
|
-
include: {
|
|
662
|
-
class: {
|
|
663
|
-
select: {
|
|
664
|
-
name: true,
|
|
665
|
-
subject: true,
|
|
666
|
-
},
|
|
667
|
-
},
|
|
668
|
-
},
|
|
669
|
-
});
|
|
670
|
-
if (!fullLabChat) {
|
|
671
|
-
throw new Error('Lab chat not found');
|
|
672
|
-
}
|
|
673
|
-
// Get recent conversation history
|
|
674
|
-
const recentMessages = await prisma.message.findMany({
|
|
675
|
-
where: {
|
|
676
|
-
conversationId,
|
|
677
|
-
},
|
|
678
|
-
include: {
|
|
679
|
-
sender: {
|
|
680
|
-
select: {
|
|
681
|
-
id: true,
|
|
682
|
-
username: true,
|
|
683
|
-
profile: {
|
|
684
|
-
select: {
|
|
685
|
-
displayName: true,
|
|
686
|
-
},
|
|
687
|
-
},
|
|
688
|
-
},
|
|
689
|
-
},
|
|
690
|
-
},
|
|
691
|
-
orderBy: {
|
|
692
|
-
createdAt: 'desc',
|
|
693
|
-
},
|
|
694
|
-
take: 10, // Last 10 messages for context
|
|
695
|
-
});
|
|
696
|
-
// Build conversation history as proper message objects
|
|
697
|
-
// Enhance the stored context with clarifying question instructions
|
|
698
|
-
const enhancedSystemPrompt = `${fullLabChat.context}
|
|
699
|
-
|
|
700
|
-
IMPORTANT INSTRUCTIONS:
|
|
701
|
-
- Use the context information provided above (subject, topic, difficulty, objectives, etc.) as your foundation
|
|
702
|
-
- Based on the teacher's input and existing context, only ask clarifying questions about details NOT already specified
|
|
703
|
-
- Focus questions on format preferences, specific requirements, quantity, or missing implementation details
|
|
704
|
-
- Only output final course materials when you have sufficient details beyond what's in the context
|
|
705
|
-
- Do not use markdown formatting in your responses - use plain text only
|
|
706
|
-
- When you do create content, make it clear and well-structured without markdown
|
|
707
|
-
- If the request is vague, ask 1-2 specific clarifying questions about missing details only
|
|
708
|
-
- You are primarily a chatbot - only provide files when it is necessary
|
|
709
|
-
|
|
710
|
-
RESPONSE FORMAT:
|
|
711
|
-
- Always respond with JSON in this format: { "text": string, "docs": null | array }
|
|
712
|
-
- "text": Your conversational response (questions, explanations, etc.) - use plain text, no markdown
|
|
713
|
-
- "docs": null for regular conversation, or array of PDF document objects when creating course materials
|
|
714
|
-
|
|
715
|
-
WHEN CREATING COURSE MATERIALS (docs field):
|
|
716
|
-
- docs: [ { "title": string, "blocks": [ { "format": <int 0-12>, "content": string | string[], "metadata"?: { fontSize?: number, lineHeight?: number, paragraphSpacing?: number, indentWidth?: number, paddingX?: number, paddingY?: number, font?: 0|1|2|3|4|5, color?: "#RGB"|"#RRGGBB", background?: "#RGB"|"#RRGGBB", align?: "left"|"center"|"right" } } ] } ]
|
|
717
|
-
- Each document in the array should have a "title" (used for filename) and "blocks" (content)
|
|
718
|
-
- You can create multiple documents when it makes sense (e.g., separate worksheets, answer keys, different topics)
|
|
719
|
-
- Use descriptive titles like "Biology_Cell_Structure_Worksheet" or "Chemistry_Lab_Instructions"
|
|
720
|
-
- Format enum (integers): 0=HEADER_1, 1=HEADER_2, 2=HEADER_3, 3=HEADER_4, 4=HEADER_5, 5=HEADER_6, 6=PARAGRAPH, 7=BULLET, 8=NUMBERED, 9=TABLE, 10=IMAGE, 11=CODE_BLOCK, 12=QUOTE
|
|
721
|
-
- Fonts enum: 0=TIMES_ROMAN, 1=COURIER, 2=HELVETICA, 3=HELVETICA_BOLD, 4=HELVETICA_ITALIC, 5=HELVETICA_BOLD_ITALIC
|
|
722
|
-
- Colors must be hex strings: "#RGB" or "#RRGGBB".
|
|
723
|
-
- Headings (0-5): content is a single string; you may set metadata.align.
|
|
724
|
-
- Paragraphs (6) and Quotes (12): content is a single string.
|
|
725
|
-
- Bullets (7) and Numbered (8): content is an array of strings (one item per list entry). DO NOT include bullet symbols (*) or numbers (1. 2. 3.) in the content - the format will automatically add these.
|
|
726
|
-
- Code blocks (11): prefer content as an array of lines; preserve indentation via leading tabs/spaces. If using a single string, include \n between lines.
|
|
727
|
-
- Table (9) and Image (10) are not supported by the renderer now; do not emit them.
|
|
728
|
-
- Use metadata sparingly; omit fields you don't need. For code blocks you may set metadata.paddingX, paddingY, background, and font (1 for Courier).
|
|
729
|
-
- Wrap text naturally; do not insert manual line breaks except where semantically required (lists, code).
|
|
730
|
-
- The JSON must be valid and ready for PDF rendering by the server.`;
|
|
731
|
-
const messages = [
|
|
732
|
-
{ role: 'system', content: enhancedSystemPrompt },
|
|
733
|
-
];
|
|
734
|
-
// Add recent conversation history
|
|
735
|
-
recentMessages.reverse().forEach(msg => {
|
|
736
|
-
const role = isAIUser(msg.senderId) ? 'assistant' : 'user';
|
|
737
|
-
const senderName = msg.sender?.profile?.displayName || msg.sender?.username || 'Teacher';
|
|
738
|
-
const content = isAIUser(msg.senderId) ? msg.content : `${senderName}: ${msg.content}`;
|
|
739
|
-
messages.push({
|
|
740
|
-
role: role,
|
|
741
|
-
content,
|
|
742
|
-
});
|
|
743
|
-
});
|
|
744
|
-
// Add the new teacher message
|
|
745
|
-
const senderName = 'Teacher'; // We could get this from the actual sender if needed
|
|
746
|
-
messages.push({
|
|
747
|
-
role: 'user',
|
|
748
|
-
content: `${senderName}: ${teacherMessage}`,
|
|
749
|
-
});
|
|
750
|
-
const completion = await inferenceClient.chat.completions.create({
|
|
751
|
-
model: 'command-a-03-2025',
|
|
752
|
-
messages,
|
|
753
|
-
temperature: 0.7,
|
|
754
|
-
response_format: {
|
|
755
|
-
type: "json_object",
|
|
756
|
-
// @ts-expect-error
|
|
757
|
-
schema: {
|
|
758
|
-
type: "object",
|
|
759
|
-
properties: {
|
|
760
|
-
text: { type: "string" },
|
|
761
|
-
docs: {
|
|
762
|
-
type: "array",
|
|
763
|
-
items: {
|
|
764
|
-
type: "object",
|
|
765
|
-
properties: {
|
|
766
|
-
title: { type: "string" },
|
|
767
|
-
blocks: {
|
|
768
|
-
type: "array",
|
|
769
|
-
items: {
|
|
770
|
-
type: "object",
|
|
771
|
-
properties: {
|
|
772
|
-
format: { type: "integer", minimum: 0, maximum: 12 },
|
|
773
|
-
content: {
|
|
774
|
-
oneOf: [
|
|
775
|
-
{ type: "string" },
|
|
776
|
-
{ type: "array", items: { type: "string" } }
|
|
777
|
-
]
|
|
778
|
-
},
|
|
779
|
-
metadata: {
|
|
780
|
-
type: "object",
|
|
781
|
-
properties: {
|
|
782
|
-
fontSize: { type: "number", minimum: 6 },
|
|
783
|
-
lineHeight: { type: "number", minimum: 0.6 },
|
|
784
|
-
paragraphSpacing: { type: "number", minimum: 0 },
|
|
785
|
-
indentWidth: { type: "number", minimum: 0 },
|
|
786
|
-
paddingX: { type: "number", minimum: 0 },
|
|
787
|
-
paddingY: { type: "number", minimum: 0 },
|
|
788
|
-
font: { type: "integer", minimum: 0, maximum: 5 },
|
|
789
|
-
color: { type: "string", pattern: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" },
|
|
790
|
-
background: { type: "string", pattern: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" },
|
|
791
|
-
align: { type: "string", enum: ["left", "center", "right"] }
|
|
792
|
-
},
|
|
793
|
-
additionalProperties: false
|
|
794
|
-
}
|
|
795
|
-
},
|
|
796
|
-
required: ["format", "content"],
|
|
797
|
-
additionalProperties: false
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
},
|
|
801
|
-
required: ["title", "blocks"],
|
|
802
|
-
additionalProperties: false
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
},
|
|
806
|
-
required: ["text"],
|
|
807
|
-
additionalProperties: false
|
|
808
|
-
}
|
|
809
|
-
},
|
|
810
|
-
});
|
|
811
|
-
const response = completion.choices[0]?.message?.content;
|
|
812
|
-
if (!response) {
|
|
813
|
-
throw new Error('No response generated from inference API');
|
|
814
|
-
}
|
|
815
|
-
// Parse the JSON response and generate PDF if docs are provided
|
|
816
|
-
try {
|
|
817
|
-
const jsonData = JSON.parse(response);
|
|
818
|
-
const attachmentIds = [];
|
|
819
|
-
// Generate PDFs if docs are provided
|
|
820
|
-
if (jsonData.docs && Array.isArray(jsonData.docs)) {
|
|
821
|
-
for (let i = 0; i < jsonData.docs.length; i++) {
|
|
822
|
-
const doc = jsonData.docs[i];
|
|
823
|
-
if (!doc.title || !doc.blocks || !Array.isArray(doc.blocks)) {
|
|
824
|
-
logger.error(`Document ${i + 1} is missing title or blocks`);
|
|
825
|
-
continue;
|
|
826
|
-
}
|
|
827
|
-
try {
|
|
828
|
-
let pdfBytes = await createPdf(doc.blocks);
|
|
829
|
-
if (pdfBytes) {
|
|
830
|
-
// Sanitize filename - remove special characters and limit length
|
|
831
|
-
const sanitizedTitle = doc.title
|
|
832
|
-
.replace(/[^a-zA-Z0-9\s\-_]/g, '')
|
|
833
|
-
.replace(/\s+/g, '_')
|
|
834
|
-
.substring(0, 50);
|
|
835
|
-
const filename = `${sanitizedTitle}_${uuidv4().substring(0, 8)}.pdf`;
|
|
836
|
-
const filePath = `class/generated/${fullLabChat.classId}/${filename}`;
|
|
837
|
-
logger.info(`PDF ${i + 1} generated successfully`, { labChatId, title: doc.title });
|
|
838
|
-
// Upload directly to Google Cloud Storage
|
|
839
|
-
const gcsFile = bucket.file(filePath);
|
|
840
|
-
await gcsFile.save(Buffer.from(pdfBytes), {
|
|
841
|
-
metadata: {
|
|
842
|
-
contentType: 'application/pdf',
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
logger.info(`PDF ${i + 1} uploaded successfully`, { labChatId, filename });
|
|
846
|
-
const file = await prisma.file.create({
|
|
847
|
-
data: {
|
|
848
|
-
name: filename,
|
|
849
|
-
path: filePath,
|
|
850
|
-
type: 'application/pdf',
|
|
851
|
-
size: pdfBytes.length,
|
|
852
|
-
userId: fullLabChat.createdById,
|
|
853
|
-
uploadStatus: 'COMPLETED',
|
|
854
|
-
uploadedAt: new Date(),
|
|
855
|
-
},
|
|
856
|
-
});
|
|
857
|
-
attachmentIds.push(file.id);
|
|
858
|
-
}
|
|
859
|
-
else {
|
|
860
|
-
logger.error(`PDF ${i + 1} creation returned undefined/null`, { labChatId, title: doc.title });
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
catch (pdfError) {
|
|
864
|
-
logger.error(`PDF creation threw an error for document ${i + 1}:`, {
|
|
865
|
-
error: pdfError instanceof Error ? {
|
|
866
|
-
message: pdfError.message,
|
|
867
|
-
stack: pdfError.stack,
|
|
868
|
-
name: pdfError.name
|
|
869
|
-
} : pdfError,
|
|
870
|
-
labChatId,
|
|
871
|
-
title: doc.title
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
// Send the text response to the conversation
|
|
877
|
-
await sendAIMessage(jsonData.text || response, conversationId, {
|
|
878
|
-
attachments: {
|
|
879
|
-
connect: attachmentIds.map(id => ({ id })),
|
|
880
|
-
},
|
|
881
|
-
subject: fullLabChat.class?.subject || 'Lab',
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
catch (parseError) {
|
|
885
|
-
logger.error('Failed to parse AI response or generate PDF:', { error: parseError, labChatId });
|
|
886
|
-
// Fallback: send the raw response if parsing fails
|
|
887
|
-
await sendAIMessage(response, conversationId, {
|
|
888
|
-
subject: fullLabChat.class?.subject || 'Lab',
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
logger.info('AI response sent', { labChatId, conversationId });
|
|
892
|
-
}
|
|
893
|
-
catch (error) {
|
|
894
|
-
console.error('Full error object:', error);
|
|
895
|
-
logger.error('Failed to generate AI response:', {
|
|
896
|
-
error: error instanceof Error ? {
|
|
897
|
-
message: error.message,
|
|
898
|
-
stack: error.stack,
|
|
899
|
-
name: error.name
|
|
900
|
-
} : error,
|
|
901
|
-
labChatId
|
|
902
|
-
});
|
|
903
|
-
throw error; // Re-throw to see the full error in the calling function
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
590
|
//# sourceMappingURL=labChat.js.map
|
|
907
|
-
//# debugId=
|
|
591
|
+
//# debugId=933db03e-fc38-599b-93a4-890502ba8281
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labChat.js","sources":["routers/labChat.ts"],"sourceRoot":"/","sourcesContent":["import { z } from 'zod';\nimport { createTRPCRouter, protectedProcedure } from '../trpc.js';\nimport { prisma } from '../lib/prisma.js';\nimport { pusher } from '../lib/pusher.js';\nimport { TRPCError } from '@trpc/server';\nimport { \n inferenceClient,\n sendAIMessage,\n type LabChatContext \n} from '../utils/inference.js';\nimport { logger } from '../utils/logger.js';\nimport { isAIUser } from '../utils/aiUser.js';\nimport { bucket } from '../lib/googleCloudStorage.js';\nimport { createPdf } from \"../lib/jsonConversion.js\"\nimport OpenAI from 'openai';\nimport { v4 as uuidv4 } from \"uuid\";\n\nexport const labChatRouter = createTRPCRouter({\n create: protectedProcedure\n .input(\n z.object({\n classId: z.string(),\n title: z.string().min(1).max(200),\n context: z.string(), // JSON string for LLM context\n })\n )\n .mutation(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { classId, title, context } = input;\n\n // Verify user is a teacher in the class\n const classWithTeachers = await prisma.class.findFirst({\n where: {\n id: classId,\n teachers: {\n some: {\n id: userId,\n },\n },\n },\n include: {\n students: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n teachers: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n });\n\n if (!classWithTeachers) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Not a teacher in this class',\n });\n }\n\n // Validate context is valid JSON\n try {\n JSON.parse(context);\n } catch (error) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Context must be valid JSON',\n });\n }\n\n // Create lab chat with associated conversation\n const result = await prisma.$transaction(async (tx) => {\n // Create conversation for the lab chat\n const conversation = await tx.conversation.create({\n data: {\n type: 'GROUP',\n name: `Lab: ${title}`,\n displayInChat: false, // Lab chats don't show in regular chat list\n },\n });\n\n // Add only teachers to the conversation (this is for course material creation)\n const teacherMembers = classWithTeachers.teachers.map(t => ({ \n userId: t.id, \n role: 'ADMIN' as const \n }));\n\n await tx.conversationMember.createMany({\n data: teacherMembers.map(member => ({\n userId: member.userId,\n conversationId: conversation.id,\n role: member.role,\n })),\n });\n\n // Create the lab chat\n const labChat = await tx.labChat.create({\n data: {\n title,\n context,\n classId,\n conversationId: conversation.id,\n createdById: userId,\n },\n include: {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n },\n });\n\n return labChat;\n });\n\n // Generate AI introduction message in parallel (don't await - fire and forget)\n generateAndSendLabIntroduction(result.id, result.conversationId, context, classWithTeachers.subject || 'Lab').catch(error => {\n logger.error('Failed to generate AI introduction:', { error, labChatId: result.id });\n });\n\n // Broadcast lab chat creation to class members\n try {\n await pusher.trigger(`class-${classId}`, 'lab-chat-created', {\n id: result.id,\n title: result.title,\n classId: result.classId,\n conversationId: result.conversationId,\n createdBy: result.createdBy,\n createdAt: result.createdAt,\n });\n } catch (error) {\n console.error('Failed to broadcast lab chat creation:', error);\n // Don't fail the request if Pusher fails\n }\n\n return result;\n }),\n\n get: protectedProcedure\n .input(z.object({ labChatId: z.string() }))\n .query(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { labChatId } = input;\n\n // First, try to find the lab chat if user is already a member\n let labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: {\n some: {\n userId,\n },\n },\n },\n },\n include: {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n },\n });\n\n // If not found, check if user is a teacher in the class\n if (!labChat) {\n const labChatForTeacher = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n class: {\n teachers: {\n some: {\n id: userId,\n },\n },\n },\n },\n include: {\n conversation: {\n select: {\n id: true,\n },\n },\n },\n });\n\n if (labChatForTeacher) {\n // Add teacher to conversation\n await prisma.conversationMember.create({\n data: {\n userId,\n conversationId: labChatForTeacher.conversation.id,\n role: 'ADMIN',\n },\n });\n\n // Now fetch the full lab chat with the user as a member\n labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n },\n include: {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n },\n });\n }\n }\n\n if (!labChat) {\n throw new TRPCError({\n code: 'NOT_FOUND',\n message: 'Lab chat not found or access denied',\n });\n }\n\n return labChat;\n }),\n\n list: protectedProcedure\n .input(z.object({ classId: z.string() }))\n .query(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { classId } = input;\n\n // Verify user is a member of the class\n const classMembership = await prisma.class.findFirst({\n where: {\n id: classId,\n OR: [\n {\n students: {\n some: {\n id: userId,\n },\n },\n },\n {\n teachers: {\n some: {\n id: userId,\n },\n },\n },\n ],\n },\n });\n\n if (!classMembership) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Not a member of this class',\n });\n }\n\n const labChats = await prisma.labChat.findMany({\n where: {\n classId,\n },\n include: {\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n conversation: {\n include: {\n messages: {\n orderBy: {\n createdAt: 'desc',\n },\n take: 1,\n 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 _count: {\n select: {\n messages: true,\n },\n },\n },\n },\n },\n orderBy: {\n createdAt: 'desc',\n },\n });\n\n return labChats.map((labChat) => ({\n id: labChat.id,\n title: labChat.title,\n classId: labChat.classId,\n conversationId: labChat.conversationId,\n createdBy: labChat.createdBy,\n createdAt: labChat.createdAt,\n updatedAt: labChat.updatedAt,\n lastMessage: labChat.conversation.messages[0] || null,\n messageCount: labChat.conversation._count.messages,\n }));\n }),\n\n postToLabChat: protectedProcedure\n .input(\n z.object({\n labChatId: z.string(),\n content: z.string().min(1).max(4000),\n mentionedUserIds: z.array(z.string()).optional(),\n })\n )\n .mutation(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { labChatId, content, mentionedUserIds = [] } = input;\n\n // Get lab chat and verify user is a member\n const labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: {\n some: {\n userId,\n },\n },\n },\n },\n include: {\n conversation: {\n select: {\n id: true,\n },\n },\n },\n });\n\n if (!labChat) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Lab chat not found or access denied',\n });\n }\n\n // Verify mentioned users are members of the conversation\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await prisma.conversationMember.findMany({\n where: {\n conversationId: labChat.conversationId,\n userId: { in: mentionedUserIds },\n },\n });\n\n if (mentionedMemberships.length !== mentionedUserIds.length) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Some mentioned users are not members of this lab chat',\n });\n }\n }\n\n // Create message and mentions\n const result = await prisma.$transaction(async (tx) => {\n const message = await tx.message.create({\n data: {\n content,\n senderId: userId,\n conversationId: labChat.conversationId,\n },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n });\n\n // Create mentions\n if (mentionedUserIds.length > 0) {\n await tx.mention.createMany({\n data: mentionedUserIds.map((mentionedUserId) => ({\n messageId: message.id,\n userId: mentionedUserId,\n })),\n });\n }\n\n // Update lab chat timestamp\n await tx.labChat.update({\n where: { id: labChatId },\n data: { updatedAt: new Date() },\n });\n\n return message;\n });\n\n // Broadcast to Pusher channel (same format as regular chat)\n try {\n await pusher.trigger(`conversation-${labChat.conversationId}`, 'new-message', {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n mentionedUserIds,\n });\n } catch (error) {\n console.error('Failed to broadcast lab chat message:', error);\n // Don't fail the request if Pusher fails\n }\n\n // Generate AI response in parallel (don't await - fire and forget)\n if (!isAIUser(userId)) {\n // Run AI response generation in background\n generateAndSendLabResponse(labChatId, content, labChat.conversationId).catch(error => {\n logger.error('Failed to generate AI response:', { error });\n });\n }\n\n return {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n mentionedUserIds,\n };\n }),\n\n delete: protectedProcedure\n .input(z.object({ labChatId: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { labChatId } = input;\n\n // Verify user is the creator of the lab chat\n const labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n createdById: userId,\n },\n });\n\n if (!labChat) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Lab chat not found or not the creator',\n });\n }\n\n // Delete lab chat and associated conversation\n await prisma.$transaction(async (tx) => {\n // Delete mentions first\n await tx.mention.deleteMany({\n where: {\n message: {\n conversationId: labChat.conversationId,\n },\n },\n });\n\n // Delete messages\n await tx.message.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n // Delete conversation members\n await tx.conversationMember.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n // Delete lab chat\n await tx.labChat.delete({\n where: { id: labChatId },\n });\n\n // Delete conversation\n await tx.conversation.delete({\n where: { id: labChat.conversationId },\n });\n });\n\n // Broadcast lab chat deletion\n try {\n await pusher.trigger(`class-${labChat.classId}`, 'lab-chat-deleted', {\n labChatId,\n classId: labChat.classId,\n });\n } catch (error) {\n console.error('Failed to broadcast lab chat deletion:', error);\n // Don't fail the request if Pusher fails\n }\n\n return { success: true };\n }),\n});\n\n/**\n * Generate and send AI introduction for lab chat\n * Uses the stored context directly from database\n */\nasync function generateAndSendLabIntroduction(\n labChatId: string,\n conversationId: string,\n contextString: string,\n subject: string\n): Promise<void> {\n try {\n // Enhance the stored context with clarifying question instructions\n const enhancedSystemPrompt = `${contextString}\n\nIMPORTANT INSTRUCTIONS:\n- You are helping teachers create course materials\n- Use the context information provided above (subject, topic, difficulty, objectives, etc.) as your foundation\n- Only ask clarifying questions about details NOT already specified in the context\n- Focus your questions on format preferences, specific requirements, or missing details needed to create the content\n- Only output final course materials when you have sufficient details beyond what's in the context\n- Do not use markdown formatting in your responses - use plain text only\n- When creating content, make it clear and well-structured without markdown`;\n\n const completion = await inferenceClient.chat.completions.create({\n model: 'command-a-03-2025',\n messages: [\n { role: 'system', content: enhancedSystemPrompt },\n { \n role: 'user', \n content: 'Please introduce yourself to the teaching team. Explain that you will help create course materials by first asking clarifying questions based on the context provided, and only output final content when you have enough information.' \n },\n ],\n max_tokens: 300,\n temperature: 0.8,\n });\n\n const response = completion.choices[0]?.message?.content;\n \n if (!response) {\n throw new Error('No response generated from inference API');\n }\n\n // Send AI introduction using centralized sender\n await sendAIMessage(response, conversationId, {\n subject,\n });\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 will help you create educational content by first asking clarifying questions based on the provided context, then outputting final materials when I have sufficient information. I won't use markdown formatting in my responses. 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 */\nasync function generateAndSendLabResponse(\n labChatId: string,\n teacherMessage: string,\n conversationId: 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 // Get recent conversation history\n const recentMessages = await prisma.message.findMany({\n where: {\n conversationId,\n },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n },\n orderBy: {\n createdAt: 'desc',\n },\n take: 10, // Last 10 messages for context\n });\n\n // Build conversation history as proper message objects\n // Enhance the stored context with clarifying question instructions\n const enhancedSystemPrompt = `${fullLabChat.context}\n\nIMPORTANT INSTRUCTIONS:\n- Use the context information provided above (subject, topic, difficulty, objectives, etc.) as your foundation\n- Based on the teacher's input and existing context, only ask clarifying questions about details NOT already specified\n- Focus questions on format preferences, specific requirements, quantity, or missing implementation details\n- Only output final course materials when you have sufficient details beyond what's in the context\n- Do not use markdown formatting in your responses - use plain text only\n- When you do create content, make it clear and well-structured without markdown\n- If the request is vague, ask 1-2 specific clarifying questions about missing details only\n- You are primarily a chatbot - only provide files when it is necessary\n\nRESPONSE FORMAT:\n- Always respond with JSON in this format: { \"text\": string, \"docs\": null | array }\n- \"text\": Your conversational response (questions, explanations, etc.) - use plain text, no markdown\n- \"docs\": null for regular conversation, or array of PDF document objects when creating course materials\n\nWHEN CREATING COURSE MATERIALS (docs field):\n- docs: [ { \"title\": string, \"blocks\": [ { \"format\": <int 0-12>, \"content\": string | string[], \"metadata\"?: { fontSize?: number, lineHeight?: number, paragraphSpacing?: number, indentWidth?: number, paddingX?: number, paddingY?: number, font?: 0|1|2|3|4|5, color?: \"#RGB\"|\"#RRGGBB\", background?: \"#RGB\"|\"#RRGGBB\", align?: \"left\"|\"center\"|\"right\" } } ] } ]\n- Each document in the array should have a \"title\" (used for filename) and \"blocks\" (content)\n- You can create multiple documents when it makes sense (e.g., separate worksheets, answer keys, different topics)\n- Use descriptive titles like \"Biology_Cell_Structure_Worksheet\" or \"Chemistry_Lab_Instructions\"\n- Format enum (integers): 0=HEADER_1, 1=HEADER_2, 2=HEADER_3, 3=HEADER_4, 4=HEADER_5, 5=HEADER_6, 6=PARAGRAPH, 7=BULLET, 8=NUMBERED, 9=TABLE, 10=IMAGE, 11=CODE_BLOCK, 12=QUOTE\n- Fonts enum: 0=TIMES_ROMAN, 1=COURIER, 2=HELVETICA, 3=HELVETICA_BOLD, 4=HELVETICA_ITALIC, 5=HELVETICA_BOLD_ITALIC\n- Colors must be hex strings: \"#RGB\" or \"#RRGGBB\".\n- Headings (0-5): content is a single string; you may set metadata.align.\n- Paragraphs (6) and Quotes (12): content is a single string.\n- Bullets (7) and Numbered (8): content is an array of strings (one item per list entry). DO NOT include bullet symbols (*) or numbers (1. 2. 3.) in the content - the format will automatically add these.\n- Code blocks (11): prefer content as an array of lines; preserve indentation via leading tabs/spaces. If using a single string, include \\n between lines.\n- Table (9) and Image (10) are not supported by the renderer now; do not emit them.\n- Use metadata sparingly; omit fields you don't need. For code blocks you may set metadata.paddingX, paddingY, background, and font (1 for Courier).\n- Wrap text naturally; do not insert manual line breaks except where semantically required (lists, code).\n- The JSON must be valid and ready for PDF rendering by the server.`;\n\n const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [\n { role: 'system', content: enhancedSystemPrompt },\n ];\n\n // Add recent conversation history\n recentMessages.reverse().forEach(msg => {\n const role = isAIUser(msg.senderId) ? 'assistant' : 'user';\n const senderName = msg.sender?.profile?.displayName || msg.sender?.username || 'Teacher';\n const content = isAIUser(msg.senderId) ? msg.content : `${senderName}: ${msg.content}`;\n \n messages.push({\n role: role as 'user' | 'assistant',\n content,\n });\n });\n\n // Add the new teacher message\n const senderName = 'Teacher'; // We could get this from the actual sender if needed\n messages.push({\n role: 'user',\n content: `${senderName}: ${teacherMessage}`,\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: {\n type: \"json_object\",\n // @ts-expect-error\n schema: {\n type: \"object\",\n properties: {\n text: { type: \"string\" },\n docs: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n title: { type: \"string\" },\n blocks: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n format: { type: \"integer\", minimum: 0, maximum: 12 },\n content: {\n oneOf: [\n { type: \"string\" },\n { type: \"array\", items: { type: \"string\" } }\n ]\n },\n metadata: {\n type: \"object\",\n properties: {\n fontSize: { type: \"number\", minimum: 6 },\n lineHeight: { type: \"number\", minimum: 0.6 },\n paragraphSpacing: { type: \"number\", minimum: 0 },\n indentWidth: { type: \"number\", minimum: 0 },\n paddingX: { type: \"number\", minimum: 0 },\n paddingY: { type: \"number\", minimum: 0 },\n font: { type: \"integer\", minimum: 0, maximum: 5 },\n color: { type: \"string\", pattern: \"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\" },\n background: { type: \"string\", pattern: \"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\" },\n align: { type: \"string\", enum: [\"left\", \"center\", \"right\"] }\n },\n additionalProperties: false\n }\n },\n required: [\"format\", \"content\"],\n additionalProperties: false\n }\n }\n },\n required: [\"title\", \"blocks\"],\n additionalProperties: false\n }\n }\n },\n required: [\"text\"],\n additionalProperties: false\n }\n },\n });\n\n const response = completion.choices[0]?.message?.content;\n \n if (!response) {\n throw new Error('No response generated from inference API');\n }\n\n // Parse the JSON response and generate PDF if docs are provided\n try {\n const jsonData = JSON.parse(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); \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}_${uuidv4().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 || response, conversationId, {\n attachments: {\n connect: attachmentIds.map(id => ({ id })),\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, conversationId, {\n subject: fullLabChat.class?.subject || 'Lab',\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 throw error; // Re-throw to see the full error in the calling function\n }\n}\n\n"],"names":[],"mappings":"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","debug_id":"84548e07-11b9-57d1-a8e2-c94c97f5e7ea"}
|
|
1
|
+
{"version":3,"file":"labChat.js","sources":["routers/labChat.ts"],"sourceRoot":"/","sourcesContent":["import { z } from 'zod';\nimport { createTRPCRouter, protectedProcedure } from '../trpc.js';\nimport { prisma } from '../lib/prisma.js';\nimport { pusher } from '../lib/pusher.js';\nimport { TRPCError } from '@trpc/server';\nimport { isAIUser } from '../utils/aiUser.js';\nimport { generateAndSendLabIntroduction, generateAndSendLabResponse } from '../server/pipelines/aiLabChat.js';\n\nexport const labChatRouter = createTRPCRouter({\n create: protectedProcedure\n .input(\n z.object({\n classId: z.string(),\n title: z.string().min(1).max(200),\n context: z.string(), // JSON string for LLM context\n })\n )\n .mutation(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { classId, title, context } = input;\n\n // Verify user is a teacher in the class\n const classWithTeachers = await prisma.class.findFirst({\n where: {\n id: classId,\n teachers: {\n some: {\n id: userId,\n },\n },\n },\n include: {\n students: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n teachers: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n });\n\n if (!classWithTeachers) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Not a teacher in this class',\n });\n }\n\n // Validate context is valid JSON\n try {\n JSON.parse(context);\n } catch (error) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Context must be valid JSON',\n });\n }\n\n // Create lab chat with associated conversation\n const result = await prisma.$transaction(async (tx) => {\n // Create conversation for the lab chat\n const conversation = await tx.conversation.create({\n data: {\n type: 'GROUP',\n name: `Lab: ${title}`,\n displayInChat: false, // Lab chats don't show in regular chat list\n },\n });\n\n // Add only teachers to the conversation (this is for course material creation)\n const teacherMembers = classWithTeachers.teachers.map(t => ({ \n userId: t.id, \n role: 'ADMIN' as const \n }));\n\n await tx.conversationMember.createMany({\n data: teacherMembers.map(member => ({\n userId: member.userId,\n conversationId: conversation.id,\n role: member.role,\n })),\n });\n\n // Create the lab chat\n const labChat = await tx.labChat.create({\n data: {\n title,\n context,\n classId,\n conversationId: conversation.id,\n createdById: userId,\n },\n include: {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n },\n });\n\n return labChat;\n });\n\n // Generate AI introduction message in parallel (don't await - fire and forget)\n generateAndSendLabIntroduction(result.id, result.conversationId, context, classWithTeachers.subject || 'Lab');\n // Broadcast lab chat creation to class members\n try {\n await pusher.trigger(`class-${classId}`, 'lab-chat-created', {\n id: result.id,\n title: result.title,\n classId: result.classId,\n conversationId: result.conversationId,\n createdBy: result.createdBy,\n createdAt: result.createdAt,\n });\n } catch (error) {\n console.error('Failed to broadcast lab chat creation:', error);\n // Don't fail the request if Pusher fails\n }\n\n return result;\n }),\n\n get: protectedProcedure\n .input(z.object({ labChatId: z.string() }))\n .query(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { labChatId } = input;\n\n // First, try to find the lab chat if user is already a member\n let labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: {\n some: {\n userId,\n },\n },\n },\n },\n include: {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n },\n });\n\n // If not found, check if user is a teacher in the class\n if (!labChat) {\n const labChatForTeacher = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n class: {\n teachers: {\n some: {\n id: userId,\n },\n },\n },\n },\n include: {\n conversation: {\n select: {\n id: true,\n },\n },\n },\n });\n\n if (labChatForTeacher) {\n // Add teacher to conversation\n await prisma.conversationMember.create({\n data: {\n userId,\n conversationId: labChatForTeacher.conversation.id,\n role: 'ADMIN',\n },\n });\n\n // Now fetch the full lab chat with the user as a member\n labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n },\n include: {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n },\n });\n }\n }\n\n if (!labChat) {\n throw new TRPCError({\n code: 'NOT_FOUND',\n message: 'Lab chat not found or access denied',\n });\n }\n\n return labChat;\n }),\n\n list: protectedProcedure\n .input(z.object({ classId: z.string() }))\n .query(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { classId } = input;\n\n // Verify user is a member of the class\n const classMembership = await prisma.class.findFirst({\n where: {\n id: classId,\n OR: [\n {\n students: {\n some: {\n id: userId,\n },\n },\n },\n {\n teachers: {\n some: {\n id: userId,\n },\n },\n },\n ],\n },\n });\n\n if (!classMembership) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Not a member of this class',\n });\n }\n\n const labChats = await prisma.labChat.findMany({\n where: {\n classId,\n },\n include: {\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n conversation: {\n include: {\n messages: {\n orderBy: {\n createdAt: 'desc',\n },\n take: 1,\n 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 _count: {\n select: {\n messages: true,\n },\n },\n },\n },\n },\n orderBy: {\n createdAt: 'desc',\n },\n });\n\n return labChats.map((labChat) => ({\n id: labChat.id,\n title: labChat.title,\n classId: labChat.classId,\n conversationId: labChat.conversationId,\n createdBy: labChat.createdBy,\n createdAt: labChat.createdAt,\n updatedAt: labChat.updatedAt,\n lastMessage: labChat.conversation.messages[0] || null,\n messageCount: labChat.conversation._count.messages,\n }));\n }),\n\n postToLabChat: protectedProcedure\n .input(\n z.object({\n labChatId: z.string(),\n content: z.string().min(1).max(4000),\n mentionedUserIds: z.array(z.string()).optional(),\n })\n )\n .mutation(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { labChatId, content, mentionedUserIds = [] } = input;\n\n // Get lab chat and verify user is a member\n const labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: {\n some: {\n userId,\n },\n },\n },\n },\n include: {\n conversation: {\n select: {\n id: true,\n },\n },\n },\n });\n\n if (!labChat) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Lab chat not found or access denied',\n });\n }\n\n // Verify mentioned users are members of the conversation\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await prisma.conversationMember.findMany({\n where: {\n conversationId: labChat.conversationId,\n userId: { in: mentionedUserIds },\n },\n });\n\n if (mentionedMemberships.length !== mentionedUserIds.length) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Some mentioned users are not members of this lab chat',\n });\n }\n }\n\n // Create message and mentions\n const result = await prisma.$transaction(async (tx) => {\n const message = await tx.message.create({\n data: {\n content,\n senderId: userId,\n conversationId: labChat.conversationId,\n },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n });\n\n // Create mentions\n if (mentionedUserIds.length > 0) {\n await tx.mention.createMany({\n data: mentionedUserIds.map((mentionedUserId) => ({\n messageId: message.id,\n userId: mentionedUserId,\n })),\n });\n }\n\n // Update lab chat timestamp\n await tx.labChat.update({\n where: { id: labChatId },\n data: { updatedAt: new Date() },\n });\n\n return message;\n });\n\n // Broadcast to Pusher channel (same format as regular chat)\n try {\n await pusher.trigger(`conversation-${labChat.conversationId}`, 'new-message', {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n mentionedUserIds,\n });\n } catch (error) {\n console.error('Failed to broadcast lab chat message:', error);\n // Don't fail the request if Pusher fails\n }\n\n // Generate AI response in parallel (don't await - fire and forget)\n if (!isAIUser(userId)) {\n // Run AI response generation in background\n generateAndSendLabResponse(labChatId, content, labChat.conversationId)\n }\n\n return {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n mentionedUserIds,\n };\n }),\n\n delete: protectedProcedure\n .input(z.object({ labChatId: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const userId = ctx.user!.id;\n const { labChatId } = input;\n\n // Verify user is the creator of the lab chat\n const labChat = await prisma.labChat.findFirst({\n where: {\n id: labChatId,\n createdById: userId,\n },\n });\n\n if (!labChat) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'Lab chat not found or not the creator',\n });\n }\n\n // Delete lab chat and associated conversation\n await prisma.$transaction(async (tx) => {\n // Delete mentions first\n await tx.mention.deleteMany({\n where: {\n message: {\n conversationId: labChat.conversationId,\n },\n },\n });\n\n // Delete messages\n await tx.message.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n // Delete conversation members\n await tx.conversationMember.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n // Delete lab chat\n await tx.labChat.delete({\n where: { id: labChatId },\n });\n\n // Delete conversation\n await tx.conversation.delete({\n where: { id: labChat.conversationId },\n });\n });\n\n // Broadcast lab chat deletion\n try {\n await pusher.trigger(`class-${labChat.classId}`, 'lab-chat-deleted', {\n labChatId,\n classId: labChat.classId,\n });\n } catch (error) {\n console.error('Failed to broadcast lab chat deletion:', error);\n // Don't fail the request if Pusher fails\n }\n\n return { success: true };\n }),\n});\n\n"],"names":[],"mappings":"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","debug_id":"933db03e-fc38-599b-93a4-890502ba8281"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["routers/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["routers/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAkGM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAufjD,CAAC"}
|
package/dist/routers/message.js
CHANGED
|
@@ -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]="5d4423a2-9e56-5193-8c62-5a133744ca99")}catch(e){}}();
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { createTRPCRouter, protectedProcedure } from '../trpc.js';
|
|
5
5
|
import { prisma } from '../lib/prisma.js';
|
|
@@ -97,6 +97,7 @@ export const messageRouter = createTRPCRouter({
|
|
|
97
97
|
name: attachment.name,
|
|
98
98
|
type: attachment.type,
|
|
99
99
|
})),
|
|
100
|
+
meta: message.meta,
|
|
100
101
|
mentions: message.mentions.map((mention) => ({
|
|
101
102
|
user: mention.user,
|
|
102
103
|
})),
|
|
@@ -545,4 +546,4 @@ export const messageRouter = createTRPCRouter({
|
|
|
545
546
|
}),
|
|
546
547
|
});
|
|
547
548
|
//# sourceMappingURL=message.js.map
|
|
548
|
-
//# debugId=
|
|
549
|
+
//# debugId=5d4423a2-9e56-5193-8c62-5a133744ca99
|