Spaces:
Configuration error
Configuration error
Add browser evidence capture control
Browse files- README.md +3 -1
- app.js +123 -4
- index.html +1 -0
- styles.css +2 -0
README.md
CHANGED
|
@@ -22,6 +22,8 @@ Direct app URL: https://mike0021-browser-speak.static.hf.space
|
|
| 22 |
|
| 23 |
After benchmark or real-mic runs, use **Download JSON** to save the evidence file locally. The export includes the hosted Space commit when the static host exposes it, a concise evidence-readiness summary, and the raw benchmark rows. The Runtime panel also shows the served build.
|
| 24 |
|
|
|
|
|
|
|
| 25 |
From the source workspace, the one-command hosted evidence path is:
|
| 26 |
|
| 27 |
```bash
|
|
@@ -35,7 +37,7 @@ Known testing notes:
|
|
| 35 |
- Use Chrome or another browser with WebGPU/WASM support and microphone permission enabled.
|
| 36 |
- The app automatically falls back to WASM if hardware WebGPU is unavailable.
|
| 37 |
- Initial load can be large because STT, LLM, TTS, and voice embeddings are all browser-side assets.
|
| 38 |
-
- For validation evidence, run **Run
|
| 39 |
- The Latency panel shows separate real-mic and hardware-WebGPU validation cards. On hardware browsers, **Run WebGPU evidence row** captures a fast row for the downloaded JSON.
|
| 40 |
- The exported `hostMetadata.hfSpaceCommit` identifies the exact static Space revision served to the browser.
|
| 41 |
- 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.
|
|
|
|
| 22 |
|
| 23 |
After benchmark or real-mic runs, use **Download JSON** to save the evidence file locally. The export includes the hosted Space commit when the static host exposes it, a concise evidence-readiness summary, and the raw benchmark rows. The Runtime panel also shows the served build.
|
| 24 |
|
| 25 |
+
In the browser UI, **Run evidence capture** is the guided validation path. It captures a hardware WebGPU row when the browser exposes a real GPU adapter, runs the 3-row real-mic series, then leaves **Download JSON** enabled for saving the evidence file.
|
| 26 |
+
|
| 27 |
From the source workspace, the one-command hosted evidence path is:
|
| 28 |
|
| 29 |
```bash
|
|
|
|
| 37 |
- Use Chrome or another browser with WebGPU/WASM support and microphone permission enabled.
|
| 38 |
- The app automatically falls back to WASM if hardware WebGPU is unavailable.
|
| 39 |
- Initial load can be large because STT, LLM, TTS, and voice embeddings are all browser-side assets.
|
| 40 |
+
- For validation evidence, run **Run evidence capture**, then **Download JSON**.
|
| 41 |
- The Latency panel shows separate real-mic and hardware-WebGPU validation cards. On hardware browsers, **Run WebGPU evidence row** captures a fast row for the downloaded JSON.
|
| 42 |
- The exported `hostMetadata.hfSpaceCommit` identifies the exact static Space revision served to the browser.
|
| 43 |
- 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.
|
app.js
CHANGED
|
@@ -11,6 +11,7 @@ const elements = {
|
|
| 11 |
loopbackButton: $("loopbackButton"),
|
| 12 |
bargeInButton: $("bargeInButton"),
|
| 13 |
gpuBenchmarkButton: $("gpuBenchmarkButton"),
|
|
|
|
| 14 |
micBenchmarkButton: $("micBenchmarkButton"),
|
| 15 |
micSeriesButton: $("micSeriesButton"),
|
| 16 |
copyResultsButton: $("copyResultsButton"),
|
|
@@ -210,6 +211,7 @@ const state = {
|
|
| 210 |
benchmarkTimeout: null,
|
| 211 |
pendingPlaybackSchedules: 0,
|
| 212 |
suiteRunning: false,
|
|
|
|
| 213 |
micSeries: {
|
| 214 |
active: false,
|
| 215 |
target: 3,
|
|
@@ -478,7 +480,7 @@ function setSessionControls(mode) {
|
|
| 478 |
}
|
| 479 |
|
| 480 |
function setBenchmarkControlsDisabled(disabled) {
|
| 481 |
-
const locked = disabled || state.micSeries.active;
|
| 482 |
elements.suiteButton.disabled = locked;
|
| 483 |
elements.benchmarkButton.disabled = locked;
|
| 484 |
elements.chatBenchmarkButton.disabled = locked;
|
|
@@ -488,6 +490,7 @@ function setBenchmarkControlsDisabled(disabled) {
|
|
| 488 |
elements.micBenchmarkButton.disabled = locked;
|
| 489 |
elements.micSeriesButton.disabled = locked;
|
| 490 |
updateGpuEvidenceButton();
|
|
|
|
| 491 |
}
|
| 492 |
|
| 493 |
function resetMetrics() {
|
|
@@ -599,6 +602,7 @@ function updateGpuValidationStatus() {
|
|
| 599 |
elements.gpuValidationDetail.textContent = detail;
|
| 600 |
elements.gpuValidationProgressBar.style.width = `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%`;
|
| 601 |
updateGpuEvidenceButton();
|
|
|
|
| 602 |
}
|
| 603 |
|
| 604 |
function updateGpuEvidenceButton() {
|
|
@@ -607,6 +611,7 @@ function updateGpuEvidenceButton() {
|
|
| 607 |
const busy =
|
| 608 |
state.modelsLoading ||
|
| 609 |
state.suiteRunning ||
|
|
|
|
| 610 |
state.micSeries.active ||
|
| 611 |
Boolean(state.activeBenchmark);
|
| 612 |
const hardwareRows = evidenceSummaryForRows(state.benchmarkResults).hardwareWebgpu.hardwareRows ?? 0;
|
|
@@ -615,6 +620,30 @@ function updateGpuEvidenceButton() {
|
|
| 615 |
hardwareRows > 0 ? "Run another WebGPU evidence row" : "Run WebGPU evidence row";
|
| 616 |
}
|
| 617 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 618 |
function resetTurnState() {
|
| 619 |
state.messages = initialMessages();
|
| 620 |
state.currentAssistant = "";
|
|
@@ -860,6 +889,7 @@ async function unloadModels({ quiet = false } = {}) {
|
|
| 860 |
state.modelLoadStartedAt = null;
|
| 861 |
state.loadedStack = null;
|
| 862 |
state.suiteRunning = false;
|
|
|
|
| 863 |
resetTurnState();
|
| 864 |
resetMetrics();
|
| 865 |
resetConversationUi();
|
|
@@ -2023,6 +2053,7 @@ function interruptForBargeIn({ preserveLoopbackFeed = false, cancelTts = true, l
|
|
| 2023 |
|
| 2024 |
function stopAll() {
|
| 2025 |
state.suiteRunning = false;
|
|
|
|
| 2026 |
cancelMicSeries();
|
| 2027 |
interruptForBargeIn();
|
| 2028 |
cancelActiveBenchmark();
|
|
@@ -2370,6 +2401,81 @@ async function runHardwareWebGpuEvidenceBenchmark() {
|
|
| 2370 |
return runBenchmark();
|
| 2371 |
}
|
| 2372 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2373 |
function applyHardwareWebGpuEvidenceStack() {
|
| 2374 |
elements.deviceSelect.value = HARDWARE_WEBGPU_EVIDENCE_STACK.device;
|
| 2375 |
elements.llmModelSelect.value = HARDWARE_WEBGPU_EVIDENCE_STACK.llm;
|
|
@@ -2467,7 +2573,11 @@ async function runSuiteStep(label, runner) {
|
|
| 2467 |
await waitForBenchmarkRow(previousCount, label);
|
| 2468 |
}
|
| 2469 |
|
| 2470 |
-
function waitForBenchmarkRow(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2471 |
return new Promise((resolve, reject) => {
|
| 2472 |
const startedAt = performance.now();
|
| 2473 |
const timer = window.setInterval(() => {
|
|
@@ -2476,12 +2586,12 @@ function waitForBenchmarkRow(previousCount, label) {
|
|
| 2476 |
resolve();
|
| 2477 |
return;
|
| 2478 |
}
|
| 2479 |
-
if (!
|
| 2480 |
window.clearInterval(timer);
|
| 2481 |
reject(new Error(`${label} benchmark was cancelled.`));
|
| 2482 |
return;
|
| 2483 |
}
|
| 2484 |
-
if (performance.now() - startedAt >
|
| 2485 |
window.clearInterval(timer);
|
| 2486 |
reject(new Error(`${label} benchmark did not finish.`));
|
| 2487 |
}
|
|
@@ -2547,6 +2657,7 @@ function automationSnapshot() {
|
|
| 2547 |
},
|
| 2548 |
activeBenchmark: state.activeBenchmark ? { ...state.activeBenchmark } : null,
|
| 2549 |
suiteRunning: state.suiteRunning,
|
|
|
|
| 2550 |
micSeries: { ...state.micSeries, timer: Boolean(state.micSeries.timer) },
|
| 2551 |
ttsChunking: { ...state.ttsChunking },
|
| 2552 |
stack: state.loadedStack ? currentBenchmarkStack() : null,
|
|
@@ -2771,6 +2882,11 @@ function installAutomationApi() {
|
|
| 2771 |
runLoopback: (options) => runSingleAutomationBenchmark(runLoopbackBenchmark, options),
|
| 2772 |
runBargeIn: (options) => runSingleAutomationBenchmark(runBargeInBenchmark, options),
|
| 2773 |
runWebGpuEvidence: (options) => runSingleAutomationBenchmark(runHardwareWebGpuEvidenceBenchmark, options),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2774 |
runMic: (options) => runSingleAutomationBenchmark(runMicBenchmark, options),
|
| 2775 |
runMicSeries: runAutomationMicSeries,
|
| 2776 |
synthesizeAudio: synthesizeAutomationAudio,
|
|
@@ -2818,6 +2934,9 @@ elements.bargeInButton.addEventListener("click", runBargeInBenchmark);
|
|
| 2818 |
elements.gpuBenchmarkButton.addEventListener("click", () => {
|
| 2819 |
runHardwareWebGpuEvidenceBenchmark().catch((error) => logEvent(`WebGPU evidence failed: ${error.message}`));
|
| 2820 |
});
|
|
|
|
|
|
|
|
|
|
| 2821 |
elements.micBenchmarkButton.addEventListener("click", runMicBenchmark);
|
| 2822 |
elements.micSeriesButton.addEventListener("click", runMicSeriesBenchmark);
|
| 2823 |
elements.copyResultsButton.addEventListener("click", () => {
|
|
|
|
| 11 |
loopbackButton: $("loopbackButton"),
|
| 12 |
bargeInButton: $("bargeInButton"),
|
| 13 |
gpuBenchmarkButton: $("gpuBenchmarkButton"),
|
| 14 |
+
evidenceCaptureButton: $("evidenceCaptureButton"),
|
| 15 |
micBenchmarkButton: $("micBenchmarkButton"),
|
| 16 |
micSeriesButton: $("micSeriesButton"),
|
| 17 |
copyResultsButton: $("copyResultsButton"),
|
|
|
|
| 211 |
benchmarkTimeout: null,
|
| 212 |
pendingPlaybackSchedules: 0,
|
| 213 |
suiteRunning: false,
|
| 214 |
+
evidenceCaptureRunning: false,
|
| 215 |
micSeries: {
|
| 216 |
active: false,
|
| 217 |
target: 3,
|
|
|
|
| 480 |
}
|
| 481 |
|
| 482 |
function setBenchmarkControlsDisabled(disabled) {
|
| 483 |
+
const locked = disabled || state.micSeries.active || state.evidenceCaptureRunning;
|
| 484 |
elements.suiteButton.disabled = locked;
|
| 485 |
elements.benchmarkButton.disabled = locked;
|
| 486 |
elements.chatBenchmarkButton.disabled = locked;
|
|
|
|
| 490 |
elements.micBenchmarkButton.disabled = locked;
|
| 491 |
elements.micSeriesButton.disabled = locked;
|
| 492 |
updateGpuEvidenceButton();
|
| 493 |
+
updateEvidenceCaptureButton();
|
| 494 |
}
|
| 495 |
|
| 496 |
function resetMetrics() {
|
|
|
|
| 602 |
elements.gpuValidationDetail.textContent = detail;
|
| 603 |
elements.gpuValidationProgressBar.style.width = `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%`;
|
| 604 |
updateGpuEvidenceButton();
|
| 605 |
+
updateEvidenceCaptureButton();
|
| 606 |
}
|
| 607 |
|
| 608 |
function updateGpuEvidenceButton() {
|
|
|
|
| 611 |
const busy =
|
| 612 |
state.modelsLoading ||
|
| 613 |
state.suiteRunning ||
|
| 614 |
+
state.evidenceCaptureRunning ||
|
| 615 |
state.micSeries.active ||
|
| 616 |
Boolean(state.activeBenchmark);
|
| 617 |
const hardwareRows = evidenceSummaryForRows(state.benchmarkResults).hardwareWebgpu.hardwareRows ?? 0;
|
|
|
|
| 620 |
hardwareRows > 0 ? "Run another WebGPU evidence row" : "Run WebGPU evidence row";
|
| 621 |
}
|
| 622 |
|
| 623 |
+
function updateEvidenceCaptureButton() {
|
| 624 |
+
if (!elements.evidenceCaptureButton) return;
|
| 625 |
+
const busy =
|
| 626 |
+
state.modelsLoading ||
|
| 627 |
+
state.suiteRunning ||
|
| 628 |
+
state.evidenceCaptureRunning ||
|
| 629 |
+
state.micSeries.active ||
|
| 630 |
+
Boolean(state.activeBenchmark);
|
| 631 |
+
elements.evidenceCaptureButton.disabled = busy;
|
| 632 |
+
elements.evidenceCaptureButton.textContent = state.evidenceCaptureRunning
|
| 633 |
+
? "Capturing evidence..."
|
| 634 |
+
: "Run evidence capture";
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
function evidenceCaptureBusy() {
|
| 638 |
+
return (
|
| 639 |
+
state.activeBenchmark ||
|
| 640 |
+
state.suiteRunning ||
|
| 641 |
+
state.micSeries.active ||
|
| 642 |
+
state.modelsLoading ||
|
| 643 |
+
state.evidenceCaptureRunning
|
| 644 |
+
);
|
| 645 |
+
}
|
| 646 |
+
|
| 647 |
function resetTurnState() {
|
| 648 |
state.messages = initialMessages();
|
| 649 |
state.currentAssistant = "";
|
|
|
|
| 889 |
state.modelLoadStartedAt = null;
|
| 890 |
state.loadedStack = null;
|
| 891 |
state.suiteRunning = false;
|
| 892 |
+
state.evidenceCaptureRunning = false;
|
| 893 |
resetTurnState();
|
| 894 |
resetMetrics();
|
| 895 |
resetConversationUi();
|
|
|
|
| 2053 |
|
| 2054 |
function stopAll() {
|
| 2055 |
state.suiteRunning = false;
|
| 2056 |
+
state.evidenceCaptureRunning = false;
|
| 2057 |
cancelMicSeries();
|
| 2058 |
interruptForBargeIn();
|
| 2059 |
cancelActiveBenchmark();
|
|
|
|
| 2401 |
return runBenchmark();
|
| 2402 |
}
|
| 2403 |
|
| 2404 |
+
async function runEvidenceCaptureSequence() {
|
| 2405 |
+
if (evidenceCaptureBusy()) {
|
| 2406 |
+
logEvent("Finish the current run before starting evidence capture.");
|
| 2407 |
+
return false;
|
| 2408 |
+
}
|
| 2409 |
+
state.evidenceCaptureRunning = true;
|
| 2410 |
+
setBenchmarkControlsDisabled(true);
|
| 2411 |
+
logEvent("Starting evidence capture: hardware WebGPU row when available, then 3 real-mic rows.");
|
| 2412 |
+
try {
|
| 2413 |
+
await supportsWebGPU();
|
| 2414 |
+
const hasHardwareWebGpu = state.webgpuAvailable === true && state.webgpuSoftwareAdapter !== true;
|
| 2415 |
+
if (!state.modelsLoaded) {
|
| 2416 |
+
if (hasHardwareWebGpu) {
|
| 2417 |
+
applyHardwareWebGpuEvidenceStack();
|
| 2418 |
+
logEvent("Hardware WebGPU available; loading the default WebGPU evidence stack.");
|
| 2419 |
+
} else {
|
| 2420 |
+
logEvent("Loading the selected stack for real-mic evidence capture.");
|
| 2421 |
+
}
|
| 2422 |
+
updateRuntimeStatus();
|
| 2423 |
+
await loadModels({ ttsWarmup: false });
|
| 2424 |
+
if (!state.modelsLoaded) throw new Error("Model load did not complete.");
|
| 2425 |
+
}
|
| 2426 |
+
|
| 2427 |
+
const beforeGpuEvidence = evidenceSummaryForRows(state.benchmarkResults).hardwareWebgpu;
|
| 2428 |
+
if (hasHardwareWebGpu && beforeGpuEvidence.hardwareRows === 0) {
|
| 2429 |
+
if (currentBenchmarkStack().device === "webgpu") {
|
| 2430 |
+
const previousCount = state.benchmarkResults.length;
|
| 2431 |
+
const started = await runHardwareWebGpuEvidenceBenchmark();
|
| 2432 |
+
if (!started) throw new Error("Hardware WebGPU evidence row did not start.");
|
| 2433 |
+
await waitForBenchmarkRow(previousCount, "Hardware WebGPU evidence", {
|
| 2434 |
+
timeoutMs: 150000,
|
| 2435 |
+
isActive: () => state.evidenceCaptureRunning,
|
| 2436 |
+
});
|
| 2437 |
+
} else {
|
| 2438 |
+
logEvent("Hardware WebGPU is available, but the loaded stack is not WebGPU; hardware evidence still needs a WebGPU row.");
|
| 2439 |
+
}
|
| 2440 |
+
} else if (!hasHardwareWebGpu) {
|
| 2441 |
+
logEvent("Hardware WebGPU is not exposed; evidence capture will collect real-mic rows only.");
|
| 2442 |
+
}
|
| 2443 |
+
|
| 2444 |
+
const realMicEvidence = evidenceSummaryForRows(state.benchmarkResults).realMic;
|
| 2445 |
+
if (!realMicEvidence.passed) {
|
| 2446 |
+
const started = await runMicSeriesBenchmark();
|
| 2447 |
+
if (!started) throw new Error("Real-mic series did not start.");
|
| 2448 |
+
await waitForAutomationCondition(
|
| 2449 |
+
() => !state.micSeries.active && !state.activeBenchmark,
|
| 2450 |
+
420000,
|
| 2451 |
+
"Evidence real-mic series",
|
| 2452 |
+
);
|
| 2453 |
+
} else {
|
| 2454 |
+
logEvent("Real-mic evidence is already complete.");
|
| 2455 |
+
}
|
| 2456 |
+
|
| 2457 |
+
const evidence = evidenceSummaryForRows(state.benchmarkResults);
|
| 2458 |
+
const missing = [];
|
| 2459 |
+
if (!evidence.realMic.passed) missing.push("real mic");
|
| 2460 |
+
if (!evidence.hardwareWebgpu.passed) missing.push("hardware WebGPU");
|
| 2461 |
+
logEvent(
|
| 2462 |
+
missing.length === 0
|
| 2463 |
+
? "Evidence capture complete; use Download JSON to save the proof."
|
| 2464 |
+
: `Evidence capture finished; still missing ${missing.join(" and ")} evidence. Use Download JSON to save current rows.`,
|
| 2465 |
+
);
|
| 2466 |
+
return true;
|
| 2467 |
+
} catch (error) {
|
| 2468 |
+
if (state.micSeries.active) cancelMicSeries();
|
| 2469 |
+
logEvent(`Evidence capture stopped: ${error.message}`);
|
| 2470 |
+
return false;
|
| 2471 |
+
} finally {
|
| 2472 |
+
state.evidenceCaptureRunning = false;
|
| 2473 |
+
if (state.modelsLoaded && !state.suiteRunning) setBenchmarkControlsDisabled(false);
|
| 2474 |
+
updateEvidenceCaptureButton();
|
| 2475 |
+
updateMicValidationStatus();
|
| 2476 |
+
}
|
| 2477 |
+
}
|
| 2478 |
+
|
| 2479 |
function applyHardwareWebGpuEvidenceStack() {
|
| 2480 |
elements.deviceSelect.value = HARDWARE_WEBGPU_EVIDENCE_STACK.device;
|
| 2481 |
elements.llmModelSelect.value = HARDWARE_WEBGPU_EVIDENCE_STACK.llm;
|
|
|
|
| 2573 |
await waitForBenchmarkRow(previousCount, label);
|
| 2574 |
}
|
| 2575 |
|
| 2576 |
+
function waitForBenchmarkRow(
|
| 2577 |
+
previousCount,
|
| 2578 |
+
label,
|
| 2579 |
+
{ timeoutMs = 135000, isActive = () => state.suiteRunning } = {},
|
| 2580 |
+
) {
|
| 2581 |
return new Promise((resolve, reject) => {
|
| 2582 |
const startedAt = performance.now();
|
| 2583 |
const timer = window.setInterval(() => {
|
|
|
|
| 2586 |
resolve();
|
| 2587 |
return;
|
| 2588 |
}
|
| 2589 |
+
if (!isActive()) {
|
| 2590 |
window.clearInterval(timer);
|
| 2591 |
reject(new Error(`${label} benchmark was cancelled.`));
|
| 2592 |
return;
|
| 2593 |
}
|
| 2594 |
+
if (performance.now() - startedAt > timeoutMs) {
|
| 2595 |
window.clearInterval(timer);
|
| 2596 |
reject(new Error(`${label} benchmark did not finish.`));
|
| 2597 |
}
|
|
|
|
| 2657 |
},
|
| 2658 |
activeBenchmark: state.activeBenchmark ? { ...state.activeBenchmark } : null,
|
| 2659 |
suiteRunning: state.suiteRunning,
|
| 2660 |
+
evidenceCaptureRunning: state.evidenceCaptureRunning,
|
| 2661 |
micSeries: { ...state.micSeries, timer: Boolean(state.micSeries.timer) },
|
| 2662 |
ttsChunking: { ...state.ttsChunking },
|
| 2663 |
stack: state.loadedStack ? currentBenchmarkStack() : null,
|
|
|
|
| 2882 |
runLoopback: (options) => runSingleAutomationBenchmark(runLoopbackBenchmark, options),
|
| 2883 |
runBargeIn: (options) => runSingleAutomationBenchmark(runBargeInBenchmark, options),
|
| 2884 |
runWebGpuEvidence: (options) => runSingleAutomationBenchmark(runHardwareWebGpuEvidenceBenchmark, options),
|
| 2885 |
+
async runEvidenceCapture() {
|
| 2886 |
+
const started = await runEvidenceCaptureSequence();
|
| 2887 |
+
if (!started) throw new Error("Evidence capture did not start.");
|
| 2888 |
+
return automationSnapshot();
|
| 2889 |
+
},
|
| 2890 |
runMic: (options) => runSingleAutomationBenchmark(runMicBenchmark, options),
|
| 2891 |
runMicSeries: runAutomationMicSeries,
|
| 2892 |
synthesizeAudio: synthesizeAutomationAudio,
|
|
|
|
| 2934 |
elements.gpuBenchmarkButton.addEventListener("click", () => {
|
| 2935 |
runHardwareWebGpuEvidenceBenchmark().catch((error) => logEvent(`WebGPU evidence failed: ${error.message}`));
|
| 2936 |
});
|
| 2937 |
+
elements.evidenceCaptureButton.addEventListener("click", () => {
|
| 2938 |
+
runEvidenceCaptureSequence().catch((error) => logEvent(`Evidence capture failed: ${error.message}`));
|
| 2939 |
+
});
|
| 2940 |
elements.micBenchmarkButton.addEventListener("click", runMicBenchmark);
|
| 2941 |
elements.micSeriesButton.addEventListener("click", runMicSeriesBenchmark);
|
| 2942 |
elements.copyResultsButton.addEventListener("click", () => {
|
index.html
CHANGED
|
@@ -191,6 +191,7 @@
|
|
| 191 |
<button id="loopbackButton" type="button" disabled>Run voice loopback</button>
|
| 192 |
<button id="bargeInButton" type="button" disabled>Run barge-in check</button>
|
| 193 |
<button id="gpuBenchmarkButton" type="button" disabled>Run WebGPU evidence row</button>
|
|
|
|
| 194 |
<button id="micBenchmarkButton" type="button" disabled>Benchmark real mic</button>
|
| 195 |
<button id="micSeriesButton" type="button" disabled>Run 3 real-mic series</button>
|
| 196 |
</section>
|
|
|
|
| 191 |
<button id="loopbackButton" type="button" disabled>Run voice loopback</button>
|
| 192 |
<button id="bargeInButton" type="button" disabled>Run barge-in check</button>
|
| 193 |
<button id="gpuBenchmarkButton" type="button" disabled>Run WebGPU evidence row</button>
|
| 194 |
+
<button id="evidenceCaptureButton" type="button" disabled>Run evidence capture</button>
|
| 195 |
<button id="micBenchmarkButton" type="button" disabled>Benchmark real mic</button>
|
| 196 |
<button id="micSeriesButton" type="button" disabled>Run 3 real-mic series</button>
|
| 197 |
</section>
|
styles.css
CHANGED
|
@@ -427,6 +427,7 @@ h2 {
|
|
| 427 |
#loopbackButton,
|
| 428 |
#bargeInButton,
|
| 429 |
#gpuBenchmarkButton,
|
|
|
|
| 430 |
#micBenchmarkButton,
|
| 431 |
#micSeriesButton {
|
| 432 |
width: 100%;
|
|
@@ -438,6 +439,7 @@ h2 {
|
|
| 438 |
#loopbackButton,
|
| 439 |
#bargeInButton,
|
| 440 |
#gpuBenchmarkButton,
|
|
|
|
| 441 |
#micBenchmarkButton,
|
| 442 |
#micSeriesButton {
|
| 443 |
margin-top: 8px;
|
|
|
|
| 427 |
#loopbackButton,
|
| 428 |
#bargeInButton,
|
| 429 |
#gpuBenchmarkButton,
|
| 430 |
+
#evidenceCaptureButton,
|
| 431 |
#micBenchmarkButton,
|
| 432 |
#micSeriesButton {
|
| 433 |
width: 100%;
|
|
|
|
| 439 |
#loopbackButton,
|
| 440 |
#bargeInButton,
|
| 441 |
#gpuBenchmarkButton,
|
| 442 |
+
#evidenceCaptureButton,
|
| 443 |
#micBenchmarkButton,
|
| 444 |
#micSeriesButton {
|
| 445 |
margin-top: 8px;
|