@studious-lms/server 1.1.14 → 1.1.15
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 +3 -0
- package/dist/lib/jsonConversion.d.ts +3 -0
- package/dist/lib/jsonConversion.d.ts.map +1 -0
- package/dist/lib/jsonConversion.js +517 -0
- package/dist/lib/jsonStyles.d.ts +29 -0
- package/dist/lib/jsonStyles.d.ts.map +1 -0
- package/dist/lib/jsonStyles.js +28 -0
- package/dist/routers/_app.d.ts +82 -4
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/assignment.d.ts +30 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +13 -3
- package/dist/routers/conversation.d.ts +1 -1
- package/dist/routers/file.d.ts +4 -0
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/labChat.d.ts +1 -1
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +159 -7
- package/dist/routers/message.d.ts +5 -0
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +18 -5
- package/dist/utils/inference.d.ts +5 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +16 -0
- package/package.json +2 -1
- package/prisma/schema.prisma +6 -0
- package/src/index.ts +4 -0
- package/src/lib/jsonConversion.ts +537 -0
- package/src/lib/jsonStyles.ts +36 -0
- package/src/routers/class.ts +14 -3
- package/src/routers/labChat.ts +166 -7
- package/src/routers/message.ts +18 -5
- package/src/utils/inference.ts +19 -0
package/dist/routers/labChat.js
CHANGED
|
@@ -6,6 +6,9 @@ import { TRPCError } from '@trpc/server';
|
|
|
6
6
|
import { inferenceClient, sendAIMessage } from '../utils/inference.js';
|
|
7
7
|
import { logger } from '../utils/logger.js';
|
|
8
8
|
import { isAIUser } from '../utils/aiUser.js';
|
|
9
|
+
import { uploadFile } from 'src/lib/googleCloudStorage.js';
|
|
10
|
+
import { createPdf } from "src/lib/jsonConversion.js";
|
|
11
|
+
import { v4 as uuidv4 } from "uuid";
|
|
9
12
|
export const labChatRouter = createTRPCRouter({
|
|
10
13
|
create: protectedProcedure
|
|
11
14
|
.input(z.object({
|
|
@@ -699,7 +702,30 @@ IMPORTANT INSTRUCTIONS:
|
|
|
699
702
|
- Only output final course materials when you have sufficient details beyond what's in the context
|
|
700
703
|
- Do not use markdown formatting in your responses - use plain text only
|
|
701
704
|
- When you do create content, make it clear and well-structured without markdown
|
|
702
|
-
- If the request is vague, ask 1-2 specific clarifying questions about missing details only
|
|
705
|
+
- If the request is vague, ask 1-2 specific clarifying questions about missing details only
|
|
706
|
+
- You are primarily a chatbot - only provide files when it is necessary
|
|
707
|
+
|
|
708
|
+
RESPONSE FORMAT:
|
|
709
|
+
- Always respond with JSON in this format: { "text": string, "docs": null | array }
|
|
710
|
+
- "text": Your conversational response (questions, explanations, etc.) - use plain text, no markdown
|
|
711
|
+
- "docs": null for regular conversation, or array of PDF document objects when creating course materials
|
|
712
|
+
|
|
713
|
+
WHEN CREATING COURSE MATERIALS (docs field):
|
|
714
|
+
- 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" } } ] } ]
|
|
715
|
+
- Each document in the array should have a "title" (used for filename) and "blocks" (content)
|
|
716
|
+
- You can create multiple documents when it makes sense (e.g., separate worksheets, answer keys, different topics)
|
|
717
|
+
- Use descriptive titles like "Biology_Cell_Structure_Worksheet" or "Chemistry_Lab_Instructions"
|
|
718
|
+
- 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
|
|
719
|
+
- Fonts enum: 0=TIMES_ROMAN, 1=COURIER, 2=HELVETICA, 3=HELVETICA_BOLD, 4=HELVETICA_ITALIC, 5=HELVETICA_BOLD_ITALIC
|
|
720
|
+
- Colors must be hex strings: "#RGB" or "#RRGGBB".
|
|
721
|
+
- Headings (0-5): content is a single string; you may set metadata.align.
|
|
722
|
+
- Paragraphs (6) and Quotes (12): content is a single string.
|
|
723
|
+
- Bullets (7) and Numbered (8): content is an array of strings (one item per list entry).
|
|
724
|
+
- 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.
|
|
725
|
+
- Table (9) and Image (10) are not supported by the renderer now; do not emit them.
|
|
726
|
+
- Use metadata sparingly; omit fields you don't need. For code blocks you may set metadata.paddingX, paddingY, background, and font (1 for Courier).
|
|
727
|
+
- Wrap text naturally; do not insert manual line breaks except where semantically required (lists, code).
|
|
728
|
+
- The JSON must be valid and ready for PDF rendering by the server.`;
|
|
703
729
|
const messages = [
|
|
704
730
|
{ role: 'system', content: enhancedSystemPrompt },
|
|
705
731
|
];
|
|
@@ -722,20 +748,146 @@ IMPORTANT INSTRUCTIONS:
|
|
|
722
748
|
const completion = await inferenceClient.chat.completions.create({
|
|
723
749
|
model: 'command-a-03-2025',
|
|
724
750
|
messages,
|
|
725
|
-
max_tokens: 500,
|
|
726
751
|
temperature: 0.7,
|
|
752
|
+
response_format: {
|
|
753
|
+
type: "json_object",
|
|
754
|
+
// @ts-expect-error
|
|
755
|
+
schema: {
|
|
756
|
+
type: "object",
|
|
757
|
+
properties: {
|
|
758
|
+
text: { type: "string" },
|
|
759
|
+
docs: {
|
|
760
|
+
type: "array",
|
|
761
|
+
items: {
|
|
762
|
+
type: "object",
|
|
763
|
+
properties: {
|
|
764
|
+
title: { type: "string" },
|
|
765
|
+
blocks: {
|
|
766
|
+
type: "array",
|
|
767
|
+
items: {
|
|
768
|
+
type: "object",
|
|
769
|
+
properties: {
|
|
770
|
+
format: { type: "integer", minimum: 0, maximum: 12 },
|
|
771
|
+
content: {
|
|
772
|
+
oneOf: [
|
|
773
|
+
{ type: "string" },
|
|
774
|
+
{ type: "array", items: { type: "string" } }
|
|
775
|
+
]
|
|
776
|
+
},
|
|
777
|
+
metadata: {
|
|
778
|
+
type: "object",
|
|
779
|
+
properties: {
|
|
780
|
+
fontSize: { type: "number", minimum: 6 },
|
|
781
|
+
lineHeight: { type: "number", minimum: 0.6 },
|
|
782
|
+
paragraphSpacing: { type: "number", minimum: 0 },
|
|
783
|
+
indentWidth: { type: "number", minimum: 0 },
|
|
784
|
+
paddingX: { type: "number", minimum: 0 },
|
|
785
|
+
paddingY: { type: "number", minimum: 0 },
|
|
786
|
+
font: { type: "integer", minimum: 0, maximum: 5 },
|
|
787
|
+
color: { type: "string", pattern: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" },
|
|
788
|
+
background: { type: "string", pattern: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" },
|
|
789
|
+
align: { type: "string", enum: ["left", "center", "right"] }
|
|
790
|
+
},
|
|
791
|
+
additionalProperties: false
|
|
792
|
+
}
|
|
793
|
+
},
|
|
794
|
+
required: ["format", "content"],
|
|
795
|
+
additionalProperties: false
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
required: ["title", "blocks"],
|
|
800
|
+
additionalProperties: false
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
required: ["text"],
|
|
805
|
+
additionalProperties: false
|
|
806
|
+
}
|
|
807
|
+
},
|
|
727
808
|
});
|
|
728
809
|
const response = completion.choices[0]?.message?.content;
|
|
729
810
|
if (!response) {
|
|
730
811
|
throw new Error('No response generated from inference API');
|
|
731
812
|
}
|
|
732
|
-
//
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
813
|
+
// Parse the JSON response and generate PDF if docs are provided
|
|
814
|
+
try {
|
|
815
|
+
const jsonData = JSON.parse(response);
|
|
816
|
+
const attachmentIds = [];
|
|
817
|
+
// Generate PDFs if docs are provided
|
|
818
|
+
if (jsonData.docs && Array.isArray(jsonData.docs)) {
|
|
819
|
+
for (let i = 0; i < jsonData.docs.length; i++) {
|
|
820
|
+
const doc = jsonData.docs[i];
|
|
821
|
+
if (!doc.title || !doc.blocks || !Array.isArray(doc.blocks)) {
|
|
822
|
+
logger.error(`Document ${i + 1} is missing title or blocks`);
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
try {
|
|
826
|
+
let pdfBytes = await createPdf(doc.blocks);
|
|
827
|
+
if (pdfBytes) {
|
|
828
|
+
// Sanitize filename - remove special characters and limit length
|
|
829
|
+
const sanitizedTitle = doc.title
|
|
830
|
+
.replace(/[^a-zA-Z0-9\s\-_]/g, '')
|
|
831
|
+
.replace(/\s+/g, '_')
|
|
832
|
+
.substring(0, 50);
|
|
833
|
+
const filename = `${sanitizedTitle}_${uuidv4().substring(0, 8)}.pdf`;
|
|
834
|
+
logger.info(`PDF ${i + 1} generated successfully`, { labChatId, title: doc.title });
|
|
835
|
+
const gcpResult = await uploadFile(Buffer.from(pdfBytes).toString('base64'), `class/generated/${fullLabChat.classId}/${filename}`, 'application/pdf');
|
|
836
|
+
logger.info(`PDF ${i + 1} uploaded successfully`, { labChatId, filename });
|
|
837
|
+
const file = await prisma.file.create({
|
|
838
|
+
data: {
|
|
839
|
+
name: filename,
|
|
840
|
+
path: `class/generated/${fullLabChat.classId}/${filename}`,
|
|
841
|
+
type: 'application/pdf',
|
|
842
|
+
userId: fullLabChat.createdById,
|
|
843
|
+
},
|
|
844
|
+
});
|
|
845
|
+
attachmentIds.push(file.id);
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
logger.error(`PDF ${i + 1} creation returned undefined/null`, { labChatId, title: doc.title });
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
catch (pdfError) {
|
|
852
|
+
logger.error(`PDF creation threw an error for document ${i + 1}:`, {
|
|
853
|
+
error: pdfError instanceof Error ? {
|
|
854
|
+
message: pdfError.message,
|
|
855
|
+
stack: pdfError.stack,
|
|
856
|
+
name: pdfError.name
|
|
857
|
+
} : pdfError,
|
|
858
|
+
labChatId,
|
|
859
|
+
title: doc.title
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
// Send the text response to the conversation
|
|
865
|
+
await sendAIMessage(jsonData.text || response, conversationId, {
|
|
866
|
+
attachments: {
|
|
867
|
+
connect: attachmentIds.map(id => ({ id })),
|
|
868
|
+
},
|
|
869
|
+
subject: fullLabChat.class?.subject || 'Lab',
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
catch (parseError) {
|
|
873
|
+
logger.error('Failed to parse AI response or generate PDF:', { error: parseError, labChatId });
|
|
874
|
+
// Fallback: send the raw response if parsing fails
|
|
875
|
+
await sendAIMessage(response, conversationId, {
|
|
876
|
+
subject: fullLabChat.class?.subject || 'Lab',
|
|
877
|
+
});
|
|
878
|
+
}
|
|
736
879
|
logger.info('AI response sent', { labChatId, conversationId });
|
|
737
880
|
}
|
|
738
881
|
catch (error) {
|
|
739
|
-
|
|
882
|
+
console.error('Full error object:', error);
|
|
883
|
+
logger.error('Failed to generate AI response:', {
|
|
884
|
+
error: error instanceof Error ? {
|
|
885
|
+
message: error.message,
|
|
886
|
+
stack: error.stack,
|
|
887
|
+
name: error.name
|
|
888
|
+
} : error,
|
|
889
|
+
labChatId
|
|
890
|
+
});
|
|
891
|
+
throw error; // Re-throw to see the full error in the calling function
|
|
740
892
|
}
|
|
741
893
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/routers/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/routers/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAylBxB,CAAC"}
|
package/dist/routers/message.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createTRPCRouter, protectedProcedure } from '../trpc.js';
|
|
|
3
3
|
import { prisma } from '../lib/prisma.js';
|
|
4
4
|
import { pusher } from '../lib/pusher.js';
|
|
5
5
|
import { TRPCError } from '@trpc/server';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
6
7
|
export const messageRouter = createTRPCRouter({
|
|
7
8
|
list: protectedProcedure
|
|
8
9
|
.input(z.object({
|
|
@@ -36,6 +37,13 @@ export const messageRouter = createTRPCRouter({
|
|
|
36
37
|
}),
|
|
37
38
|
},
|
|
38
39
|
include: {
|
|
40
|
+
attachments: {
|
|
41
|
+
select: {
|
|
42
|
+
id: true,
|
|
43
|
+
name: true,
|
|
44
|
+
type: true,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
39
47
|
sender: {
|
|
40
48
|
select: {
|
|
41
49
|
id: true,
|
|
@@ -82,6 +90,11 @@ export const messageRouter = createTRPCRouter({
|
|
|
82
90
|
conversationId: message.conversationId,
|
|
83
91
|
createdAt: message.createdAt,
|
|
84
92
|
sender: message.sender,
|
|
93
|
+
attachments: message.attachments.map((attachment) => ({
|
|
94
|
+
id: attachment.id,
|
|
95
|
+
name: attachment.name,
|
|
96
|
+
type: attachment.type,
|
|
97
|
+
})),
|
|
85
98
|
mentions: message.mentions.map((mention) => ({
|
|
86
99
|
user: mention.user,
|
|
87
100
|
})),
|
|
@@ -179,7 +192,7 @@ export const messageRouter = createTRPCRouter({
|
|
|
179
192
|
});
|
|
180
193
|
}
|
|
181
194
|
catch (error) {
|
|
182
|
-
|
|
195
|
+
logger.error('Failed to broadcast message:', { error });
|
|
183
196
|
// Don't fail the request if Pusher fails
|
|
184
197
|
}
|
|
185
198
|
return {
|
|
@@ -308,7 +321,7 @@ export const messageRouter = createTRPCRouter({
|
|
|
308
321
|
});
|
|
309
322
|
}
|
|
310
323
|
catch (error) {
|
|
311
|
-
|
|
324
|
+
logger.error('Failed to broadcast message update:', { error });
|
|
312
325
|
// Don't fail the request if Pusher fails
|
|
313
326
|
}
|
|
314
327
|
return {
|
|
@@ -385,7 +398,7 @@ export const messageRouter = createTRPCRouter({
|
|
|
385
398
|
});
|
|
386
399
|
}
|
|
387
400
|
catch (error) {
|
|
388
|
-
|
|
401
|
+
logger.error('Failed to broadcast message deletion:', { error });
|
|
389
402
|
// Don't fail the request if Pusher fails
|
|
390
403
|
}
|
|
391
404
|
return {
|
|
@@ -430,7 +443,7 @@ export const messageRouter = createTRPCRouter({
|
|
|
430
443
|
});
|
|
431
444
|
}
|
|
432
445
|
catch (error) {
|
|
433
|
-
|
|
446
|
+
logger.error('Failed to broadcast conversation view:', { error });
|
|
434
447
|
// Don't fail the request if Pusher fails
|
|
435
448
|
}
|
|
436
449
|
return { success: true };
|
|
@@ -472,7 +485,7 @@ export const messageRouter = createTRPCRouter({
|
|
|
472
485
|
});
|
|
473
486
|
}
|
|
474
487
|
catch (error) {
|
|
475
|
-
|
|
488
|
+
logger.error('Failed to broadcast mentions view:', { error });
|
|
476
489
|
// Don't fail the request if Pusher fails
|
|
477
490
|
}
|
|
478
491
|
return { success: true };
|
|
@@ -23,6 +23,11 @@ export interface InferenceResponse {
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function sendAIMessage(content: string, conversationId: string, options?: {
|
|
25
25
|
subject?: string;
|
|
26
|
+
attachments?: {
|
|
27
|
+
connect: {
|
|
28
|
+
id: string;
|
|
29
|
+
}[];
|
|
30
|
+
};
|
|
26
31
|
customSender?: {
|
|
27
32
|
displayName: string;
|
|
28
33
|
profilePicture?: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/utils/inference.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAW5B,eAAO,MAAM,eAAe,QAG1B,CAAC;AAGH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,UAAU,CAAC;IACrD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;CACE,GACL,OAAO,CAAC;IACT,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;CACjB,CAAC,
|
|
1
|
+
{"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/utils/inference.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAW5B,eAAO,MAAM,eAAe,QAG1B,CAAC;AAGH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,UAAU,CAAC;IACrD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC3B,CAAC;IACF,YAAY,CAAC,EAAE;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;CACE,GACL,OAAO,CAAC;IACT,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;CACjB,CAAC,CAmED;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,iBAAiB,CAAC,CAsC5B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAW5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD"}
|
package/dist/utils/inference.js
CHANGED
|
@@ -23,6 +23,14 @@ export async function sendAIMessage(content, conversationId, options = {}) {
|
|
|
23
23
|
content,
|
|
24
24
|
senderId: getAIUserId(),
|
|
25
25
|
conversationId,
|
|
26
|
+
...(options.attachments && {
|
|
27
|
+
attachments: {
|
|
28
|
+
connect: options.attachments.connect,
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
include: {
|
|
33
|
+
attachments: true,
|
|
26
34
|
},
|
|
27
35
|
});
|
|
28
36
|
logger.info('AI Message sent', {
|
|
@@ -49,6 +57,14 @@ export async function sendAIMessage(content, conversationId, options = {}) {
|
|
|
49
57
|
createdAt: aiMessage.createdAt,
|
|
50
58
|
sender: senderInfo,
|
|
51
59
|
mentionedUserIds: [],
|
|
60
|
+
attachments: aiMessage.attachments.map(attachment => ({
|
|
61
|
+
id: attachment.id,
|
|
62
|
+
attachmentId: attachment.id,
|
|
63
|
+
name: attachment.name,
|
|
64
|
+
type: attachment.type,
|
|
65
|
+
size: attachment.size,
|
|
66
|
+
path: attachment.path,
|
|
67
|
+
})),
|
|
52
68
|
});
|
|
53
69
|
}
|
|
54
70
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studious-lms/server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.15",
|
|
4
4
|
"description": "Backend server for Studious application",
|
|
5
5
|
"main": "dist/exportType.js",
|
|
6
6
|
"types": "dist/exportType.d.ts",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"express": "^4.18.3",
|
|
32
32
|
"nodemailer": "^7.0.4",
|
|
33
33
|
"openai": "^5.23.0",
|
|
34
|
+
"pdf-lib": "^1.17.1",
|
|
34
35
|
"prisma": "^6.7.0",
|
|
35
36
|
"pusher": "^5.2.0",
|
|
36
37
|
"sharp": "^0.34.2",
|
package/prisma/schema.prisma
CHANGED
|
@@ -183,6 +183,11 @@ model File {
|
|
|
183
183
|
folder Folder? @relation("FolderFiles", fields: [folderId], references: [id], onDelete: Cascade)
|
|
184
184
|
folderId String?
|
|
185
185
|
|
|
186
|
+
conversationId String?
|
|
187
|
+
|
|
188
|
+
messageId String?
|
|
189
|
+
message Message? @relation("MessageAttachments", fields: [messageId], references: [id], onDelete: Cascade)
|
|
190
|
+
|
|
186
191
|
schools School[]
|
|
187
192
|
}
|
|
188
193
|
|
|
@@ -379,6 +384,7 @@ model Message {
|
|
|
379
384
|
sender User @relation("SentMessages", fields: [senderId], references: [id], onDelete: Cascade)
|
|
380
385
|
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
|
|
381
386
|
mentions Mention[]
|
|
387
|
+
attachments File[] @relation("MessageAttachments")
|
|
382
388
|
}
|
|
383
389
|
|
|
384
390
|
model Mention {
|
package/src/index.ts
CHANGED
|
@@ -88,6 +88,10 @@ app.use((req, res, next) => {
|
|
|
88
88
|
// Create HTTP server
|
|
89
89
|
const httpServer = createServer(app);
|
|
90
90
|
|
|
91
|
+
app.get('/health', (req, res) => {
|
|
92
|
+
res.status(200).json({ message: 'OK' });
|
|
93
|
+
});
|
|
94
|
+
|
|
91
95
|
// Setup Socket.IO
|
|
92
96
|
const io = new Server(httpServer, {
|
|
93
97
|
cors: {
|