@studious-lms/server 1.26.0 → 1.28.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/index.d.ts.map +1 -1
- package/dist/index.js +31 -4
- package/dist/index.js.map +1 -1
- package/dist/instrument.js +9 -4
- package/dist/instrument.js.map +1 -1
- package/dist/lib/fileUpload.d.ts +15 -0
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +43 -2
- package/dist/lib/fileUpload.js.map +1 -1
- 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/generateVisualization.d.ts +33 -0
- package/dist/pipelines/generateVisualization.d.ts.map +1 -0
- package/dist/pipelines/generateVisualization.js +79 -0
- package/dist/pipelines/generateVisualization.js.map +1 -0
- 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 +1118 -70
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +4 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/admin.d.ts +297 -1
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/admin.js +43 -2
- package/dist/routers/admin.js.map +1 -1
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +16 -3
- 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 +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/module.d.ts +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/routers/visualization.d.ts +124 -0
- package/dist/routers/visualization.d.ts.map +1 -0
- package/dist/routers/visualization.js +41 -0
- package/dist/routers/visualization.js.map +1 -0
- 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/adminClass.d.ts +153 -0
- package/dist/services/adminClass.d.ts.map +1 -0
- package/dist/services/adminClass.js +196 -0
- package/dist/services/adminClass.js.map +1 -0
- package/dist/services/adminInstitution.d.ts +2 -0
- package/dist/services/adminInstitution.d.ts.map +1 -1
- package/dist/services/adminInstitution.js +11 -4
- package/dist/services/adminInstitution.js.map +1 -1
- package/dist/services/adminMetrics.d.ts +8 -0
- package/dist/services/adminMetrics.d.ts.map +1 -1
- package/dist/services/adminMetrics.js +30 -3
- package/dist/services/adminMetrics.js.map +1 -1
- package/dist/services/adminSpam.d.ts +76 -0
- package/dist/services/adminSpam.d.ts.map +1 -0
- package/dist/services/adminSpam.js +163 -0
- package/dist/services/adminSpam.js.map +1 -0
- package/dist/services/adminSql.d.ts +36 -0
- package/dist/services/adminSql.d.ts.map +1 -0
- package/dist/services/adminSql.js +172 -0
- package/dist/services/adminSql.js.map +1 -0
- package/dist/services/adminTelemetry.d.ts +56 -0
- package/dist/services/adminTelemetry.d.ts.map +1 -0
- package/dist/services/adminTelemetry.js +147 -0
- package/dist/services/adminTelemetry.js.map +1 -0
- package/dist/services/adminUser.d.ts +45 -1
- package/dist/services/adminUser.d.ts.map +1 -1
- package/dist/services/adminUser.js +53 -13
- 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/auth.d.ts.map +1 -1
- package/dist/services/auth.js +10 -4
- package/dist/services/auth.js.map +1 -1
- package/dist/services/conversation.d.ts +1 -0
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/fees.d.ts.map +1 -1
- package/dist/services/fees.js +7 -3
- package/dist/services/fees.js.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/module.d.ts.map +1 -1
- package/dist/services/module.js +23 -4
- package/dist/services/module.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/services/visualization.d.ts +72 -0
- package/dist/services/visualization.d.ts.map +1 -0
- package/dist/services/visualization.js +122 -0
- package/dist/services/visualization.js.map +1 -0
- 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/utils/sentryTrpc.d.ts +3 -0
- package/dist/utils/sentryTrpc.d.ts.map +1 -0
- package/dist/utils/sentryTrpc.js +26 -0
- package/dist/utils/sentryTrpc.js.map +1 -0
- 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/migrations/20261025120000_add_visualizations/migration.sql +27 -0
- package/prisma/schema.prisma +32 -0
- package/src/index.ts +30 -4
- package/src/instrument.ts +8 -2
- package/src/lib/fileUpload.ts +49 -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/generateVisualization.ts +107 -0
- package/src/pipelines/labChatPrompt.ts +11 -12
- package/src/routers/_app.ts +2 -0
- package/src/routers/admin.ts +72 -0
- package/src/routers/class.ts +14 -1
- 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/routers/visualization.ts +60 -0
- package/src/routes/aiStream.ts +32 -0
- package/src/seedDatabase.ts +107 -2
- package/src/services/adminClass.ts +222 -0
- package/src/services/adminInstitution.ts +9 -2
- package/src/services/adminMetrics.ts +35 -0
- package/src/services/adminSpam.ts +224 -0
- package/src/services/adminSql.ts +214 -0
- package/src/services/adminTelemetry.ts +194 -0
- package/src/services/adminUser.ts +66 -11
- 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/auth.ts +8 -2
- package/src/services/fees.ts +5 -1
- package/src/services/labChat.ts +65 -0
- package/src/services/message.ts +205 -142
- package/src/services/module.ts +29 -2
- package/src/services/parentPortal.ts +85 -0
- package/src/services/studentProgress.ts +135 -0
- package/src/services/visualization.ts +147 -0
- package/src/utils/inference.ts +85 -0
- package/src/utils/sentryTrpc.ts +25 -0
- package/src/workers/processors/createRunJobWorker.ts +17 -17
- package/tests/unit/lib/fileUploadSession.test.ts +67 -0
- package/tests/unit/services/adminClass.test.ts +102 -0
- package/tests/unit/services/adminSpam.test.ts +108 -0
- package/tests/unit/services/adminSql.test.ts +56 -0
- package/tests/unit/services/adminTelemetry.test.ts +47 -0
- package/tests/unit/services/adminUser.test.ts +77 -0
- package/tests/unit/services/visualization.test.ts +130 -0
- package/tests/unit/utils/adminPanelAccess.test.ts +26 -0
- package/tests/unit/utils/sentryTrpc.test.ts +15 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fees.js","sources":["services/fees.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Fee ledger service — school-admin gated monthly invoicing + payment recording.\n * Payment gateway intentionally deferred; this is a manual ledger.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { FeeInvoiceStatus, FeePaymentMethod } from \"@prisma/client\";\nimport { findActiveSchoolAdminMembership } from \"../models/school.js\";\nimport {\n createFeeInvoices,\n createFeePayment,\n createFeePlan,\n getFeeInvoice,\n getFeePayment,\n getFeePlan,\n listActiveStudentIds,\n listFeeInvoices,\n listFeePlans,\n listPlanEnrollmentIds,\n listStudentBalances,\n setFeePlanEnrollments,\n sumPaymentsForInvoice,\n updateFeePlan,\n updateInvoiceStatus,\n} from \"../models/fees.js\";\nimport {\n buildFeePdf,\n formatNpr,\n monthLabel,\n} from \"../lib/feePdf.js\";\n\nasync function requireAdmin(schoolId: string, userId: string) {\n const membership = await findActiveSchoolAdminMembership(schoolId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only school admins can manage fees\",\n });\n }\n return membership;\n}\n\nasync function assertActiveSchoolStudents(\n schoolId: string,\n studentIds: string[]\n) {\n const activeIds = new Set(await listActiveStudentIds(schoolId));\n const invalid = studentIds.filter((id) => !activeIds.has(id));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more students are not active members of this school\",\n });\n }\n}\n\nfunction assertMonth(month: number) {\n if (month < 1 || month > 12) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"month must be between 1 and 12\",\n });\n }\n}\n\nfunction invoicePaidTotal(payments: { amountNpr: number }[]): number {\n return payments.reduce((s, p) => s + p.amountNpr, 0);\n}\n\nfunction deriveStatus(\n amountNpr: number,\n paidNpr: number,\n current: FeeInvoiceStatus\n): FeeInvoiceStatus {\n if (current === \"VOID\") return \"VOID\";\n if (paidNpr <= 0) return \"UNPAID\";\n if (paidNpr >= amountNpr) return \"PAID\";\n return \"PARTIAL\";\n}\n\nfunction withBalance<\n T extends {\n amountNpr: number;\n status: FeeInvoiceStatus;\n payments: { amountNpr: number }[];\n },\n>(invoice: T) {\n const paidNpr = invoicePaidTotal(invoice.payments);\n return {\n ...invoice,\n paidNpr,\n balanceNpr: Math.max(0, invoice.amountNpr - paidNpr),\n };\n}\n\nexport async function listPlans(userId: string, schoolId: string) {\n await requireAdmin(schoolId, userId);\n const plans = await listFeePlans(schoolId);\n return { plans };\n}\n\nexport async function getPlan(userId: string, planId: string) {\n const plan = await getFeePlan(planId);\n if (!plan) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n await requireAdmin(plan.schoolId, userId);\n return { plan };\n}\n\nexport async function createPlan(\n userId: string,\n input: {\n schoolId: string;\n name: string;\n description?: string | null;\n amountNpr: number;\n studentIds?: string[];\n }\n) {\n await requireAdmin(input.schoolId, userId);\n if (input.amountNpr <= 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"amountNpr must be positive\",\n });\n }\n const plan = await createFeePlan({\n schoolId: input.schoolId,\n name: input.name.trim(),\n description: input.description?.trim() || null,\n amountNpr: input.amountNpr,\n });\n if (input.studentIds?.length) {\n await assertActiveSchoolStudents(input.schoolId, input.studentIds);\n const updated = await setFeePlanEnrollments(plan.id, input.studentIds);\n return { plan: updated };\n }\n return { plan };\n}\n\nexport async function updatePlan(\n userId: string,\n input: {\n planId: string;\n name?: string;\n description?: string | null;\n amountNpr?: number;\n isActive?: boolean;\n }\n) {\n const existing = await getFeePlan(input.planId);\n if (!existing) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n await requireAdmin(existing.schoolId, userId);\n if (input.amountNpr != null && input.amountNpr <= 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"amountNpr must be positive\",\n });\n }\n const plan = await updateFeePlan(input.planId, {\n name: input.name?.trim(),\n description: input.description,\n amountNpr: input.amountNpr,\n isActive: input.isActive,\n });\n return { plan };\n}\n\nexport async function setPlanStudents(\n userId: string,\n input: { planId: string; studentIds: string[] }\n) {\n const existing = await getFeePlan(input.planId);\n if (!existing) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n await requireAdmin(existing.schoolId, userId);\n await assertActiveSchoolStudents(existing.schoolId, input.studentIds);\n const plan = await setFeePlanEnrollments(input.planId, input.studentIds);\n return { plan };\n}\n\nexport async function generateMonthlyInvoices(\n userId: string,\n input: {\n schoolId: string;\n feePlanId: string;\n year: number;\n month: number;\n studentIds?: string[];\n dueDate?: Date | null;\n }\n) {\n await requireAdmin(input.schoolId, userId);\n assertMonth(input.month);\n\n const plan = await getFeePlan(input.feePlanId);\n if (!plan || plan.schoolId !== input.schoolId) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n if (!plan.isActive) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Cannot generate invoices for an inactive fee plan\",\n });\n }\n\n let studentIds = input.studentIds;\n if (studentIds?.length) {\n await assertActiveSchoolStudents(input.schoolId, studentIds);\n } else {\n const enrolled = await listPlanEnrollmentIds(plan.id);\n studentIds = enrolled.length\n ? enrolled\n : await listActiveStudentIds(input.schoolId);\n }\n\n if (!studentIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No students to invoice\",\n });\n }\n\n const result = await createFeeInvoices(\n studentIds.map((studentId) => ({\n schoolId: input.schoolId,\n feePlanId: plan.id,\n studentId,\n year: input.year,\n month: input.month,\n amountNpr: plan.amountNpr,\n currency: plan.currency,\n dueDate: input.dueDate ?? null,\n }))\n );\n\n const invoices = await listFeeInvoices({\n schoolId: input.schoolId,\n feePlanId: plan.id,\n year: input.year,\n month: input.month,\n });\n\n return {\n createdCount: result.count,\n invoices: invoices.map(withBalance),\n };\n}\n\nexport async function listInvoices(\n userId: string,\n input: {\n schoolId: string;\n year?: number;\n month?: number;\n studentId?: string;\n feePlanId?: string;\n status?: FeeInvoiceStatus;\n }\n) {\n await requireAdmin(input.schoolId, userId);\n if (input.month != null) assertMonth(input.month);\n const invoices = await listFeeInvoices(input);\n return { invoices: invoices.map(withBalance) };\n}\n\nexport async function getInvoice(userId: string, invoiceId: string) {\n const invoice = await getFeeInvoice(invoiceId);\n if (!invoice) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Invoice not found\" });\n }\n await requireAdmin(invoice.schoolId, userId);\n return { invoice: withBalance(invoice) };\n}\n\nexport async function recordPayment(\n userId: string,\n input: {\n invoiceId: string;\n amountNpr: number;\n method: FeePaymentMethod;\n reference?: string | null;\n paidAt?: Date;\n notes?: string | null;\n }\n) {\n const invoice = await getFeeInvoice(input.invoiceId);\n if (!invoice) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Invoice not found\" });\n }\n await requireAdmin(invoice.schoolId, userId);\n\n if (invoice.status === \"VOID\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Cannot record payment on a void invoice\",\n });\n }\n if (input.amountNpr <= 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"amountNpr must be positive\",\n });\n }\n if (\n (input.method === \"BANK\" || input.method === \"ESEWA_REF\") &&\n !input.reference?.trim()\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"reference is required for BANK and ESEWA_REF payments\",\n });\n }\n\n const paidSoFar = invoicePaidTotal(invoice.payments);\n const balance = Math.max(0, invoice.amountNpr - paidSoFar);\n if (input.amountNpr > balance) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: `Payment exceeds balance (${formatNpr(balance)})`,\n });\n }\n\n const payment = await createFeePayment({\n schoolId: invoice.schoolId,\n invoiceId: invoice.id,\n amountNpr: input.amountNpr,\n method: input.method,\n reference: input.reference?.trim() || null,\n paidAt: input.paidAt,\n recordedById: userId,\n notes: input.notes?.trim() || null,\n });\n\n const aggregate = await sumPaymentsForInvoice(invoice.id);\n const newPaid = aggregate._sum.amountNpr ?? 0;\n const status = deriveStatus(invoice.amountNpr, newPaid, invoice.status);\n await updateInvoiceStatus(invoice.id, status);\n\n const refreshed = await getFeeInvoice(invoice.id);\n return {\n payment,\n invoice: refreshed ? withBalance(refreshed) : null,\n };\n}\n\nexport async function listBalances(userId: string, schoolId: string) {\n await requireAdmin(schoolId, userId);\n const balances = await listStudentBalances(schoolId);\n return { balances };\n}\n\nexport async function getInvoicePdf(userId: string, invoiceId: string) {\n const invoice = await getFeeInvoice(invoiceId);\n if (!invoice) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Invoice not found\" });\n }\n await requireAdmin(invoice.schoolId, userId);\n\n const paidNpr = invoicePaidTotal(invoice.payments);\n const balanceNpr = Math.max(0, invoice.amountNpr - paidNpr);\n const studentName =\n invoice.student.profile?.displayName || invoice.student.username;\n\n const bytes = await buildFeePdf({\n schoolName: invoice.school.name,\n title: \"Fee Invoice\",\n subtitle: `${invoice.feePlan.name} · ${monthLabel(invoice.year, invoice.month)}`,\n letterhead: invoice.school.letterhead\n ? {\n path: invoice.school.letterhead.path,\n type: invoice.school.letterhead.type,\n }\n : null,\n lines: [\n { label: \"Invoice ID\", value: invoice.id.slice(0, 8).toUpperCase() },\n { label: \"Student\", value: studentName },\n { label: \"Period\", value: monthLabel(invoice.year, invoice.month) },\n { label: \"Plan\", value: invoice.feePlan.name },\n { label: \"Amount\", value: formatNpr(invoice.amountNpr) },\n { label: \"Paid\", value: formatNpr(paidNpr) },\n { label: \"Balance\", value: formatNpr(balanceNpr) },\n { label: \"Status\", value: invoice.status },\n ],\n footerNote: \"Manual ledger — not a gateway receipt. Keep for school records.\",\n });\n\n return {\n fileName: `invoice-${invoice.year}-${String(invoice.month).padStart(2, \"0\")}-${invoice.id.slice(0, 8)}.pdf`,\n contentType: \"application/pdf\",\n base64: Buffer.from(bytes).toString(\"base64\"),\n };\n}\n\nexport async function getReceiptPdf(userId: string, paymentId: string) {\n const payment = await getFeePayment(paymentId);\n if (!payment) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Payment not found\" });\n }\n await requireAdmin(payment.schoolId, userId);\n\n const invoice = payment.invoice;\n const studentName =\n invoice.student.profile?.displayName || invoice.student.username;\n\n const bytes = await buildFeePdf({\n schoolName: invoice.school.name,\n title: \"Payment Receipt\",\n subtitle: `${invoice.feePlan.name} · ${monthLabel(invoice.year, invoice.month)}`,\n letterhead: invoice.school.letterhead\n ? {\n path: invoice.school.letterhead.path,\n type: invoice.school.letterhead.type,\n }\n : null,\n lines: [\n { label: \"Receipt ID\", value: payment.id.slice(0, 8).toUpperCase() },\n { label: \"Student\", value: studentName },\n { label: \"Period\", value: monthLabel(invoice.year, invoice.month) },\n { label: \"Amount paid\", value: formatNpr(payment.amountNpr) },\n { label: \"Method\", value: payment.method },\n {\n label: \"Reference\",\n value: payment.reference?.trim() || \"—\",\n },\n {\n label: \"Paid at\",\n value: new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: \"Asia/Kathmandu\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).format(payment.paidAt),\n },\n ],\n footerNote: \"Manual ledger receipt — gateway integration deferred.\",\n });\n\n return {\n fileName: `receipt-${payment.id.slice(0, 8)}.pdf`,\n contentType: \"application/pdf\",\n base64: Buffer.from(bytes).toString(\"base64\"),\n };\n}\n"],"names":[],"mappings":"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","debug_id":"17f46206-bcf6-5721-9d78-09801ff55dea"}
|
|
1
|
+
{"version":3,"file":"fees.js","sources":["services/fees.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Fee ledger service — school-admin gated monthly invoicing + payment recording.\n * Payment gateway intentionally deferred; this is a manual ledger.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { FeeInvoiceStatus, FeePaymentMethod } from \"@prisma/client\";\nimport { findActiveSchoolAdminMembership } from \"../models/school.js\";\nimport {\n createFeeInvoices,\n createFeePayment,\n createFeePlan,\n getFeeInvoice,\n getFeePayment,\n getFeePlan,\n listActiveStudentIds,\n listFeeInvoices,\n listFeePlans,\n listPlanEnrollmentIds,\n listStudentBalances,\n setFeePlanEnrollments,\n sumPaymentsForInvoice,\n updateFeePlan,\n updateInvoiceStatus,\n} from \"../models/fees.js\";\nimport {\n buildFeePdf,\n formatNpr,\n monthLabel,\n} from \"../lib/feePdf.js\";\n\nasync function requireAdmin(schoolId: string, userId: string) {\n const membership = await findActiveSchoolAdminMembership(schoolId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only school admins can manage fees\",\n });\n }\n return membership;\n}\n\nasync function assertActiveSchoolStudents(\n schoolId: string,\n studentIds: string[]\n) {\n const activeIds = new Set(await listActiveStudentIds(schoolId));\n const invalid = studentIds.filter((id) => !activeIds.has(id));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more students are not active members of this school\",\n });\n }\n}\n\nfunction assertMonth(month: number) {\n if (month < 1 || month > 12) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"month must be between 1 and 12\",\n });\n }\n}\n\nfunction invoicePaidTotal(payments: { amountNpr: number }[]): number {\n return payments.reduce((s, p) => s + p.amountNpr, 0);\n}\n\nfunction deriveStatus(\n amountNpr: number,\n paidNpr: number,\n current: FeeInvoiceStatus\n): FeeInvoiceStatus {\n if (current === \"VOID\") return \"VOID\";\n if (paidNpr <= 0) return \"UNPAID\";\n if (paidNpr >= amountNpr) return \"PAID\";\n return \"PARTIAL\";\n}\n\nfunction withBalance<\n T extends {\n amountNpr: number;\n status: FeeInvoiceStatus;\n payments: { amountNpr: number }[];\n },\n>(invoice: T) {\n const paidNpr = invoicePaidTotal(invoice.payments);\n return {\n ...invoice,\n paidNpr,\n balanceNpr: Math.max(0, invoice.amountNpr - paidNpr),\n };\n}\n\nexport async function listPlans(userId: string, schoolId: string) {\n await requireAdmin(schoolId, userId);\n const plans = await listFeePlans(schoolId);\n return { plans };\n}\n\nexport async function getPlan(userId: string, planId: string) {\n const plan = await getFeePlan(planId);\n if (!plan) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n await requireAdmin(plan.schoolId, userId);\n return { plan };\n}\n\nexport async function createPlan(\n userId: string,\n input: {\n schoolId: string;\n name: string;\n description?: string | null;\n amountNpr: number;\n studentIds?: string[];\n }\n) {\n await requireAdmin(input.schoolId, userId);\n if (input.amountNpr <= 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"amountNpr must be positive\",\n });\n }\n const plan = await createFeePlan({\n schoolId: input.schoolId,\n name: input.name.trim(),\n description: input.description?.trim() || null,\n amountNpr: input.amountNpr,\n });\n if (input.studentIds?.length) {\n await assertActiveSchoolStudents(input.schoolId, input.studentIds);\n const updated = await setFeePlanEnrollments(plan.id, input.studentIds);\n return { plan: updated };\n }\n return { plan };\n}\n\nexport async function updatePlan(\n userId: string,\n input: {\n planId: string;\n name?: string;\n description?: string | null;\n amountNpr?: number;\n isActive?: boolean;\n }\n) {\n const existing = await getFeePlan(input.planId);\n if (!existing) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n await requireAdmin(existing.schoolId, userId);\n if (input.amountNpr != null && input.amountNpr <= 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"amountNpr must be positive\",\n });\n }\n const plan = await updateFeePlan(input.planId, {\n name: input.name?.trim(),\n description: input.description,\n amountNpr: input.amountNpr,\n isActive: input.isActive,\n });\n return { plan };\n}\n\nexport async function setPlanStudents(\n userId: string,\n input: { planId: string; studentIds: string[] }\n) {\n const existing = await getFeePlan(input.planId);\n if (!existing) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n await requireAdmin(existing.schoolId, userId);\n await assertActiveSchoolStudents(existing.schoolId, input.studentIds);\n const plan = await setFeePlanEnrollments(input.planId, input.studentIds);\n return { plan };\n}\n\nexport async function generateMonthlyInvoices(\n userId: string,\n input: {\n schoolId: string;\n feePlanId: string;\n year: number;\n month: number;\n studentIds?: string[];\n dueDate?: Date | null;\n }\n) {\n await requireAdmin(input.schoolId, userId);\n assertMonth(input.month);\n\n const plan = await getFeePlan(input.feePlanId);\n if (!plan || plan.schoolId !== input.schoolId) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Fee plan not found\" });\n }\n if (!plan.isActive) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Cannot generate invoices for an inactive fee plan\",\n });\n }\n\n let studentIds = input.studentIds;\n if (studentIds?.length) {\n await assertActiveSchoolStudents(input.schoolId, studentIds);\n } else {\n const enrolled = await listPlanEnrollmentIds(plan.id);\n studentIds = enrolled.length\n ? enrolled\n : await listActiveStudentIds(input.schoolId);\n }\n\n if (!studentIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No students to invoice\",\n });\n }\n\n const result = await createFeeInvoices(\n studentIds.map((studentId) => ({\n schoolId: input.schoolId,\n feePlanId: plan.id,\n studentId,\n year: input.year,\n month: input.month,\n amountNpr: plan.amountNpr,\n currency: plan.currency,\n dueDate: input.dueDate ?? null,\n }))\n );\n\n const invoices = await listFeeInvoices({\n schoolId: input.schoolId,\n feePlanId: plan.id,\n year: input.year,\n month: input.month,\n });\n\n return {\n createdCount: result.count,\n invoices: invoices.map(withBalance),\n };\n}\n\nexport async function listInvoices(\n userId: string,\n input: {\n schoolId: string;\n year?: number;\n month?: number;\n studentId?: string;\n feePlanId?: string;\n status?: FeeInvoiceStatus;\n }\n) {\n await requireAdmin(input.schoolId, userId);\n if (input.month != null) assertMonth(input.month);\n const invoices = await listFeeInvoices(input);\n return { invoices: invoices.map(withBalance) };\n}\n\nexport async function getInvoice(userId: string, invoiceId: string) {\n const invoice = await getFeeInvoice(invoiceId);\n if (!invoice) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Invoice not found\" });\n }\n await requireAdmin(invoice.schoolId, userId);\n return { invoice: withBalance(invoice) };\n}\n\nexport async function recordPayment(\n userId: string,\n input: {\n invoiceId: string;\n amountNpr: number;\n method: FeePaymentMethod;\n reference?: string | null;\n paidAt?: Date;\n notes?: string | null;\n }\n) {\n const invoice = await getFeeInvoice(input.invoiceId);\n if (!invoice) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Invoice not found\" });\n }\n await requireAdmin(invoice.schoolId, userId);\n\n if (invoice.status === \"VOID\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Cannot record payment on a void invoice\",\n });\n }\n if (input.amountNpr <= 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"amountNpr must be positive\",\n });\n }\n if (\n (input.method === \"BANK\" || input.method === \"ESEWA_REF\") &&\n !input.reference?.trim()\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"reference is required for BANK and ESEWA_REF payments\",\n });\n }\n\n const paidSoFar = invoicePaidTotal(invoice.payments);\n const balance = Math.max(0, invoice.amountNpr - paidSoFar);\n if (input.amountNpr > balance) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: `Payment exceeds balance (${formatNpr(balance)})`,\n });\n }\n\n const payment = await createFeePayment({\n schoolId: invoice.schoolId,\n invoiceId: invoice.id,\n amountNpr: input.amountNpr,\n method: input.method,\n reference: input.reference?.trim() || null,\n paidAt: input.paidAt,\n recordedById: userId,\n notes: input.notes?.trim() || null,\n });\n\n const aggregate = await sumPaymentsForInvoice(invoice.id);\n const newPaid = aggregate._sum.amountNpr ?? 0;\n const status = deriveStatus(invoice.amountNpr, newPaid, invoice.status);\n await updateInvoiceStatus(invoice.id, status);\n\n const refreshed = await getFeeInvoice(invoice.id);\n return {\n payment,\n invoice: refreshed ? withBalance(refreshed) : null,\n };\n}\n\nexport async function listBalances(userId: string, schoolId: string) {\n await requireAdmin(schoolId, userId);\n const balances = await listStudentBalances(schoolId);\n return { balances };\n}\n\nexport async function getInvoicePdf(userId: string, invoiceId: string) {\n const invoice = await getFeeInvoice(invoiceId);\n if (!invoice) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Invoice not found\" });\n }\n await requireAdmin(invoice.schoolId, userId);\n\n const paidNpr = invoicePaidTotal(invoice.payments);\n const balanceNpr = Math.max(0, invoice.amountNpr - paidNpr);\n const studentName =\n invoice.student.profile?.displayName || invoice.student.username;\n\n const bytes = await buildFeePdf({\n schoolName: invoice.school.name,\n title: \"Fee Invoice\",\n subtitle: `${invoice.feePlan.name} · ${monthLabel(invoice.year, invoice.month)}`,\n letterhead: invoice.school.letterhead\n ? {\n path: invoice.school.letterhead.path,\n type: invoice.school.letterhead.type,\n }\n : null,\n lines: [\n { label: \"Invoice ID\", value: invoice.id.slice(0, 8).toUpperCase() },\n { label: \"Student\", value: studentName },\n { label: \"Period\", value: monthLabel(invoice.year, invoice.month) },\n { label: \"Plan\", value: invoice.feePlan.name },\n { label: \"Amount\", value: formatNpr(invoice.amountNpr) },\n { label: \"Paid\", value: formatNpr(paidNpr) },\n { label: \"Balance\", value: formatNpr(balanceNpr) },\n { label: \"Status\", value: invoice.status },\n ],\n footerNote: \"Manual ledger — not a gateway receipt. Keep for school records.\",\n });\n\n return {\n fileName: `invoice-${invoice.year}-${String(invoice.month).padStart(2, \"0\")}-${invoice.id.slice(0, 8)}.pdf`,\n contentType: \"application/pdf\",\n base64: Buffer.from(bytes).toString(\"base64\"),\n };\n}\n\nexport async function getReceiptPdf(userId: string, paymentId: string) {\n const payment = await getFeePayment(paymentId);\n if (!payment) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Payment not found\" });\n }\n await requireAdmin(payment.schoolId, userId);\n\n const invoice = payment.invoice;\n const studentName =\n invoice.student.profile?.displayName || invoice.student.username;\n\n const bytes = await buildFeePdf({\n schoolName: invoice.school.name,\n title: \"Payment Receipt\",\n subtitle: `${invoice.feePlan.name} · ${monthLabel(invoice.year, invoice.month)}`,\n letterhead: invoice.school.letterhead\n ? {\n path: invoice.school.letterhead.path,\n type: invoice.school.letterhead.type,\n }\n : null,\n lines: [\n { label: \"Receipt ID\", value: payment.id.slice(0, 8).toUpperCase() },\n { label: \"Student\", value: studentName },\n { label: \"Period\", value: monthLabel(invoice.year, invoice.month) },\n { label: \"Amount paid\", value: formatNpr(payment.amountNpr) },\n { label: \"Method\", value: payment.method },\n {\n label: \"Reference\",\n value: payment.reference?.trim() || \"—\",\n },\n {\n label: \"Paid at\",\n // Format in UTC rather than a hardcoded country timezone — Studious is\n // used internationally, and this matches the UTC basis already used\n // for invoice month labels. A per-school timezone setting is the\n // proper long-term home for this (see docs/audits bug audit §2).\n value: new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).format(payment.paidAt),\n },\n ],\n footerNote: \"Manual ledger receipt — gateway integration deferred.\",\n });\n\n return {\n fileName: `receipt-${payment.id.slice(0, 8)}.pdf`,\n contentType: \"application/pdf\",\n base64: Buffer.from(bytes).toString(\"base64\"),\n };\n}\n"],"names":[],"mappings":"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","debug_id":"fbc59022-10f0-5feb-9396-4ad26a37b74a"}
|
|
@@ -108,6 +108,15 @@ export declare function getLabChat(userId: string, labChatId: string): Promise<{
|
|
|
108
108
|
conversationId: string;
|
|
109
109
|
createdById: string;
|
|
110
110
|
}>;
|
|
111
|
+
/**
|
|
112
|
+
* Suggest starter prompts for a class's lab chat, derived from recent
|
|
113
|
+
* class context (upcoming due work, recent assignments, units, subject).
|
|
114
|
+
* Pure heuristics — no LLM call — so it stays cheap and fast. Falls back
|
|
115
|
+
* to generic defaults when the class has no usable context.
|
|
116
|
+
*/
|
|
117
|
+
export declare function getStarterPrompts(userId: string, classId: string): Promise<{
|
|
118
|
+
prompts: string[];
|
|
119
|
+
}>;
|
|
111
120
|
export declare function listLabChats(userId: string, classId: string): Promise<{
|
|
112
121
|
id: string;
|
|
113
122
|
title: string;
|
|
@@ -138,6 +147,7 @@ export declare function listLabChats(userId: string, classId: string): Promise<{
|
|
|
138
147
|
content: string;
|
|
139
148
|
senderId: string;
|
|
140
149
|
conversationId: string;
|
|
150
|
+
aiFeedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
141
151
|
runJobId: string | null;
|
|
142
152
|
};
|
|
143
153
|
messageCount: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["services/labChat.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["services/labChat.ts"],"names":[],"mappings":"AA8BA,sFAAsF;AACtF,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiI3D;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCjE;AAWD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;GA+CtE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqBjE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+JF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiDpE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiGxE"}
|
package/dist/services/labChat.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* trigger AI responses, and broadcast via Pusher.
|
|
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]="0ed3ef9f-a8ca-5f2e-90d9-3ceddc9af625")}catch(e){}}();
|
|
7
7
|
import { TRPCError } from "@trpc/server";
|
|
8
8
|
import { JobStatus } from "@prisma/client";
|
|
9
9
|
import { prisma } from "../lib/prisma.js";
|
|
10
10
|
import { chatChannel, teacherChannel, pusher } from "../lib/pusher.js";
|
|
11
|
-
import { findClassWithTeachersForLab, findLabChatByMember, findLabChatByTeacher, findLabChatById, findClassMembership, findLabChatsByClass, findLabChatForPost, findLabChatForDelete, findMentionedMemberships, } from "../models/labChat.js";
|
|
11
|
+
import { findClassWithTeachersForLab, findLabChatByMember, findLabChatByTeacher, findLabChatById, findClassContextForStarterPrompts, findClassMembership, findLabChatsByClass, findLabChatForPost, findLabChatForDelete, findMentionedMemberships, } from "../models/labChat.js";
|
|
12
12
|
import { getAIUserId, isAIUser } from "../utils/aiUser.js";
|
|
13
13
|
import { createPendingAIMessage } from "../utils/inference.js";
|
|
14
14
|
import { logger } from "../utils/logger.js";
|
|
@@ -171,6 +171,55 @@ export async function getLabChat(userId, labChatId) {
|
|
|
171
171
|
pendingGenerationMessageId: pendingMessage?.id ?? null,
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
+
const GENERIC_STARTER_PROMPTS = [
|
|
175
|
+
"Create a quiz on this week's topic",
|
|
176
|
+
"Draft a worksheet with 10 practice questions",
|
|
177
|
+
"Plan a lesson with objectives and activities",
|
|
178
|
+
"Suggest an assignment with a mark scheme",
|
|
179
|
+
];
|
|
180
|
+
const MAX_STARTER_PROMPTS = 5;
|
|
181
|
+
/**
|
|
182
|
+
* Suggest starter prompts for a class's lab chat, derived from recent
|
|
183
|
+
* class context (upcoming due work, recent assignments, units, subject).
|
|
184
|
+
* Pure heuristics — no LLM call — so it stays cheap and fast. Falls back
|
|
185
|
+
* to generic defaults when the class has no usable context.
|
|
186
|
+
*/
|
|
187
|
+
export async function getStarterPrompts(userId, classId) {
|
|
188
|
+
const classContext = await findClassContextForStarterPrompts(classId, userId);
|
|
189
|
+
if (!classContext) {
|
|
190
|
+
throw new TRPCError({
|
|
191
|
+
code: "FORBIDDEN",
|
|
192
|
+
message: "Not a teacher in this class",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const prompts = [];
|
|
196
|
+
const now = new Date();
|
|
197
|
+
const published = classContext.assignments.filter((a) => !a.inProgress);
|
|
198
|
+
const upcoming = published
|
|
199
|
+
.filter((a) => a.dueDate > now)
|
|
200
|
+
.sort((a, b) => a.dueDate.getTime() - b.dueDate.getTime())[0];
|
|
201
|
+
if (upcoming) {
|
|
202
|
+
prompts.push(`Create a worksheet to help students prepare for "${upcoming.title}"`);
|
|
203
|
+
}
|
|
204
|
+
const recent = published.find((a) => a.title !== upcoming?.title);
|
|
205
|
+
if (recent) {
|
|
206
|
+
prompts.push(`Draft a quiz reviewing "${recent.title}"`);
|
|
207
|
+
}
|
|
208
|
+
const unit = classContext.sections[0];
|
|
209
|
+
if (unit) {
|
|
210
|
+
prompts.push(`Plan a lesson for the unit "${unit.name}"`);
|
|
211
|
+
}
|
|
212
|
+
if (classContext.subject) {
|
|
213
|
+
prompts.push(`Suggest a ${classContext.subject} assignment with a mark scheme`);
|
|
214
|
+
}
|
|
215
|
+
for (const generic of GENERIC_STARTER_PROMPTS) {
|
|
216
|
+
if (prompts.length >= 3)
|
|
217
|
+
break;
|
|
218
|
+
if (!prompts.includes(generic))
|
|
219
|
+
prompts.push(generic);
|
|
220
|
+
}
|
|
221
|
+
return { prompts: prompts.slice(0, MAX_STARTER_PROMPTS) };
|
|
222
|
+
}
|
|
174
223
|
export async function listLabChats(userId, classId) {
|
|
175
224
|
const classMembership = await findClassMembership(classId, userId);
|
|
176
225
|
if (!classMembership) {
|
|
@@ -466,4 +515,4 @@ export async function rerunLastResponse(userId, labChatId) {
|
|
|
466
515
|
return { success: true };
|
|
467
516
|
}
|
|
468
517
|
//# sourceMappingURL=labChat.js.map
|
|
469
|
-
//# debugId=
|
|
518
|
+
//# debugId=0ed3ef9f-a8ca-5f2e-90d9-3ceddc9af625
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labChat.js","sources":["services/labChat.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Lab chat service – AI-powered lab conversations. Create lab chats, post messages,\n * trigger AI responses, and broadcast via Pusher.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { JobStatus } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { chatChannel, teacherChannel, pusher } from \"../lib/pusher.js\";\nimport {\n findClassWithTeachersForLab,\n findLabChatByMember,\n findLabChatByTeacher,\n findLabChatById,\n findClassMembership,\n findLabChatsByClass,\n findLabChatForPost,\n findLabChatForDelete,\n findMentionedMemberships,\n} from \"../models/labChat.js\";\nimport { getAIUserId, isAIUser } from \"../utils/aiUser.js\";\nimport { createPendingAIMessage } from \"../utils/inference.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { enqueueRunJob } from \"../workers/queues.js\";\nimport {\n claimMessageAttachments,\n ensureMessageHasContentOrAttachments,\n} from \"./message.js\";\nimport { messageFileSelect } from \"../models/message.js\";\n\n/** Create a lab chat with conversation and teacher members. Broadcasts via Pusher. */\nexport async function createLabChat(\n userId: string,\n input: { classId: string; title: string; context: string }\n) {\n const { classId, title, context } = input;\n\n const classWithTeachers = await findClassWithTeachersForLab(classId, userId);\n if (!classWithTeachers) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a teacher in this class\",\n });\n }\n\n try {\n JSON.parse(context);\n } catch {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Context must be valid JSON\",\n });\n }\n\n const result = await prisma.$transaction(async (tx) => {\n const conversation = await tx.conversation.create({\n data: {\n type: \"GROUP\",\n name: `Lab: ${title}`,\n displayInChat: false,\n },\n });\n\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 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 // Create the AI introduction placeholder *outside* the transaction (the\n // helper triggers Pusher and we don't want a tx rollback to leave a\n // dangling broadcast). The pipeline will fill this row in.\n try {\n const aiPlaceholder = await createPendingAIMessage(result.conversationId);\n await enqueueRunJob(\n \"AI_LAB_CHAT\",\n { conversationId: result.conversationId, aiMessageId: aiPlaceholder.id },\n { classId, userId },\n );\n } catch (error) {\n logger.error(\"Failed to enqueue lab chat introduction\", {\n error,\n labChatId: result.id,\n conversationId: result.conversationId,\n });\n }\n\n try {\n await pusher.trigger(teacherChannel(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 }\n\n return result;\n}\n\nexport async function getLabChat(userId: string, labChatId: string) {\n let labChat = await findLabChatByMember(labChatId, userId);\n\n if (!labChat) {\n const labChatForTeacher = await findLabChatByTeacher(labChatId, userId);\n if (labChatForTeacher) {\n await prisma.conversationMember.create({\n data: {\n userId,\n conversationId: labChatForTeacher.conversation.id,\n role: \"ADMIN\",\n },\n });\n labChat = await findLabChatById(labChatId);\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 // The AI placeholder row IS the pending generation marker now — its\n // status flips PENDING → COMPLETED / FAILED as the pipeline runs.\n const pendingMessage = await prisma.message.findFirst({\n where: {\n conversationId: labChat.conversationId,\n status: JobStatus.PENDING,\n senderId: getAIUserId(),\n },\n orderBy: { createdAt: \"desc\" },\n select: { id: true },\n });\n\n return {\n ...labChat,\n pendingGenerationMessageId: pendingMessage?.id ?? null,\n };\n}\n\nexport async function listLabChats(userId: string, classId: string) {\n const classMembership = await findClassMembership(classId, userId);\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 findLabChatsByClass(classId);\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\nexport async function postToLabChat(\n userId: string,\n input: {\n labChatId: string;\n content: string;\n mentionedUserIds?: string[];\n fileIds?: string[];\n }\n) {\n const { labChatId, content, mentionedUserIds = [], fileIds = [] } = input;\n\n const labChat = await findLabChatForPost(labChatId, userId);\n if (!labChat) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Lab chat not found or access denied\",\n });\n }\n\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await findMentionedMemberships(\n labChat.conversationId,\n mentionedUserIds\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 ensureMessageHasContentOrAttachments(content, fileIds);\n const normalizedContent = content.trim();\n\n const result = await prisma.$transaction(async (tx) => {\n const message = await tx.message.create({\n data: {\n content: normalizedContent,\n senderId: userId,\n conversationId: labChat.conversationId,\n },\n });\n\n await claimMessageAttachments(\n tx,\n userId,\n labChat.conversationId,\n message.id,\n fileIds\n );\n\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 await tx.labChat.update({\n where: { id: labChatId },\n data: { updatedAt: new Date() },\n });\n\n return tx.message.findUniqueOrThrow({\n where: { id: message.id },\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 attachments: {\n select: messageFileSelect,\n },\n },\n });\n });\n\n try {\n await pusher.trigger(\n chatChannel(labChat.conversationId),\n \"new-message\",\n {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n status: result.status,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n mentionedUserIds,\n meta: {},\n }\n );\n } catch (error) {\n console.error(\"Failed to broadcast lab chat message:\", error);\n }\n\n if (!isAIUser(userId)) {\n // Create the AI placeholder post-tx so a transaction rollback can never\n // leave a dangling PENDING bubble. The placeholder broadcast IS the\n // \"AI is thinking...\" indicator inline in the chat; the teacher-channel\n // event below drives the lab list \"AI busy in this lab\" pill.\n let aiPlaceholderId: string | null = null;\n try {\n const aiPlaceholder = await createPendingAIMessage(labChat.conversationId);\n aiPlaceholderId = aiPlaceholder.id;\n } catch (error) {\n logger.error(\"Failed to create AI placeholder for lab response\", {\n error,\n labChatId,\n teacherMessageId: result.id,\n conversationId: labChat.conversationId,\n });\n }\n\n try {\n await pusher.trigger(teacherChannel(labChat.classId), \"lab-response-pending\", {\n labChatId,\n messageId: aiPlaceholderId ?? result.id,\n conversationId: labChat.conversationId,\n });\n } catch (error) {\n console.error(\"Failed to broadcast lab response pending:\", error);\n }\n\n if (aiPlaceholderId) {\n try {\n await enqueueRunJob(\n \"AI_LAB_CHAT\",\n { conversationId: labChat.conversationId, aiMessageId: aiPlaceholderId },\n { classId: labChat.classId, userId },\n );\n } catch (error) {\n logger.error(\"Failed to enqueue lab response\", {\n error,\n labChatId,\n aiMessageId: aiPlaceholderId,\n conversationId: labChat.conversationId,\n });\n }\n }\n }\n\n return {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n status: result.status,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n mentionedUserIds,\n };\n}\n\nexport async function deleteLabChat(userId: string, labChatId: string) {\n const labChat = await findLabChatForDelete(labChatId, userId);\n if (!labChat) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Lab chat not found or not the creator\",\n });\n }\n\n await prisma.$transaction(async (tx) => {\n await tx.mention.deleteMany({\n where: {\n message: {\n conversationId: labChat.conversationId,\n },\n },\n });\n\n await tx.message.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n await tx.conversationMember.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n await tx.labChat.delete({\n where: { id: labChatId },\n });\n\n await tx.conversation.delete({\n where: { id: labChat.conversationId },\n });\n });\n\n try {\n await pusher.trigger(teacherChannel(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 }\n\n return { success: true };\n}\n\n/**\n * Rerun the last AI response in a lab chat.\n *\n * The AI message bubble is reused (not deleted): we flip it back to\n * PENDING and clear its content so the existing message id stays stable\n * for the client. The pipeline then refills it.\n */\nexport async function rerunLastResponse(userId: string, labChatId: string) {\n const labChat = await findLabChatForPost(labChatId, userId);\n if (!labChat) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Lab chat not found or access denied\",\n });\n }\n\n const messages = await prisma.message.findMany({\n where: { conversationId: labChat.conversationId },\n orderBy: { createdAt: \"desc\" },\n take: 10,\n select: { id: true, content: true, senderId: true, createdAt: true },\n });\n\n const lastMessage = messages[0];\n if (!lastMessage) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No messages to rerun\",\n });\n }\n\n if (!isAIUser(lastMessage.senderId)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Last message is not from AI – nothing to rerun\",\n });\n }\n\n const teacherMessage = messages.find((m) => !isAIUser(m.senderId));\n if (!teacherMessage) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No teacher message found to rerun from\",\n });\n }\n\n const reusedAiMessage = await prisma.message.update({\n where: { id: lastMessage.id },\n data: {\n content: \"\",\n status: JobStatus.PENDING,\n meta: {},\n },\n select: { id: true, conversationId: true, status: true, content: true, senderId: true, createdAt: true },\n });\n\n // Tell the chat UI the existing AI bubble is regenerating — content\n // cleared, status flipped back to PENDING, same id.\n try {\n await pusher.trigger(chatChannel(labChat.conversationId), \"message-updated\", {\n id: reusedAiMessage.id,\n content: reusedAiMessage.content,\n senderId: reusedAiMessage.senderId,\n conversationId: reusedAiMessage.conversationId,\n status: reusedAiMessage.status,\n createdAt: reusedAiMessage.createdAt,\n sender: {\n id: reusedAiMessage.senderId,\n username: 'Newton_AI',\n profile: { displayName: 'Newton AI', profilePicture: null },\n },\n mentionedUserIds: [],\n meta: {},\n attachments: [],\n });\n } catch (error) {\n console.error(\"Failed to broadcast AI message rerun reset:\", error);\n }\n try {\n await pusher.trigger(teacherChannel(labChat.classId), \"lab-response-pending\", {\n labChatId,\n messageId: reusedAiMessage.id,\n conversationId: labChat.conversationId,\n });\n } catch (error) {\n console.error(\"Failed to broadcast lab response pending:\", error);\n }\n\n try {\n await enqueueRunJob(\n \"AI_LAB_CHAT\",\n { conversationId: labChat.conversationId, aiMessageId: reusedAiMessage.id },\n { classId: labChat.classId, userId },\n );\n } catch (error) {\n logger.error(\"Failed to enqueue lab response rerun\", {\n error,\n labChatId,\n aiMessageId: reusedAiMessage.id,\n conversationId: labChat.conversationId,\n });\n }\n\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"cc39fd7c-6d0d-5f42-9fd5-2f11bde711b3"}
|
|
1
|
+
{"version":3,"file":"labChat.js","sources":["services/labChat.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Lab chat service – AI-powered lab conversations. Create lab chats, post messages,\n * trigger AI responses, and broadcast via Pusher.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { JobStatus } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { chatChannel, teacherChannel, pusher } from \"../lib/pusher.js\";\nimport {\n findClassWithTeachersForLab,\n findLabChatByMember,\n findLabChatByTeacher,\n findLabChatById,\n findClassContextForStarterPrompts,\n findClassMembership,\n findLabChatsByClass,\n findLabChatForPost,\n findLabChatForDelete,\n findMentionedMemberships,\n} from \"../models/labChat.js\";\nimport { getAIUserId, isAIUser } from \"../utils/aiUser.js\";\nimport { createPendingAIMessage } from \"../utils/inference.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { enqueueRunJob } from \"../workers/queues.js\";\nimport {\n claimMessageAttachments,\n ensureMessageHasContentOrAttachments,\n} from \"./message.js\";\nimport { messageFileSelect } from \"../models/message.js\";\n\n/** Create a lab chat with conversation and teacher members. Broadcasts via Pusher. */\nexport async function createLabChat(\n userId: string,\n input: { classId: string; title: string; context: string }\n) {\n const { classId, title, context } = input;\n\n const classWithTeachers = await findClassWithTeachersForLab(classId, userId);\n if (!classWithTeachers) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a teacher in this class\",\n });\n }\n\n try {\n JSON.parse(context);\n } catch {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Context must be valid JSON\",\n });\n }\n\n const result = await prisma.$transaction(async (tx) => {\n const conversation = await tx.conversation.create({\n data: {\n type: \"GROUP\",\n name: `Lab: ${title}`,\n displayInChat: false,\n },\n });\n\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 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 // Create the AI introduction placeholder *outside* the transaction (the\n // helper triggers Pusher and we don't want a tx rollback to leave a\n // dangling broadcast). The pipeline will fill this row in.\n try {\n const aiPlaceholder = await createPendingAIMessage(result.conversationId);\n await enqueueRunJob(\n \"AI_LAB_CHAT\",\n { conversationId: result.conversationId, aiMessageId: aiPlaceholder.id },\n { classId, userId },\n );\n } catch (error) {\n logger.error(\"Failed to enqueue lab chat introduction\", {\n error,\n labChatId: result.id,\n conversationId: result.conversationId,\n });\n }\n\n try {\n await pusher.trigger(teacherChannel(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 }\n\n return result;\n}\n\nexport async function getLabChat(userId: string, labChatId: string) {\n let labChat = await findLabChatByMember(labChatId, userId);\n\n if (!labChat) {\n const labChatForTeacher = await findLabChatByTeacher(labChatId, userId);\n if (labChatForTeacher) {\n await prisma.conversationMember.create({\n data: {\n userId,\n conversationId: labChatForTeacher.conversation.id,\n role: \"ADMIN\",\n },\n });\n labChat = await findLabChatById(labChatId);\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 // The AI placeholder row IS the pending generation marker now — its\n // status flips PENDING → COMPLETED / FAILED as the pipeline runs.\n const pendingMessage = await prisma.message.findFirst({\n where: {\n conversationId: labChat.conversationId,\n status: JobStatus.PENDING,\n senderId: getAIUserId(),\n },\n orderBy: { createdAt: \"desc\" },\n select: { id: true },\n });\n\n return {\n ...labChat,\n pendingGenerationMessageId: pendingMessage?.id ?? null,\n };\n}\n\nconst GENERIC_STARTER_PROMPTS = [\n \"Create a quiz on this week's topic\",\n \"Draft a worksheet with 10 practice questions\",\n \"Plan a lesson with objectives and activities\",\n \"Suggest an assignment with a mark scheme\",\n];\n\nconst MAX_STARTER_PROMPTS = 5;\n\n/**\n * Suggest starter prompts for a class's lab chat, derived from recent\n * class context (upcoming due work, recent assignments, units, subject).\n * Pure heuristics — no LLM call — so it stays cheap and fast. Falls back\n * to generic defaults when the class has no usable context.\n */\nexport async function getStarterPrompts(userId: string, classId: string) {\n const classContext = await findClassContextForStarterPrompts(\n classId,\n userId\n );\n if (!classContext) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a teacher in this class\",\n });\n }\n\n const prompts: string[] = [];\n const now = new Date();\n\n const published = classContext.assignments.filter((a) => !a.inProgress);\n const upcoming = published\n .filter((a) => a.dueDate > now)\n .sort((a, b) => a.dueDate.getTime() - b.dueDate.getTime())[0];\n if (upcoming) {\n prompts.push(\n `Create a worksheet to help students prepare for \"${upcoming.title}\"`\n );\n }\n\n const recent = published.find((a) => a.title !== upcoming?.title);\n if (recent) {\n prompts.push(`Draft a quiz reviewing \"${recent.title}\"`);\n }\n\n const unit = classContext.sections[0];\n if (unit) {\n prompts.push(`Plan a lesson for the unit \"${unit.name}\"`);\n }\n\n if (classContext.subject) {\n prompts.push(\n `Suggest a ${classContext.subject} assignment with a mark scheme`\n );\n }\n\n for (const generic of GENERIC_STARTER_PROMPTS) {\n if (prompts.length >= 3) break;\n if (!prompts.includes(generic)) prompts.push(generic);\n }\n\n return { prompts: prompts.slice(0, MAX_STARTER_PROMPTS) };\n}\n\nexport async function listLabChats(userId: string, classId: string) {\n const classMembership = await findClassMembership(classId, userId);\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 findLabChatsByClass(classId);\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\nexport async function postToLabChat(\n userId: string,\n input: {\n labChatId: string;\n content: string;\n mentionedUserIds?: string[];\n fileIds?: string[];\n }\n) {\n const { labChatId, content, mentionedUserIds = [], fileIds = [] } = input;\n\n const labChat = await findLabChatForPost(labChatId, userId);\n if (!labChat) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Lab chat not found or access denied\",\n });\n }\n\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await findMentionedMemberships(\n labChat.conversationId,\n mentionedUserIds\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 ensureMessageHasContentOrAttachments(content, fileIds);\n const normalizedContent = content.trim();\n\n const result = await prisma.$transaction(async (tx) => {\n const message = await tx.message.create({\n data: {\n content: normalizedContent,\n senderId: userId,\n conversationId: labChat.conversationId,\n },\n });\n\n await claimMessageAttachments(\n tx,\n userId,\n labChat.conversationId,\n message.id,\n fileIds\n );\n\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 await tx.labChat.update({\n where: { id: labChatId },\n data: { updatedAt: new Date() },\n });\n\n return tx.message.findUniqueOrThrow({\n where: { id: message.id },\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 attachments: {\n select: messageFileSelect,\n },\n },\n });\n });\n\n try {\n await pusher.trigger(\n chatChannel(labChat.conversationId),\n \"new-message\",\n {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n status: result.status,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n mentionedUserIds,\n meta: {},\n }\n );\n } catch (error) {\n console.error(\"Failed to broadcast lab chat message:\", error);\n }\n\n if (!isAIUser(userId)) {\n // Create the AI placeholder post-tx so a transaction rollback can never\n // leave a dangling PENDING bubble. The placeholder broadcast IS the\n // \"AI is thinking...\" indicator inline in the chat; the teacher-channel\n // event below drives the lab list \"AI busy in this lab\" pill.\n let aiPlaceholderId: string | null = null;\n try {\n const aiPlaceholder = await createPendingAIMessage(labChat.conversationId);\n aiPlaceholderId = aiPlaceholder.id;\n } catch (error) {\n logger.error(\"Failed to create AI placeholder for lab response\", {\n error,\n labChatId,\n teacherMessageId: result.id,\n conversationId: labChat.conversationId,\n });\n }\n\n try {\n await pusher.trigger(teacherChannel(labChat.classId), \"lab-response-pending\", {\n labChatId,\n messageId: aiPlaceholderId ?? result.id,\n conversationId: labChat.conversationId,\n });\n } catch (error) {\n console.error(\"Failed to broadcast lab response pending:\", error);\n }\n\n if (aiPlaceholderId) {\n try {\n await enqueueRunJob(\n \"AI_LAB_CHAT\",\n { conversationId: labChat.conversationId, aiMessageId: aiPlaceholderId },\n { classId: labChat.classId, userId },\n );\n } catch (error) {\n logger.error(\"Failed to enqueue lab response\", {\n error,\n labChatId,\n aiMessageId: aiPlaceholderId,\n conversationId: labChat.conversationId,\n });\n }\n }\n }\n\n return {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n status: result.status,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n mentionedUserIds,\n };\n}\n\nexport async function deleteLabChat(userId: string, labChatId: string) {\n const labChat = await findLabChatForDelete(labChatId, userId);\n if (!labChat) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Lab chat not found or not the creator\",\n });\n }\n\n await prisma.$transaction(async (tx) => {\n await tx.mention.deleteMany({\n where: {\n message: {\n conversationId: labChat.conversationId,\n },\n },\n });\n\n await tx.message.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n await tx.conversationMember.deleteMany({\n where: {\n conversationId: labChat.conversationId,\n },\n });\n\n await tx.labChat.delete({\n where: { id: labChatId },\n });\n\n await tx.conversation.delete({\n where: { id: labChat.conversationId },\n });\n });\n\n try {\n await pusher.trigger(teacherChannel(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 }\n\n return { success: true };\n}\n\n/**\n * Rerun the last AI response in a lab chat.\n *\n * The AI message bubble is reused (not deleted): we flip it back to\n * PENDING and clear its content so the existing message id stays stable\n * for the client. The pipeline then refills it.\n */\nexport async function rerunLastResponse(userId: string, labChatId: string) {\n const labChat = await findLabChatForPost(labChatId, userId);\n if (!labChat) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Lab chat not found or access denied\",\n });\n }\n\n const messages = await prisma.message.findMany({\n where: { conversationId: labChat.conversationId },\n orderBy: { createdAt: \"desc\" },\n take: 10,\n select: { id: true, content: true, senderId: true, createdAt: true },\n });\n\n const lastMessage = messages[0];\n if (!lastMessage) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No messages to rerun\",\n });\n }\n\n if (!isAIUser(lastMessage.senderId)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Last message is not from AI – nothing to rerun\",\n });\n }\n\n const teacherMessage = messages.find((m) => !isAIUser(m.senderId));\n if (!teacherMessage) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No teacher message found to rerun from\",\n });\n }\n\n const reusedAiMessage = await prisma.message.update({\n where: { id: lastMessage.id },\n data: {\n content: \"\",\n status: JobStatus.PENDING,\n meta: {},\n },\n select: { id: true, conversationId: true, status: true, content: true, senderId: true, createdAt: true },\n });\n\n // Tell the chat UI the existing AI bubble is regenerating — content\n // cleared, status flipped back to PENDING, same id.\n try {\n await pusher.trigger(chatChannel(labChat.conversationId), \"message-updated\", {\n id: reusedAiMessage.id,\n content: reusedAiMessage.content,\n senderId: reusedAiMessage.senderId,\n conversationId: reusedAiMessage.conversationId,\n status: reusedAiMessage.status,\n createdAt: reusedAiMessage.createdAt,\n sender: {\n id: reusedAiMessage.senderId,\n username: 'Newton_AI',\n profile: { displayName: 'Newton AI', profilePicture: null },\n },\n mentionedUserIds: [],\n meta: {},\n attachments: [],\n });\n } catch (error) {\n console.error(\"Failed to broadcast AI message rerun reset:\", error);\n }\n try {\n await pusher.trigger(teacherChannel(labChat.classId), \"lab-response-pending\", {\n labChatId,\n messageId: reusedAiMessage.id,\n conversationId: labChat.conversationId,\n });\n } catch (error) {\n console.error(\"Failed to broadcast lab response pending:\", error);\n }\n\n try {\n await enqueueRunJob(\n \"AI_LAB_CHAT\",\n { conversationId: labChat.conversationId, aiMessageId: reusedAiMessage.id },\n { classId: labChat.classId, userId },\n );\n } catch (error) {\n logger.error(\"Failed to enqueue lab response rerun\", {\n error,\n labChatId,\n aiMessageId: reusedAiMessage.id,\n conversationId: labChat.conversationId,\n });\n }\n\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"0ed3ef9f-a8ca-5f2e-90d9-3ceddc9af625"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Broadcasts real-time updates via Pusher.
|
|
4
4
|
*/
|
|
5
5
|
import type { AppTransactionClient } from "../lib/prisma.js";
|
|
6
|
+
import { type AiMessageFeedback } from "@prisma/client";
|
|
6
7
|
export declare function ensureMessageHasContentOrAttachments(content: string, fileIds?: string[]): void;
|
|
7
8
|
export declare function claimMessageAttachments(tx: AppTransactionClient, userId: string, conversationId: string, messageId: string, fileIds?: string[]): Promise<void>;
|
|
8
9
|
export declare function getMessageUploadUrls(userId: string, input: {
|
|
@@ -43,6 +44,7 @@ export declare function listMessages(userId: string, input: {
|
|
|
43
44
|
} | null;
|
|
44
45
|
};
|
|
45
46
|
status: import(".prisma/client").$Enums.JobStatus | null;
|
|
47
|
+
aiFeedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
46
48
|
attachments: {
|
|
47
49
|
path: string;
|
|
48
50
|
type: string;
|
|
@@ -133,14 +135,6 @@ export declare function deleteMessage(userId: string, messageId: string): Promis
|
|
|
133
135
|
success: boolean;
|
|
134
136
|
messageId: string;
|
|
135
137
|
}>;
|
|
136
|
-
/** Mark an AI-suggested item as created. Stores in message meta for fast reads. */
|
|
137
|
-
export declare function markSuggestionCreated(userId: string, input: {
|
|
138
|
-
messageId: string;
|
|
139
|
-
type: "assignment" | "worksheet" | "section";
|
|
140
|
-
index: number;
|
|
141
|
-
}): Promise<{
|
|
142
|
-
success: boolean;
|
|
143
|
-
}>;
|
|
144
138
|
export declare function markAsRead(userId: string, conversationId: string): Promise<{
|
|
145
139
|
success: boolean;
|
|
146
140
|
}>;
|
|
@@ -151,4 +145,26 @@ export declare function getUnreadCount(userId: string, conversationId: string):
|
|
|
151
145
|
unreadCount: number;
|
|
152
146
|
unreadMentionCount: number;
|
|
153
147
|
}>;
|
|
148
|
+
/**
|
|
149
|
+
* Regenerate an AI response in a lab or Newton chat.
|
|
150
|
+
*
|
|
151
|
+
* The existing AI bubble is reused (not deleted): the row flips back to
|
|
152
|
+
* PENDING with cleared content/meta/feedback so the message id stays stable
|
|
153
|
+
* for the client, then the matching pipeline refills it. Only the latest
|
|
154
|
+
* message in the conversation can be regenerated — the pipelines build
|
|
155
|
+
* their context from the messages preceding the placeholder.
|
|
156
|
+
*/
|
|
157
|
+
export declare function regenerateAIResponse(userId: string, messageId: string): Promise<{
|
|
158
|
+
success: boolean;
|
|
159
|
+
messageId: string;
|
|
160
|
+
}>;
|
|
161
|
+
/** Record (or clear) thumbs up/down feedback on an AI message. */
|
|
162
|
+
export declare function setAIMessageFeedback(userId: string, input: {
|
|
163
|
+
messageId: string;
|
|
164
|
+
feedback: AiMessageFeedback | null;
|
|
165
|
+
}): Promise<{
|
|
166
|
+
success: boolean;
|
|
167
|
+
messageId: string;
|
|
168
|
+
feedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
169
|
+
}>;
|
|
154
170
|
//# sourceMappingURL=message.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["services/message.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["services/message.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAa,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAqBnE,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,EAAO,QAQvB;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAM,EAAO,iBA2BvB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD,8DAmBF;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;;;GAuCF;AAED,oEAAoE;AACpE,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAkCyB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;GAMlD;AAED,oFAAoF;AACpF,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIF;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;;;;;;;;;;;;;;;;GAqHF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;GAuDpE;AAGD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;;GA+BtE;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM;;GAgCvB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM;;;GA8BvB;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;GA4I3E;AAED,kEAAkE;AAClE,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACpC;;;;GA0CF"}
|