depgraph-core 1.9.0 → 1.9.2
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/.vscode/depgraph-output.json +979 -143
- package/depgraph-mcp.js +447 -36
- package/depgraph.js +517 -8
- package/package.json +2 -1
- package/scripts/benchmark.js +138 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Measures real response sizes from depgraph-mcp.js for each tool variant.
|
|
3
|
+
// Usage:
|
|
4
|
+
// node scripts/benchmark.js
|
|
5
|
+
// BENCH_DIR=/other/project node scripts/benchmark.js
|
|
6
|
+
'use strict';
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const SERVER = path.resolve(__dirname, '..', 'depgraph-mcp.js');
|
|
11
|
+
const DIR = process.env.BENCH_DIR ?? path.resolve(__dirname, '..');
|
|
12
|
+
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const TMPF = path.join(os.tmpdir(), `depgraph-bench-${process.pid}.txt`);
|
|
16
|
+
|
|
17
|
+
function call(toolName, args) {
|
|
18
|
+
const messages = [
|
|
19
|
+
{
|
|
20
|
+
jsonrpc: '2.0', id: 1, method: 'initialize',
|
|
21
|
+
params: { protocolVersion: '2024-11-05', capabilities: {},
|
|
22
|
+
clientInfo: { name: 'bench', version: '1.0' } },
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
jsonrpc: '2.0', id: 2, method: 'tools/call',
|
|
26
|
+
params: { name: toolName, arguments: args },
|
|
27
|
+
},
|
|
28
|
+
].map(m => JSON.stringify(m)).join('\n') + '\n';
|
|
29
|
+
|
|
30
|
+
fs.writeFileSync(TMPF, messages, 'utf-8');
|
|
31
|
+
|
|
32
|
+
const out = execSync(
|
|
33
|
+
`cat ${TMPF} | node ${SERVER}`,
|
|
34
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const lines = out.trim().split('\n').filter(Boolean);
|
|
38
|
+
const result = JSON.parse(lines[lines.length - 1]);
|
|
39
|
+
return result?.result?.content?.[0]?.text ?? '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ~3.5 chars/token for dense JSON; ~4.0 for prose (matches Claude/GPT-4 closely enough)
|
|
43
|
+
const tokEst = (s, prose = false) => Math.round(s.length / (prose ? 4.0 : 3.5));
|
|
44
|
+
|
|
45
|
+
const CASES = [
|
|
46
|
+
{
|
|
47
|
+
label: 'analyze_project — full (BASELINE)',
|
|
48
|
+
fn: () => call('analyze_project', { projectDir: DIR }),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: 'analyze_project — verbosity:"overview"',
|
|
52
|
+
fn: () => call('analyze_project', { projectDir: DIR, verbosity: 'overview' }),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: 'analyze_project — verbosity:"sketch"',
|
|
56
|
+
fn: () => call('analyze_project', { projectDir: DIR, verbosity: 'sketch' }),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: 'analyze_project — focus + depth:2 (overview)',
|
|
60
|
+
fn: () => call('analyze_project',
|
|
61
|
+
{ projectDir: DIR, focus: 'buildGraph', depth: 2, verbosity: 'overview' }),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: 'analyze_project — focus + depth:1 (overview)',
|
|
65
|
+
fn: () => call('analyze_project',
|
|
66
|
+
{ projectDir: DIR, focus: 'buildGraph', depth: 1, verbosity: 'overview' }),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: 'get_graph_summary — overview',
|
|
70
|
+
fn: () => call('get_graph_summary', { projectDir: DIR }),
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
label: 'get_graph_summary — verbosity:"sketch"',
|
|
74
|
+
fn: () => call('get_graph_summary', { projectDir: DIR, verbosity: 'sketch' }),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: 'get_graph_summary — format:"prose"',
|
|
78
|
+
fn: () => call('get_graph_summary', { projectDir: DIR, format: 'prose' }),
|
|
79
|
+
prose: true,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
label: 'describe_node("buildGraph")',
|
|
83
|
+
fn: () => call('describe_node', { projectDir: DIR, nodeName: 'buildGraph' }),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: 'describe_node("simulateImpact")',
|
|
87
|
+
fn: () => call('describe_node', { projectDir: DIR, nodeName: 'simulateImpact' }),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: 'simulate_impact("buildGraph", ...)',
|
|
91
|
+
fn: () => call('simulate_impact', {
|
|
92
|
+
projectDir: DIR,
|
|
93
|
+
targetNode: 'buildGraph',
|
|
94
|
+
changeDescription: 'removing parsed arg',
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
console.log(`\nBenchmarking against: ${DIR}`);
|
|
100
|
+
console.log(`MCP server: ${SERVER}\n`);
|
|
101
|
+
|
|
102
|
+
const rows = CASES.map(c => {
|
|
103
|
+
process.stdout.write(` ${c.label} ...`);
|
|
104
|
+
const text = c.fn();
|
|
105
|
+
const chars = text.length;
|
|
106
|
+
const tokens = tokEst(text, c.prose ?? false);
|
|
107
|
+
process.stdout.write(` ${tokens.toLocaleString()} tokens\n`);
|
|
108
|
+
return { label: c.label, chars, tokens };
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const baseline = rows[0].tokens;
|
|
112
|
+
|
|
113
|
+
const W = { label: 50, chars: 10, tokens: 9, pct: 14 };
|
|
114
|
+
const rule = '─'.repeat(W.label + W.chars + W.tokens + W.pct + 4);
|
|
115
|
+
|
|
116
|
+
console.log('\n' + rule);
|
|
117
|
+
console.log(
|
|
118
|
+
'Tool call'.padEnd(W.label) +
|
|
119
|
+
'Chars'.padStart(W.chars) +
|
|
120
|
+
'Tokens'.padStart(W.tokens) +
|
|
121
|
+
'vs baseline'.padStart(W.pct)
|
|
122
|
+
);
|
|
123
|
+
console.log(rule);
|
|
124
|
+
|
|
125
|
+
rows.forEach((r, i) => {
|
|
126
|
+
const pct = i === 0
|
|
127
|
+
? '(baseline)'
|
|
128
|
+
: `−${Math.round((1 - r.tokens / baseline) * 100)} %`;
|
|
129
|
+
console.log(
|
|
130
|
+
r.label.padEnd(W.label) +
|
|
131
|
+
r.chars.toLocaleString().padStart(W.chars) +
|
|
132
|
+
r.tokens.toLocaleString().padStart(W.tokens) +
|
|
133
|
+
pct.padStart(W.pct)
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
console.log(rule);
|
|
138
|
+
console.log(`\nBaseline: ${baseline.toLocaleString()} tokens (${rows[0].chars.toLocaleString()} chars)\n`);
|