Skip to content

fix: WebGPU CPU fallback, Gemma 3 1B registry, HTML mobile dedup (#97 #165 #170)#302

Merged
sauravpanda merged 2 commits intomainfrom
worktree-fix+170-97-165-gemma-wasm-dedup
Apr 16, 2026
Merged

fix: WebGPU CPU fallback, Gemma 3 1B registry, HTML mobile dedup (#97 #165 #170)#302
sauravpanda merged 2 commits intomainfrom
worktree-fix+170-97-165-gemma-wasm-dedup

Conversation

@sauravpanda
Copy link
Copy Markdown
Owner

@sauravpanda sauravpanda commented Apr 15, 2026

Summary

Three independent fixes across the BrowserAI library.

Closes #97, #165, #170.


Fix #97 — WebGPU fallback to CPU/WASM in Transformers engine

Problem: TransformersEngineWrapper unconditionally set device = 'webgpu', causing Whisper transcription and TTS to fail silently in Firefox and browsers without WebGPU support.

Fix: Added detectBestDevice() which probes navigator.gpu.requestAdapter() before loading a model. Falls back to 'cpu' (ONNX WASM backend) automatically. Callers can still override by passing options.device explicitly.

// Before: hard-coded to WebGPU — silent failure in Firefox
options.device = 'webgpu';

// After: probes then falls back
options.device = await detectBestDevice(); // 'webgpu' or 'cpu'

Fix #165 — Remove mobile/desktop duplicate content during HTML cleaning

Problem: Many websites serve the same text twice — once for mobile and once for desktop layout — controlled by CSS. Since DOMParser runs without stylesheets, both copies ended up in the cleaned output.

Fix: Added removeHiddenAndDuplicateElements() called before text extraction in clean(), cleanSemantic(), and preserveSemanticHierarchy(). It removes:

  1. Elements with aria-hidden="true" (explicitly hidden from AT, almost always decorative duplicates)
  2. Elements with inline display:none or visibility:hidden
  3. Elements whose CSS class name matches a responsive utility heuristic (e.g. mobile-only, desktop-hidden, show-on-tablet)

Fix #170 — Add Gemma 3 1B Instruct to model registry

Problem: Only Gemma 2B was registered; no 1B Gemma model was available.

Fix: Added gemma-3-1b-it to mlc-models.json following the same pattern as gemma-2b-it:

await ai.loadModel('gemma-3-1b-it');
  • Repo: mlc-ai/gemma-3-1b-it-{quantization}-MLC
  • Quantizations: q4f16_1, q4f32_1
  • Context: 32K tokens
  • Requires shader-f16 feature

Test plan

  • npm run build succeeds
  • npm test — 22/22 tests pass
  • TypeScript: no new type errors in changed files
  • Manual: load Whisper in Firefox (CPU fallback)
  • Manual: scrape a responsive site and verify no duplicate paragraphs
  • Manual: ai.loadModel('gemma-3-1b-it') loads correctly

Summary by CodeRabbit

  • New Features
    • Added support for the Gemma 3 1B IT model with selectable quantization options and sensible defaults for generation.
    • Enhanced HTML content cleaning to remove hidden and duplicate elements for more accurate content extraction.
    • Implemented automatic GPU detection with a graceful fallback to CPU when WebGPU is unavailable.

- fix(#97): detect WebGPU availability in TransformersEngineWrapper;
  fall back to CPU/WASM when navigator.gpu is absent or adapter request
  fails — Whisper and TTS now load in non-WebGPU browsers automatically

- feat(#170): add gemma-3-1b-it to MLC model registry (Q4 quantizations,
  32K context, shader-f16 required) alongside existing gemma-2b-it

- fix(#165): add removeHiddenAndDuplicateElements() to HTMLCleaner;
  prunes aria-hidden=true, inline display:none/visibility:hidden, and
  responsive class-name heuristics (mobile-only, desktop-hidden, etc.)
  before text extraction — eliminates mobile/desktop content duplication
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ed4ee102-b492-4968-8305-2d1ecfb05cce

📥 Commits

Reviewing files that changed from the base of the PR and between c31653a and 3e686b4.

📒 Files selected for processing (1)
  • src/engines/transformer-engine-wrapper.ts
✅ Files skipped from review due to trivial changes (1)
  • src/engines/transformer-engine-wrapper.ts

📝 Walkthrough

Walkthrough

Adds Gemma 3 1B instruction-tuned model config with quantizations, integrates removal of hidden/duplicate HTML elements early in the cleaning pipeline, and implements async device detection that prefers WebGPU but falls back to CPU/WASM when WebGPU is unavailable.

Changes

Cohort / File(s) Summary
Model Configuration
src/config/models/mlc-models.json
Adds new gemma-3-1b-it model entry: MLC text-generation model, repo pattern mlc-ai/gemma-3-1b-it-{quantization}-MLC, quantizations q4f16_1/q4f32_1 (q4f32_1 default), defaults for generation (temperature: 0.7, maxTokens: 2048), pipeline: "text-generation", required feature shader-f16, and context_window_size: 32768.
HTML Element Pruning
src/core/agent/html-cleaner.ts
Adds private removeHiddenAndDuplicateElements(root: HTMLElement) that removes elements with aria-hidden="true", inline display:none/visibility:hidden, and responsive-visibility utility-class heuristics; called at start of clean, cleanSemantic, and preserveSemanticHierarchy.
Device Fallback Detection
src/engines/transformer-engine-wrapper.ts
Adds internal async detectBestDevice() which probes navigator.gpu and requestAdapter() and returns 'webgpu' only if adapter available, otherwise 'cpu'; loadModel() sets options.device from detection only when not provided and logs when falling back to CPU/WASM; removed unconditional WebGPU assignment.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Browser
  participant Wrapper as TransformersEngineWrapper
  participant WebGPU as WebGPU Adapter
  participant CPU as CPU/WASM Runtime

  Client->>Wrapper: request loadModel(options?)
  Wrapper->>WebGPU: check navigator.gpu && requestAdapter()
  alt adapter obtained
    WebGPU-->>Wrapper: adapter
    Wrapper->>Wrapper: return 'webgpu' device
    Wrapper->>WebGPU: initialize model using WebGPU
  else no adapter
    WebGPU-->>Wrapper: fail / undefined
    Wrapper->>CPU: choose 'cpu' / WASM
    Wrapper->>CPU: initialize model in CPU/WASM
  end
  Wrapper-->>Client: model loaded
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through JSON, Gemma in tow,

Hidden divs I nibble, duplicate woe,
I sniffed for GPU, then CPU I greet,
Models and cleanups — a rabbitly feat! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes all three main changes: WebGPU CPU fallback, Gemma 3 1B registry addition, and HTML mobile deduplication, directly matching the changeset.
Linked Issues check ✅ Passed All three issues are addressed: WebGPU fallback via detectBestDevice() [#97], HTML duplicate removal via removeHiddenAndDuplicateElements() [#165], and Gemma 3 1B registry addition [#170].
Out of Scope Changes check ✅ Passed All changes directly correspond to the three linked issues with no unrelated modifications introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-fix+170-97-165-gemma-wasm-dedup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/core/agent/html-cleaner.ts (1)

63-98: Add regression tests for the new pruning heuristic.

This path is high-impact and currently untested in the provided suite. Please add focused cases for aria-hidden, inline hidden styles, responsive hidden classes, and non-hidden responsive classes to prevent accidental content drops.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/agent/html-cleaner.ts` around lines 63 - 98, Add regression tests
for removeHiddenAndDuplicateElements: create unit tests that (1) assert elements
with aria-hidden="true" are removed, (2) assert elements with inline style
display:none or visibility:hidden are removed, (3) assert elements whose
className matches the RESPONSIVE_PATTERN (e.g., "mobile-only", "desktop-hidden",
"show-on-tablet", "hidden-xs") are removed, and (4) assert elements with similar
but non-matching responsive class names are kept; call the
removeHiddenAndDuplicateElements method on a constructed DOM root and verify
removed vs retained nodes to prevent future regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/core/agent/html-cleaner.ts`:
- Around line 56-57: The cleanup currently treats responsive classes containing
utility words including "show" and "visible" as duplicates and removes those
elements; update the class-matching logic in src/core/agent/html-cleaner.ts so
that the responsive-utility list/regex no longer includes "show" and "visible"
(leave "hidden", "only", "hide", etc.), i.e., remove "show" and "visible" from
the array/regex that detects responsive hide/show classes and adjust any logic
in the function that prunes responsive duplicate nodes to skip elements matched
only by "show"/"visible" variants; run or add tests covering cases like
"show-on-tablet" and "visible-desktop" to ensure they are preserved.

In `@src/engines/transformer-engine-wrapper.ts`:
- Around line 16-28: Prettier flagged formatting in
src/engines/transformer-engine-wrapper.ts; run the project's Prettier formatter
on this file (or run the formatter task/IDE integration) to reformat
detectBestDevice and surrounding code; specifically locate the detectBestDevice
function and apply Prettier so spacing/line breaks and semicolons match repo
style and CI will pass.
- Around line 68-73: The engine wrapper's auto-detection is bypassed because
callers are hardcoding options.device; remove the hardcoded device in the caller
(textToSpeech in src/core/llm/index.ts) so that TransformerEngineWrapper code
(the options.device check and detectBestDevice call inside
transformer-engine-wrapper.ts) can run; specifically, stop passing device:
'webgpu' from textToSpeech and instead pass no device (or forward undefined) so
TransformerEngineWrapper's logic using detectBestDevice() can pick the best
device at runtime.

---

Nitpick comments:
In `@src/core/agent/html-cleaner.ts`:
- Around line 63-98: Add regression tests for removeHiddenAndDuplicateElements:
create unit tests that (1) assert elements with aria-hidden="true" are removed,
(2) assert elements with inline style display:none or visibility:hidden are
removed, (3) assert elements whose className matches the RESPONSIVE_PATTERN
(e.g., "mobile-only", "desktop-hidden", "show-on-tablet", "hidden-xs") are
removed, and (4) assert elements with similar but non-matching responsive class
names are kept; call the removeHiddenAndDuplicateElements method on a
constructed DOM root and verify removed vs retained nodes to prevent future
regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1dd5e013-fb36-4e60-9d69-83a52f94a3f5

📥 Commits

Reviewing files that changed from the base of the PR and between d48fa13 and c31653a.

📒 Files selected for processing (3)
  • src/config/models/mlc-models.json
  • src/core/agent/html-cleaner.ts
  • src/engines/transformer-engine-wrapper.ts

Comment on lines +56 to +57
* 3. CSS class names containing "mobile", "desktop", "tablet" combined with common
* hide/show utility words ("hidden", "only", "show", "hide", "visible") — a heuristic
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not prune show/visible responsive classes as duplicates.

Line [67] removes elements matching show/visible responsive classes (e.g., show-on-tablet, visible-desktop). That can delete the only intended content variant and cause downstream content loss.

Proposed fix
-    const RESPONSIVE_PATTERN =
-      /\b(mobile|desktop|tablet|phone|sm|md|lg|xl)\b.{0,10}\b(only|hidden|hide|show|visible|invisible)\b|\b(hidden|hide|show|visible|invisible)\b.{0,10}\b(mobile|desktop|tablet|phone|sm|md|lg|xl)\b/i;
+    const RESPONSIVE_HIDDEN_PATTERN =
+      /\b(mobile|desktop|tablet|phone|xs|sm|md|lg|xl)\b.{0,10}\b(hidden|hide|invisible|only)\b|\b(hidden|hide|invisible|only)\b.{0,10}\b(mobile|desktop|tablet|phone|xs|sm|md|lg|xl)\b/i;
@@
-      if (typeof className === 'string' && RESPONSIVE_PATTERN.test(className)) {
+      if (typeof className === 'string' && RESPONSIVE_HIDDEN_PATTERN.test(className)) {
         toRemove.push(el);
       }

Also applies to: 67-90

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/agent/html-cleaner.ts` around lines 56 - 57, The cleanup currently
treats responsive classes containing utility words including "show" and
"visible" as duplicates and removes those elements; update the class-matching
logic in src/core/agent/html-cleaner.ts so that the responsive-utility
list/regex no longer includes "show" and "visible" (leave "hidden", "only",
"hide", etc.), i.e., remove "show" and "visible" from the array/regex that
detects responsive hide/show classes and adjust any logic in the function that
prunes responsive duplicate nodes to skip elements matched only by
"show"/"visible" variants; run or add tests covering cases like "show-on-tablet"
and "visible-desktop" to ensure they are preserved.

Comment thread src/engines/transformer-engine-wrapper.ts
Comment on lines +68 to +73
if (!options.device) {
options.device = await detectBestDevice();
if (options.device === 'cpu') {
console.info('[Transformers] WebGPU unavailable — falling back to CPU/WASM inference');
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify default call sites that hard-code WebGPU and bypass auto-fallback.

rg -n -C3 "textToSpeech\(" src/core/llm/index.ts
rg -n -C3 "device:\s*'webgpu'" src/core/llm/index.ts src/engines/transformer-engine-wrapper.ts
rg -n -C3 "if \(!options\.device\)" src/engines/transformer-engine-wrapper.ts

Repository: sauravpanda/BrowserAI

Length of output: 1139


Remove hardcoded device: 'webgpu' to allow the engine's auto-detection fallback to run.

At Line 68 of src/engines/transformer-engine-wrapper.ts, the auto-detection only executes when options.device is unset. However, textToSpeech() in src/core/llm/index.ts (Line 197) hardcodes device: 'webgpu', bypassing this fallback and breaking on non-WebGPU browsers.

Suggested fix
-      await this.engine.loadModel(MODEL_CONFIG['kokoro-tts'], {
-        quantized: true,
-        device: 'webgpu',
-        ...options,
-      });
+      await this.engine.loadModel(MODEL_CONFIG['kokoro-tts'], {
+        quantized: true,
+        ...options,
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engines/transformer-engine-wrapper.ts` around lines 68 - 73, The engine
wrapper's auto-detection is bypassed because callers are hardcoding
options.device; remove the hardcoded device in the caller (textToSpeech in
src/core/llm/index.ts) so that TransformerEngineWrapper code (the options.device
check and detectBestDevice call inside transformer-engine-wrapper.ts) can run;
specifically, stop passing device: 'webgpu' from textToSpeech and instead pass
no device (or forward undefined) so TransformerEngineWrapper's logic using
detectBestDevice() can pick the best device at runtime.

CI was rejecting the PR branch on a prettier format check in the WebGPU
fallback code path. Pure formatting, no logic change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sauravpanda sauravpanda merged commit e970683 into main Apr 16, 2026
9 of 10 checks passed
@sauravpanda sauravpanda deleted the worktree-fix+170-97-165-gemma-wasm-dedup branch April 16, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for gemma Remove repeated text while parsing html Add Wasm fallback while running llms

1 participant