import { FFN, DECODE_CAP, D_MODEL, VOCAB, SRC_CAP } from './constants.js'; // WebGPU default maxStorageBufferBindingSize — the conservative fallback when // a caller-built ctx has no granted-limits info. const DEFAULT_MAX_BINDING = 134217728; // SRC_CAP (the active model's max source length) is a multiple of 32, so the // bucket never exceeds the cap (applyModelConfig invariant). export function bucketFor(nTokens) { if (nTokens > SRC_CAP) { throw new Error(`input too long: ${nTokens} tokens (max ${SRC_CAP})`); } return Math.max(32, alignUp(nTokens, 32)); } // Per-row decode budget: short rows keep a small ring, long rows get the full // model cap. The old worker formula clamped at 192 "to stay under the engine // cap" — but split.js's own estimate (~1.08 tok/source-char) puts a 200-char // row at ~215 output tokens, so long TYPED rows lost their tails while the // same text as a file (packed passes maxNewTokens=DECODE_CAP) decoded fully. // EOS early-stop means rows that finish sooner never pay the larger cap, and // maxBatchForLimits below already budgets KV at DECODE_CAP per row. export function maxNewTokensFor(chars) { return Math.max(24, Math.min(DECODE_CAP, Math.ceil(chars * 3.2) + 12)); } // Largest batch B that fits the device's storage-binding limit at bucketed // source length S, across every per-batch-row allocation the engine makes. // The per-row ceiling is the max of: // - encoder ffnTmp activation [B·S, FFN=1792] → S·1792·dtypeBytes // - decode KV cache per buffer [B, 224, 448] → 224·448·dtypeBytes // (dominates encoder ffnTmp when S < 56, i.e. the S=32 bucket) // - f32 logits [B, VOCAB=24000] → 24000·4 (dtype-independent) // ctx.limits comes from initDevice(); without it the WebGPU default (128 MiB) // is assumed. export function maxBatchForLimits(ctx, S, dtypeBytes) { if (!Number.isFinite(S) || S <= 0) throw new Error(`maxBatchForLimits: bad S=${S}`); if (!Number.isFinite(dtypeBytes) || dtypeBytes <= 0) { throw new Error(`maxBatchForLimits: bad dtypeBytes=${dtypeBytes}`); } const limit = ctx?.limits?.maxStorageBufferBindingSize ?? DEFAULT_MAX_BINDING; const perRow = Math.max(S * FFN * dtypeBytes, DECODE_CAP * D_MODEL * dtypeBytes, VOCAB * 4); return Math.max(1, Math.floor(limit / perRow)); } export function alignUp(n, a) { return Math.ceil(n / a) * a; }