@studious-lms/server 1.27.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/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/pipelines/aiLabChatContract.d.ts +2 -2
- 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/routers/_app.d.ts +891 -53
- 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 -0
- 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/module.d.ts +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/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 -0
- package/dist/services/adminUser.d.ts.map +1 -1
- package/dist/services/adminUser.js +48 -11
- package/dist/services/adminUser.js.map +1 -1
- 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/fees.d.ts.map +1 -1
- package/dist/services/fees.js +7 -3
- package/dist/services/fees.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/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/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/package.json +1 -1
- package/prisma/migrations/20261025120000_add_visualizations/migration.sql +27 -0
- package/prisma/schema.prisma +24 -0
- package/src/index.ts +30 -4
- package/src/instrument.ts +8 -2
- package/src/lib/fileUpload.ts +49 -0
- package/src/pipelines/generateVisualization.ts +107 -0
- package/src/routers/_app.ts +2 -0
- package/src/routers/admin.ts +72 -0
- package/src/routers/class.ts +14 -1
- package/src/routers/visualization.ts +60 -0
- 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 +61 -9
- package/src/services/auth.ts +8 -2
- package/src/services/fees.ts +5 -1
- package/src/services/module.ts +29 -2
- package/src/services/visualization.ts +147 -0
- package/src/utils/sentryTrpc.ts +25 -0
- 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 +2 -0
- package/tests/unit/services/visualization.test.ts +130 -0
- package/tests/unit/utils/sentryTrpc.test.ts +15 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newton visualisation generator: turns a teacher's description into a
|
|
3
|
+
* self-contained interactive HTML document (sliders, canvas/SVG, labels)
|
|
4
|
+
* that the frontend renders inside a sandboxed iframe.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
!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]="a40fb646-920a-50d5-8ec6-c7d4ba47bcd5")}catch(e){}}();
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { inference } from "../utils/inference.js";
|
|
10
|
+
export const VISUALIZATION_MODEL = "gpt-5-mini";
|
|
11
|
+
export const MAX_VISUALIZATION_HTML_LENGTH = 200000;
|
|
12
|
+
export const generatedVisualizationSchema = z.object({
|
|
13
|
+
title: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe("Short student-facing title, at most 80 characters."),
|
|
16
|
+
html: z
|
|
17
|
+
.string()
|
|
18
|
+
.describe("A complete, self-contained HTML document starting with <!DOCTYPE html>."),
|
|
19
|
+
});
|
|
20
|
+
export const VISUALIZATION_SYSTEM_PROMPT = `You are Newton, the teaching assistant inside the Studious LMS.
|
|
21
|
+
A teacher wants an interactive visualisation to attach to a lesson for students.
|
|
22
|
+
|
|
23
|
+
Return ONE complete, self-contained HTML document:
|
|
24
|
+
- All CSS in a <style> tag and all JavaScript in inline <script> tags. No external scripts, stylesheets, fonts, images or network requests (no fetch, XMLHttpRequest, WebSocket, import()). It runs in a sandboxed iframe with no network access.
|
|
25
|
+
- No localStorage, sessionStorage, cookies, alert, prompt or confirm.
|
|
26
|
+
- Use <canvas> or inline <svg> for the visual. Give students real controls (sliders, toggles, buttons, draggable points) that update the visual live, and show the key values numerically next to each control.
|
|
27
|
+
- Label axes, units and important quantities. Include a one-sentence instruction at the top telling students what to explore.
|
|
28
|
+
- Responsive: fill the viewport width, work from 320px to 1200px wide, no horizontal scroll. Use a clean neutral look with system-ui font and support prefers-color-scheme: dark.
|
|
29
|
+
- The science/maths must be correct. Prefer accurate formulas over approximations, and clamp inputs to physically sensible ranges.
|
|
30
|
+
- Keep it focused on what the teacher asked for; no quizzes, scoring or chat.`;
|
|
31
|
+
function classContext(options) {
|
|
32
|
+
const parts = [
|
|
33
|
+
options.subject ? `Subject: ${options.subject}` : null,
|
|
34
|
+
options.className ? `Class: ${options.className}` : null,
|
|
35
|
+
].filter(Boolean);
|
|
36
|
+
return parts.length ? `${parts.join("\n")}\n\n` : "";
|
|
37
|
+
}
|
|
38
|
+
function assertValidHtml(result) {
|
|
39
|
+
if (!result?.html || !/<html[\s>]/i.test(result.html)) {
|
|
40
|
+
throw new Error("Newton did not return an HTML document");
|
|
41
|
+
}
|
|
42
|
+
if (result.html.length > MAX_VISUALIZATION_HTML_LENGTH) {
|
|
43
|
+
throw new Error("Generated visualisation is too large");
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
title: result.title.trim().slice(0, 120) || "Visualisation",
|
|
47
|
+
html: result.html,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export async function generateVisualization(prompt, options = {}) {
|
|
51
|
+
const result = await inference([
|
|
52
|
+
{ role: "system", content: VISUALIZATION_SYSTEM_PROMPT },
|
|
53
|
+
{
|
|
54
|
+
role: "user",
|
|
55
|
+
content: `${classContext(options)}Teacher request:\n${prompt}`,
|
|
56
|
+
},
|
|
57
|
+
], generatedVisualizationSchema, {
|
|
58
|
+
reason: "visualization.generate",
|
|
59
|
+
model: VISUALIZATION_MODEL,
|
|
60
|
+
attribution: options.attribution,
|
|
61
|
+
});
|
|
62
|
+
return assertValidHtml(result);
|
|
63
|
+
}
|
|
64
|
+
export async function reviseVisualization(current, instruction, options = {}) {
|
|
65
|
+
const result = await inference([
|
|
66
|
+
{ role: "system", content: VISUALIZATION_SYSTEM_PROMPT },
|
|
67
|
+
{
|
|
68
|
+
role: "user",
|
|
69
|
+
content: `${classContext(options)}Current visualisation "${current.title}":\n\n${current.html}\n\nRevise it as the teacher asks, keeping everything else working. Return the full updated document.\n\nTeacher request:\n${instruction}`,
|
|
70
|
+
},
|
|
71
|
+
], generatedVisualizationSchema, {
|
|
72
|
+
reason: "visualization.revise",
|
|
73
|
+
model: VISUALIZATION_MODEL,
|
|
74
|
+
attribution: options.attribution,
|
|
75
|
+
});
|
|
76
|
+
return assertValidHtml(result);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=generateVisualization.js.map
|
|
79
|
+
//# debugId=a40fb646-920a-50d5-8ec6-c7d4ba47bcd5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateVisualization.js","sources":["pipelines/generateVisualization.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Newton visualisation generator: turns a teacher's description into a\n * self-contained interactive HTML document (sliders, canvas/SVG, labels)\n * that the frontend renders inside a sandboxed iframe.\n */\nimport { z } from \"zod\";\nimport { inference, type AiUsageAttribution } from \"../utils/inference.js\";\n\nexport const VISUALIZATION_MODEL = \"gpt-5-mini\";\nexport const MAX_VISUALIZATION_HTML_LENGTH = 200_000;\n\nexport const generatedVisualizationSchema = z.object({\n title: z\n .string()\n .describe(\"Short student-facing title, at most 80 characters.\"),\n html: z\n .string()\n .describe(\n \"A complete, self-contained HTML document starting with <!DOCTYPE html>.\",\n ),\n});\n\nexport type GeneratedVisualization = z.infer<typeof generatedVisualizationSchema>;\n\nexport const VISUALIZATION_SYSTEM_PROMPT = `You are Newton, the teaching assistant inside the Studious LMS.\nA teacher wants an interactive visualisation to attach to a lesson for students.\n\nReturn ONE complete, self-contained HTML document:\n- All CSS in a <style> tag and all JavaScript in inline <script> tags. No external scripts, stylesheets, fonts, images or network requests (no fetch, XMLHttpRequest, WebSocket, import()). It runs in a sandboxed iframe with no network access.\n- No localStorage, sessionStorage, cookies, alert, prompt or confirm.\n- Use <canvas> or inline <svg> for the visual. Give students real controls (sliders, toggles, buttons, draggable points) that update the visual live, and show the key values numerically next to each control.\n- Label axes, units and important quantities. Include a one-sentence instruction at the top telling students what to explore.\n- Responsive: fill the viewport width, work from 320px to 1200px wide, no horizontal scroll. Use a clean neutral look with system-ui font and support prefers-color-scheme: dark.\n- The science/maths must be correct. Prefer accurate formulas over approximations, and clamp inputs to physically sensible ranges.\n- Keep it focused on what the teacher asked for; no quizzes, scoring or chat.`;\n\ninterface GenerateOptions {\n attribution?: AiUsageAttribution;\n className?: string | null;\n subject?: string | null;\n}\n\nfunction classContext(options: GenerateOptions) {\n const parts = [\n options.subject ? `Subject: ${options.subject}` : null,\n options.className ? `Class: ${options.className}` : null,\n ].filter(Boolean);\n return parts.length ? `${parts.join(\"\\n\")}\\n\\n` : \"\";\n}\n\nfunction assertValidHtml(result: GeneratedVisualization | null | undefined) {\n if (!result?.html || !/<html[\\s>]/i.test(result.html)) {\n throw new Error(\"Newton did not return an HTML document\");\n }\n if (result.html.length > MAX_VISUALIZATION_HTML_LENGTH) {\n throw new Error(\"Generated visualisation is too large\");\n }\n return {\n title: result.title.trim().slice(0, 120) || \"Visualisation\",\n html: result.html,\n };\n}\n\nexport async function generateVisualization(\n prompt: string,\n options: GenerateOptions = {},\n): Promise<GeneratedVisualization> {\n const result = await inference<GeneratedVisualization>(\n [\n { role: \"system\", content: VISUALIZATION_SYSTEM_PROMPT },\n {\n role: \"user\",\n content: `${classContext(options)}Teacher request:\\n${prompt}`,\n },\n ],\n generatedVisualizationSchema,\n {\n reason: \"visualization.generate\",\n model: VISUALIZATION_MODEL,\n attribution: options.attribution,\n },\n );\n return assertValidHtml(result);\n}\n\nexport async function reviseVisualization(\n current: { title: string; html: string },\n instruction: string,\n options: GenerateOptions = {},\n): Promise<GeneratedVisualization> {\n const result = await inference<GeneratedVisualization>(\n [\n { role: \"system\", content: VISUALIZATION_SYSTEM_PROMPT },\n {\n role: \"user\",\n content: `${classContext(options)}Current visualisation \"${current.title}\":\\n\\n${current.html}\\n\\nRevise it as the teacher asks, keeping everything else working. Return the full updated document.\\n\\nTeacher request:\\n${instruction}`,\n },\n ],\n generatedVisualizationSchema,\n {\n reason: \"visualization.revise\",\n model: VISUALIZATION_MODEL,\n attribution: options.attribution,\n },\n );\n return assertValidHtml(result);\n}\n"],"names":[],"mappings":"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","debug_id":"a40fb646-920a-50d5-8ec6-c7d4ba47bcd5"}
|