@mlbottleneck/engine 0.4.1 → 0.5.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.
@@ -1,4 +1,4 @@
1
- /*! ML Bottleneck engine v0.4.1 | https://mlbottleneck.com | MIT */
1
+ /*! ML Bottleneck engine v0.5.1 | https://mlbottleneck.com | MIT */
2
2
  // Generated by scripts/build-sdk.mjs from engine.js and sdk/api.js. Do not edit.
3
3
  (function (root, factory) {
4
4
  if (typeof define === 'function' && define.amd) {
@@ -32,13 +32,22 @@
32
32
  }
33
33
  }
34
34
 
35
- // Benchmark evidence (Localmaxxing gold rows) used for peer calibration.
36
- const ENGINE_EVIDENCE = { goldCases: [] };
35
+ // Benchmark evidence. Localmaxxing gold rows calibrate the engine (peer
36
+ // correction, optimized target). Lab rows (data/lab-evidence.json, the
37
+ // author's neural.download Intel lab) never calibrate it: they are shown
38
+ // beside a plan as measured references — stock / lab-baseline rows can be
39
+ // the nearest measured run, tuned rows show what a tuned stack reached.
40
+ const ENGINE_EVIDENCE = { goldCases: [], labCases: [] };
37
41
  let goldValidationCache = null;
42
+ let labEvidenceCache = null;
43
+ let labValidationCache = null;
38
44
 
39
45
  function setEngineEvidence(snapshot) {
40
46
  ENGINE_EVIDENCE.goldCases = Array.isArray(snapshot?.goldCases) ? snapshot.goldCases : [];
47
+ ENGINE_EVIDENCE.labCases = Array.isArray(snapshot?.labCases) ? snapshot.labCases : [];
41
48
  goldValidationCache = null;
49
+ labEvidenceCache = null;
50
+ labValidationCache = null;
42
51
  }
43
52
 
44
53
  // Nominal storage bytes per weight for each quantization family. Real
@@ -7366,6 +7375,244 @@
7366
7375
  }
7367
7376
 
7368
7377
 
7378
+ // ---- Lab evidence (neural.download) ------------------------------------
7379
+ const LAB_EVIDENCE_ORIGIN = 'neural.download';
7380
+ const TENSOR_CAPABLE_RUNTIMES = ['vllm', 'sglang', 'tensorrt_llm'];
7381
+
7382
+ // One measured lab point in the shape the gold helpers understand. Depth
7383
+ // sweeps and speculation ladders expand into one row per point so a plan
7384
+ // at 32K depth finds the 32K measurement, not the 128-token one.
7385
+ function normalizeLabEvidenceRow(row, overrides = {}) {
7386
+ const preset = MODEL_PRESETS[row.presetKey];
7387
+ const template = DEVICE_TEMPLATES[row.hardwareTemplate];
7388
+ if (!preset || !template) return null;
7389
+ const format = getQuantFormat(row.quantization);
7390
+ const quantKey = format ? format.family : String(row.quantization || 'q4').toLowerCase();
7391
+ const promptTokens = Number.isFinite(overrides.promptTokens) ? overrides.promptTokens : (Number.isFinite(row.promptTokens) ? row.promptTokens : 128);
7392
+ const outputTokens = Number.isFinite(row.outputTokens) && row.outputTokens > 0 ? row.outputTokens : 128;
7393
+ const speculation = overrides.speculation !== undefined ? overrides.speculation : (row.speculation || null);
7394
+ const observedTokS = Number.isFinite(overrides.observedTokS) ? overrides.observedTokS : row.observedTokS;
7395
+ if (!Number.isFinite(observedTokS) || observedTokS <= 0) return null;
7396
+ const batchSize = Math.max(1, Number(overrides.batchSize) || 1);
7397
+ const deviceCount = Math.max(1, Number(row.deviceCount) || 1);
7398
+ const runtime = FRAMEWORK_PROFILES[row.runtimeKey];
7399
+ const strategy = row.strategy || (deviceCount > 1 ? (TENSOR_CAPABLE_RUNTIMES.includes(row.runtimeKey) ? 'tensor' : 'pipeline') : 'pipeline');
7400
+ return {
7401
+ id: overrides.id || row.id,
7402
+ origin: LAB_EVIDENCE_ORIGIN,
7403
+ isLab: true,
7404
+ stack: row.stack || 'stock',
7405
+ source: row.url || '',
7406
+ url: row.url || '',
7407
+ note: row.note || '',
7408
+ model: preset.label || row.presetKey,
7409
+ hfId: preset.hfId || '',
7410
+ presetKey: row.presetKey,
7411
+ hardware: template.name || row.hardwareTemplate,
7412
+ hardwareTemplate: row.hardwareTemplate,
7413
+ deviceCount,
7414
+ runtimeKey: row.runtimeKey,
7415
+ engine: runtime ? runtime.label : row.runtimeKey,
7416
+ quantization: row.quantization || quantKey,
7417
+ quantKey,
7418
+ strategy,
7419
+ speculation,
7420
+ promptTokens,
7421
+ outputTokens,
7422
+ contextLength: promptTokens + outputTokens,
7423
+ decodeContextTokens: promptTokens + outputTokens / 2,
7424
+ // Per-request decode rate (one sequence's tokens per second); the
7425
+ // aggregate across a batched run is kept separately.
7426
+ observedTokS,
7427
+ batchSize,
7428
+ aggregateTokS: Number.isFinite(overrides.aggregateTokS) ? overrides.aggregateTokS : observedTokS * batchSize,
7429
+ prefillTokS: Number.isFinite(overrides.prefillTokS) ? overrides.prefillTokS : (Number.isFinite(row.prefillTokS) ? row.prefillTokS : null),
7430
+ reproducibility: 1
7431
+ };
7432
+ }
7433
+
7434
+ function getLabEvidenceRows() {
7435
+ if (labEvidenceCache) return labEvidenceCache;
7436
+ const rows = [];
7437
+ for (const row of ENGINE_EVIDENCE.labCases || []) {
7438
+ if (!row || typeof row !== 'object' || !row.id) continue;
7439
+ const base = normalizeLabEvidenceRow(row);
7440
+ if (base) rows.push(base);
7441
+ for (const point of Array.isArray(row.depthSweep) ? row.depthSweep : []) {
7442
+ const swept = normalizeLabEvidenceRow(row, {
7443
+ id: `${row.id}@${point.promptTokens}`,
7444
+ promptTokens: point.promptTokens,
7445
+ observedTokS: point.decodeTokS,
7446
+ prefillTokS: point.prefillTokS
7447
+ });
7448
+ if (swept) rows.push(swept);
7449
+ }
7450
+ for (const rung of Array.isArray(row.speculationLadder) ? row.speculationLadder : []) {
7451
+ const stepped = normalizeLabEvidenceRow(row, {
7452
+ id: `${row.id}@${rung.method}${rung.tokens}`,
7453
+ speculation: { method: rung.method, tokens: rung.tokens },
7454
+ observedTokS: rung.observedTokS
7455
+ });
7456
+ if (stepped) rows.push(stepped);
7457
+ }
7458
+ // Concurrency sweeps: { users, perUserTokS, aggregateTokS } per level
7459
+ // (llama-batched-bench -npl / vllm bench serve --max-concurrency).
7460
+ for (const level of Array.isArray(row.concurrencySweep) ? row.concurrencySweep : []) {
7461
+ const users = Math.max(1, Number(level.users) || 1);
7462
+ const perUser = Number.isFinite(level.perUserTokS) ? level.perUserTokS : (Number.isFinite(level.aggregateTokS) ? level.aggregateTokS / users : NaN);
7463
+ const batched = normalizeLabEvidenceRow(row, {
7464
+ id: `${row.id}@u${users}`,
7465
+ batchSize: users,
7466
+ observedTokS: perUser,
7467
+ aggregateTokS: Number.isFinite(level.aggregateTokS) ? level.aggregateTokS : perUser * users
7468
+ });
7469
+ if (batched) rows.push(batched);
7470
+ }
7471
+ }
7472
+ labEvidenceCache = rows;
7473
+ return rows;
7474
+ }
7475
+
7476
+ // Rows that can stand in as the plan's "nearest measured" run: community
7477
+ // gold runs plus the lab's stock / baseline rows without speculation. A
7478
+ // tuned stack is not a stock reference.
7479
+ function getMeasuredReferenceRows() {
7480
+ return getGoldValidationRows().concat(getLabEvidenceRows().filter(row => row.stack !== 'tuned' && !row.speculation));
7481
+ }
7482
+
7483
+ function describeSameSetup(target, row) {
7484
+ return row.runtimeKey === target.runtimeKey && row.quantKey === target.quantKey &&
7485
+ (row.deviceCount || 1) === (target.deviceCount || 1);
7486
+ }
7487
+
7488
+ // A reference run must serve the same number of concurrent requests as
7489
+ // the plan: per-request decode at 16 users says nothing about one user.
7490
+ function sameBatch(target, row) {
7491
+ return (row.batchSize || 1) === (target.batchSize || 1);
7492
+ }
7493
+
7494
+ function depthDistance(target, row) {
7495
+ const targetDepth = Math.max(64, Number.isFinite(target.decodeContextTokens) ? target.decodeContextTokens : (target.contextLength || 64));
7496
+ const rowDepth = Math.max(64, Number.isFinite(row.decodeContextTokens) ? row.decodeContextTokens : (row.contextLength || 64));
7497
+ return Math.abs(Math.log(targetDepth / rowDepth));
7498
+ }
7499
+
7500
+ // Closest measured run for the ladder: same preset and hardware template
7501
+ // are required; runtime, quantization, device count, and decode depth
7502
+ // refine the choice. Community runs and lab stock rows compete equally.
7503
+ function findNearestMeasuredRun(target) {
7504
+ if (!target?.presetKey || !target.hardwareTemplate) return null;
7505
+ const candidates = getMeasuredReferenceRows()
7506
+ .filter(row => row.presetKey === target.presetKey && row.hardwareTemplate === target.hardwareTemplate && sameBatch(target, row))
7507
+ .map(row => ({ row, score: getGoldSimilarity(target, row) }))
7508
+ .sort((a, b) => b.score - a.score ||
7509
+ depthDistance(target, a.row) - depthDistance(target, b.row) ||
7510
+ (b.row.reproducibility || 0) - (a.row.reproducibility || 0));
7511
+ const best = candidates[0];
7512
+ if (!best) return null;
7513
+ return { ...best.row, sameSetup: describeSameSetup(target, best.row), similarity: best.score };
7514
+ }
7515
+
7516
+ // The tuned lab result closest to this plan on the same machine (same
7517
+ // hardware template and device count — a different card count is a
7518
+ // different roofline); runtime, quantization, and depth refine the
7519
+ // choice and ties go to the faster run. Labeled as tuned, never used as
7520
+ // a stock reference.
7521
+ function findLabTunedRun(target) {
7522
+ if (!target?.presetKey || !target.hardwareTemplate) return null;
7523
+ const candidates = getLabEvidenceRows()
7524
+ .filter(row => row.stack === 'tuned' && row.presetKey === target.presetKey &&
7525
+ row.hardwareTemplate === target.hardwareTemplate && (row.deviceCount || 1) === (target.deviceCount || 1) && sameBatch(target, row))
7526
+ .map(row => ({ row, score: getGoldSimilarity(target, row) }))
7527
+ .sort((a, b) => b.score - a.score ||
7528
+ depthDistance(target, a.row) - depthDistance(target, b.row) ||
7529
+ b.row.observedTokS - a.row.observedTokS);
7530
+ const best = candidates[0];
7531
+ if (!best) return null;
7532
+ return { ...best.row, sameSetup: describeSameSetup(target, best.row), similarity: best.score };
7533
+ }
7534
+
7535
+ function buildEvidenceTarget(modelConfig, devicesArray, metrics = null) {
7536
+ return {
7537
+ hfId: modelConfig.hfId,
7538
+ presetKey: modelConfig.modelPreset,
7539
+ hardwareTemplate: devicesArray[0]?.template || '',
7540
+ runtimeKey: getFrameworkProfile(modelConfig, devicesArray).key,
7541
+ quantKey: modelConfig.quantizationType,
7542
+ quantization: modelConfig.quantFormat || '',
7543
+ deviceCount: devicesArray.length,
7544
+ contextLength: modelConfig.seqLength,
7545
+ decodeContextTokens: getDecodeContextTokens(modelConfig),
7546
+ hasOverflow: Array.isArray(metrics) ? metrics.some(metric => metric.hasOverflow) : false,
7547
+ batchSize: modelConfig.batchSize || 1
7548
+ };
7549
+ }
7550
+
7551
+ // The engine's view of one lab row in the row's own setup (speculation
7552
+ // included when the row used it): stock projection, optimized target, and
7553
+ // physical roofline. Stock rows show how the engine tracks the lab's
7554
+ // hardware; tuned rows show how much of the optimized target a tuned
7555
+ // stack actually reached.
7556
+ function calculateLabCaseProjection(row) {
7557
+ const preset = MODEL_PRESETS[row.presetKey];
7558
+ const template = DEVICE_TEMPLATES[row.hardwareTemplate];
7559
+ if (!preset || !template || !DTYPE_SIZES[row.quantKey]) return null;
7560
+ const devices = Array.from({ length: row.deviceCount }, (_, index) => ({
7561
+ id: index + 1,
7562
+ template: row.hardwareTemplate,
7563
+ name: `${template.name || row.hardwareTemplate}${row.deviceCount > 1 ? ` #${index + 1}` : ''}`,
7564
+ ...JSON.parse(JSON.stringify(template))
7565
+ }));
7566
+ const strategy = row.strategy;
7567
+ const spec = row.speculation;
7568
+ const modelConfig = normalizeModelConfig({
7569
+ ...preset,
7570
+ modelPreset: row.presetKey,
7571
+ quantizationType: row.quantKey,
7572
+ quantFormat: getQuantFormat(row.quantization) ? row.quantization : '',
7573
+ runtimeFramework: row.runtimeKey,
7574
+ parallelismStrategy: strategy,
7575
+ optimizationMode: spec ? 'speculative' : 'none',
7576
+ specMethod: spec ? spec.method : 'mtp',
7577
+ specTokens: spec ? spec.tokens : null,
7578
+ specAcceptance: null,
7579
+ specDraftRatio: spec && Number.isFinite(spec.draftRatio) ? spec.draftRatio : null,
7580
+ kvCacheCompression: 'none',
7581
+ batchSize: row.batchSize || 1,
7582
+ promptTokens: row.promptTokens,
7583
+ outputTokens: row.outputTokens,
7584
+ seqLength: row.promptTokens + row.outputTokens
7585
+ });
7586
+ const metrics = calculateMetricsForConfig(modelConfig, devices);
7587
+ const genericTokS = calculateSystemRateFromDeviceRates(metrics.map(metric => metric.decodeTokensPerSecond), strategy, row.batchSize || 1, devices, getSystemRateOptions(modelConfig));
7588
+ const prefillProjectedTokS = getSystemPrefillRateForMetrics(modelConfig, metrics, devices, strategy);
7589
+ const calibration = calculateCurrentCalibration(modelConfig, metrics, genericTokS, strategy, devices);
7590
+ if (!calibration || !Number.isFinite(genericTokS) || genericTokS <= 0) return null;
7591
+ return {
7592
+ ...row,
7593
+ genericTokS,
7594
+ prefillProjectedTokS,
7595
+ expectedTokS: calibration.expectedTokS,
7596
+ optimizedTokS: calibration.optimizedTokS,
7597
+ physicalTokS: calibration.physicalTokS,
7598
+ latencyBoundTokS: calibration.latencyBoundTokS,
7599
+ calibrationPeers: calibration.peers,
7600
+ confidence: calibration.confidence,
7601
+ observedToGeneric: row.observedTokS / genericTokS,
7602
+ observedToExpected: row.observedTokS / calibration.expectedTokS,
7603
+ observedToOptimized: row.observedTokS / calibration.optimizedTokS,
7604
+ observedToPhysical: row.observedTokS / calibration.physicalTokS,
7605
+ hasOverflow: metrics.some(metric => metric.hasOverflow)
7606
+ };
7607
+ }
7608
+
7609
+ function getLabValidationRows() {
7610
+ if (labValidationCache) return labValidationCache;
7611
+ labValidationCache = getLabEvidenceRows().map(calculateLabCaseProjection).filter(Boolean);
7612
+ return labValidationCache;
7613
+ }
7614
+
7615
+
7369
7616
  function calculateCurrentCalibration(modelConfig, metrics, genericSystemRate, strategy, devicesArray = null) {
7370
7617
  const devices = devicesArray || defaultDevices();
7371
7618
  if (!metrics?.length || !Number.isFinite(genericSystemRate)) return null;
@@ -7678,6 +7925,7 @@
7678
7925
  }
7679
7926
  const power = calculatePowerAndCost(devices, aggregateDecode, metrics, request.usage || {});
7680
7927
  const speculationActive = Boolean(primary?.speculation && primary.speculationMultiplier > 1);
7928
+ const evidenceTarget = buildEvidenceTarget(config, devices, metrics);
7681
7929
  return {
7682
7930
  fits,
7683
7931
  strategy: { key: strategy, label: strategy, reasoning: strategyInfo?.reasoning || null, auto: Boolean(strategyInfo) },
@@ -7711,6 +7959,10 @@
7711
7959
  kvCacheGB: sdkRound(metrics.reduce((sum, metric) => sum + (metric.residentKvCacheGB || 0), 0), 2),
7712
7960
  availableGB: sdkRound(devices.reduce((sum, device) => sum + (parseFloat(device.memoryGB) || 0), 0), 1)
7713
7961
  },
7962
+ measured: {
7963
+ nearest: sdkSummarizeMeasuredRun(findNearestMeasuredRun(evidenceTarget)),
7964
+ labTuned: sdkSummarizeMeasuredRun(findLabTunedRun(evidenceTarget))
7965
+ },
7714
7966
  bottleneck: primary?.decodeTimeBreakdown?.dominant || null,
7715
7967
  power: power ? { watts: sdkRound(power.actualPowerWatts, 0), tdpWatts: sdkRound(power.totalTDP, 0), costPerDay: sdkRound(power.dailyCost, 3), costPer1KTokens: sdkRound(power.costPer1KTokens, 5) } : null,
7716
7968
  devices: metrics.map((metric, index) => sdkSummarizeDevice(metric, devices[index])),
@@ -7740,6 +7992,29 @@
7740
7992
  };
7741
7993
  }
7742
7994
 
7995
+ // A measured reference run (community gold row or neural.download lab row)
7996
+ // in a stable shape; null when nothing on the same model + hardware exists.
7997
+ function sdkSummarizeMeasuredRun(row) {
7998
+ if (!row) return null;
7999
+ return {
8000
+ tokensPerSecond: sdkRound(row.observedTokS, 2),
8001
+ origin: row.isLab ? 'lab' : 'community',
8002
+ stack: row.isLab ? row.stack : 'stock',
8003
+ model: row.model,
8004
+ hardware: row.hardware,
8005
+ deviceCount: row.deviceCount || 1,
8006
+ runtime: row.runtimeKey,
8007
+ quantization: row.quantization || row.quantKey,
8008
+ depthTokens: Math.round(row.decodeContextTokens || row.contextLength || 0),
8009
+ concurrency: row.batchSize || 1,
8010
+ aggregateTokensPerSecond: sdkRound(row.aggregateTokS || row.observedTokS * (row.batchSize || 1), 2),
8011
+ speculation: row.speculation ? { method: row.speculation.method, tokens: row.speculation.tokens ?? null } : null,
8012
+ sameSetup: Boolean(row.sameSetup),
8013
+ url: row.source || row.url || null,
8014
+ note: row.note || null
8015
+ };
8016
+ }
8017
+
7743
8018
  function listModels() {
7744
8019
  return Object.entries(MODEL_PRESETS).map(([key, preset]) => ({
7745
8020
  key,
@@ -7801,10 +8076,10 @@
7801
8076
 
7802
8077
  if (options.snapshot) setEngineEvidence(options.snapshot);
7803
8078
  const api = createApi();
7804
- api.version = "0.4.1";
8079
+ api.version = "0.5.1";
7805
8080
  api.evidenceGeneratedAt = options.snapshot?.generatedAt || null;
7806
8081
  return api;
7807
8082
  }
7808
8083
 
7809
- return { createEngine, version: "0.4.1" };
8084
+ return { createEngine, version: "0.5.1" };
7810
8085
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mlbottleneck/engine",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Physics-based LLM inference planner: decode/prefill tokens per second, memory fit, multi-GPU strategy, and speculative-decoding gains for any model on any hardware, calibrated on community benchmarks.",
5
5
  "keywords": [
6
6
  "llm",