mirror of
https://github.com/Egonex-AI/Understand-Anything.git
synced 2026-06-22 10:58:03 +08:00
chore: add large graph generator script for perf testing
Generates fake knowledge-graph.json with configurable node count for testing Web Worker layout performance. Forward-only edges to avoid dagre stack overflow on cyclic graphs. Usage: node scripts/generate-large-graph.mjs [nodeCount] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Generate a large fake knowledge graph for testing PR #18
|
||||
* (Web Worker layout for large graphs).
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/generate-large-graph.mjs [nodeCount]
|
||||
*
|
||||
* Default: 3000 nodes. Writes to .understand-anything/knowledge-graph.json
|
||||
*/
|
||||
|
||||
import { writeFileSync, mkdirSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const NODE_COUNT = parseInt(process.argv[2] || "3000", 10);
|
||||
const EDGE_RATIO = 1.7; // edges per node (realistic for codebases)
|
||||
|
||||
const nodeTypes = ["file", "function", "class", "module", "concept"];
|
||||
const edgeTypes = [
|
||||
"imports", "exports", "contains", "inherits", "implements",
|
||||
"calls", "subscribes", "publishes", "middleware",
|
||||
"reads_from", "writes_to", "transforms", "validates",
|
||||
"depends_on", "tested_by", "configures",
|
||||
"related", "similar_to",
|
||||
];
|
||||
const complexities = ["simple", "moderate", "complex"];
|
||||
const languages = ["TypeScript", "JavaScript", "Python", "Go", "Rust"];
|
||||
const frameworks = ["React", "Express", "FastAPI", "Gin", "Actix"];
|
||||
|
||||
function pick(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
function generateNodes(count) {
|
||||
const nodes = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const type = pick(nodeTypes);
|
||||
const name = `${type}_${i}`;
|
||||
nodes.push({
|
||||
id: `node-${i}`,
|
||||
type,
|
||||
name,
|
||||
filePath: type === "file" ? `src/${name}.ts` : undefined,
|
||||
summary: `Auto-generated ${type} node #${i} for performance testing.`,
|
||||
tags: [type, `group-${i % 20}`],
|
||||
complexity: pick(complexities),
|
||||
});
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function generateEdges(nodes, edgeCount) {
|
||||
const edges = [];
|
||||
const seen = new Set();
|
||||
const n = nodes.length;
|
||||
|
||||
for (let i = 0; i < edgeCount; i++) {
|
||||
let src, tgt;
|
||||
// Forward-only edges to avoid cycles (dagre blows the stack on large cyclic graphs)
|
||||
do {
|
||||
src = Math.floor(Math.random() * (n - 1));
|
||||
const offset = Math.floor(Math.random() * Math.min(50, n - src - 1)) + 1;
|
||||
tgt = src + offset;
|
||||
} while (tgt >= n || src === tgt || seen.has(`${src}-${tgt}`));
|
||||
|
||||
seen.add(`${src}-${tgt}`);
|
||||
edges.push({
|
||||
source: nodes[src].id,
|
||||
target: nodes[tgt].id,
|
||||
type: pick(edgeTypes),
|
||||
direction: "forward",
|
||||
weight: Math.round(Math.random() * 100) / 100,
|
||||
});
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
function generateLayers(nodes) {
|
||||
const layers = [];
|
||||
const layerNames = [
|
||||
"Presentation", "Application", "Domain", "Infrastructure",
|
||||
"API Gateway", "Data Access", "Utilities", "Testing",
|
||||
];
|
||||
|
||||
for (let i = 0; i < layerNames.length; i++) {
|
||||
const start = Math.floor((i / layerNames.length) * nodes.length);
|
||||
const end = Math.floor(((i + 1) / layerNames.length) * nodes.length);
|
||||
layers.push({
|
||||
id: `layer-${i}`,
|
||||
name: layerNames[i],
|
||||
description: `${layerNames[i]} layer (auto-generated)`,
|
||||
nodeIds: nodes.slice(start, end).map((n) => n.id),
|
||||
});
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
|
||||
function generateTour(nodes) {
|
||||
const steps = [];
|
||||
const stepCount = Math.min(8, Math.floor(nodes.length / 100));
|
||||
for (let i = 0; i < stepCount; i++) {
|
||||
const idx = Math.floor((i / stepCount) * nodes.length);
|
||||
steps.push({
|
||||
order: i + 1,
|
||||
title: `Step ${i + 1}: Explore ${nodes[idx].name}`,
|
||||
description: `This tour step highlights node **${nodes[idx].name}** and its surrounding context.`,
|
||||
nodeIds: [nodes[idx].id, nodes[Math.min(idx + 1, nodes.length - 1)].id],
|
||||
});
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
// ── Generate ──
|
||||
|
||||
const nodes = generateNodes(NODE_COUNT);
|
||||
const edgeCount = Math.floor(NODE_COUNT * EDGE_RATIO);
|
||||
const edges = generateEdges(nodes, edgeCount);
|
||||
const layers = generateLayers(nodes);
|
||||
const tour = generateTour(nodes);
|
||||
|
||||
const graph = {
|
||||
version: "1.0",
|
||||
project: {
|
||||
name: "large-test-project",
|
||||
languages: languages.slice(0, 3),
|
||||
frameworks: frameworks.slice(0, 2),
|
||||
description: `Auto-generated project with ${NODE_COUNT} nodes for performance testing.`,
|
||||
analyzedAt: new Date().toISOString(),
|
||||
gitCommitHash: "0000000000000000000000000000000000000000",
|
||||
},
|
||||
nodes,
|
||||
edges,
|
||||
layers,
|
||||
tour,
|
||||
};
|
||||
|
||||
const outDir = resolve(process.cwd(), ".understand-anything");
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
const outPath = resolve(outDir, "knowledge-graph.json");
|
||||
writeFileSync(outPath, JSON.stringify(graph, null, 2));
|
||||
|
||||
console.log(`Generated knowledge graph:`);
|
||||
console.log(` Nodes: ${nodes.length}`);
|
||||
console.log(` Edges: ${edges.length}`);
|
||||
console.log(` Layers: ${layers.length}`);
|
||||
console.log(` Tour steps: ${tour.length}`);
|
||||
console.log(` Written to: ${outPath}`);
|
||||
Reference in New Issue
Block a user