diff --git a/index.html b/index.html index 4b58b9b..613ef61 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,10 @@ .narrative { max-width: 40rem; } + /* Handle markdown output */ + #recommendations li p { + margin-bottom: 0; + } @@ -39,16 +43,28 @@
-

Report Gen

+

Automated letter generation

+ +
+

Automated letter generation is increasingly important in various industries, particularly in compliance and regulatory environments. Organizations often need to generate standardized letters for enforcement actions, notifications, or compliance communications.

+

The solution consists of three main components:

+ +

This tool aims to streamline the creation of compliance-related documents while ensuring accuracy and adherence to regulations. The use of advanced technology enhances efficiency and reduces manual effort in document preparation.

+
-
+ +
VAPT test

Generate a VAPT test report.

- - Download + + Download
@@ -68,7 +84,6 @@
Custom Report
-
@@ -76,18 +91,6 @@
Custom Report

Designed by Gramener

- - diff --git a/script.js b/script.js index bfb3c6c..f96d4d5 100644 --- a/script.js +++ b/script.js @@ -1,14 +1,20 @@ import { html, render } from "https://cdn.jsdelivr.net/npm/lit-html@3/+esm"; +import { unsafeHTML } from "https://cdn.jsdelivr.net/npm/lit-html@3/directives/unsafe-html.js"; +import { Marked } from "https://cdn.jsdelivr.net/npm/marked@13/+esm"; import { read, utils } from "https://cdn.jsdelivr.net/npm/xlsx/+esm"; import { Chart, + Colors, BarController, BarElement, CategoryScale, LinearScale, Tooltip, } from "https://cdn.jsdelivr.net/npm/chart.js@4/+esm"; +import { asyncSSE } from "https://cdn.jsdelivr.net/npm/asyncsse@1"; +let llmContent; +const marked = new Marked(); const { token } = await fetch("https://llmfoundry.straive.com/token", { credentials: "include" }).then((r) => r.json()); if (!token) { const url = "https://llmfoundry.straive.com/login?" + new URLSearchParams({ next: location.href }); @@ -161,7 +167,16 @@ const vaptReport = ({ Summary, ...data }) => html`

Recommended Actions

-
+ +
+
+ + +
+ +
+ +
`; @@ -192,9 +207,9 @@ document.querySelector("#file-upload").addEventListener("change", (event) => { } }); -function renderWorkbook(workbook) { +async function renderWorkbook(workbook) { const oldOutput = document.querySelector("#output"); - oldOutput.insertAdjacentHTML('afterend', '
'); + oldOutput.insertAdjacentHTML("afterend", '
'); oldOutput.remove(); const summarySheet = workbook.SheetNames.includes("Summary") @@ -208,14 +223,20 @@ function renderWorkbook(workbook) { try { render(vaptReport({ Summary, ...data }), document.querySelector("#output")); - Chart.register(BarController, BarElement, CategoryScale, LinearScale, Tooltip); + Chart.register(Colors, BarController, BarElement, CategoryScale, LinearScale, Tooltip); new Chart(document.getElementById("bandwidth-usage"), { type: "bar", options: { animation: true, plugins: { tooltip: { enabled: true } } }, data: { labels: data.Bandwidth.map((row) => row.Time), - datasets: [{ label: "Bandwidth Utilization", data: data.Bandwidth.map((row) => row["Bandwidth Utilization"]) }], + datasets: [ + { + label: "Bandwidth Utilization", + backgroundColor: "rgba(25, 135, 84, 0.8)", + data: data.Bandwidth.map((row) => row["Bandwidth Utilization"]), + }, + ], }, }); new Chart(document.getElementById("session-usage"), { @@ -223,16 +244,59 @@ function renderWorkbook(workbook) { options: { animation: true, plugins: { tooltip: { enabled: true } } }, data: { labels: data.Sessions.map((row) => row.Time), - datasets: [{ label: "Number of Sessions", data: data.Sessions.map((row) => row["Sessions"]) }], + datasets: [ + { + label: "Number of Sessions", + backgroundColor: "rgba(25, 135, 84, 0.8)", + data: data.Sessions.map((row) => row["Sessions"]), + }, + ], }, }); + llmContent = Object.entries(data) + .map(([name, rows]) => { + if (rows.length === 0) return ""; + const headers = Object.keys(rows[0]).join("\t"); + const values = rows.map((row) => Object.values(row).join("\t")).join("\n"); + return `\n${headers}\n${values}\n`; + }) + .join("\n\n"); + document.querySelector("#recommendations-form").dispatchEvent(new Event("submit", { bubbles: true })); } catch (error) { return notify(`Error rendering report: ${error.message}`); } } +document.querySelector("body").addEventListener("submit", async (event) => { + if (event.target.id !== "recommendations-form") return; + + event.preventDefault(); + render(html`
`, document.querySelector("#recommendations")); + let content = ""; + for await (const event of asyncSSE("https://llmfoundry.straive.com/openai/v1/chat/completions", { + method: "POST", + headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}:reportgen` }, + stream: true, + stream_options: { include_usage: true }, + body: JSON.stringify({ + model: "gpt-4o-mini", + stream: true, + messages: [ + { role: "system", content: document.querySelector("#recommendations-prompt").value }, + { role: "user", content: llmContent }, + ], + }), + })) { + if (event.data == "[DONE]") break; + const message = JSON.parse(event.data); + const content_delta = message.choices?.[0]?.delta?.content; + if (content_delta) content += content_delta; + render(unsafeHTML(marked.parse(content)), document.querySelector("#recommendations")); + } +}); + function notify(message) { render(html`
${message}
`, document.querySelector("#output")); }