kiku-agent 1.3.1 → 1.3.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/README.md +1 -1
- package/package.json +4 -12
- package/server/agent/eval/AGENTS.md +228 -0
- package/server/agent/eval/analyze.py +684 -0
- package/server/agent/eval/gates.json +15 -0
- package/server/agent/eval/modules/analysis/compare.js +198 -0
- package/server/agent/eval/modules/analysis/gates.js +72 -0
- package/server/agent/eval/modules/analysis/judge.js +70 -0
- package/server/agent/eval/modules/core/cli.js +453 -0
- package/server/agent/eval/modules/core/config.js +60 -0
- package/server/agent/eval/modules/core/store.js +236 -0
- package/server/agent/eval/modules/evaluation/cases.js +689 -0
- package/server/agent/eval/modules/evaluation/runner.js +298 -0
- package/server/agent/eval/modules/evaluation/scoring.js +351 -0
- package/server/agent/eval/modules/evaluation/worker.js +122 -0
- package/server/agent/eval/modules/reporting/benchmark.js +265 -0
- package/server/agent/eval/modules/reporting/benchmarkPng.js +487 -0
- package/server/agent/eval/modules/reporting/dashboard.js +1406 -0
- package/server/agent/eval/modules/server/evalApi.js +1099 -0
- package/server/agent/eval/modules/ui/evalUi.js +4796 -0
- package/server/agent/eval/modules/utils/smokes.js +119 -0
- package/server/agent/tests/agentBubbleSystem.test.js +65 -0
- package/server/agent/tests/agentFollowUps.test.js +127 -0
- package/server/agent/tests/apiRouter.test.js +108 -0
- package/server/agent/tests/architectureFixes.test.js +81 -0
- package/server/agent/tests/architectureRefactoring.test.js +194 -0
- package/server/agent/tests/autoResearch.test.js +64 -0
- package/server/agent/tests/bilibiliEmbed.test.js +251 -0
- package/server/agent/tests/catalogSync.test.js +130 -0
- package/server/agent/tests/citations.test.js +180 -0
- package/server/agent/tests/clarificationAnalytics.test.js +219 -0
- package/server/agent/tests/clarifyingQuestions.test.js +594 -0
- package/server/agent/tests/cliAutomation.test.js +121 -0
- package/server/agent/tests/cloudProviders.test.js +234 -0
- package/server/agent/tests/commandInput.test.js +24 -0
- package/server/agent/tests/commands.test.js +417 -0
- package/server/agent/tests/contextIntelligence.test.js +162 -0
- package/server/agent/tests/conversationSafety.test.js +53 -0
- package/server/agent/tests/decisionAndSearchEnhancements.test.js +135 -0
- package/server/agent/tests/dramaAnswerQuality.test.js +73 -0
- package/server/agent/tests/dynamicCatalogIngestion.test.js +285 -0
- package/server/agent/tests/dynamicRoutingAndSearch.test.js +92 -0
- package/server/agent/tests/edgeCasesScenarios.test.js +49 -0
- package/server/agent/tests/entertainmentMetrics.test.js +126 -0
- package/server/agent/tests/envBootstrap.test.js +87 -0
- package/server/agent/tests/envCommand.test.js +215 -0
- package/server/agent/tests/errorHandling.test.js +80 -0
- package/server/agent/tests/errors.test.js +13 -0
- package/server/agent/tests/evalAnalyze.test.js +54 -0
- package/server/agent/tests/evalCommand.test.js +54 -0
- package/server/agent/tests/evalCorpus.test.js +126 -0
- package/server/agent/tests/evalHarness.test.js +62 -0
- package/server/agent/tests/evalJudgeContext.test.js +38 -0
- package/server/agent/tests/evalMetrics.test.js +312 -0
- package/server/agent/tests/evalStore.test.js +190 -0
- package/server/agent/tests/evalStudio.test.js +179 -0
- package/server/agent/tests/feedbackLearning.test.js +186 -0
- package/server/agent/tests/firstRun.test.js +44 -0
- package/server/agent/tests/gemma4eOptimization.test.js +95 -0
- package/server/agent/tests/hardwareTiers.test.js +206 -0
- package/server/agent/tests/hybridWebSearch.test.js +192 -0
- package/server/agent/tests/judgeService.test.js +251 -0
- package/server/agent/tests/lanDiscovery.test.js +319 -0
- package/server/agent/tests/mcpClient.test.js +201 -0
- package/server/agent/tests/modelManager.test.js +109 -0
- package/server/agent/tests/multilingualScenarios.test.js +69 -0
- package/server/agent/tests/newCommands.test.js +347 -0
- package/server/agent/tests/newCommandsTui.test.js +141 -0
- package/server/agent/tests/newTools.test.js +113 -0
- package/server/agent/tests/newsIntelligence.test.js +178 -0
- package/server/agent/tests/nextGenArchitecture.test.js +99 -0
- package/server/agent/tests/offlineInput.test.js +217 -0
- package/server/agent/tests/packagingGuard.test.js +57 -0
- package/server/agent/tests/presentation.test.js +61 -0
- package/server/agent/tests/providerPersistence.test.js +39 -0
- package/server/agent/tests/queryBenchmarks.test.js +83 -0
- package/server/agent/tests/queryDetails.test.js +126 -0
- package/server/agent/tests/realWorldValidation.test.js +391 -0
- package/server/agent/tests/resilienceAndConcurrency.test.js +117 -0
- package/server/agent/tests/runtimeWatchdog.test.js +32 -0
- package/server/agent/tests/sanitizer.test.js +37 -0
- package/server/agent/tests/scenariosData.js +378 -0
- package/server/agent/tests/schemaRouting.test.js +54 -0
- package/server/agent/tests/securityAuditRemediation.test.js +214 -0
- package/server/agent/tests/securityHardening.test.js +146 -0
- package/server/agent/tests/securitySanitizer.test.js +38 -0
- package/server/agent/tests/sessionAndCopy.test.js +232 -0
- package/server/agent/tests/sidebarControls.test.js +101 -0
- package/server/agent/tests/statefulMemory.test.js +270 -0
- package/server/agent/tests/subagent.test.js +216 -0
- package/server/agent/tests/subagentProgress.test.js +109 -0
- package/server/agent/tests/subagentTextFallback.test.js +115 -0
- package/server/agent/tests/suggestions.test.js +199 -0
- package/server/agent/tests/suggestionsTui.test.js +251 -0
- package/server/agent/tests/telemetryAndProfiles.test.js +179 -0
- package/server/agent/tests/terminalImage.test.js +80 -0
- package/server/agent/tests/terminalMarkdown.test.js +166 -0
- package/server/agent/tests/terminalText.test.js +133 -0
- package/server/agent/tests/textProtocolLoop.test.js +126 -0
- package/server/agent/tests/textToolParsing.test.js +139 -0
- package/server/agent/tests/theme.test.js +26 -0
- package/server/agent/tests/toolRegistryParity.test.js +53 -0
- package/server/agent/tests/toolRoutingRegressions.test.js +119 -0
- package/server/agent/tests/tools.test.js +259 -0
- package/server/agent/tests/unifiedEndpoints.test.js +152 -0
- package/server/agent/tests/v110Features.test.js +262 -0
- package/server/agent/tests/versionBump.test.js +69 -0
- package/server/agent/tests/vramEstimator.test.js +520 -0
- package/server/agent/tests/webChatInspection.test.js +300 -0
- package/server/agent/tests/webChatMemory.test.js +112 -0
- package/server/agent/tests/webChatSecurity.test.js +169 -0
- package/server/agent/tests/webExtract.test.js +261 -0
- package/server/agent/tests/webSources.test.js +247 -0
- package/server/eval/judgeRubrics.js +110 -0
- package/server/eval/judgeService.js +290 -0
- package/server/eval/runBenchmark.js +182 -0
- package/server/mcp/examples/librechat.example.yaml +16 -0
- package/server/mcp/tests/connection.test.js +156 -0
- package/server/supabase_schema.sql +63 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { deflateSync } from 'node:zlib';
|
|
6
|
+
import { BENCHMARK_ROWS, ROADMAP, computeBenchmarkSummary } from './benchmark.js';
|
|
7
|
+
|
|
8
|
+
const REPO_ROOT = fileURLToPath(new URL('../../../../', import.meta.url));
|
|
9
|
+
|
|
10
|
+
const FONT = {
|
|
11
|
+
32: [0, 0, 0, 0, 0, 0, 0],
|
|
12
|
+
33: [4, 4, 4, 4, 4, 0, 4],
|
|
13
|
+
34: [10, 10, 10, 0, 0, 0, 0],
|
|
14
|
+
35: [10, 10, 31, 10, 31, 10, 10],
|
|
15
|
+
36: [4, 15, 20, 14, 5, 30, 4],
|
|
16
|
+
37: [24, 25, 2, 4, 8, 13, 11],
|
|
17
|
+
38: [12, 18, 20, 8, 21, 18, 13],
|
|
18
|
+
39: [4, 4, 8, 0, 0, 0, 0],
|
|
19
|
+
40: [2, 4, 8, 8, 8, 4, 2],
|
|
20
|
+
41: [8, 4, 2, 2, 2, 4, 8],
|
|
21
|
+
42: [0, 4, 21, 14, 21, 4, 0],
|
|
22
|
+
43: [0, 4, 4, 31, 4, 4, 0],
|
|
23
|
+
44: [0, 0, 0, 0, 12, 4, 8],
|
|
24
|
+
45: [0, 0, 0, 31, 0, 0, 0],
|
|
25
|
+
46: [0, 0, 0, 0, 12, 12, 0],
|
|
26
|
+
47: [1, 1, 2, 4, 8, 16, 16],
|
|
27
|
+
48: [14, 17, 19, 21, 25, 17, 14],
|
|
28
|
+
49: [4, 12, 4, 4, 4, 4, 14],
|
|
29
|
+
50: [14, 17, 1, 2, 4, 8, 31],
|
|
30
|
+
51: [31, 2, 4, 2, 1, 17, 14],
|
|
31
|
+
52: [2, 6, 10, 18, 31, 2, 2],
|
|
32
|
+
53: [31, 16, 30, 1, 1, 17, 14],
|
|
33
|
+
54: [6, 8, 16, 30, 17, 17, 14],
|
|
34
|
+
55: [31, 1, 2, 4, 8, 8, 8],
|
|
35
|
+
56: [14, 17, 17, 14, 17, 17, 14],
|
|
36
|
+
57: [14, 17, 17, 15, 1, 2, 12],
|
|
37
|
+
58: [0, 12, 12, 0, 12, 12, 0],
|
|
38
|
+
59: [0, 12, 12, 0, 12, 4, 8],
|
|
39
|
+
60: [2, 4, 8, 16, 8, 4, 2],
|
|
40
|
+
61: [0, 0, 31, 0, 31, 0, 0],
|
|
41
|
+
62: [8, 4, 2, 1, 2, 4, 8],
|
|
42
|
+
63: [14, 17, 1, 2, 4, 0, 4],
|
|
43
|
+
64: [14, 17, 27, 21, 23, 16, 14],
|
|
44
|
+
65: [14, 17, 17, 31, 17, 17, 17],
|
|
45
|
+
66: [30, 17, 17, 30, 17, 17, 30],
|
|
46
|
+
67: [14, 17, 16, 16, 16, 17, 14],
|
|
47
|
+
68: [28, 18, 17, 17, 17, 18, 28],
|
|
48
|
+
69: [31, 16, 16, 30, 16, 16, 31],
|
|
49
|
+
70: [31, 16, 16, 30, 16, 16, 16],
|
|
50
|
+
71: [14, 17, 16, 23, 17, 17, 14],
|
|
51
|
+
72: [17, 17, 17, 31, 17, 17, 17],
|
|
52
|
+
73: [14, 4, 4, 4, 4, 4, 14],
|
|
53
|
+
74: [7, 2, 2, 2, 2, 18, 12],
|
|
54
|
+
75: [17, 18, 20, 24, 20, 18, 17],
|
|
55
|
+
76: [16, 16, 16, 16, 16, 16, 31],
|
|
56
|
+
77: [17, 27, 21, 21, 17, 17, 17],
|
|
57
|
+
78: [17, 25, 25, 21, 19, 19, 17],
|
|
58
|
+
79: [14, 17, 17, 17, 17, 17, 14],
|
|
59
|
+
80: [30, 17, 17, 30, 16, 16, 16],
|
|
60
|
+
81: [14, 17, 17, 17, 21, 18, 13],
|
|
61
|
+
82: [30, 17, 17, 30, 20, 18, 17],
|
|
62
|
+
83: [14, 17, 16, 14, 1, 17, 14],
|
|
63
|
+
84: [31, 4, 4, 4, 4, 4, 4],
|
|
64
|
+
85: [17, 17, 17, 17, 17, 17, 14],
|
|
65
|
+
86: [17, 17, 17, 17, 17, 10, 4],
|
|
66
|
+
87: [17, 17, 17, 21, 21, 27, 17],
|
|
67
|
+
88: [17, 17, 10, 4, 10, 17, 17],
|
|
68
|
+
89: [17, 17, 10, 4, 4, 4, 4],
|
|
69
|
+
90: [31, 1, 2, 4, 8, 16, 31],
|
|
70
|
+
91: [14, 8, 8, 8, 8, 8, 14],
|
|
71
|
+
92: [16, 16, 8, 4, 2, 1, 1],
|
|
72
|
+
93: [14, 2, 2, 2, 2, 2, 14],
|
|
73
|
+
94: [4, 10, 17, 0, 0, 0, 0],
|
|
74
|
+
95: [0, 0, 0, 0, 0, 0, 31],
|
|
75
|
+
96: [8, 4, 2, 0, 0, 0, 0],
|
|
76
|
+
97: [0, 0, 14, 1, 15, 17, 15],
|
|
77
|
+
98: [16, 16, 30, 17, 17, 17, 30],
|
|
78
|
+
99: [0, 0, 14, 16, 16, 17, 14],
|
|
79
|
+
100: [1, 1, 15, 17, 17, 17, 15],
|
|
80
|
+
101: [0, 0, 14, 17, 30, 16, 14],
|
|
81
|
+
102: [6, 8, 8, 30, 8, 8, 8],
|
|
82
|
+
103: [0, 14, 17, 17, 15, 16, 14],
|
|
83
|
+
104: [16, 16, 30, 17, 17, 17, 17],
|
|
84
|
+
105: [4, 0, 12, 4, 4, 4, 14],
|
|
85
|
+
106: [2, 0, 6, 2, 2, 18, 12],
|
|
86
|
+
107: [16, 16, 18, 20, 24, 20, 18],
|
|
87
|
+
108: [12, 4, 4, 4, 4, 4, 14],
|
|
88
|
+
109: [0, 0, 26, 21, 21, 21, 21],
|
|
89
|
+
110: [0, 0, 30, 17, 17, 17, 17],
|
|
90
|
+
111: [0, 0, 14, 17, 17, 17, 14],
|
|
91
|
+
112: [0, 0, 30, 17, 17, 30, 16],
|
|
92
|
+
113: [0, 0, 15, 17, 17, 15, 1],
|
|
93
|
+
114: [0, 0, 13, 18, 16, 16, 16],
|
|
94
|
+
115: [0, 0, 15, 16, 14, 1, 30],
|
|
95
|
+
116: [8, 8, 30, 8, 8, 9, 6],
|
|
96
|
+
117: [0, 0, 17, 17, 17, 17, 15],
|
|
97
|
+
118: [0, 0, 17, 17, 17, 10, 4],
|
|
98
|
+
119: [0, 0, 17, 17, 21, 21, 10],
|
|
99
|
+
120: [0, 0, 17, 10, 4, 10, 17],
|
|
100
|
+
121: [0, 0, 17, 17, 15, 1, 14],
|
|
101
|
+
122: [0, 0, 31, 2, 4, 8, 31],
|
|
102
|
+
123: [2, 4, 4, 8, 4, 4, 2],
|
|
103
|
+
124: [4, 4, 4, 4, 4, 4, 4],
|
|
104
|
+
125: [8, 4, 4, 2, 4, 4, 8],
|
|
105
|
+
126: [0, 0, 0, 9, 21, 0, 0]
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const C_BG = [11, 15, 25];
|
|
109
|
+
const C_CARD = [22, 27, 46];
|
|
110
|
+
const C_LINE = [45, 55, 85];
|
|
111
|
+
const C_TEXT = [241, 245, 249];
|
|
112
|
+
const C_MUTED = [148, 163, 184];
|
|
113
|
+
const C_ROSE = [244, 63, 94];
|
|
114
|
+
const C_GREEN = [52, 211, 153];
|
|
115
|
+
const C_AMBER = [251, 191, 36];
|
|
116
|
+
const C_RED = [248, 113, 113];
|
|
117
|
+
const C_PURPLE = [165, 180, 252];
|
|
118
|
+
const C_GRAY = [148, 163, 184];
|
|
119
|
+
|
|
120
|
+
const PARETO_COLORS = { candidate: C_GREEN, baseline: C_AMBER, legacy: C_GRAY, live: C_ROSE };
|
|
121
|
+
|
|
122
|
+
function asciiOnly(s) {
|
|
123
|
+
return String(s ?? '').split('').map(ch => {
|
|
124
|
+
const code = ch.charCodeAt(0);
|
|
125
|
+
if (code >= 32 && code <= 126) return ch;
|
|
126
|
+
if (code === 8594) return '>';
|
|
127
|
+
return ' ';
|
|
128
|
+
}).join('');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function charStep(scale) {
|
|
132
|
+
return 6 * scale;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function textWidth(s, scale) {
|
|
136
|
+
return asciiOnly(s).length * charStep(scale);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function wrapText(s, maxChars) {
|
|
140
|
+
const words = asciiOnly(s).split(' ').filter(w => w.length > 0);
|
|
141
|
+
const lines = [];
|
|
142
|
+
let cur = '';
|
|
143
|
+
for (const w of words) {
|
|
144
|
+
if ((cur + (cur ? ' ' : '') + w).length <= maxChars) {
|
|
145
|
+
cur = cur ? cur + ' ' + w : w;
|
|
146
|
+
} else {
|
|
147
|
+
if (cur) lines.push(cur);
|
|
148
|
+
if (w.length > maxChars) {
|
|
149
|
+
for (let i = 0; i < w.length; i += maxChars) lines.push(w.slice(i, i + maxChars));
|
|
150
|
+
cur = '';
|
|
151
|
+
} else {
|
|
152
|
+
cur = w;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (cur) lines.push(cur);
|
|
157
|
+
if (!lines.length) lines.push('');
|
|
158
|
+
return lines;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function fitText(s, maxChars) {
|
|
162
|
+
const t = asciiOnly(s);
|
|
163
|
+
if (t.length <= maxChars) return t;
|
|
164
|
+
return t.slice(0, Math.max(0, maxChars - 1)) + '>';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function makeCanvas(w, h) {
|
|
168
|
+
return { w, h, buf: Buffer.alloc(w * h * 3, 0) };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function fillRect(cv, x, y, w, h, c) {
|
|
172
|
+
const x0 = Math.max(0, Math.round(x));
|
|
173
|
+
const y0 = Math.max(0, Math.round(y));
|
|
174
|
+
const x1 = Math.min(cv.w, Math.round(x + w));
|
|
175
|
+
const y1 = Math.min(cv.h, Math.round(y + h));
|
|
176
|
+
for (let yy = y0; yy < y1; yy++) {
|
|
177
|
+
for (let xx = x0; xx < x1; xx++) {
|
|
178
|
+
const o = (yy * cv.w + xx) * 3;
|
|
179
|
+
cv.buf[o] = c[0];
|
|
180
|
+
cv.buf[o + 1] = c[1];
|
|
181
|
+
cv.buf[o + 2] = c[2];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function drawChar(cv, ch, x, y, scale, c) {
|
|
187
|
+
const code = asciiOnly(ch).charCodeAt(0);
|
|
188
|
+
const glyph = FONT[code] || FONT[32];
|
|
189
|
+
for (let r = 0; r < 7; r++) {
|
|
190
|
+
const bits = glyph[r];
|
|
191
|
+
for (let col = 0; col < 5; col++) {
|
|
192
|
+
if (bits & (1 << (4 - col))) {
|
|
193
|
+
fillRect(cv, x + col * scale, y + r * scale, scale, scale, c);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function drawText(cv, s, x, y, scale, c) {
|
|
200
|
+
const t = asciiOnly(s);
|
|
201
|
+
for (let i = 0; i < t.length; i++) {
|
|
202
|
+
drawChar(cv, t[i], x + i * charStep(scale), y, scale, c);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function drawCircle(cv, cx, cy, r, c) {
|
|
207
|
+
const rr = Math.round(r);
|
|
208
|
+
for (let yy = -rr; yy <= rr; yy++) {
|
|
209
|
+
for (let xx = -rr; xx <= rr; xx++) {
|
|
210
|
+
if (xx * xx + yy * yy <= rr * rr) {
|
|
211
|
+
const px = Math.round(cx) + xx;
|
|
212
|
+
const py = Math.round(cy) + yy;
|
|
213
|
+
if (px >= 0 && py >= 0 && px < cv.w && py < cv.h) {
|
|
214
|
+
const o = (py * cv.w + px) * 3;
|
|
215
|
+
cv.buf[o] = c[0];
|
|
216
|
+
cv.buf[o + 1] = c[1];
|
|
217
|
+
cv.buf[o + 2] = c[2];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const CRC_TABLE = (() => {
|
|
225
|
+
const t = new Int32Array(256);
|
|
226
|
+
for (let n = 0; n < 256; n++) {
|
|
227
|
+
let c = n;
|
|
228
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
229
|
+
t[n] = c;
|
|
230
|
+
}
|
|
231
|
+
return t;
|
|
232
|
+
})();
|
|
233
|
+
|
|
234
|
+
function crc32(buf) {
|
|
235
|
+
let c = 0xffffffff;
|
|
236
|
+
for (let i = 0; i < buf.length; i++) c = CRC_TABLE[(c ^ buf[i]) & 255] ^ (c >>> 8);
|
|
237
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function chunk(type, data) {
|
|
241
|
+
const len = Buffer.alloc(4);
|
|
242
|
+
len.writeUInt32BE(data.length, 0);
|
|
243
|
+
const td = Buffer.from(type, 'ascii');
|
|
244
|
+
const crc = Buffer.alloc(4);
|
|
245
|
+
crc.writeUInt32BE(crc32(Buffer.concat([td, data])), 0);
|
|
246
|
+
return Buffer.concat([len, td, data, crc]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function encodePng(cv) {
|
|
250
|
+
const stride = cv.w * 3 + 1;
|
|
251
|
+
const raw = Buffer.alloc(stride * cv.h, 0);
|
|
252
|
+
for (let y = 0; y < cv.h; y++) {
|
|
253
|
+
raw[y * stride] = 0;
|
|
254
|
+
cv.buf.copy(raw, y * stride + 1, y * cv.w * 3, (y + 1) * cv.w * 3);
|
|
255
|
+
}
|
|
256
|
+
const ihdr = Buffer.alloc(13);
|
|
257
|
+
ihdr.writeUInt32BE(cv.w, 0);
|
|
258
|
+
ihdr.writeUInt32BE(cv.h, 4);
|
|
259
|
+
ihdr[8] = 8;
|
|
260
|
+
ihdr[9] = 2;
|
|
261
|
+
ihdr[10] = 0;
|
|
262
|
+
ihdr[11] = 0;
|
|
263
|
+
ihdr[12] = 0;
|
|
264
|
+
const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
265
|
+
return Buffer.concat([sig, chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw)), chunk('IEND', Buffer.alloc(0))]);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function formatObserved(row, values) {
|
|
269
|
+
const v = values[row.key];
|
|
270
|
+
if (v === null || v === undefined) return 'N-A';
|
|
271
|
+
if (row.format === 'percent') return (v * 100).toFixed(1) + '%';
|
|
272
|
+
if (row.format === 'cost') return '$' + Number(v).toFixed(3);
|
|
273
|
+
return Number(v).toFixed(2);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function statusOf(statusMap, key) {
|
|
277
|
+
const st = statusMap[key];
|
|
278
|
+
if (st === true) return { label: 'PASS', color: C_GREEN };
|
|
279
|
+
if (st === false) return { label: 'FAIL', color: C_RED };
|
|
280
|
+
return { label: 'N-A', color: C_MUTED };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function renderBenchmarkPng(rows = [], summary = {}, opts = {}) {
|
|
284
|
+
const width = Math.max(900, Math.min(2400, Number(opts.width) || 1440));
|
|
285
|
+
const S = 2;
|
|
286
|
+
const ST = 3;
|
|
287
|
+
const margin = 48;
|
|
288
|
+
const W = width - margin * 2;
|
|
289
|
+
const lineH = 7 * S + 8;
|
|
290
|
+
const mathOnly = (opts.mode || 'full') === 'math';
|
|
291
|
+
const bench = summary && summary.benchmark && summary.benchmark.values ? summary.benchmark : computeBenchmarkSummary(rows, summary);
|
|
292
|
+
const values = bench.values;
|
|
293
|
+
const model = asciiOnly((summary && summary.model) || (rows && rows.model) || 'agent-model');
|
|
294
|
+
const stamp = new Date().toISOString().slice(0, 19).replace('T', ' ');
|
|
295
|
+
|
|
296
|
+
const colDim = Math.round(W * 0.24);
|
|
297
|
+
const colInd = Math.round(W * 0.35);
|
|
298
|
+
const colTgt = Math.round(W * 0.15);
|
|
299
|
+
const colObs = Math.round(W * 0.13);
|
|
300
|
+
const colSta = W - colDim - colInd - colTgt - colObs;
|
|
301
|
+
const maxDim = Math.floor(colDim / charStep(S)) - 1;
|
|
302
|
+
const maxInd = Math.floor(colInd / charStep(S)) - 1;
|
|
303
|
+
const maxTgt = Math.floor(colTgt / charStep(S)) - 1;
|
|
304
|
+
const maxObs = Math.floor(colObs / charStep(S)) - 1;
|
|
305
|
+
|
|
306
|
+
const tableRows = BENCHMARK_ROWS.map(r => {
|
|
307
|
+
const dim = wrapText(r.dimension, maxDim);
|
|
308
|
+
const ind = wrapText(r.indicator + ' [' + r.benchmark + ']', maxInd);
|
|
309
|
+
const lines = Math.max(dim.length, ind.length, 1);
|
|
310
|
+
return { def: r, observed: formatObserved(r, values), state: statusOf(bench.status, r.key), dim, ind, lines };
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const failKeys = Object.keys(bench.failureDistribution || {});
|
|
314
|
+
const formulas = [
|
|
315
|
+
'Pass@k = E[ 1 - C(n-c,k) / C(n,k) ] (n runs, c passes)',
|
|
316
|
+
'eta_traj = L* / max(L, L*) (L* shortest path, L actual)',
|
|
317
|
+
'S_faith = sum I(Claim in K) / m (K tool output, m claims)'
|
|
318
|
+
];
|
|
319
|
+
const road = ROADMAP.map(p => ({
|
|
320
|
+
head: p.phase + ' ' + p.weeks + ': ' + p.title,
|
|
321
|
+
lines: p.actions.reduce((acc, a) => acc.concat(wrapText('- ' + a, Math.floor(W / charStep(S)) - 4)), [])
|
|
322
|
+
}));
|
|
323
|
+
|
|
324
|
+
const rowH = tableRows.map(r => r.lines * lineH + 18);
|
|
325
|
+
const tableH = 52 + rowH.reduce((a, b) => a + b, 0);
|
|
326
|
+
const paretoH = 430;
|
|
327
|
+
const failH = 60 + failKeys.length * 52;
|
|
328
|
+
const formulaH = 60 + formulas.length * 34;
|
|
329
|
+
const roadH = road.reduce((a, p) => a + 44 + p.lines.length * 30, 56);
|
|
330
|
+
const totalH = mathOnly
|
|
331
|
+
? 330 + tableH + formulaH
|
|
332
|
+
: 390 + tableH + 40 + paretoH + 40 + failH + 40 + formulaH + 40 + roadH + 60;
|
|
333
|
+
|
|
334
|
+
const cv = makeCanvas(width, totalH);
|
|
335
|
+
fillRect(cv, 0, 0, width, totalH, C_BG);
|
|
336
|
+
let y = 0;
|
|
337
|
+
fillRect(cv, 0, y, width, 150, C_CARD);
|
|
338
|
+
fillRect(cv, 0, 150, width, 4, C_ROSE);
|
|
339
|
+
drawText(cv, 'Kiku Agent Benchmark', margin, y + 30, ST, C_TEXT);
|
|
340
|
+
drawText(cv, 'ISO-IEC 42001 + NIST AI RMF | model: ' + model, margin, y + 92, S, C_MUTED);
|
|
341
|
+
drawText(cv, stamp + ' UTC', width - margin - textWidth(stamp + ' UTC', S), y + 92, S, C_MUTED);
|
|
342
|
+
y = 190;
|
|
343
|
+
|
|
344
|
+
const drawSection = (title) => {
|
|
345
|
+
drawText(cv, title, margin, y, S, C_PURPLE);
|
|
346
|
+
y += 40;
|
|
347
|
+
return y;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
drawSection('Benchmark Dashboard');
|
|
351
|
+
const tx = margin;
|
|
352
|
+
fillRect(cv, tx, y, W, tableH, C_CARD);
|
|
353
|
+
drawText(cv, 'Metric Dimension', tx + 12, y + 14, S, C_MUTED);
|
|
354
|
+
drawText(cv, 'Indicator [Benchmark]', tx + colDim + 12, y + 14, S, C_MUTED);
|
|
355
|
+
drawText(cv, 'Target', tx + colDim + colInd + 12, y + 14, S, C_MUTED);
|
|
356
|
+
drawText(cv, 'Observed', tx + colDim + colInd + colTgt + 12, y + 14, S, C_MUTED);
|
|
357
|
+
drawText(cv, 'Status', tx + colDim + colInd + colTgt + colObs + 12, y + 14, S, C_MUTED);
|
|
358
|
+
let ry = y + 52;
|
|
359
|
+
tableRows.forEach((r, idx) => {
|
|
360
|
+
if (idx > 0) fillRect(cv, tx, ry - 9, W, 2, C_LINE);
|
|
361
|
+
r.dim.forEach((ln, i) => drawText(cv, ln, tx + 12, ry + i * lineH, S, C_TEXT));
|
|
362
|
+
r.ind.forEach((ln, i) => drawText(cv, ln, tx + colDim + 12, ry + i * lineH, S, i === 0 ? C_TEXT : C_MUTED));
|
|
363
|
+
drawText(cv, fitText(r.def.target, maxTgt), tx + colDim + colInd + 12, ry, S, C_MUTED);
|
|
364
|
+
drawText(cv, fitText(r.observed, maxObs), tx + colDim + colInd + colTgt + 12, ry, S, C_TEXT);
|
|
365
|
+
const bx = tx + colDim + colInd + colTgt + colObs + 12;
|
|
366
|
+
fillRect(cv, bx, ry - 2, 78, 26, r.state.color);
|
|
367
|
+
drawText(cv, r.state.label, bx + 8, ry + 2, S, C_BG);
|
|
368
|
+
ry += rowH[idx];
|
|
369
|
+
});
|
|
370
|
+
y += tableH + 40;
|
|
371
|
+
|
|
372
|
+
if (!mathOnly) {
|
|
373
|
+
drawSection('Trajectory vs Cost Pareto Frontier');
|
|
374
|
+
const px = margin;
|
|
375
|
+
const py = y;
|
|
376
|
+
const pw = W;
|
|
377
|
+
const ph = paretoH - 60;
|
|
378
|
+
fillRect(cv, px, py, pw, ph + 40, C_CARD);
|
|
379
|
+
const ox = px + 90;
|
|
380
|
+
const oy = py + ph - 30;
|
|
381
|
+
const axW = pw - 150;
|
|
382
|
+
const axH = ph - 80;
|
|
383
|
+
fillRect(cv, ox, oy, axW, 2, C_LINE);
|
|
384
|
+
fillRect(cv, ox, oy - axH, 2, axH, C_LINE);
|
|
385
|
+
for (let g = 0; g <= 4; g++) {
|
|
386
|
+
const gx = ox + Math.round(axW * g / 4);
|
|
387
|
+
fillRect(cv, gx, oy - axH, 2, axH, [28, 34, 56]);
|
|
388
|
+
drawText(cv, '$' + (0.08 * g / 4).toFixed(2), gx - 24, oy + 10, S, C_MUTED);
|
|
389
|
+
}
|
|
390
|
+
for (let g = 0; g <= 3; g++) {
|
|
391
|
+
const gy = oy - Math.round(axH * g / 3);
|
|
392
|
+
fillRect(cv, ox, gy, axW, 2, [28, 34, 56]);
|
|
393
|
+
drawText(cv, String(40 + Math.round(60 * g / 3)), ox - 48, gy - 6, S, C_MUTED);
|
|
394
|
+
}
|
|
395
|
+
const capX = ox + Math.round(axW / 2) - Math.round(textWidth('Cost per task (USD) 0.00 - 0.08', S) / 2);
|
|
396
|
+
drawText(cv, 'Cost per task (USD) 0.00 - 0.08', capX, oy + 32, S, C_MUTED);
|
|
397
|
+
drawText(cv, 'Success %', ox - 48, py + 8, S, C_MUTED);
|
|
398
|
+
const dotX = c => ox + Math.round(Math.min(Math.max(Number(c) || 0, 0), 0.08) * (axW / 0.08));
|
|
399
|
+
const dotY = s => oy - Math.round(((Math.min(Math.max(Number(s) || 0, 40), 100) - 40) / 60) * axH);
|
|
400
|
+
(bench.pareto || []).forEach((p, i) => {
|
|
401
|
+
const cx = dotX(p.cost);
|
|
402
|
+
const cy = dotY(p.successRate);
|
|
403
|
+
drawCircle(cv, cx, cy, p.kind === 'live' ? 9 : 7, PARETO_COLORS[p.kind] || C_TEXT);
|
|
404
|
+
const label = p.label + ' (' + p.successRate + '%, $' + Number(p.cost).toFixed(3) + ')';
|
|
405
|
+
const lx = Math.min(cx + 14, px + pw - textWidth(label, S) - 8);
|
|
406
|
+
const ly = Math.max(py + 8, cy - 24 - (i % 2) * 20);
|
|
407
|
+
drawText(cv, label, lx, ly, S, PARETO_COLORS[p.kind] || C_TEXT);
|
|
408
|
+
});
|
|
409
|
+
y += paretoH + 40;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (!mathOnly) {
|
|
413
|
+
drawSection('Failure Mode Distribution');
|
|
414
|
+
fillRect(cv, margin, y, W, failH - 20, C_CARD);
|
|
415
|
+
let fy = y + 16;
|
|
416
|
+
for (const key of failKeys) {
|
|
417
|
+
const f = bench.failureDistribution[key];
|
|
418
|
+
const pct = Math.round((f.rate || 0) * 1000) / 10;
|
|
419
|
+
drawText(cv, fitText(f.label, 30), margin + 12, fy, S, C_TEXT);
|
|
420
|
+
const barX = margin + 380;
|
|
421
|
+
const barW = W - 380 - 200;
|
|
422
|
+
fillRect(cv, barX, fy, barW, 20, C_LINE);
|
|
423
|
+
fillRect(cv, barX, fy, Math.round(barW * (f.rate || 0)), 20, C_ROSE);
|
|
424
|
+
drawText(cv, pct.toFixed(1) + '% (' + f.count + ')', barX + barW + 16, fy, S, C_MUTED);
|
|
425
|
+
fy += 52;
|
|
426
|
+
}
|
|
427
|
+
y += failH + 40;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
drawSection('Formulas');
|
|
431
|
+
fillRect(cv, margin, y, W, formulaH - 20, C_CARD);
|
|
432
|
+
let my = y + 16;
|
|
433
|
+
for (const f of formulas) {
|
|
434
|
+
drawText(cv, fitText(f, Math.floor(W / charStep(S)) - 4), margin + 12, my, S, C_TEXT);
|
|
435
|
+
my += 34;
|
|
436
|
+
}
|
|
437
|
+
y += formulaH + 40;
|
|
438
|
+
|
|
439
|
+
if (!mathOnly) {
|
|
440
|
+
drawSection('Enhancement Roadmap');
|
|
441
|
+
fillRect(cv, margin, y, W, roadH - 16, C_CARD);
|
|
442
|
+
let dy = y + 16;
|
|
443
|
+
for (const p of road) {
|
|
444
|
+
drawText(cv, fitText(p.head, Math.floor(W / charStep(S)) - 4), margin + 12, dy, S, C_PURPLE);
|
|
445
|
+
dy += 34;
|
|
446
|
+
for (const ln of p.lines) {
|
|
447
|
+
drawText(cv, ln, margin + 28, dy, S, C_TEXT);
|
|
448
|
+
dy += 30;
|
|
449
|
+
}
|
|
450
|
+
dy += 10;
|
|
451
|
+
}
|
|
452
|
+
y += roadH + 40;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
drawText(cv, 'kiku-agent eval | offline snapshot', margin, totalH - 36, S, C_MUTED);
|
|
456
|
+
return encodePng(cv);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export async function writeBenchmarkPng(report, outputPath, opts = {}) {
|
|
460
|
+
const meta = report.metadata || {};
|
|
461
|
+
const base = report.summary || {};
|
|
462
|
+
const summary = Object.assign({}, base, { model: base.model || meta.model });
|
|
463
|
+
const png = renderBenchmarkPng(report.rows || [], summary, opts);
|
|
464
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
465
|
+
await fs.writeFile(outputPath, png);
|
|
466
|
+
return outputPath;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
|
|
470
|
+
const { values } = parseArgs({
|
|
471
|
+
options: {
|
|
472
|
+
in: { type: 'string', default: 'artifacts/eval/live.json' },
|
|
473
|
+
out: { type: 'string', default: 'artifacts/eval/benchmark.png' },
|
|
474
|
+
width: { type: 'string', default: '1440' },
|
|
475
|
+
mode: { type: 'string', default: 'full' }
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
const inPath = path.resolve(REPO_ROOT, values.in);
|
|
479
|
+
const outPath = path.resolve(REPO_ROOT, values.out);
|
|
480
|
+
try {
|
|
481
|
+
const report = JSON.parse(await fs.readFile(inPath, 'utf8'));
|
|
482
|
+
await writeBenchmarkPng(report, outPath, { width: Number(values.width), mode: values.mode });
|
|
483
|
+
} catch (err) {
|
|
484
|
+
console.error(String((err && err.message) || err));
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
}
|