@localmode/bench 0.1.0 → 0.3.0

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.d.cts CHANGED
@@ -5,9 +5,9 @@
5
5
  * published statistics can be recomputed and audited server-side.
6
6
  */
7
7
  /** Protocol identifier embedded in every result. Bump only with a spec change. */
8
- declare const BENCH_PROTOCOL_VERSION = "localmode-bench/1";
8
+ declare const BENCH_PROTOCOL_VERSION = "localmode-bench/2";
9
9
  /** Result JSON schema version (independent of the protocol semantics version). */
10
- declare const BENCH_SCHEMA_VERSION = 1;
10
+ declare const BENCH_SCHEMA_VERSION = 2;
11
11
  /** Benchmark suite presets. `custom` = user-picked cells. */
12
12
  type BenchSuiteId = 'quick' | 'standard' | 'thorough' | 'custom';
13
13
  /**
@@ -69,6 +69,12 @@ interface BenchModelRef {
69
69
  /** Direct model URL when applicable (wllama GGUF, litert). */
70
70
  url?: string;
71
71
  requiresWebGPU?: boolean;
72
+ /**
73
+ * Appended to the quality-lane instruction line for every runtime of this
74
+ * pairing (e.g. " /no_think" to hold Qwen3 in non-thinking mode). Uniform
75
+ * across runtimes by construction, so fidelity comparisons stay valid.
76
+ */
77
+ qualityPromptSuffix?: string;
72
78
  }
73
79
  /** One recorded stream chunk: wall-clock timestamp + delta length in chars. */
74
80
  interface BenchChunk {
@@ -131,6 +137,8 @@ interface MemorySample {
131
137
  baseline?: number;
132
138
  postLoad?: number;
133
139
  postRun?: number;
140
+ /** Bytes at the moment a cell errored (load or workload failure). */
141
+ atError?: number;
134
142
  api: 'uaSpecific' | 'legacyHeap' | 'none';
135
143
  }
136
144
  /** Quality lane outcome attached to a cell. */
@@ -141,6 +149,13 @@ interface QualityResult {
141
149
  n: number;
142
150
  /** Per-item correctness or per-pair cosine, for auditability. */
143
151
  details?: number[];
152
+ /**
153
+ * Raw model outputs per item (MMLU lanes; capped at 400 chars each) so the
154
+ * score is recomputable server-side and parse failures are auditable.
155
+ */
156
+ outputs?: string[];
157
+ /** Fraction of items whose answer was parseable (MMLU lanes). */
158
+ parseRate?: number;
144
159
  }
145
160
  type BenchCellStatus = 'ok' | 'invalid' | 'error' | 'skipped';
146
161
  /** One benchmark cell: (runtime x model x workload) with its raw iterations. */
@@ -162,9 +177,11 @@ interface BenchCellResult {
162
177
  quality?: QualityResult;
163
178
  status: BenchCellStatus;
164
179
  invalidReasons?: string[];
180
+ /** Error that ended the cell; `cause` carries the wrapped provider error's message when present. */
165
181
  error?: {
166
182
  name: string;
167
183
  message: string;
184
+ cause?: string;
168
185
  };
169
186
  }
170
187
  /** Trace events global to the suite run (validity accounting). */
@@ -182,6 +199,14 @@ interface BrowserInfo {
182
199
  brand: string;
183
200
  version: string;
184
201
  }>;
202
+ /** Rendering engine, derived from the UA (Blink / Gecko / WebKit). */
203
+ engine?: 'Blink' | 'Gecko' | 'WebKit' | 'unknown';
204
+ /** `navigator.vendor` (e.g. "Google Inc.", "Apple Computer, Inc."). */
205
+ vendor?: string;
206
+ /** `navigator.webdriver` — true under browser automation. */
207
+ webdriver?: boolean;
208
+ /** `navigator.pdfViewerEnabled`, a cheap headless/kiosk signal. */
209
+ pdfViewerEnabled?: boolean;
185
210
  }
186
211
  /** OS identification. Version is 'unknown-frozen' on engines with frozen UAs. */
187
212
  interface OSInfo {
@@ -189,7 +214,30 @@ interface OSInfo {
189
214
  version: string;
190
215
  architecture?: string;
191
216
  bitness?: string;
217
+ /** Device model from UA-CH (Android only; empty elsewhere by spec). */
192
218
  model?: string;
219
+ /** Whether the OS is running in a WoW64-style emulation layer (UA-CH `wow64`). */
220
+ wow64?: boolean;
221
+ /** `navigator.platform` (legacy, frozen but still informative: "MacIntel", "Win32", "iPhone"). */
222
+ navigatorPlatform?: string;
223
+ }
224
+ /** Form-factor classification, derived from UA-CH form factors, the UA, and touch. */
225
+ type DeviceType = 'phone' | 'tablet' | 'desktop' | 'xr' | 'tv' | 'unknown';
226
+ /** Device / form-factor signals. */
227
+ interface DeviceInfo {
228
+ type: DeviceType;
229
+ /** UA-CH `mobile` bit (Chromium) or a UA-derived guess elsewhere. */
230
+ mobile: boolean;
231
+ /** UA-CH `formFactors` (Chromium 125+): Desktop / Mobile / Tablet / XR / EInk / Watch / Automotive. */
232
+ formFactors?: string[];
233
+ /** `navigator.maxTouchPoints`. */
234
+ maxTouchPoints: number;
235
+ /** `(pointer: coarse)` media query — primary input is a touch surface. */
236
+ pointerCoarse?: boolean;
237
+ /** `(hover: none)` media query — no hover-capable primary input. */
238
+ hoverNone?: boolean;
239
+ /** Standalone / fullscreen display mode (installed PWA or kiosk). */
240
+ displayMode?: string;
193
241
  }
194
242
  /** WebGPU adapter identity + selected limits (all fields may be empty strings). */
195
243
  interface GPUInfo {
@@ -198,11 +246,155 @@ interface GPUInfo {
198
246
  architecture?: string;
199
247
  device?: string;
200
248
  description?: string;
249
+ /** Adapter-reported subgroup sizes (Chromium 130+ `info.subgroupMinSize/MaxSize`). */
250
+ subgroupMinSize?: number;
251
+ subgroupMaxSize?: number;
201
252
  isFallbackAdapter?: boolean;
202
253
  features?: string[];
203
254
  limits?: Record<string, number>;
255
+ /** `navigator.gpu.getPreferredCanvasFormat()`. */
256
+ preferredCanvasFormat?: string;
257
+ /** WGSL language features the implementation reports (`navigator.gpu.wgslLanguageFeatures`). */
258
+ wgslLanguageFeatures?: string[];
259
+ }
260
+ /** WebGL identity + capacity signals (a second, older GPU identity channel). */
261
+ interface WebGLInfo {
262
+ /** Best context available: 'webgl2', 'webgl', or null when neither creates. */
263
+ contextKind: 'webgl2' | 'webgl' | null;
264
+ /** UNMASKED_VENDOR_WEBGL when the debug extension exists, else VENDOR. */
265
+ vendor?: string;
266
+ /** UNMASKED_RENDERER_WEBGL when the debug extension exists, else RENDERER. */
267
+ renderer?: string;
268
+ version?: string;
269
+ shadingLanguageVersion?: string;
270
+ maxTextureSize?: number;
271
+ maxRenderbufferSize?: number;
272
+ maxVertexUniformVectors?: number;
273
+ maxFragmentUniformVectors?: number;
274
+ /** Number of extensions exposed (identity signal without shipping the whole list). */
275
+ extensionCount?: number;
276
+ /** Whether the renderer string names a software rasterizer (SwiftShader, llvmpipe, ...). */
277
+ softwareRenderer?: boolean;
278
+ }
279
+ /**
280
+ * WebAssembly proposal support, each probed by validating a canonical module
281
+ * (the same byte sequences `wasm-feature-detect` uses). Missing keys mean the
282
+ * probe itself failed, not that the feature is absent.
283
+ */
284
+ interface WasmFeatureSupport {
285
+ simd: boolean;
286
+ relaxedSimd: boolean;
287
+ threads: boolean;
288
+ bulkMemory: boolean;
289
+ exceptions: boolean;
290
+ /** Exception handling with `exnref` (the newer, standardized form). */
291
+ exceptionsFinal: boolean;
292
+ extendedConst: boolean;
293
+ gc: boolean;
294
+ memory64: boolean;
295
+ multiMemory: boolean;
296
+ multiValue: boolean;
297
+ mutableGlobals: boolean;
298
+ referenceTypes: boolean;
299
+ saturatedFloatToInt: boolean;
300
+ signExtensions: boolean;
301
+ tailCall: boolean;
302
+ typedFunctionReferences: boolean;
303
+ /** 128-bit wide arithmetic (`i64.add128` family). */
304
+ wideArithmetic: boolean;
305
+ /** JavaScript Promise Integration (`WebAssembly.Suspending`). */
306
+ jspi: boolean;
307
+ /** Type reflection (`WebAssembly.Function`). */
308
+ typeReflection: boolean;
309
+ /** `WebAssembly.compileStreaming` exists. */
310
+ streamingCompilation: boolean;
311
+ /** JS String Builtins (`js-string` import module), probed via the imports/builtins option. */
312
+ jsStringBuiltins: boolean;
313
+ /** Type-level `WebAssembly.Memory` growth limit reachable in this engine (pages of 64 KiB). */
314
+ maxMemoryPages?: number;
315
+ }
316
+ /**
317
+ * Availability of the browser APIs the runtimes and the paper care about.
318
+ * Each entry is a plain presence check (the feature exists on this page),
319
+ * not a functional test. Chrome Built-in AI is reported by its
320
+ * `availability()` string where the API exists.
321
+ */
322
+ interface APIAvailability {
323
+ webgpu: boolean;
324
+ webgl2: boolean;
325
+ webnn: boolean;
326
+ /** OPFS: `navigator.storage.getDirectory` exists AND resolved. */
327
+ opfs: boolean;
328
+ /** Result of `navigator.storage.persisted()` where supported. */
329
+ persistedStorage?: boolean;
330
+ indexedDB: boolean;
331
+ cacheApi: boolean;
332
+ serviceWorker: boolean;
333
+ webWorkers: boolean;
334
+ offscreenCanvas: boolean;
335
+ webLocks: boolean;
336
+ broadcastChannel: boolean;
337
+ wakeLock: boolean;
338
+ computePressure: boolean;
339
+ performanceMemory: boolean;
340
+ measureUserAgentSpecificMemory: boolean;
341
+ schedulerYield: boolean;
342
+ webCodecs: boolean;
343
+ audioWorklet: boolean;
344
+ mediaDevices: boolean;
345
+ webTransport: boolean;
346
+ /** Chrome Built-in AI (Gemini Nano) surfaces, by `availability()` verdict when reachable. */
347
+ promptApi?: string;
348
+ summarizerApi?: string;
349
+ translatorApi?: string;
350
+ languageDetectorApi?: string;
204
351
  }
205
- /** Full environment capture for a run. Clamped fields are labeled as such. */
352
+ /** Network Information API (Chromium + Android) snapshot at capture time. */
353
+ interface NetworkInfo {
354
+ supported: boolean;
355
+ effectiveType?: string;
356
+ /** Connection type (wifi / cellular / ethernet / ...) where the UA exposes it. */
357
+ type?: string;
358
+ downlinkMbps?: number;
359
+ rttMs?: number;
360
+ saveData?: boolean;
361
+ online?: boolean;
362
+ }
363
+ /** Display / viewport snapshot. */
364
+ interface DisplayInfo {
365
+ width: number;
366
+ height: number;
367
+ availWidth?: number;
368
+ availHeight?: number;
369
+ dpr: number;
370
+ colorDepth?: number;
371
+ orientation?: string;
372
+ viewportWidth?: number;
373
+ viewportHeight?: number;
374
+ /** `(dynamic-range: high)` media query. */
375
+ hdr?: boolean;
376
+ /** `(color-gamut: p3)` media query. */
377
+ wideGamut?: boolean;
378
+ /** `screen.isExtended` (Window Management API) — more than one display attached. */
379
+ isExtended?: boolean;
380
+ /** Reduced-motion / forced-colors preferences, cheap OS-level signals. */
381
+ prefersReducedMotion?: boolean;
382
+ prefersColorScheme?: 'light' | 'dark' | 'no-preference';
383
+ }
384
+ /** Locale / clock signals. */
385
+ interface LocaleInfo {
386
+ timeZone?: string;
387
+ /** Minutes offset from UTC at capture time (`Date#getTimezoneOffset`). */
388
+ timeZoneOffsetMinutes?: number;
389
+ locale?: string;
390
+ /** `Intl.DateTimeFormat().resolvedOptions().calendar`. */
391
+ calendar?: string;
392
+ }
393
+ /**
394
+ * Full environment capture for a run. Every field beyond the first block is
395
+ * additive and best-effort: a probe that fails records nothing for its key and
396
+ * never affects the others. Clamped or capped fields are labeled as such.
397
+ */
206
398
  interface EnvironmentCapture {
207
399
  capturedAt: string;
208
400
  browser: BrowserInfo;
@@ -214,23 +406,46 @@ interface EnvironmentCapture {
214
406
  /** navigator.deviceMemory (GB) — Chromium-only, capped at 8. */
215
407
  deviceMemoryGB: number | null;
216
408
  deviceMemoryCapped: boolean;
409
+ /** `performance.memory.jsHeapSizeLimit` (Chromium) — the V8 heap ceiling for this tab. */
410
+ jsHeapSizeLimitBytes?: number;
411
+ /** `performance.memory.usedJSHeapSize` at capture (the idle baseline). */
412
+ jsHeapUsedBytes?: number;
217
413
  };
218
414
  gpu: GPUInfo;
219
415
  /** WebGL renderer string, a secondary GPU identity signal. */
220
416
  webglRenderer: string | null;
417
+ /** Detailed WebGL identity + capacity (superset of `webglRenderer`). */
418
+ webgl?: WebGLInfo;
419
+ /**
420
+ * GPU model parsed from the WebGL renderer string (ANGLE unwrapped),
421
+ * e.g. "Apple M4", "NVIDIA GeForce RTX 4070", "Mali-G78 MP20". Absent when
422
+ * no WebGL context could be created.
423
+ */
424
+ gpuModel?: string;
221
425
  flags: {
222
426
  crossOriginIsolated: boolean;
223
427
  sharedArrayBuffer: boolean;
224
428
  wasmSimd: boolean;
429
+ /** `window.isSecureContext`. */
430
+ secureContext?: boolean;
431
+ /** Full WebAssembly proposal matrix (superset of `wasmSimd`). */
432
+ wasm?: WasmFeatureSupport;
225
433
  };
434
+ /** Presence checks for the APIs the runtimes depend on. */
435
+ apis?: APIAvailability;
436
+ device?: DeviceInfo;
226
437
  storage: {
227
438
  quotaBytes?: number;
228
439
  usageBytes?: number;
440
+ usageDetails?: Record<string, number>;
229
441
  } | null;
230
442
  power: {
231
443
  batterySupported: boolean;
232
444
  charging?: boolean;
233
445
  level?: number;
446
+ /** Seconds until full / empty (Infinity serialized as absent). */
447
+ chargingTimeSec?: number;
448
+ dischargingTimeSec?: number;
234
449
  };
235
450
  pressure: {
236
451
  supported: boolean;
@@ -243,7 +458,17 @@ interface EnvironmentCapture {
243
458
  height: number;
244
459
  dpr: number;
245
460
  } | null;
461
+ /** Detailed display snapshot (superset of `screen`). */
462
+ display?: DisplayInfo;
463
+ network?: NetworkInfo;
464
+ locale?: LocaleInfo;
246
465
  languages?: string[];
466
+ /** Raw `navigator.userAgent` — kept verbatim so future parsers can re-derive fields. */
467
+ userAgent?: string;
468
+ /** Page origin the run executed on (distinguishes production from local/staging). */
469
+ pageOrigin?: string;
470
+ /** `document.visibilityState` at capture; a hidden tab is throttled. */
471
+ visibilityState?: string;
247
472
  /** Free-text device self-report — displayed as "user-reported", never trusted. */
248
473
  userReportedDevice?: string;
249
474
  }
@@ -281,6 +506,16 @@ interface CellSummary {
281
506
  decodeChunksPerSec?: MetricSummary;
282
507
  prefillTokPerSecApprox?: MetricSummary;
283
508
  generatedChars?: MetricSummary;
509
+ /** Request wall time (startT → endT), all LLM lanes. */
510
+ totalMs?: MetricSummary;
511
+ /** End-to-end chars/s over the whole request (prefill + decode conflated). */
512
+ overallCharsPerSec?: MetricSummary;
513
+ /**
514
+ * False when the chunk trace is not genuinely incremental (single chunk, or
515
+ * the visible stream spans <20% of the request) — TTFT and decode metrics
516
+ * are then omitted because they would be timing artifacts, not measurements.
517
+ */
518
+ streamIncremental?: boolean;
284
519
  /** Embedding lanes. */
285
520
  singleLatencyMs?: MetricSummary;
286
521
  batchTextsPerSec?: MetricSummary;
@@ -288,19 +523,32 @@ interface CellSummary {
288
523
  loadMs?: number;
289
524
  loadCached?: boolean;
290
525
  qualityScore?: number;
526
+ /** Fraction of MMLU items whose answer parsed; a low value marks a format failure, not a fidelity one. */
527
+ qualityParseRate?: number;
291
528
  highVariance: boolean;
292
529
  }
530
+ /** Identity of the software that produced a run. */
531
+ interface HarnessInfo {
532
+ name: string;
533
+ version: string;
534
+ appVersion?: string;
535
+ /**
536
+ * Versions of the runtime packages bundled into the harness at build time
537
+ * (e.g. `{ "@huggingface/transformers": "4.2.0", "@wllama/wllama": "3.5.1" }`),
538
+ * keyed by npm package name. Per-cell `runtimeVersion` names the same value
539
+ * for the runtime that produced that cell.
540
+ */
541
+ runtimeVersions?: Record<string, string>;
542
+ /** Git commit of the harness build where the host exposes it. */
543
+ commit?: string;
544
+ }
293
545
  /** The unit of submission: one full suite run on one device. */
294
546
  interface BenchRunResult {
295
547
  protocol: typeof BENCH_PROTOCOL_VERSION;
296
548
  schemaVersion: typeof BENCH_SCHEMA_VERSION;
297
549
  runId: string;
298
550
  createdAt: string;
299
- harness: {
300
- name: string;
301
- version: string;
302
- appVersion?: string;
303
- };
551
+ harness: HarnessInfo;
304
552
  suite: BenchSuiteId;
305
553
  environment: EnvironmentCapture;
306
554
  fingerprint: FingerprintResult | null;
@@ -340,6 +588,14 @@ interface ValidationReport {
340
588
 
341
589
  /** Fixed generation budget for timed lanes (tg128, MLPerf-Client-sized). */
342
590
  declare const GENERATION_BUDGET = 128;
591
+ /**
592
+ * Minimum generated characters for a timed iteration to count as a decode
593
+ * measurement (part of the versioned protocol). An instruct model that emits
594
+ * EOS after a handful of characters - typically an untemplated prompt or a
595
+ * tokenizer mismatch - produces no decode phase to time; such iterations are
596
+ * gated `degenerate-output` and the cell is marked invalid rather than scored.
597
+ */
598
+ declare const MIN_GENERATED_CHARS = 16;
343
599
  /** LLM performance workloads. */
344
600
  declare const LLM_WORKLOADS: readonly LLMWorkloadSpec[];
345
601
  /** Embedding performance workloads. */
@@ -348,6 +604,25 @@ declare const EMBED_WORKLOADS: readonly EmbedWorkloadSpec[];
348
604
  declare const QUALITY_WORKLOADS: readonly QualityWorkloadSpec[];
349
605
  /** All workloads indexed by id. */
350
606
  declare const WORKLOADS_BY_ID: ReadonlyMap<string, LLMWorkloadSpec | EmbedWorkloadSpec | QualityWorkloadSpec>;
607
+ /**
608
+ * Fixed runtime execution order (part of the versioned protocol). This makes
609
+ * the order deterministic so runtime interleaving is not a confounder across
610
+ * runs; it is a reproducibility measure, not a correctness fix. (The
611
+ * Transformers.js ORT-web lanes fail during session creation regardless of
612
+ * where they run in the order - a separate open runtime/adapter issue.) The
613
+ * order runs the WASM-arena runtimes first and the multi-GB-heap runtimes last.
614
+ * Within a runtime, catalog order is preserved.
615
+ */
616
+ declare const RUNTIME_EXECUTION_ORDER: readonly BenchRuntimeId[];
617
+ /**
618
+ * Sort planned cells into the protocol execution order (stable within a
619
+ * runtime). The runner applies this itself; exported for hosts and tests.
620
+ */
621
+ declare function orderCells<T extends {
622
+ model: {
623
+ runtimeId: BenchRuntimeId;
624
+ };
625
+ }>(cells: readonly T[]): T[];
351
626
  /** Run policy for a suite (part of the versioned protocol). */
352
627
  interface RunPolicy {
353
628
  /** Untimed warmup generations per cell (absorbs shader compile/JIT). */
@@ -508,11 +783,7 @@ interface RunSuiteOptions {
508
783
  policy: RunPolicy;
509
784
  llmAdapters: ReadonlyMap<string, LLMRuntimeAdapter>;
510
785
  embedAdapters: ReadonlyMap<string, EmbeddingRuntimeAdapter>;
511
- harness: {
512
- name: string;
513
- version: string;
514
- appVersion?: string;
515
- };
786
+ harness: HarnessInfo;
516
787
  hooks?: RunnerHooks;
517
788
  abortSignal?: AbortSignal;
518
789
  /** Skip the fingerprint microbenchmark (tests only; submissions require it). */
@@ -539,6 +810,11 @@ declare function runBenchmarkSuite(options: RunSuiteOptions): Promise<BenchRunRe
539
810
  * provenance: UA Client Hints on Chromium; UA parsing elsewhere with the OS
540
811
  * version marked 'unknown-frozen' (UA strings are frozen by design on Gecko
541
812
  * and WebKit). Clamped fields (cores, deviceMemory) are labeled clamped.
813
+ *
814
+ * Everything a browser will disclose is recorded, whether or not the current
815
+ * analysis uses it: WebGPU + WebGL identity, the WebAssembly proposal matrix,
816
+ * API availability, form factor, display, network, locale, power. Every probe
817
+ * is individually guarded; a failing probe records nothing for its key.
542
818
  */
543
819
 
544
820
  /**
@@ -553,6 +829,64 @@ declare function runBenchmarkSuite(options: RunSuiteOptions): Promise<BenchRunRe
553
829
  declare function captureEnvironment(options?: {
554
830
  userReportedDevice?: string;
555
831
  }): Promise<EnvironmentCapture>;
832
+ /**
833
+ * UA-string fallback for browsers without UA Client Hints (every WebKit
834
+ * browser). Conservative: browser name + version token, platform, and the OS
835
+ * version only where the UA genuinely carries one.
836
+ *
837
+ * @example
838
+ * parseUserAgent('... CriOS/145.0.7632.72 Mobile/15E148 Safari/604.1').browser.name; // 'Chrome iOS'
839
+ */
840
+ declare function parseUserAgent(ua: string): {
841
+ browser: BrowserInfo;
842
+ os: OSInfo;
843
+ };
844
+ /**
845
+ * Rendering engine from the UA. Every iOS browser is WebKit regardless of its
846
+ * brand (App Store policy), so the iOS check comes first.
847
+ *
848
+ * @example
849
+ * detectEngine('... CriOS/145.0 ...'); // 'WebKit'
850
+ */
851
+ declare function detectEngine(ua: string): BrowserInfo['engine'];
852
+ /** Inputs to the form-factor derivation, each optional. */
853
+ interface DeviceTypeSignals {
854
+ ua: string;
855
+ maxTouchPoints: number;
856
+ /** UA-CH `formFactors` high-entropy hint (Chromium 125+). */
857
+ formFactors?: string[];
858
+ /** UA-CH `mobile` bit. */
859
+ mobile?: boolean;
860
+ }
861
+ /**
862
+ * Derive a form factor. UA-CH form factors win when present; otherwise the UA
863
+ * decides, with `maxTouchPoints` unmasking an iPad that reports itself as a
864
+ * Mac (iPadOS 13+ default) and separating Android tablets (no `Mobile` token)
865
+ * from phones.
866
+ *
867
+ * @example
868
+ * deriveDeviceType({ ua: navigator.userAgent, maxTouchPoints: navigator.maxTouchPoints });
869
+ */
870
+ declare function deriveDeviceType(signals: DeviceTypeSignals): DeviceType;
871
+ /**
872
+ * GPU model from a WebGL renderer string. ANGLE wraps the model as
873
+ * `ANGLE (<vendor>, <model>[ (0x...)] <backend...>, <api>)`; Metal adds a
874
+ * `ANGLE Metal Renderer: ` prefix. Native strings are returned as-is.
875
+ *
876
+ * @example
877
+ * parseGpuModel('ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)'); // 'Apple M4'
878
+ */
879
+ declare function parseGpuModel(renderer: string | null | undefined): string | undefined;
880
+ /**
881
+ * WebAssembly proposal support, probed the way `wasm-feature-detect` does:
882
+ * validate or compile the smallest module that uses each feature. The byte
883
+ * sequences are those of wasm-feature-detect 1.9.0, inlined so the harness
884
+ * keeps its zero-dependency contract.
885
+ *
886
+ * @example
887
+ * const wasm = await detectWasmFeatures(); // { simd: true, threads: true, ... }
888
+ */
889
+ declare function detectWasmFeatures(): Promise<WasmFeatureSupport>;
556
890
 
557
891
  /**
558
892
  * Memory sampling at protocol points (baseline / post-load / post-run).
@@ -704,19 +1038,40 @@ declare function spearman(a: readonly number[], b: readonly number[]): number;
704
1038
  */
705
1039
 
706
1040
  /** Version of the plausibility rule set (recorded alongside moderation). */
707
- declare const PLAUSIBILITY_RULES_VERSION = 1;
1041
+ declare const PLAUSIBILITY_RULES_VERSION = 2;
1042
+ /**
1043
+ * Minimum fraction of the request that the visible chunk stream must span for
1044
+ * the trace to count as incremental. LiteRT-LM's web surface delivers every
1045
+ * chunk in a terminal burst (~0.8ms of a 30s request), so TTFT/decode derived
1046
+ * from such a trace are timing artifacts; a real token stream spans most of
1047
+ * the request by construction.
1048
+ */
1049
+ declare const STREAM_COHERENCE_MIN_SPAN_RATIO = 0.2;
708
1050
  /**
709
1051
  * Structural validation of a submitted result. Returns human-readable errors
710
1052
  * (empty array = shape ok). Deliberately hand-rolled: this package is
711
1053
  * zero-dependency and the checks double as executable schema documentation.
712
1054
  */
713
1055
  declare function validateRunShape(value: unknown): string[];
1056
+ /**
1057
+ * True when an iteration's chunk trace is genuinely incremental: at least two
1058
+ * chunks, and the visible stream spans at least
1059
+ * `STREAM_COHERENCE_MIN_SPAN_RATIO` of the request wall time. Anything else
1060
+ * (a doGenerate fallback's single chunk, or a runtime that computes the whole
1061
+ * generation and flushes it in a terminal burst) carries no usable TTFT or
1062
+ * decode timing.
1063
+ */
1064
+ declare function isIncrementalStream(it: LLMIteration): boolean;
714
1065
  /**
715
1066
  * Recompute a cell's summary purely from its raw iteration traces.
716
1067
  * This function IS the metric definition:
717
1068
  * - TTFT = first non-empty chunk timestamp − startT
718
1069
  * - decode rate = (chars after first chunk) / (endT_lastChunk − t_firstChunk)
719
1070
  * - prefill tok/s ≈ approxPromptTokens / TTFT (approximate by construction)
1071
+ * - overall rate = total chars / (endT − startT), always derivable
1072
+ * TTFT/decode/prefill are derived only when EVERY iteration passes the
1073
+ * stream-coherence test (`streamIncremental`); overall rate and total wall
1074
+ * time are reported for all LLM lanes.
720
1075
  */
721
1076
  declare function summarizeCell(cell: BenchCellResult, highVarianceCv?: number, approxPromptTokens?: number): CellSummary;
722
1077
  /** Recompute all cell summaries for a run (workload prompt sizes looked up). */
@@ -784,11 +1139,15 @@ interface LeaderboardRow {
784
1139
  /** Median-of-medians metrics (only those applicable to the workload). */
785
1140
  ttftMs?: number;
786
1141
  decodeCharsPerSec?: number;
1142
+ /** End-to-end chars/s (prefill + decode); the only rate for lanes whose stream is not incremental. */
1143
+ overallCharsPerSec?: number;
787
1144
  singleLatencyMs?: number;
788
1145
  batchTextsPerSec?: number;
789
1146
  loadColdMs?: number;
790
1147
  loadWarmMs?: number;
791
1148
  qualityScore?: number;
1149
+ /** Median MMLU parse rate; below 1 the quality score is format-limited. */
1150
+ qualityParseRate?: number;
792
1151
  resolvedBackends: string[];
793
1152
  browsers: string[];
794
1153
  /** True when any contributing submission had a high-variance metric. */
@@ -843,26 +1202,44 @@ declare const TINY_MMLU: readonly MMLUItem[];
843
1202
  * model's training data. Always run at temperature 0, outside timed regions.
844
1203
  */
845
1204
 
1205
+ /**
1206
+ * Generation budget for MMLU items (part of the versioned protocol). Large
1207
+ * enough to absorb an empty Qwen3-style `<think></think>` block plus a
1208
+ * verbose "The answer is B." — the v1 budget of 8 truncated before any
1209
+ * parseable letter on thinking-mode builds, scoring fidelity as 0.
1210
+ */
1211
+ declare const MMLU_MAX_TOKENS = 48;
1212
+ /** Cap on each stored raw output (auditability without payload bloat). */
1213
+ declare const MMLU_OUTPUT_CAP = 400;
846
1214
  /** Fixed MCQ prompt template (part of the versioned protocol). */
847
- declare function formatMMLUPrompt(item: (typeof TINY_MMLU)[number]): string;
1215
+ declare function formatMMLUPrompt(item: (typeof TINY_MMLU)[number], promptSuffix?: string): string;
848
1216
  /**
849
- * Parse the answer letter from a model response. Accepts "B", "B.", "(B)",
850
- * "Answer: B", or a response beginning with the exact choice text.
1217
+ * Parse the answer letter from a model response. Reasoning blocks are
1218
+ * stripped first; markdown emphasis around the letter is ignored. Accepts
1219
+ * "B", "B.", "(B)", "**B**", "Answer: B", "Option C", "choice (B)", or a
1220
+ * response beginning with the exact choice text. Letters are matched
1221
+ * case-sensitively after a keyword so the article "a" ("the answer is a
1222
+ * bit unclear") is never read as answer A; a lowercase letter counts only
1223
+ * when it is the whole reply or leads it as "b." / "b)".
851
1224
  *
852
1225
  * @returns 0-3, or null when no unambiguous answer is present.
853
1226
  */
854
1227
  declare function parseMMLUAnswer(response: string, choices: readonly string[]): number | null;
855
1228
  /**
856
1229
  * Run the tinyMMLU fidelity task on a language model (temperature 0,
857
- * `maxTokens` 8, greedy answer parsing). Unparseable answers count as wrong.
1230
+ * `MMLU_MAX_TOKENS` budget, greedy answer parsing). Unparseable answers count
1231
+ * as wrong; raw outputs and the parse rate are recorded so the score is
1232
+ * auditable and recomputable server-side.
858
1233
  *
859
1234
  * @param model - Any structurally-compatible LanguageModel.
860
- * @param items - Number of items from the 100-item set (25 or 100 in v1).
861
- * @returns Accuracy in [0,1] with per-item correctness details.
1235
+ * @param items - Number of items from the 100-item set (25 or 100).
1236
+ * @returns Accuracy in [0,1] with per-item correctness details and outputs.
862
1237
  */
863
1238
  declare function runMMLUFidelity(model: BenchLanguageModel, items: number, options?: {
864
1239
  abortSignal?: AbortSignal;
865
1240
  onProgress?: (done: number, total: number) => void;
1241
+ /** Appended to the instruction line of every item (from the model catalog). */
1242
+ promptSuffix?: string;
866
1243
  }): Promise<QualityResult>;
867
1244
  /**
868
1245
  * Run the STS-B embedding-quality task: Spearman correlation of cosine
@@ -898,4 +1275,4 @@ interface STSPair {
898
1275
  /** 100-pair STS-B test subset (order preserved from the source dataset). */
899
1276
  declare const STSB_SUBSET: readonly STSPair[];
900
1277
 
901
- export { type AdapterAvailability, type AdapterLoadProgress, BENCH_PROTOCOL_VERSION, BENCH_SCHEMA_VERSION, type BenchCellResult, type BenchCellStatus, type BenchChunk, type BenchEmbeddingModel, type BenchLanguageModel, type BenchModelRef, type BenchRunResult, type BenchRuntimeId, type BenchStreamChunk, type BenchSuiteId, type BenchWorkloadKind, type BenchWorkloadSpec, type BrowserInfo, type CellSummary, EMBED_WORKLOADS, type EmbedIteration, type EmbedWorkloadSpec, type EmbeddingRuntimeAdapter, type EnvironmentCapture, type FingerprintResult, GENERATION_BUDGET, type GPUInfo, HEADLINE_MIN_SUBMISSIONS, type LLMIteration, type LLMRuntimeAdapter, type LLMWorkloadSpec, LLM_WORKLOADS, type LeaderboardRow, type LoadRecord, type LoadedEmbedder, type LoadedLLM, type MMLUItem, type MemorySample, type MetricSummary, type OSInfo, PLAUSIBILITY_RULES_VERSION, type PlannedCell, type PlausibilityFlag, type ProviderUsage, QUALITY_WORKLOADS, type QualityResult, type QualityWorkloadSpec, RUN_POLICIES, type RunPolicy, type RunSuiteOptions, type RunnerHooks, STSB_SUBSET, type STSPair, TINY_MMLU, type TraceEvent, TraceRecorder, USAGE_FIDELITY, type ValidationReport, WORKLOADS_BY_ID, aggregateRuns, canonicalJson, captureEnvironment, checkPlausibility, computeRunDigest, deviceClassOf, formatMMLUPrompt, geomean, hrNow, inferTimerResolutionUs, mean, median, memoryApiAvailable, parseMMLUAnswer, quantile, rowsToCSV, runBenchmarkSuite, runFingerprint, runMMLUFidelity, runSTSQuality, runsToLongCSV, sampleMemoryBytes, sha256Hex, sleep, spearman, stddev, summarize, summarizeCell, summarizeRun, validateRunShape, validateSubmission, verifyRunDigest };
1278
+ export { type APIAvailability, type AdapterAvailability, type AdapterLoadProgress, BENCH_PROTOCOL_VERSION, BENCH_SCHEMA_VERSION, type BenchCellResult, type BenchCellStatus, type BenchChunk, type BenchEmbeddingModel, type BenchLanguageModel, type BenchModelRef, type BenchRunResult, type BenchRuntimeId, type BenchStreamChunk, type BenchSuiteId, type BenchWorkloadKind, type BenchWorkloadSpec, type BrowserInfo, type CellSummary, type DeviceInfo, type DeviceType, type DeviceTypeSignals, type DisplayInfo, EMBED_WORKLOADS, type EmbedIteration, type EmbedWorkloadSpec, type EmbeddingRuntimeAdapter, type EnvironmentCapture, type FingerprintResult, GENERATION_BUDGET, type GPUInfo, HEADLINE_MIN_SUBMISSIONS, type HarnessInfo, type LLMIteration, type LLMRuntimeAdapter, type LLMWorkloadSpec, LLM_WORKLOADS, type LeaderboardRow, type LoadRecord, type LoadedEmbedder, type LoadedLLM, type LocaleInfo, MIN_GENERATED_CHARS, type MMLUItem, MMLU_MAX_TOKENS, MMLU_OUTPUT_CAP, type MemorySample, type MetricSummary, type NetworkInfo, type OSInfo, PLAUSIBILITY_RULES_VERSION, type PlannedCell, type PlausibilityFlag, type ProviderUsage, QUALITY_WORKLOADS, type QualityResult, type QualityWorkloadSpec, RUNTIME_EXECUTION_ORDER, RUN_POLICIES, type RunPolicy, type RunSuiteOptions, type RunnerHooks, STREAM_COHERENCE_MIN_SPAN_RATIO, STSB_SUBSET, type STSPair, TINY_MMLU, type TraceEvent, TraceRecorder, USAGE_FIDELITY, type ValidationReport, WORKLOADS_BY_ID, type WasmFeatureSupport, type WebGLInfo, aggregateRuns, canonicalJson, captureEnvironment, checkPlausibility, computeRunDigest, deriveDeviceType, detectEngine, detectWasmFeatures, deviceClassOf, formatMMLUPrompt, geomean, hrNow, inferTimerResolutionUs, isIncrementalStream, mean, median, memoryApiAvailable, orderCells, parseGpuModel, parseMMLUAnswer, parseUserAgent, quantile, rowsToCSV, runBenchmarkSuite, runFingerprint, runMMLUFidelity, runSTSQuality, runsToLongCSV, sampleMemoryBytes, sha256Hex, sleep, spearman, stddev, summarize, summarizeCell, summarizeRun, validateRunShape, validateSubmission, verifyRunDigest };