@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
375 lines (372 loc) • 13 kB
JavaScript
/**
* @file Report Generator
* Generate evaluation reports in various formats
*/
/**
* Default report configuration
*/
const DEFAULT_REPORT_CONFIG = {
format: "text",
includeReasoning: true,
includeMetadata: true,
includeTiming: true,
};
/**
* Report generator class
*/
export class ReportGenerator {
_config;
constructor(config) {
this._config = { ...DEFAULT_REPORT_CONFIG, ...config };
}
/**
* Generate a report
*/
generate(data) {
let content;
switch (this._config.format) {
case "json":
content = this._generateJsonReport(data);
break;
case "markdown":
content = this._generateMarkdownReport(data);
break;
case "html":
content = this._generateHtmlReport(data);
break;
case "text":
default:
content = this._generateTextReport(data);
break;
}
return {
format: this._config.format,
content,
metadata: {
generatedAt: Date.now(),
format: this._config.format,
config: this._config,
},
};
}
/**
* Generate text report
*/
_generateTextReport(data) {
const lines = [];
const result = data.result;
lines.push("=".repeat(60));
lines.push(data.title);
lines.push("=".repeat(60));
lines.push(`Generated: ${new Date(data.timestamp).toISOString()}`);
lines.push("");
// Overall result
lines.push("OVERALL RESULT");
lines.push("-".repeat(40));
lines.push(`Score: ${result.overallScore.toFixed(2)}/10`);
lines.push(`Status: ${result.passed ? "PASSED" : "FAILED"}`);
lines.push(`Aggregation: ${result.aggregationMethod}`);
if (this._config.includeTiming && result.totalComputeTime) {
lines.push(`Duration: ${result.totalComputeTime}ms`);
}
lines.push("");
// Individual scores
lines.push("INDIVIDUAL SCORES");
lines.push("-".repeat(40));
for (const score of result.scores) {
const status = score.passed ? "[PASS]" : "[FAIL]";
lines.push(`${status} ${score.scorerName}: ${score.score.toFixed(2)}/10`);
if (this._config.includeReasoning) {
lines.push(` Reasoning: ${score.reasoning}`);
}
if (this._config.includeTiming) {
lines.push(` Duration: ${score.computeTime}ms`);
}
lines.push("");
}
// Errors if any
const pipelineResult = result;
if (pipelineResult.errors && pipelineResult.errors.length > 0) {
lines.push("ERRORS");
lines.push("-".repeat(40));
for (const error of pipelineResult.errors) {
lines.push(`[${error.scorerId}] ${error.error}`);
}
lines.push("");
}
// Custom sections
if (data.customSections) {
for (const section of data.customSections) {
lines.push(section.title.toUpperCase());
lines.push("-".repeat(40));
lines.push(typeof section.content === "string"
? section.content
: JSON.stringify(section.content, null, 2));
lines.push("");
}
}
lines.push("=".repeat(60));
return lines.join("\n");
}
/**
* Generate JSON report
*/
_generateJsonReport(data) {
const report = {
title: data.title,
timestamp: data.timestamp,
generatedAt: new Date(data.timestamp).toISOString(),
overall: {
score: data.result.overallScore,
passed: data.result.passed,
aggregationMethod: data.result.aggregationMethod,
},
scores: data.result.scores.map((score) => {
const scoreObj = {
scorerId: score.scorerId,
scorerName: score.scorerName,
score: score.score,
normalizedScore: score.normalizedScore,
passed: score.passed,
threshold: score.threshold,
};
if (this._config.includeReasoning) {
scoreObj.reasoning = score.reasoning;
}
if (this._config.includeTiming) {
scoreObj.computeTime = score.computeTime;
}
if (this._config.includeMetadata && score.metadata) {
scoreObj.metadata = score.metadata;
}
return scoreObj;
}),
};
if (this._config.includeTiming) {
report.totalComputeTime = data.result.totalComputeTime;
}
const pipelineResult = data.result;
if (pipelineResult.errors && pipelineResult.errors.length > 0) {
report.errors = pipelineResult.errors;
}
if (data.customSections) {
report.customSections = data.customSections;
}
return JSON.stringify(report, null, 2);
}
/**
* Generate Markdown report
*/
_generateMarkdownReport(data) {
const lines = [];
const result = data.result;
lines.push(`# ${data.title}`);
lines.push("");
lines.push(`*Generated: ${new Date(data.timestamp).toISOString()}*`);
lines.push("");
// Overall result
lines.push("## Overall Result");
lines.push("");
lines.push(`| Metric | Value |`);
lines.push(`| ------ | ----- |`);
lines.push(`| Score | ${result.overallScore.toFixed(2)}/10 |`);
lines.push(`| Status | ${result.passed ? "**PASSED**" : "**FAILED**"} |`);
lines.push(`| Aggregation | ${result.aggregationMethod} |`);
if (this._config.includeTiming && result.totalComputeTime) {
lines.push(`| Duration | ${result.totalComputeTime}ms |`);
}
lines.push("");
// Individual scores
lines.push("## Individual Scores");
lines.push("");
lines.push(`| Scorer | Score | Status | ${this._config.includeTiming ? "Duration |" : ""}`);
lines.push(`| ------ | ----- | ------ | ${this._config.includeTiming ? "-------- |" : ""}`);
for (const score of result.scores) {
const status = score.passed ? "Pass" : "Fail";
let row = `| ${score.scorerName} | ${score.score.toFixed(2)} | ${status} |`;
if (this._config.includeTiming) {
row += ` ${score.computeTime}ms |`;
}
lines.push(row);
}
lines.push("");
// Reasoning
if (this._config.includeReasoning) {
lines.push("### Reasoning");
lines.push("");
for (const score of result.scores) {
lines.push(`**${score.scorerName}**: ${score.reasoning}`);
lines.push("");
}
}
// Errors
const pipelineResult = result;
if (pipelineResult.errors && pipelineResult.errors.length > 0) {
lines.push("## Errors");
lines.push("");
for (const error of pipelineResult.errors) {
lines.push(`- **${error.scorerId}**: ${error.error}`);
}
lines.push("");
}
// Custom sections
if (data.customSections) {
for (const section of data.customSections) {
lines.push(`## ${section.title}`);
lines.push("");
lines.push(typeof section.content === "string"
? section.content
: "```json\n" + JSON.stringify(section.content, null, 2) + "\n```");
lines.push("");
}
}
return lines.join("\n");
}
/**
* Generate HTML report
*/
_generateHtmlReport(data) {
const result = data.result;
const statusClass = result.passed ? "passed" : "failed";
let html = `
<!DOCTYPE html>
<html>
<head>
<title>${this._escapeHtml(data.title)}</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { color: #333; border-bottom: 2px solid #eee; padding-bottom: 10px; }
h2 { color: #555; margin-top: 30px; }
.meta { color: #888; font-size: 0.9em; }
.overall { background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0; }
.overall.passed { border-left: 4px solid #4caf50; }
.overall.failed { border-left: 4px solid #f44336; }
.score-value { font-size: 2em; font-weight: bold; }
.passed .score-value { color: #4caf50; }
.failed .score-value { color: #f44336; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; }
th { background: #f9f9f9; font-weight: 600; }
.status-pass { color: #4caf50; }
.status-fail { color: #f44336; }
.reasoning { color: #666; font-size: 0.9em; font-style: italic; }
.error { background: #ffebee; padding: 10px; border-radius: 4px; margin: 5px 0; }
pre { background: #f5f5f5; padding: 15px; border-radius: 4px; overflow-x: auto; }
</style>
</head>
<body>
<h1>${this._escapeHtml(data.title)}</h1>
<p class="meta">Generated: ${new Date(data.timestamp).toISOString()}</p>
<div class="overall ${statusClass}">
<div class="score-value">${result.overallScore.toFixed(2)}/10</div>
<div>Status: <strong>${result.passed ? "PASSED" : "FAILED"}</strong></div>
<div>Aggregation: ${result.aggregationMethod}</div>
${this._config.includeTiming ? `<div>Duration: ${result.totalComputeTime}ms</div>` : ""}
</div>
<h2>Individual Scores</h2>
<table>
<thead>
<tr>
<th>Scorer</th>
<th>Score</th>
<th>Status</th>
${this._config.includeTiming ? "<th>Duration</th>" : ""}
</tr>
</thead>
<tbody>
`;
for (const score of result.scores) {
const statusClass = score.passed ? "status-pass" : "status-fail";
html += `
<tr>
<td>${this._escapeHtml(score.scorerName)}</td>
<td>${score.score.toFixed(2)}</td>
<td class="${statusClass}">${score.passed ? "Pass" : "Fail"}</td>
${this._config.includeTiming ? `<td>${score.computeTime}ms</td>` : ""}
</tr>
`;
if (this._config.includeReasoning) {
html += `
<tr>
<td colspan="${this._config.includeTiming ? 4 : 3}" class="reasoning">
${this._escapeHtml(score.reasoning)}
</td>
</tr>
`;
}
}
html += `
</tbody>
</table>
`;
// Errors
const pipelineResult = result;
if (pipelineResult.errors && pipelineResult.errors.length > 0) {
html += `
<h2>Errors</h2>
`;
for (const error of pipelineResult.errors) {
html += `
<div class="error">
<strong>${this._escapeHtml(error.scorerId)}</strong>: ${this._escapeHtml(error.error)}
</div>
`;
}
}
// Custom sections
if (data.customSections) {
for (const section of data.customSections) {
html += `
<h2>${this._escapeHtml(section.title)}</h2>
`;
if (typeof section.content === "string") {
html += `<p>${this._escapeHtml(section.content)}</p>`;
}
else {
html += `<pre>${this._escapeHtml(JSON.stringify(section.content, null, 2))}</pre>`;
}
}
}
html += `
</body>
</html>
`;
return html;
}
/**
* Escape HTML special characters
*/
_escapeHtml(text) {
return text
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
/**
* Update configuration
*/
configure(config) {
this._config = { ...this._config, ...config };
}
}
/**
* Create a report generator
*/
export function createReportGenerator(config) {
return new ReportGenerator(config);
}
/**
* Quick report generation functions
*/
export const Reports = {
/** Generate text report */
text: (data) => new ReportGenerator({ format: "text" }).generate(data),
/** Generate JSON report */
json: (data) => new ReportGenerator({ format: "json" }).generate(data),
/** Generate Markdown report */
markdown: (data) => new ReportGenerator({ format: "markdown" }).generate(data),
/** Generate HTML report */
html: (data) => new ReportGenerator({ format: "html" }).generate(data),
};