@camstack/addon-benchmark 0.1.0 → 0.1.1

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/dist/index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,317 +15,15 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
31
21
  var src_exports = {};
32
22
  __export(src_exports, {
33
- BenchmarkAddon: () => BenchmarkAddon,
34
- BenchmarkAgent: () => BenchmarkAgent,
35
- BenchmarkHub: () => BenchmarkHub,
36
- BenchmarkRunner: () => BenchmarkRunner,
37
- annotateImage: () => annotateImage,
38
- collectSystemInfo: () => collectSystemInfo,
39
- computeAccuracy: () => computeAccuracy,
40
- computeFps: () => computeFps,
41
- computeLatencyStats: () => computeLatencyStats,
42
- iou: () => iou
23
+ BenchmarkAddon: () => BenchmarkAddon
43
24
  });
44
25
  module.exports = __toCommonJS(src_exports);
45
26
 
46
- // src/runner/stats.ts
47
- function percentile(sorted, p) {
48
- if (sorted.length === 0) return 0;
49
- const idx = p * (sorted.length - 1);
50
- const lo = Math.floor(idx);
51
- const hi = Math.ceil(idx);
52
- const loVal = sorted[lo] ?? 0;
53
- const hiVal = sorted[hi] ?? 0;
54
- return loVal + (hiVal - loVal) * (idx - lo);
55
- }
56
- function computeLatencyStats(timingsMs) {
57
- if (timingsMs.length === 0) {
58
- return { mean: 0, median: 0, p95: 0, p99: 0, min: 0, max: 0 };
59
- }
60
- const sorted = [...timingsMs].sort((a, b) => a - b);
61
- const sum = sorted.reduce((acc, v) => acc + v, 0);
62
- const mean = sum / sorted.length;
63
- return {
64
- mean,
65
- median: percentile(sorted, 0.5),
66
- p95: percentile(sorted, 0.95),
67
- p99: percentile(sorted, 0.99),
68
- min: sorted[0] ?? 0,
69
- max: sorted[sorted.length - 1] ?? 0
70
- };
71
- }
72
- function computeFps(timingsMs) {
73
- if (timingsMs.length === 0) return 0;
74
- const stats = computeLatencyStats(timingsMs);
75
- if (stats.mean === 0) return 0;
76
- return 1e3 / stats.mean;
77
- }
78
-
79
- // src/runner/accuracy.ts
80
- function iou(pred, gt) {
81
- const predX2 = pred.x + pred.w;
82
- const predY2 = pred.y + pred.h;
83
- const gtX2 = gt.x + gt.w;
84
- const gtY2 = gt.y + gt.h;
85
- const interX1 = Math.max(pred.x, gt.x);
86
- const interY1 = Math.max(pred.y, gt.y);
87
- const interX2 = Math.min(predX2, gtX2);
88
- const interY2 = Math.min(predY2, gtY2);
89
- const interW = Math.max(0, interX2 - interX1);
90
- const interH = Math.max(0, interY2 - interY1);
91
- const interArea = interW * interH;
92
- if (interArea === 0) return 0;
93
- const predArea = pred.w * pred.h;
94
- const gtArea = gt.w * gt.h;
95
- const unionArea = predArea + gtArea - interArea;
96
- if (unionArea <= 0) return 0;
97
- return interArea / unionArea;
98
- }
99
- function areaUnderPRCurve(precision, recall) {
100
- if (precision.length < 2) return precision[0] ?? 0;
101
- let area = 0;
102
- for (let i = 1; i < recall.length; i++) {
103
- const dr = (recall[i] ?? 0) - (recall[i - 1] ?? 0);
104
- const avgP = ((precision[i] ?? 0) + (precision[i - 1] ?? 0)) / 2;
105
- area += dr * avgP;
106
- }
107
- return Math.abs(area);
108
- }
109
- function computeClassAP(predictions, groundTruths, iouThreshold) {
110
- const nGt = groundTruths.length;
111
- if (nGt === 0) return 0;
112
- if (predictions.length === 0) return 0;
113
- const matched = /* @__PURE__ */ new Set();
114
- const tp = [];
115
- const fp = [];
116
- for (const pred of predictions) {
117
- let bestIou = -1;
118
- let bestGtIdx = -1;
119
- for (let i = 0; i < groundTruths.length; i++) {
120
- if (matched.has(i)) continue;
121
- const gt = groundTruths[i];
122
- if (!gt) continue;
123
- const score = iou(pred.bbox, gt.bbox);
124
- if (score > bestIou) {
125
- bestIou = score;
126
- bestGtIdx = i;
127
- }
128
- }
129
- if (bestIou >= iouThreshold && bestGtIdx >= 0) {
130
- matched.add(bestGtIdx);
131
- tp.push(1);
132
- fp.push(0);
133
- } else {
134
- tp.push(0);
135
- fp.push(1);
136
- }
137
- }
138
- const cumTp = [];
139
- const cumFp = [];
140
- let sumTp = 0;
141
- let sumFp = 0;
142
- for (let i = 0; i < tp.length; i++) {
143
- sumTp += tp[i] ?? 0;
144
- sumFp += fp[i] ?? 0;
145
- cumTp.push(sumTp);
146
- cumFp.push(sumFp);
147
- }
148
- const precisions = [];
149
- const recalls = [];
150
- precisions.push(1);
151
- recalls.push(0);
152
- for (let i = 0; i < cumTp.length; i++) {
153
- const cTp = cumTp[i] ?? 0;
154
- const cFp = cumFp[i] ?? 0;
155
- const denom = cTp + cFp;
156
- precisions.push(denom > 0 ? cTp / denom : 0);
157
- recalls.push(nGt > 0 ? cTp / nGt : 0);
158
- }
159
- return areaUnderPRCurve(precisions, recalls);
160
- }
161
- function computeAccuracy(predictions, groundTruth, iouThreshold = 0.5) {
162
- if (groundTruth.length === 0) {
163
- return { mAP50: void 0, precision: void 0, recall: void 0 };
164
- }
165
- const classes = [...new Set(groundTruth.map((gt) => gt.class))];
166
- const aps = [];
167
- let totalTp = 0;
168
- let totalFp = 0;
169
- let totalGt = 0;
170
- for (const cls of classes) {
171
- const classPredictions = predictions.filter((p) => p.class === cls).sort((a, b) => b.score - a.score);
172
- const classGt = groundTruth.filter((gt) => gt.class === cls);
173
- totalGt += classGt.length;
174
- const ap = computeClassAP(classPredictions, classGt, iouThreshold);
175
- aps.push(ap);
176
- const matched = /* @__PURE__ */ new Set();
177
- for (const pred of classPredictions) {
178
- let bestIou = -1;
179
- let bestGtIdx = -1;
180
- for (let i = 0; i < classGt.length; i++) {
181
- if (matched.has(i)) continue;
182
- const gt = classGt[i];
183
- if (!gt) continue;
184
- const score = iou(pred.bbox, gt.bbox);
185
- if (score > bestIou) {
186
- bestIou = score;
187
- bestGtIdx = i;
188
- }
189
- }
190
- if (bestIou >= iouThreshold && bestGtIdx >= 0) {
191
- matched.add(bestGtIdx);
192
- totalTp++;
193
- } else {
194
- totalFp++;
195
- }
196
- }
197
- }
198
- const mAP50 = aps.length > 0 ? aps.reduce((sum, ap) => sum + ap, 0) / aps.length : void 0;
199
- const precision = totalTp + totalFp > 0 ? totalTp / (totalTp + totalFp) : void 0;
200
- const recall = totalGt > 0 ? totalTp / totalGt : void 0;
201
- return { mAP50, precision, recall };
202
- }
203
-
204
- // src/runner/benchmark-runner.ts
205
- function measureResources() {
206
- const memUsage = process.memoryUsage();
207
- return {
208
- peakMemoryMB: Math.round(memUsage.heapUsed / (1024 * 1024)),
209
- avgCpuPercent: 0
210
- // CPU profiling requires native tooling; best-effort zero
211
- };
212
- }
213
- var BenchmarkRunner = class {
214
- constructor(options) {
215
- this.options = options;
216
- }
217
- /** Run inference on a single frame, picking a reference image round-robin if needed */
218
- async runOnce(target, frameIdx = 0) {
219
- const images = this.options.referenceImages;
220
- if (images.length === 0) {
221
- const syntheticFrame = {
222
- data: Buffer.alloc(320 * 240 * 3),
223
- format: "rgb",
224
- width: 320,
225
- height: 240,
226
- timestamp: Date.now()
227
- };
228
- return this.options.runInference(syntheticFrame, target.config);
229
- }
230
- const idx = frameIdx % images.length;
231
- const entry = images[idx];
232
- if (!entry) {
233
- throw new Error(`Reference image at index ${idx} is undefined`);
234
- }
235
- return this.options.runInference(entry.frame, target.config);
236
- }
237
- /** Measure accuracy across all reference images */
238
- async measureAccuracy(target) {
239
- const images = this.options.referenceImages;
240
- if (images.length === 0) return void 0;
241
- const allPredictions = [];
242
- const allGroundTruth = [];
243
- for (const entry of images) {
244
- const output = await this.options.runInference(entry.frame, target.config);
245
- allPredictions.push(...output.detections);
246
- allGroundTruth.push(...entry.groundTruth.annotations);
247
- }
248
- return computeAccuracy(allPredictions, allGroundTruth);
249
- }
250
- /** Run benchmark for a single target configuration */
251
- async runTarget(target, config) {
252
- const warmup = config.warmup ?? 10;
253
- const iterations = config.iterations ?? 100;
254
- const timings = [];
255
- for (let i = 0; i < warmup; i++) {
256
- await this.runOnce(target, i);
257
- }
258
- for (let i = 0; i < iterations; i++) {
259
- const start = performance.now();
260
- await this.runOnce(target, i);
261
- timings.push(performance.now() - start);
262
- this.options.onProgress?.(i + 1, iterations, target.label);
263
- }
264
- const latency = computeLatencyStats(timings);
265
- const fps = computeFps(timings);
266
- const accuracy = await this.measureAccuracy(target);
267
- const resources = measureResources();
268
- return {
269
- label: target.label,
270
- addonId: target.addonId,
271
- config: target.config,
272
- latency,
273
- fps,
274
- accuracy,
275
- resources
276
- };
277
- }
278
- /** Run benchmark for all targets defined in the config */
279
- async runAll(config) {
280
- const results = [];
281
- for (const target of config.targets) {
282
- results.push(await this.runTarget(target, config));
283
- }
284
- return results;
285
- }
286
- };
287
-
288
- // src/runner/system-info.ts
289
- var import_node_os = __toESM(require("os"));
290
- var import_node_child_process = require("child_process");
291
- function detectGpuModel() {
292
- try {
293
- const output = (0, import_node_child_process.execSync)("nvidia-smi --query-gpu=name --format=csv,noheader", {
294
- timeout: 3e3,
295
- stdio: ["ignore", "pipe", "ignore"]
296
- }).toString().trim();
297
- if (output.length > 0) {
298
- return output.split("\n")[0]?.trim();
299
- }
300
- } catch {
301
- }
302
- if (process.platform === "darwin") {
303
- try {
304
- const output = (0, import_node_child_process.execSync)(
305
- "system_profiler SPDisplaysDataType 2>/dev/null | grep 'Chipset Model:' | head -1",
306
- { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }
307
- ).toString().trim();
308
- const match = output.match(/Chipset Model:\s*(.+)/);
309
- if (match?.[1]) {
310
- return match[1].trim();
311
- }
312
- } catch {
313
- }
314
- }
315
- return void 0;
316
- }
317
- function collectSystemInfo() {
318
- const cpus = import_node_os.default.cpus();
319
- const firstCpu = cpus[0];
320
- return {
321
- os: `${import_node_os.default.type()} ${import_node_os.default.release()}`,
322
- arch: import_node_os.default.arch(),
323
- cpuModel: firstCpu?.model ?? "Unknown",
324
- cpuCores: cpus.length,
325
- totalMemoryMB: Math.round(import_node_os.default.totalmem() / (1024 * 1024)),
326
- gpuModel: detectGpuModel(),
327
- nodeVersion: process.version
328
- };
329
- }
330
-
331
27
  // src/benchmark-addon.ts
332
28
  var BenchmarkAddon = class {
333
29
  id = "benchmark";
@@ -335,8 +31,8 @@ var BenchmarkAddon = class {
335
31
  id: "benchmark",
336
32
  name: "Benchmark",
337
33
  version: "0.1.0",
338
- description: "Detection addon benchmarking with reference images and distributed execution",
339
- packageName: "@camstack/addon-benchmark"
34
+ packageName: "@camstack/addon-benchmark",
35
+ description: "Detection benchmark, image tester, and pipeline runner"
340
36
  };
341
37
  pages = [
342
38
  {
@@ -344,18 +40,12 @@ var BenchmarkAddon = class {
344
40
  label: "Benchmark",
345
41
  icon: "gauge",
346
42
  path: "/addon/benchmark",
347
- bundle: "benchmark.js",
348
- element: "camstack-benchmark"
43
+ bundle: "page.mjs"
349
44
  }
350
45
  ];
351
- context = null;
352
- options = {};
353
- async initialize(ctx, opts) {
354
- this.context = ctx;
355
- this.options = opts ?? {};
46
+ async initialize(_ctx) {
356
47
  }
357
48
  async shutdown() {
358
- this.context = null;
359
49
  }
360
50
  getCapabilityProvider(name) {
361
51
  if (name === "addon-pages") {
@@ -367,436 +57,9 @@ var BenchmarkAddon = class {
367
57
  }
368
58
  return null;
369
59
  }
370
- /**
371
- * Run a benchmark locally and return a MultiBenchmarkReport.
372
- */
373
- async run(config, onProgress) {
374
- const systemInfo = collectSystemInfo();
375
- const results = await this.runLocally(config, onProgress);
376
- const leaderboard = results.sort((a, b) => a.latency.mean - b.latency.mean).map((r, idx) => ({
377
- rank: idx + 1,
378
- agentId: "local",
379
- label: r.label,
380
- meanMs: r.latency.mean,
381
- fps: r.fps,
382
- peakMemoryMB: r.resources.peakMemoryMB,
383
- accuracy: r.accuracy
384
- }));
385
- return {
386
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
387
- source: config.source,
388
- agents: [
389
- {
390
- agentId: "local",
391
- status: "completed",
392
- systemInfo,
393
- results
394
- }
395
- ],
396
- leaderboard
397
- };
398
- }
399
- /**
400
- * Run a distributed benchmark across agents.
401
- *
402
- * Stub: distributed execution requires a BenchmarkHub instance bound to
403
- * live WebSocket connections. Call BenchmarkHub.dispatch() directly from
404
- * the server transport layer. This method runs locally as a fallback.
405
- */
406
- async runDistributed(config, onProgress) {
407
- return this.run(config, onProgress);
408
- }
409
- async runLocally(config, onProgress) {
410
- const { inferenceFactory, referenceImages = [] } = this.options;
411
- const firstTarget = config.targets[0];
412
- if (!firstTarget) {
413
- throw new Error("BenchmarkConfig must have at least one target");
414
- }
415
- if (!inferenceFactory) {
416
- return config.targets.map((target) => ({
417
- label: target.label,
418
- addonId: target.addonId,
419
- config: target.config,
420
- latency: { mean: 0, median: 0, p95: 0, p99: 0, min: 0, max: 0 },
421
- fps: 0,
422
- resources: { peakMemoryMB: 0, avgCpuPercent: 0 }
423
- }));
424
- }
425
- const runInference = inferenceFactory(firstTarget.addonId, firstTarget.config);
426
- const runner = new BenchmarkRunner({
427
- runInference,
428
- referenceImages,
429
- onProgress
430
- });
431
- return runner.runAll(config);
432
- }
433
- };
434
-
435
- // src/annotator/result-annotator.ts
436
- var import_sharp = __toESM(require("sharp"));
437
- var COLOR_PALETTE = [
438
- "#FF4136",
439
- "#2ECC40",
440
- "#0074D9",
441
- "#FF851B",
442
- "#B10DC9",
443
- "#FFDC00",
444
- "#7FDBFF",
445
- "#01FF70",
446
- "#F012BE",
447
- "#3D9970"
448
- ];
449
- function classColor(cls) {
450
- let hash = 0;
451
- for (let i = 0; i < cls.length; i++) {
452
- hash = hash * 31 + (cls.codePointAt(i) ?? 0) >>> 0;
453
- }
454
- return COLOR_PALETTE[hash % COLOR_PALETTE.length] ?? "#FF4136";
455
- }
456
- function escSvg(str) {
457
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
458
- }
459
- function buildSvgOverlay(detections, width, height) {
460
- const rects = detections.map((det) => {
461
- const color = classColor(det.class);
462
- const label = escSvg(`${det.class} ${(det.score * 100).toFixed(0)}%`);
463
- const { x, y, w, h } = det.bbox;
464
- const clampX = Math.max(0, Math.min(x, width - 1));
465
- const clampY = Math.max(0, Math.min(y, height - 1));
466
- const clampW = Math.max(1, Math.min(w, width - clampX));
467
- const clampH = Math.max(1, Math.min(h, height - clampY));
468
- const labelY = clampY > 14 ? clampY - 2 : clampY + clampH + 12;
469
- return `
470
- <rect x="${clampX}" y="${clampY}" width="${clampW}" height="${clampH}"
471
- stroke="${color}" stroke-width="2" fill="none"/>
472
- <rect x="${clampX}" y="${labelY - 12}" width="${label.length * 7}" height="14"
473
- fill="${color}" fill-opacity="0.75"/>
474
- <text x="${clampX + 2}" y="${labelY}" font-family="monospace" font-size="11"
475
- fill="white">${label}</text>`;
476
- });
477
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">${rects.join("")}</svg>`;
478
- }
479
- async function buildMaskOverlays(detections, width, height) {
480
- const overlays = [];
481
- for (const det of detections) {
482
- if (!det.mask || !det.maskWidth || !det.maskHeight) continue;
483
- const color = classColor(det.class);
484
- const r = parseInt(color.slice(1, 3), 16);
485
- const g = parseInt(color.slice(3, 5), 16);
486
- const b = parseInt(color.slice(5, 7), 16);
487
- const rgba = Buffer.alloc(det.maskWidth * det.maskHeight * 4);
488
- for (let i = 0; i < det.mask.length; i++) {
489
- const alpha = det.mask[i] ?? 0;
490
- if (alpha > 0) {
491
- const base = i * 4;
492
- rgba[base] = r;
493
- rgba[base + 1] = g;
494
- rgba[base + 2] = b;
495
- rgba[base + 3] = Math.round(alpha * 0.5);
496
- }
497
- }
498
- const maskBuffer = await (0, import_sharp.default)(rgba, {
499
- raw: { width: det.maskWidth, height: det.maskHeight, channels: 4 }
500
- }).resize(width, height).png().toBuffer();
501
- overlays.push(maskBuffer);
502
- }
503
- return overlays;
504
- }
505
- async function annotateImage(frame, detections, outputPath) {
506
- let image;
507
- if (frame.format === "jpeg") {
508
- image = (0, import_sharp.default)(frame.data);
509
- } else {
510
- image = (0, import_sharp.default)(frame.data, {
511
- raw: { width: frame.width, height: frame.height, channels: 3 }
512
- });
513
- }
514
- const composites = [];
515
- const maskBuffers = await buildMaskOverlays(detections, frame.width, frame.height);
516
- for (const maskBuf of maskBuffers) {
517
- composites.push({ input: maskBuf, blend: "over" });
518
- }
519
- const svgBuf = Buffer.from(buildSvgOverlay(detections, frame.width, frame.height));
520
- composites.push({ input: svgBuf, blend: "over" });
521
- await image.composite(composites).jpeg({ quality: 90 }).toFile(outputPath);
522
- }
523
-
524
- // src/distributed/benchmark-hub.ts
525
- var BenchmarkHub = class {
526
- constructor(send) {
527
- this.send = send;
528
- }
529
- agents = /* @__PURE__ */ new Map();
530
- activeTaskId = null;
531
- resolveTask = null;
532
- rejectTask = null;
533
- pendingConfig = null;
534
- /** Register a connected agent (typically on WS connect + hello message) */
535
- registerAgent(agentId, systemInfo) {
536
- this.agents.set(agentId, {
537
- agentId,
538
- systemInfo,
539
- status: "idle"
540
- });
541
- }
542
- /** Mark an agent as disconnected */
543
- disconnectAgent(agentId) {
544
- const entry = this.agents.get(agentId);
545
- if (entry) {
546
- this.agents.set(agentId, { ...entry, status: "disconnected" });
547
- }
548
- this.checkCompletion();
549
- }
550
- /** Handle incoming message from any agent */
551
- handleMessage(message) {
552
- switch (message.type) {
553
- case "agent.hello":
554
- this.onAgentHello(message);
555
- break;
556
- case "benchmark.progress":
557
- this.onProgress(message);
558
- break;
559
- case "benchmark.result":
560
- this.onResult(message);
561
- break;
562
- case "benchmark.error":
563
- this.onError(message);
564
- break;
565
- default:
566
- break;
567
- }
568
- }
569
- /**
570
- * Dispatch a benchmark to a subset of agents.
571
- * Returns a promise that resolves when all targeted agents have finished.
572
- *
573
- * @param config - Benchmark configuration
574
- * @param agentIds - If empty, dispatches to all connected/idle agents
575
- * @param referenceImages - Reference image buffers to send to agents
576
- */
577
- async dispatch(config, agentIds, referenceImages = []) {
578
- const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2)}`;
579
- this.activeTaskId = taskId;
580
- this.pendingConfig = config;
581
- const targets = agentIds.length > 0 ? agentIds : [...this.agents.values()].filter((a) => a.status === "idle").map((a) => a.agentId);
582
- if (targets.length === 0) {
583
- throw new Error("No idle agents available for benchmark dispatch");
584
- }
585
- for (const agentId of targets) {
586
- const entry = this.agents.get(agentId);
587
- if (entry) {
588
- this.agents.set(agentId, { ...entry, status: "running", results: void 0, error: void 0 });
589
- }
590
- }
591
- for (const agentId of targets) {
592
- this.send(agentId, {
593
- type: "benchmark.start",
594
- taskId,
595
- config,
596
- referenceImages
597
- });
598
- }
599
- return new Promise((resolve, reject) => {
600
- this.resolveTask = resolve;
601
- this.rejectTask = reject;
602
- });
603
- }
604
- onAgentHello(message) {
605
- this.registerAgent(message.agentId, message.systemInfo);
606
- }
607
- onProgress(message) {
608
- const entry = this.agents.get(message.agentId);
609
- if (entry) {
610
- this.agents.set(message.agentId, {
611
- ...entry,
612
- completedIterations: message.completed
613
- });
614
- }
615
- }
616
- onResult(message) {
617
- const entry = this.agents.get(message.agentId);
618
- if (entry) {
619
- this.agents.set(message.agentId, {
620
- ...entry,
621
- status: "done",
622
- systemInfo: message.systemInfo,
623
- results: message.results
624
- });
625
- }
626
- this.checkCompletion();
627
- }
628
- onError(message) {
629
- const entry = this.agents.get(message.agentId);
630
- if (entry) {
631
- this.agents.set(message.agentId, {
632
- ...entry,
633
- status: "error",
634
- error: message.error
635
- });
636
- }
637
- this.checkCompletion();
638
- }
639
- checkCompletion() {
640
- if (!this.resolveTask || !this.activeTaskId) return;
641
- const runningAgents = [...this.agents.values()].filter(
642
- (a) => a.status === "running"
643
- );
644
- if (runningAgents.length > 0) return;
645
- const report = this.buildReport();
646
- this.resolveTask(report);
647
- this.resolveTask = null;
648
- this.rejectTask = null;
649
- this.activeTaskId = null;
650
- }
651
- buildReport() {
652
- const source = this.pendingConfig?.source ?? { type: "reference", images: "all" };
653
- const agentEntries = [...this.agents.values()];
654
- const allResults = [];
655
- for (const agent of agentEntries) {
656
- if (agent.results) {
657
- for (const result of agent.results) {
658
- allResults.push({ agentId: agent.agentId, result });
659
- }
660
- }
661
- }
662
- const leaderboard = allResults.sort((a, b) => a.result.latency.mean - b.result.latency.mean).map((entry, idx) => ({
663
- rank: idx + 1,
664
- agentId: entry.agentId,
665
- label: entry.result.label,
666
- meanMs: entry.result.latency.mean,
667
- fps: entry.result.fps,
668
- peakMemoryMB: entry.result.resources.peakMemoryMB,
669
- accuracy: entry.result.accuracy
670
- }));
671
- return {
672
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
673
- source,
674
- agents: agentEntries.map((a) => ({
675
- agentId: a.agentId,
676
- status: a.status === "done" ? "completed" : a.status === "error" ? "failed" : a.status === "disconnected" ? "disconnected" : "partial",
677
- systemInfo: a.systemInfo ?? {
678
- os: "unknown",
679
- arch: "unknown",
680
- cpuModel: "unknown",
681
- cpuCores: 0,
682
- totalMemoryMB: 0,
683
- nodeVersion: "unknown"
684
- },
685
- results: a.results ?? [],
686
- error: a.error,
687
- completedIterations: a.completedIterations
688
- })),
689
- leaderboard
690
- };
691
- }
692
- };
693
-
694
- // src/distributed/benchmark-agent.ts
695
- var BenchmarkAgent = class {
696
- constructor(agentId, send, inferenceFactory) {
697
- this.send = send;
698
- this.inferenceFactory = inferenceFactory;
699
- this.agentId = agentId;
700
- this.systemInfo = collectSystemInfo();
701
- }
702
- agentId;
703
- systemInfo;
704
- cancelRequested = false;
705
- /** Send hello to the hub — call this on WS connect */
706
- hello() {
707
- this.send({
708
- type: "agent.hello",
709
- agentId: this.agentId,
710
- systemInfo: this.systemInfo
711
- });
712
- }
713
- /** Handle incoming message from the hub */
714
- handleMessage(message) {
715
- switch (message.type) {
716
- case "benchmark.start":
717
- void this.onStart(message);
718
- break;
719
- case "benchmark.cancel":
720
- this.onCancel(message);
721
- break;
722
- default:
723
- break;
724
- }
725
- }
726
- async onStart(message) {
727
- this.cancelRequested = false;
728
- const { taskId, config } = message;
729
- try {
730
- const referenceImages = message.referenceImages.map((img) => ({
731
- frame: {
732
- data: img.data,
733
- format: "jpeg",
734
- width: img.width,
735
- height: img.height,
736
- timestamp: Date.now()
737
- },
738
- groundTruth: { image: img.name, annotations: [] }
739
- }));
740
- const firstTarget = config.targets[0];
741
- if (!firstTarget) {
742
- throw new Error("No benchmark targets specified");
743
- }
744
- const runInference = this.inferenceFactory(firstTarget.addonId, firstTarget.config);
745
- const runner = new BenchmarkRunner({
746
- runInference,
747
- referenceImages,
748
- onProgress: (completed, total, currentTarget) => {
749
- if (this.cancelRequested) return;
750
- this.send({
751
- type: "benchmark.progress",
752
- taskId,
753
- agentId: this.agentId,
754
- completed,
755
- total,
756
- currentTarget
757
- });
758
- }
759
- });
760
- const results = await runner.runAll(config);
761
- if (!this.cancelRequested) {
762
- this.send({
763
- type: "benchmark.result",
764
- taskId,
765
- agentId: this.agentId,
766
- systemInfo: this.systemInfo,
767
- results
768
- });
769
- }
770
- } catch (err) {
771
- this.send({
772
- type: "benchmark.error",
773
- taskId,
774
- agentId: this.agentId,
775
- error: err instanceof Error ? err.message : String(err)
776
- });
777
- }
778
- }
779
- onCancel(message) {
780
- if (message.taskId === this.activeTaskId) {
781
- this.cancelRequested = true;
782
- }
783
- }
784
- /** Returns the current task ID (last started), or null if idle */
785
- get activeTaskId() {
786
- return null;
787
- }
788
60
  };
789
61
  // Annotate the CommonJS export names for ESM import in node:
790
62
  0 && (module.exports = {
791
- BenchmarkAddon,
792
- BenchmarkAgent,
793
- BenchmarkHub,
794
- BenchmarkRunner,
795
- annotateImage,
796
- collectSystemInfo,
797
- computeAccuracy,
798
- computeFps,
799
- computeLatencyStats,
800
- iou
63
+ BenchmarkAddon
801
64
  });
802
65
  //# sourceMappingURL=index.js.map