Mike0021 commited on
Commit
c750135
·
verified ·
1 Parent(s): 7234d87

Add hardware WebGPU validation card

Browse files
Files changed (3) hide show
  1. README.md +1 -0
  2. app.js +41 -0
  3. index.html +8 -0
README.md CHANGED
@@ -28,6 +28,7 @@ Known testing notes:
28
  - The app automatically falls back to WASM if hardware WebGPU is unavailable.
29
  - Initial load can be large because STT, LLM, TTS, and voice embeddings are all browser-side assets.
30
  - For validation evidence, run **Run benchmark suite**, **Run 3 real-mic series**, then **Download JSON**.
 
31
  - The exported `hostMetadata.hfSpaceCommit` identifies the exact static Space revision served to the browser.
32
  - Hosted smoke validation passed on May 28, 2026: TTS, identity, and loopback rows completed with zero benchmark-phase network requests, zero benchmark errors, and zero server-inference suspects.
33
  - The latest local validation also passed UI smoke, client-side/no-server smoke, synthetic loopback stability, and first-TTS-chunk safety. The remaining external gates are real human microphone rows and a hardware WebGPU row from a browser that exposes a real GPU adapter.
 
28
  - The app automatically falls back to WASM if hardware WebGPU is unavailable.
29
  - Initial load can be large because STT, LLM, TTS, and voice embeddings are all browser-side assets.
30
  - For validation evidence, run **Run benchmark suite**, **Run 3 real-mic series**, then **Download JSON**.
31
+ - The Latency panel shows separate real-mic and hardware-WebGPU validation cards.
32
  - The exported `hostMetadata.hfSpaceCommit` identifies the exact static Space revision served to the browser.
33
  - Hosted smoke validation passed on May 28, 2026: TTS, identity, and loopback rows completed with zero benchmark-phase network requests, zero benchmark errors, and zero server-inference suspects.
34
  - The latest local validation also passed UI smoke, client-side/no-server smoke, synthetic loopback stability, and first-TTS-chunk safety. The remaining external gates are real human microphone rows and a hardware WebGPU row from a browser that exposes a real GPU adapter.
app.js CHANGED
@@ -41,6 +41,10 @@ const elements = {
41
  micValidationStatus: $("micValidationStatus"),
42
  micValidationDetail: $("micValidationDetail"),
43
  micValidationProgressBar: $("micValidationProgressBar"),
 
 
 
 
44
  vadCloseLatency: $("vadCloseLatency"),
45
  asrLatency: $("asrLatency"),
46
  firstTokenLatency: $("firstTokenLatency"),
@@ -350,6 +354,7 @@ function updateRuntimeStatus() {
350
  elements.runtimeStatus.dataset.state = mode;
351
  elements.runtimeDeviceStatus.textContent = status;
352
  elements.runtimeDeviceDetail.textContent = detail;
 
353
  }
354
 
355
  function formatMs(value) {
@@ -538,6 +543,42 @@ function updateMicValidationStatus() {
538
  elements.micValidationStatus.textContent = status;
539
  elements.micValidationDetail.textContent = detail;
540
  elements.micValidationProgressBar.style.width = `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  }
542
 
543
  function resetTurnState() {
 
41
  micValidationStatus: $("micValidationStatus"),
42
  micValidationDetail: $("micValidationDetail"),
43
  micValidationProgressBar: $("micValidationProgressBar"),
44
+ gpuValidationCard: $("gpuValidationCard"),
45
+ gpuValidationStatus: $("gpuValidationStatus"),
46
+ gpuValidationDetail: $("gpuValidationDetail"),
47
+ gpuValidationProgressBar: $("gpuValidationProgressBar"),
48
  vadCloseLatency: $("vadCloseLatency"),
49
  asrLatency: $("asrLatency"),
50
  firstTokenLatency: $("firstTokenLatency"),
 
354
  elements.runtimeStatus.dataset.state = mode;
355
  elements.runtimeDeviceStatus.textContent = status;
356
  elements.runtimeDeviceDetail.textContent = detail;
357
+ updateGpuValidationStatus();
358
  }
359
 
360
  function formatMs(value) {
 
543
  elements.micValidationStatus.textContent = status;
544
  elements.micValidationDetail.textContent = detail;
545
  elements.micValidationProgressBar.style.width = `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%`;
546
+ updateGpuValidationStatus();
547
+ }
548
+
549
+ function updateGpuValidationStatus() {
550
+ if (!elements.gpuValidationCard) return;
551
+ const evidence = evidenceSummaryForRows(state.benchmarkResults).hardwareWebgpu;
552
+ const hardwareRows = evidence.hardwareRows ?? 0;
553
+ let mode = "idle";
554
+ let status = "Checking adapter";
555
+ let detail = "Adapter probe pending.";
556
+ let progress = 0;
557
+
558
+ if (hardwareRows > 0) {
559
+ mode = "ready";
560
+ status = `${hardwareRows} hardware WebGPU row${hardwareRows === 1 ? "" : "s"}`;
561
+ detail = evidence.adapters[0]?.label ?? "Hardware adapter recorded in benchmark rows.";
562
+ progress = 1;
563
+ } else if (state.webgpuAvailable === false) {
564
+ mode = "warn";
565
+ status = "No WebGPU adapter";
566
+ detail = "Hardware WebGPU is not exposed in this browser.";
567
+ } else if (state.webgpuAvailable === true && state.webgpuSoftwareAdapter) {
568
+ mode = "warn";
569
+ status = "Software WebGPU only";
570
+ detail = `Software adapter: ${formatGpuAdapter(state.webgpuAdapterInfo)}`;
571
+ } else if (state.webgpuAvailable === true) {
572
+ mode = "warn";
573
+ status = "Hardware WebGPU available";
574
+ detail = `${formatGpuAdapter(state.webgpuAdapterInfo)}. Run a WebGPU stack to capture a row.`;
575
+ progress = 0.5;
576
+ }
577
+
578
+ elements.gpuValidationCard.dataset.state = mode;
579
+ elements.gpuValidationStatus.textContent = status;
580
+ elements.gpuValidationDetail.textContent = detail;
581
+ elements.gpuValidationProgressBar.style.width = `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%`;
582
  }
583
 
584
  function resetTurnState() {
index.html CHANGED
@@ -176,6 +176,14 @@
176
  <span id="micValidationProgressBar"></span>
177
  </div>
178
  </div>
 
 
 
 
 
 
 
 
179
  <button id="suiteButton" type="button" disabled>Run benchmark suite</button>
180
  <button id="benchmarkButton" type="button" disabled>Run identity benchmark</button>
181
  <button id="chatBenchmarkButton" type="button" disabled>Run chat benchmark</button>
 
176
  <span id="micValidationProgressBar"></span>
177
  </div>
178
  </div>
179
+ <div id="gpuValidationCard" class="mic-validation" data-state="idle" aria-live="polite">
180
+ <span>Hardware WebGPU validation</span>
181
+ <strong id="gpuValidationStatus">Checking adapter</strong>
182
+ <small id="gpuValidationDetail">Adapter probe pending.</small>
183
+ <div class="mic-progress" aria-hidden="true">
184
+ <span id="gpuValidationProgressBar"></span>
185
+ </div>
186
+ </div>
187
  <button id="suiteButton" type="button" disabled>Run benchmark suite</button>
188
  <button id="benchmarkButton" type="button" disabled>Run identity benchmark</button>
189
  <button id="chatBenchmarkButton" type="button" disabled>Run chat benchmark</button>