Skip to content

fix(ai): download progress bytes/speed + voice progress-scale bug (#333 item 1) - #346

Merged
qnbs merged 5 commits into
mainfrom
fix/333-download-progress-metrics
Aug 13, 2026
Merged

fix(ai): download progress bytes/speed + voice progress-scale bug (#333 item 1)#346
qnbs merged 5 commits into
mainfrom
fix/333-download-progress-metrics

Conversation

@qnbs

@qnbsqnbs commented Aug 12, 2026

Copy link
Copy Markdown
Owner

User description

Summary

Addresses #333 item 1 (local AI / voice model download UX should show bytes/speed, not just a bare percentage), with a genuine root-cause bug fix discovered while investigating.

Voice model download (VoiceModelDownloadModal.tsx, services/voice/voiceCommandService.ts):

  • Bug fix: transformers.js's own onProgress payload is { progress: <0-100>, loaded: <bytes>, total: <bytes> } — verified directly against its readResponse() source (progress = loaded/total*100). The existing code treated progress as if it were already a 0-1 fraction, so the safety clamp Math.min(0.95, pct) kicked in almost immediately (any progress value over 0.95%, i.e. after the very first chunk of the download) and stayed there for the rest of the download — the progress bar looked stuck/frozen the whole time. This plausibly contributes to the "local AI download appears frozen" complaint in Stability issues with Local AI, Gemini API keys, LM Studio and UI + workflow suggestions #333.
  • Now derives the fraction from the payload's real loaded/total byte counts (falling back to progress / 100 only when those are genuinely absent), and threads the real bytes through Redux to the modal for a live "X MB of Y MB" display plus a client-side-computed transfer speed.

WebLLM text-model download (LocalAiDownloadProgress.tsx, services/localAiFacade.ts, services/ai/inferenceProgressEmitter.ts):

  • The installed @mlc-ai/web-llm's own progress callback exposes only a 0-1 fraction + a status string — no structured byte counts (confirmed against its installed .d.ts). Real byte-level progress isn't obtainable here without patching library internals.
  • Added a machine-readable per-model size table (WEBLLM_MODEL_APPROX_MB in packages/ai-core), mirroring the free-text GB figures already embedded in WEBLLM_SUPPORTED_MODELS' labels, and derive an approximate downloaded/total MB + speed from progress × totalBytes. This is clearly labeled as an estimate in code comments and is not presented as measured telemetry.

Both UIs share a small services/downloadProgressFormat.ts for consistent MB/speed formatting.

Test plan

  • pnpm run typecheck (exact CI command) — clean
  • pnpm run lint (full project, --error-on-warnings) — clean
  • pnpm run i18n:check — 19 locales, 2908 keys, parity OK; bundles rebuilt
  • New/updated unit tests, all green (81 tests across 7 files): inferenceProgressEmitter (both existing suites + new byte-metric cases), downloadProgressFormat (new), localAiFacade (2 pre-existing assertions updated for the new modelId arg), voiceCommandService (3 new cases pinning the progress-scale fix and byte threading), LocalAiDownloadProgress (2 new size/speed display cases), VoiceModelDownloadModal (2 new size-display cases)
  • CI (this repo is CI-cloud-first on this constrained host per CLAUDE.md)

🤖 Generated with Claude Code

Summary by Sourcery

Improve AI model download UX by showing byte-level progress, estimated size, and speed for both voice and WebLLM text models, while fixing the mis-scaled voice download progress reporting.

New Features:

  • Show downloaded/total megabytes and approximate download speed in the local AI (WebLLM) text model download modal.
  • Show downloaded/total megabytes and computed download speed in the voice model download modal using real byte counts from transformers.js.

Bug Fixes:

  • Correct voice model download progress scaling by treating transformers.js progress values as 0–100 percent and deriving a proper 0–1 fraction, preventing the progress bar from appearing stuck near 95%.

Enhancements:

  • Expose and thread byte-level download metrics through inferenceProgressEmitter and Redux for WebLLM text models, based on a per-model approximate size table.
  • Add shared formatting utilities for megabyte and MB/s displays to keep download progress text consistent across UIs.

Documentation:

  • Update CHANGELOG and README badges to document the new download progress behavior and increased i18n key count.

Tests:

  • Extend unit tests for local AI and voice download flows, inferenceProgressEmitter, and localAiFacade to cover byte metrics, new model size mapping, and progress formatting.
  • Add unit tests for the new downloadProgressFormat helpers and for the byte-level progress display in both download modals.

CodeAnt-AI Description

Fix model download progress and show transfer details

What Changed

  • Voice model downloads now advance using the actual downloaded and total bytes instead of appearing stuck near 95%.
  • Voice downloads show the downloaded size, total size, and current transfer speed when available.
  • Local text-model downloads show downloaded size and speed using model-specific size estimates when measured byte counts are unavailable.
  • Added coverage for progress conversion, byte metrics, formatting, and both download displays.

Impact

✅ Voice downloads no longer appear frozen
✅ Clearer model download progress
✅ Visible download speed

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • New Features

    • Download progress now shows loaded size and transfer speed for local AI and voice models.
    • Progress estimates include percentage and remaining-time details when available.
    • Added localized download metrics and connection diagnostics across supported languages.
  • Bug Fixes

    • Voice-model progress bars now accurately reach completion instead of stopping near 95%.
  • Documentation

    • Updated localization metrics in the README and documented download-progress improvements.

… item 1)
Voice model download (VoiceModelDownloadModal, transformers.js pipeline):
transformers.js's own onProgress payload is `{ progress: <0-100>, loaded:
<bytes>, total: <bytes> }` (verified against its readResponse() source —
progress = loaded/total*100). voiceCommandService.ts treated `progress` as if
it were already a 0-1 fraction, so the existing `Math.min(0.95, pct)` safety
clamp kicked in almost immediately (any progress value over 0.95%, i.e. after
the very first chunk) and stayed there for the rest of the download, making
the progress bar look stuck. Now derives the fraction from the payload's real
loaded/total byte counts (falling back to progress/100 only when absent), and
threads the real bytes through to the modal for a live "X MB of Y MB" + speed
display.
WebLLM text-model download (LocalAiDownloadProgress, services/localAiFacade):
the installed @mlc-ai/web-llm's own progress callback exposes only a 0-1
fraction, no structured byte counts. Adds a machine-readable per-model size
table (WEBLLM_MODEL_APPROX_MB, mirroring the free-text GB figures already in
WEBLLM_SUPPORTED_MODELS' labels) and derives an approximate downloaded/total
MB + speed from progress × that table — clearly labeled as an estimate, not
measured telemetry, since no real byte-level data is obtainable here without
patching library internals.
Both UIs share services/downloadProgressFormat.ts for MB/speed formatting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@codeant-ai

codeant-aiBot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

StatusCommitStarted (UTC)Finished (UTC)
✅ Reviewed your PRefeb639Aug 12, 2026 · 19:5119:57

@codeant-ai

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
worldscript-studioReadyReadyPreviewAug 13, 2026 5:18am

@sourcery-ai

sourcery-aiBot commented Aug 12, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements byte-level progress and speed display for both voice and local AI model downloads, and fixes a mis-scaled transformers.js progress handler that made the voice download bar appear stuck around 95%.

Sequence diagram for WebLLM text model download approximate size and speed

sequenceDiagram
participant User
participant LocalAiDownloadProgress
participant localAiFacade
participant WebLlmLibrary
participant inferenceProgressEmitter
participant aiCore
User->>LocalAiDownloadProgress: open Settings → AI
LocalAiDownloadProgress->>localAiFacade: preloadLocalModel(modelId) / generateLocalText
localAiFacade->>WebLlmLibrary: start model download with reportProgress
WebLlmLibrary-->>localAiFacade: reportProgress({ progress, text })
localAiFacade->>inferenceProgressEmitter: reportWebLlmProgress(progress, text, workerModelId/modelId)
inferenceProgressEmitter->>aiCore: WEBLLM_MODEL_APPROX_MB[modelId]
aiCore-->>inferenceProgressEmitter: approxMb
inferenceProgressEmitter->>inferenceProgressEmitter: compute totalBytes, loadedBytes, bytesPerSecond
inferenceProgressEmitter-->>LocalAiDownloadProgress: WebLlmLoadProgress snapshot
LocalAiDownloadProgress-->>User: show percent + ETA + "≈X MB of Y MB" + "≈Z MB/s"
Loading

File-Level Changes

ChangeDetailsFiles
Fix transformers.js voice model download progress scaling and thread real byte counts into Redux/UI.
  • Change voiceCommandService downloadVoiceModels onProgress handler to prefer loaded/total bytes and only fall back to progress/100.
  • Clamp wasmModelDownloadProgress using a correctly computed 0–1 fraction instead of misusing the 0–100 progress field.
  • Dispatch new wasmModelDownloadLoadedBytes and wasmModelDownloadTotalBytes fields when byte counts are present.
  • Extend VoiceSettings type with wasmModelDownloadLoadedBytes and wasmModelDownloadTotalBytes fields.
services/voice/voiceCommandService.ts
types.ts
Add real byte-size and speed display to the voice model download modal.
  • Select wasmModelDownloadLoadedBytes and wasmModelDownloadTotalBytes from Redux in VoiceModelDownloadModal.
  • Compute client-side bytesPerSecond from cumulative loadedBytes and wall-clock elapsed time.
  • Render translated "X MB of Y MB" and "Z MB/s" lines when byte metrics are available, using shared formatting helpers.
  • Adjust VoiceModelDownloadModal unit tests to use selector-aware mock voice state and assert size/speed text rendering.
components/voice/VoiceModelDownloadModal.tsx
tests/unit/components/voice/VoiceModelDownloadModal.test.tsx
Derive approximate byte metrics for WebLLM text model downloads and show them in LocalAiDownloadProgress.
  • Extend WebLlmLoadProgress snapshot with loadedBytes, totalBytes, and bytesPerSecond fields, with appropriate initialization and reset behavior.
  • Update InferenceProgressEmitter.reportWebLlmProgress to accept an optional modelId, look up approximate size from WEBLLM_MODEL_APPROX_MB, and derive loaded/total bytes and bytesPerSecond from progress and elapsed time.
  • Ensure reportWebLlmReady and reportWebLlmError clear byte-related fields.
  • Update localAiFacade to pass workerModelId/modelId through to inferenceProgressEmitter for both generateLocalText and preloadLocalModel paths.
  • Update LocalAiDownloadProgress UI to render approximate size and speed text when progress includes byte metrics.
  • Add unit tests for inferenceProgressEmitter derived byte metrics and LocalAiDownloadProgress size/speed display, and update existing tests for new method signatures.
services/ai/inferenceProgressEmitter.ts
services/localAiFacade.ts
components/settings/LocalAiDownloadProgress.tsx
tests/unit/services/inferenceProgressEmitter.test.ts
tests/unit/localAiFacade.test.ts
tests/unit/LocalAiDownloadProgress.test.tsx
Introduce shared formatting utilities for megabyte and MB/s values and cover them with tests.
  • Add services/downloadProgressFormat.ts with formatMegabytes and formatMegabytesPerSecond helpers.
  • Use these helpers in both VoiceModelDownloadModal and LocalAiDownloadProgress for consistent display.
  • Add unit tests for downloadProgressFormat behavior and rounding semantics.
services/downloadProgressFormat.ts
tests/unit/services/downloadProgressFormat.test.ts
components/voice/VoiceModelDownloadModal.tsx
components/settings/LocalAiDownloadProgress.tsx
Add a machine-readable WebLLM model size table and wire it into progress computation.
  • Define WEBLLM_MODEL_APPROX_MB mapping WebLlmModelId to approximate megabyte sizes, kept in sync with WEBLLM_SUPPORTED_MODELS labels.
  • Export WebLlmModelId type and use it in inferenceProgressEmitter for typed lookup.
  • Rely on this table when deriving totalBytes/loadedBytes in the WebLLM progress path.
packages/ai-core/src/index.ts
services/ai/inferenceProgressEmitter.ts
Update documentation and i18n bundles for the new progress texts and key counts.
  • Add changelog entries explaining the new downloaded/total size and speed displays and the transformers.js progress-scale fix.
  • Update README badges and text to reflect 2908 i18n keys.
  • Add new translation keys for voice.modelDownload.progressBytes and voice.modelDownload.speed and regenerate locale bundles across all languages.
CHANGELOG.md
README.md
locales/*/settings.json
public/locales/*/bundle.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 88e6ca99-a3ef-464a-a257-ac30864a9e47

📥 Commits

Reviewing files that changed from the base of the PR and between 54bd0dc and dd1c553.

📒 Files selected for processing (51)
  • CHANGELOG.md
  • README.md
  • components/settings/LocalAiDownloadProgress.tsx
  • components/voice/VoiceModelDownloadModal.tsx
  • locales/ar/settings.json
  • locales/de/settings.json
  • locales/el/settings.json
  • locales/en/settings.json
  • locales/es/settings.json
  • locales/eu/settings.json
  • locales/fa/settings.json
  • locales/fi/settings.json
  • locales/fr/settings.json
  • locales/he/settings.json
  • locales/hu/settings.json
  • locales/is/settings.json
  • locales/it/settings.json
  • locales/ja/settings.json
  • locales/ko/settings.json
  • locales/pt/settings.json
  • locales/ru/settings.json
  • locales/sv/settings.json
  • locales/zh/settings.json
  • public/locales/ar/bundle.json
  • public/locales/de/bundle.json
  • public/locales/el/bundle.json
  • public/locales/en/bundle.json
  • public/locales/es/bundle.json
  • public/locales/eu/bundle.json
  • public/locales/fa/bundle.json
  • public/locales/fi/bundle.json
  • public/locales/fr/bundle.json
  • public/locales/he/bundle.json
  • public/locales/hu/bundle.json
  • public/locales/is/bundle.json
  • public/locales/it/bundle.json
  • public/locales/ja/bundle.json
  • public/locales/ko/bundle.json
  • public/locales/pt/bundle.json
  • public/locales/ru/bundle.json
  • public/locales/sv/bundle.json
  • public/locales/zh/bundle.json
  • services/ai/inferenceProgressEmitter.ts
  • services/ai/localModelStorageService.ts
  • services/downloadProgressFormat.ts
  • services/voice/voiceCommandService.ts
  • tests/unit/LocalAiDownloadProgress.test.tsx
  • tests/unit/components/voice/VoiceModelDownloadModal.test.tsx
  • tests/unit/services/downloadProgressFormat.test.ts
  • tests/unit/services/voice/voiceCommandService.test.ts
  • types.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 259c83c1-633d-48af-ac6f-90af72101336

📥 Commits

Reviewing files that changed from the base of the PR and between efeb639 and 54bd0dc.

📒 Files selected for processing (2)
  • AUDIT.md
  • src-tauri/osv-scanner.toml

📝 Walkthrough

Walkthrough

WebLLM and voice model downloads now report byte totals and transfer speed. Voice progress uses byte ratios with percentage fallback. The UI, translations, generated locale bundles, documentation, audit records, and unit tests were updated.

Changes

Download progress reporting

Layer / File(s)Summary
WebLLM byte metrics
packages/ai-core/src/index.ts, services/ai/inferenceProgressEmitter.ts, services/localAiFacade.ts, services/downloadProgressFormat.ts
WebLLM progress uses model-size metadata to derive loaded bytes, total bytes, and transfer speed.
Voice download tracking
types.ts, services/voice/voiceCommandService.ts, components/voice/VoiceModelDownloadModal.tsx
Voice downloads use byte-based progress when available, store byte counts, calculate speed, and display localized metrics.
Progress UI and validation
components/settings/LocalAiDownloadProgress.tsx, tests/unit/*
Both progress UIs conditionally render formatted size and speed values. Tests cover formatting, state transitions, byte calculations, and percentage fallback.
Localization and supporting records
locales/*/settings.json, public/locales/*/bundle.json, CHANGELOG.md, README.md
Locale files add download metrics and diagnostic labels. Documentation records the progress changes and updated localization count.
Dependency audit exception
AUDIT.md, src-tauri/osv-scanner.toml
The accepted extract-zip@2.0.1 development-dependency exception is documented and added to the OSV scanner ignore list.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 75.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main changes: download byte and speed reporting and the voice progress scaling fix.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/333-download-progress-metrics

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

@codeant-aicodeant-aiBot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Aug 12, 2026

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • The new reportWebLlmProgress(progress, text, modelId?) signature is now a bit brittle and loosely typed (accepting any string and casting to WebLlmModelId); consider tightening this to a WebLlmModelId | undefined or an options object so the model id domain is explicit and mismatches are caught at compile time.
  • The size/speed UI blocks in LocalAiDownloadProgress and VoiceModelDownloadModal duplicate very similar layout and conditional logic; you might factor a small shared presentational component that takes the formatted strings to keep the download progress UI consistent and easier to adjust across both surfaces.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The new `reportWebLlmProgress(progress, text, modelId?)` signature is now a bit brittle and loosely typed (accepting any string and casting to `WebLlmModelId`); consider tightening this to a `WebLlmModelId | undefined` or an options object so the model id domain is explicit and mismatches are caught at compile time.
- The size/speed UI blocks in `LocalAiDownloadProgress` and `VoiceModelDownloadModal` duplicate very similar layout and conditional logic; you might factor a small shared presentational component that takes the formatted strings to keep the download progress UI consistent and easier to adjust across both surfaces.
## Individual Comments### Comment 1
<locationpath="services/ai/inferenceProgressEmitter.ts"line_range="56-58" />
<code_context>
}
- reportWebLlmProgress(progress: number, text: string): void {
+ // QNBS-v3 (#333 item 1): modelId is optional (existing callers with no known model id keep
+ // working; loadedBytes/totalBytes/bytesPerSecond simply stay null when it's absent or unknown).
+ reportWebLlmProgress(progress: number, text: string, modelId?: string): void {
if (this.snapshot.state !== 'loading') {
this.loadStartMs = Date.now();
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Use the specific WebLlmModelId type for modelId instead of string to avoid unsafe casts.
`reportWebLlmProgress` takes `modelId?: string` and then casts to `WebLlmModelId` when indexing `WEBLLM_MODEL_APPROX_MB`. If only known WebLlm IDs are expected here, update the signature to `modelId?: WebLlmModelId` so you can drop the `as WebLlmModelId` cast and get compile-time checks. If arbitrary strings must be supported, validate or guard the ID before indexing instead of casting.
Suggested implementation:
```typescript// QNBS-v3 (#333 item 1): modelId is optional (existing callers with no known model id keep// working; loadedBytes/totalBytes/bytesPerSecond simply stay null when it's absent or unknown).reportWebLlmProgress(progress: number, text: string, modelId?:WebLlmModelId): void {
``````typescriptconst approxMb =modelId?WEBLLM_MODEL_APPROX_MB[modelId] :undefined;
```1. Ensure `WebLlmModelId` is imported or declared in `services/ai/inferenceProgressEmitter.ts` (e.g., `import { WebLlmModelId } from'...';`) in line with existing type imports in this file.2. Verify all call sites of `reportWebLlmProgress` now pass a `WebLlmModelId` (or `undefined`) rather than a `string`; adjust their types/values accordingly.</issue_to_address>### Comment 2<location path="services/downloadProgressFormat.ts" line_range="5-7" /><code_context>+// (LocalAiDownloadProgress, VoiceModelDownloadModal) — kept as pure functions so both can render+// identical "X MB of Y MB" / "Z MB/s" text without duplicating the rounding/formatting logic.++/** Formats a byte count as whole megabytes, e.g. `formatMegabytes(43_000_000)``"41"`. */+export function formatMegabytes(bytes: number): string {+ return (bytes / (1024 * 1024)).toFixed(0);+}+</code_context><issue_to_address>**question:** Clarify whether the UI intends MiB or decimal MB, and adjust the divisor accordingly.This helper divides by `1024*1024` (MiB), while the UI and comments refer to MB/GB (e.g. `"~0.4 GB"`). Please confirm whether the product intends decimal MB or binary MiB and align the divisor, labels, and possibly the function name (`formatMebibytes` for binary) to avoid unit confusion and small discrepancies with external size references.</issue_to_address>### Comment 3<location path="tests/unit/components/voice/VoiceModelDownloadModal.test.tsx" line_range="175-184" /><code_context> progress: 0, estimatedSecondsRemaining: null as number | null, text: '',+ // QNBS-v3 (#333 item 1)+ loadedBytes: null as number | null,+ totalBytes: null as number | null,</code_context><issue_to_address>**suggestion (testing):** Byte-level tests cover size but not the new speed text; consider a test that exercises the MB/s displayThe existing progress-display tests exercise the MB text and `isDownloading` wiring, but not the new `speedText` (`voice.modelDownload.speed`). Since the `useEffect` derives `bytesPerSecond` from `loadedBytes` and `Date.now`, add a test that:- uses `vi.useFakeTimers()` / `vi.setSystemTime()` to simulate elapsed time > 0.5s,- initializes `mockVoiceState.wasmModelDownloadLoadedBytes` before render and advances time so the effect re-runs,- asserts that the expected "X.Y MB/s" speed label is rendered.This will cover the MB/s path and guard against regressions in timing/formatting logic for the download UX.</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment threadservices/ai/inferenceProgressEmitter.ts
Comment threadservices/downloadProgressFormat.ts
Comment threadtests/unit/components/voice/VoiceModelDownloadModal.test.tsx
Comment threadservices/voice/voiceCommandService.ts
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix AI/voice model download progress with bytes + speed metrics

🐞 Bug fix✨ Enhancement🧪 Tests📝 Documentation⚙️ Configuration changes🕐 40+ Minutes

Grey Divider

AI Description

• Fix voice WASM download progress scaling (transformers.js progress is 0–100, not 0–1).
• Show downloaded/total MB and MB/s in voice + local-AI download progress UIs.
• Add WebLLM per-model size table to estimate byte progress when real bytes are unavailable.
Diagram

graph TD
ext1{{"transformers.js"}} --> svc1["VoiceCommandService"] --> store[("Settings store")] --> ui1(["VoiceModelDownloadModal"])
ext2{{"WebLLM"}} --> svc2["localAiFacade"] --> svc3["inferenceProgressEmitter (+ size table)"] --> ui2(["LocalAiDownloadProgress"])
ui1 --> util[["downloadProgressFormat"]]
ui2 --> util
subgraph Legend
direction LR
_ext{{"External"}} ~~~ _svc["Service"] ~~~ _store[("Store")] ~~~ _ui(["UI"]) ~~~ _util[["Util"]]
end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Patch/fork WebLLM to expose real byte counts
  • ➕ Would display measured bytes/speed instead of estimates
  • ➕ Avoids maintaining a separate model-size lookup table
  • ➖ Requires maintaining a fork/patch across library upgrades
  • ➖ May be brittle if progress internals change; higher long-term maintenance cost
2. Infer totals via network interception (Service Worker / fetch wrapper)
  • ➕ Could provide real Content-Length and measured throughput
  • ➕ Reusable for other downloads
  • ➖ Harder in worker-driven/WebGPU flows; may not see underlying requests
  • ➖ Adds complexity and potential security/caching edge cases

Recommendation: Keep the PR’s approach: use real transformer bytes for voice (correct + low risk), and a clearly-labeled size-table estimate for WebLLM given current library API limitations. The alternatives add significant maintenance/complexity compared to the UX gain.

Files changed (54) +1023 / -387

Enhancement (5) +100 / -8
LocalAiDownloadProgress.tsxShow estimated downloaded/total MB and MB/s for WebLLM model downloads+26/-1

Show estimated downloaded/total MB and MB/s for WebLLM model downloads

• Renders size and speed lines when the progress snapshot includes derived byte metrics, using shared formatting helpers. Adjusts layout spacing when the extra line is present.

components/settings/LocalAiDownloadProgress.tsx

index.tsAdd WebLLM per-model approximate size table+15/-0

Add WebLLM per-model approximate size table

• Introduces WEBLLM_MODEL_APPROX_MB keyed by supported model IDs, mirroring label text sizes. Enables downstream estimation of downloaded/total MB for WebLLM progress.

packages/ai-core/src/index.ts

inferenceProgressEmitter.tsDerive WebLLM loaded/total bytes and speed from model size table+44/-5

Derive WebLLM loaded/total bytes and speed from model size table

• Extends the progress snapshot with derived byte metrics and resets them on ready/error. Accepts an optional modelId to look up approximate totals and compute bytesPerSecond.

services/ai/inferenceProgressEmitter.ts

localAiFacade.tsPass modelId into WebLLM progress reporting for byte estimation+9/-2

Pass modelId into WebLLM progress reporting for byte estimation

• Threads the active WebLLM modelId into inferenceProgressEmitter.reportWebLlmProgress so the emitter can compute approximate byte totals.

services/localAiFacade.ts

types.tsExtend VoiceSettings with real byte counts for voice model downloads+6/-0

Extend VoiceSettings with real byte counts for voice model downloads

• Adds optional wasmModelDownloadLoadedBytes and wasmModelDownloadTotalBytes to settings types, documenting that these are real transformer-reported bytes.

types.ts

Bug fix (2) +62 / -1
VoiceModelDownloadModal.tsxShow real loaded/total MB and computed MB/s during voice model download+43/-0

Show real loaded/total MB and computed MB/s during voice model download

• Reads loaded/total byte counts from settings state and computes an elapsed-time-based transfer rate. Displays byte and speed text beneath the percent progress when available.

components/voice/VoiceModelDownloadModal.tsx

voiceCommandService.tsFix transformers.js progress scaling and dispatch real byte counts+19/-1

Fix transformers.js progress scaling and dispatch real byte counts

• Correctly interprets transformers.js progress payload (progress is 0–100 percent) by preferring loaded/total ratio and falling back to progress/100. Dispatches loaded/total bytes into settings state when present.

services/voice/voiceCommandService.ts

Refactor (1) +13 / -0
downloadProgressFormat.tsAdd shared MB and MB/s formatting helpers+13/-0

Add shared MB and MB/s formatting helpers

• Introduces pure formatting utilities used by both download progress UIs to keep rounding/output consistent.

services/downloadProgressFormat.ts

Tests (6) +314 / -10
LocalAiDownloadProgress.test.tsxCover size/speed rendering for local AI download progress UI+104/-6

Cover size/speed rendering for local AI download progress UI

• Updates snapshot shape to include byte metrics and adds assertions that size/speed text appears only when metrics are present.

tests/unit/LocalAiDownloadProgress.test.tsx

VoiceModelDownloadModal.test.tsxCover voice modal byte progress text behavior+41/-2

Cover voice modal byte progress text behavior

• Adds selector-aware state mocking and verifies loaded/total text appears only when byte counts are known.

tests/unit/components/voice/VoiceModelDownloadModal.test.tsx

localAiFacade.test.tsUpdate expectations for modelId-threaded progress calls+2/-2

Update expectations for modelId-threaded progress calls

• Adjusts assertions to account for the new modelId argument passed into reportWebLlmProgress.

tests/unit/localAiFacade.test.ts

downloadProgressFormat.test.tsAdd unit tests for MB and MB/s formatting helpers+29/-0

Add unit tests for MB and MB/s formatting helpers

• Validates rounding and formatting behavior for both byte count and byte-rate helpers.

tests/unit/services/downloadProgressFormat.test.ts

inferenceProgressEmitter.test.tsAdd unit tests for derived WebLLM byte metrics+52/-0

Add unit tests for derived WebLLM byte metrics

• Covers known-model computations, unknown/no-model behavior, and clearing on ready/error.

tests/unit/services/inferenceProgressEmitter.test.ts

voiceCommandService.test.tsAdd unit tests pinning the voice progress-scale fix and byte threading+86/-0

Add unit tests pinning the voice progress-scale fix and byte threading

• Mocks transformers pipeline progress payloads to verify correct 0–1 fraction derivation from bytes, fallback behavior, and Redux dispatch of loaded/total bytes.

tests/unit/services/voice/voiceCommandService.test.ts

Documentation (2) +18 / -4
CHANGELOG.mdDocument byte/speed progress improvements and voice progress-scale fix+14/-0

Document byte/speed progress improvements and voice progress-scale fix

• Adds release notes describing new byte/speed display for voice and WebLLM downloads, and explains the root-cause fix for the voice progress bar clamping issue.

CHANGELOG.md

README.mdUpdate i18n key counts to reflect new strings+4/-4

Update i18n key counts to reflect new strings

• Bumps documented i18n key totals (badges/metrics) to account for newly added download progress strings.

README.md

Other (38) +516 / -364
settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/ar/settings.json

settings.jsonAdd i18n strings for download size/speed+4/-0

Add i18n strings for download size/speed

• Adds new keys for local AI download size/speed and voice model progress bytes/speed.

locales/de/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/el/settings.json

settings.jsonAdd i18n strings for download size/speed+4/-0

Add i18n strings for download size/speed

• Adds English strings for local AI download size/speed and voice model progress bytes/speed.

locales/en/settings.json

settings.jsonAdd i18n strings for download size/speed+4/-0

Add i18n strings for download size/speed

• Adds new keys for local AI download size/speed and voice model progress bytes/speed.

locales/es/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/eu/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/fa/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/fi/settings.json

settings.jsonAdd i18n strings for download size/speed+4/-0

Add i18n strings for download size/speed

• Adds new keys for local AI download size/speed and voice model progress bytes/speed.

locales/fr/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/he/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/hu/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/is/settings.json

settings.jsonAdd i18n strings for download size/speed+4/-0

Add i18n strings for download size/speed

• Adds new keys for local AI download size/speed and voice model progress bytes/speed.

locales/it/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/ja/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/ko/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/pt/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/ru/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/sv/settings.json

settings.jsonAdd i18n strings for download size/speed+17/-13

Add i18n strings for download size/speed

• Adds translations/keys for local AI download size/speed and voice model progress bytes/speed. Includes minor key reordering to maintain locale parity.

locales/zh/settings.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/ar/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+4/-0

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys.

public/locales/de/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/el/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+4/-0

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys.

public/locales/en/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+4/-0

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys.

public/locales/es/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/eu/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/fa/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/fi/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+4/-0

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys.

public/locales/fr/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/he/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/hu/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/is/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+4/-0

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys.

public/locales/it/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/ja/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/ko/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/pt/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/ru/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/sv/bundle.json

bundle.jsonRegenerate locale bundle with new download size/speed keys+17/-13

Regenerate locale bundle with new download size/speed keys

• Updates compiled i18n bundle to include the new local AI and voice download byte/speed keys, plus parity-driven ordering changes.

public/locales/zh/bundle.json

Comment threadcomponents/voice/VoiceModelDownloadModal.tsx
@codeant-ai

codeant-aiBot commented Aug 12, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit:dd1c553b
Scan Time: 2026-08-13 05:43:58 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality GateStatusDetails
Secrets✅ PASSED0 secrets found
Duplicate Code✅ PASSED0.0% duplicated
SAST✅ PASSEDNo security issues
Bugs✅ PASSEDRating S: 2 bugs
IAC✅ PASSEDRating S: No issues

View Full Results

@qodo-code-review

qodo-code-reviewBot commented Aug 12, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0)📘 Rule violations (2)📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Stale byte counts persist across voice download retry✓ Resolved🐞 Bug≡ Correctness
Description
The voice model download flow resets only wasmModelDownloadProgress on failure/cancel while
leaving wasmModelDownloadLoadedBytes and wasmModelDownloadTotalBytes untouched in Redux, so a
retry, switching to the other model, or reopening the modal can briefly render stale “X MB of Y MB”
and compute a misleading speed from prior bytes against a freshly-reset timer. Because the byte
fields are only written when progress callbacks include byte counts and are never cleared at
download lifecycle boundaries, the merge-based reducer retains the previous attempt’s values until
the next byte-bearing progress tick arrives.
Code

services/voice/voiceCommandService.ts[R621-630]

 this.d(
settingsActions.setVoiceSettings({
wasmModelDownloadProgress: Math.min(0.95, pct),
+ ...(hasBytes+ ? {+ wasmModelDownloadLoadedBytes: p.loaded,+ wasmModelDownloadTotalBytes: p.total,+ }+ : {}),
}),
Relevance

●●● Strong

Clearing stale download state on cancel/retry matches prior accepted voice download state-reset
fixes.

PR-#109

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The cited code shows wasmModelDownloadLoadedBytes/wasmModelDownloadTotalBytes are assigned only
in the hasBytes branch of the onProgress handler, and there is no corresponding reset in the
download lifecycle actions (start/completion/failure/cancel). In voiceCommandService.ts, the
catch/error path dispatches updates that reset only wasmModelDownloadProgress (and set an error)
but do not clear the byte fields, and VoiceModelDownloadModal.tsx’s handleCancel similarly
resets only wasmModelDownloadProgress. Since setVoiceSettings merges partial voice state rather
than replacing it, the previously stored byte values persist in Redux, and because the modal selects
and renders these fields directly, subsequent attempts can display retained size/loaded bytes (and
use them in speed calculation) until new progress data overwrites them.

services/voice/voiceCommandService.ts[657-666]
components/voice/VoiceModelDownloadModal.tsx[92-100]
components/voice/VoiceModelDownloadModal.tsx[34-60]
services/voice/voiceCommandService.ts[587-663]
features/settings/settingsSlice.ts[250-252]
components/voice/VoiceModelDownloadModal.tsx[113-125]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Reset the voice download byte metrics (`wasmModelDownloadLoadedBytes` and `wasmModelDownloadTotalBytes`) at download lifecycle boundaries (start, cancel, failure, and completion), not just `wasmModelDownloadProgress`. This prevents retries, switching to a different voice model, or reopening the modal from briefly showing stale “X MB of Y MB” values and computing misleading download speed from prior-attempt bytes.
## Issue Context
The byte fields were introduced to support byte-level progress UI in `VoiceModelDownloadModal.tsx` and are currently only written when the `onProgress` callback reports byte counts (`hasBytes` branch) in `voiceCommandService.ts`. Error handling in `voiceCommandService.ts` and cancellation in `VoiceModelDownloadModal.tsx` reset only `wasmModelDownloadProgress`, and because `setVoiceSettings` merges partial state, any previously-set byte values remain in Redux and are immediately selected/rendered by the modal on the next attempt until a new byte-bearing progress tick arrives. Ensure the speed baseline is tied to the current attempt (i.e., bytes and any timing assumptions start from a clean slate per attempt).
## Fix Focus Areas
- services/voice/voiceCommandService.ts[587-663]
- services/voice/voiceCommandService.ts[657-666]
- components/voice/VoiceModelDownloadModal.tsx[34-60]
- components/voice/VoiceModelDownloadModal.tsx[62-100]
- components/voice/VoiceModelDownloadModal.tsx[92-100]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Duplicate hardcoded WEBLLM model size tables✓ Resolved🐞 Bug⚙ Maintainability
Description
This PR adds WEBLLM_MODEL_APPROX_MB in packages/ai-core/src/index.ts, duplicating an
already-existing, differently-located WEBLLM_MODEL_APPROX_MB table in
services/ai/localModelStorageService.ts with the same model IDs and MB values. Maintaining both
tables (and related WEBLLM_SUPPORTED_MODELS labels) creates multiple sources of truth that can
silently drift, causing download-progress totals to disagree with storage-warning UI.
Code

packages/ai-core/src/index.ts[R190-198]

+export const WEBLLM_MODEL_APPROX_MB: Record<WebLlmModelId, number> = {+ 'Qwen2.5-0.5B-Instruct-q4f16_1-MLC': 400,+ 'Llama-3.2-1B-Instruct-q4f16_1-MLC': 700,+ 'Llama-3.2-3B-Instruct-q4f16_1-MLC': 1800,+ 'Phi-4-mini-instruct-q4f16_1-MLC': 2300,+ 'gemma-3-1b-it-q4f16_1-MLC': 800,+ 'gemma-3-4b-it-q4f32_1-MLC': 4900,+ 'Llama-3.3-70B-Instruct-q3f16_1-MLC': 35000,+};
Relevance

●● Moderate

Deduplicating model-size tables is architectural/ownership-level; no close precedent found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
services/ai/localModelStorageService.ts already exports WEBLLM_MODEL_APPROX_MB with the listed
identical model keys/values (e.g., Qwen2.5-0.5B-Instruct-q4f16_1-MLC: 400, Llama-3.2-1B: 700,
Llama-3.2-3B: 1800, Phi-4-mini: 2300, gemma-3-1b: 800, gemma-3-4b: 4900, Llama-3.3-70B: 35000) and
this is used by components/settings/LocalAiSection.tsx to show storage size warnings. The PR
introduces a second export with the same name and values in packages/ai-core/src/index.ts, which
is then consumed by services/ai/inferenceProgressEmitter.ts for byte/progress estimation, meaning
the progress UI and storage warnings now depend on different, manually-synchronized literals with no
shared source of truth or type-level enforcement to keep them consistent.

services/ai/localModelStorageService.ts[21-29]
packages/ai-core/src/index.ts[190-198]
components/settings/LocalAiSection.tsx[18-23]
packages/ai-core/src/index.ts[185-198]
services/ai/localModelStorageService.ts[19-29]
components/settings/LocalAiSection.tsx[282-306]
services/ai/inferenceProgressEmitter.ts[67-71]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
The PR introduces a new `WEBLLM_MODEL_APPROX_MB` table in `packages/ai-core/src/index.ts` that duplicates the identically named (and identically valued) table already present in `services/ai/localModelStorageService.ts`. This creates multiple sources of truth for approximate WebLLM model sizes, increasing the risk that future model additions/size changes will make the download-progress totals and storage-warning UI disagree.
## Issue Context
- `services/ai/localModelStorageService.ts` already defines/exports `WEBLLM_MODEL_APPROX_MB` and it is used for the pre-download storage-size warning (via `components/settings/LocalAiSection.tsx`).
- The PR-added `WEBLLM_MODEL_APPROX_MB` in `packages/ai-core/src/index.ts` is consumed by `services/ai/inferenceProgressEmitter.ts` for the new byte/progress estimation.
- Consolidate the approximate size metadata into a single canonical source (preferably dependency-neutral in `ai-core`), and have other modules import or re-export it rather than maintaining duplicate literals.
## Fix Focus Areas
- packages/ai-core/src/index.ts[185-198]
- services/ai/localModelStorageService.ts[19-29]
- services/ai/inferenceProgressEmitter.ts[5-71]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Voice download speed uses cumulative bytes, not delta✓ Resolved🐞 Bug≡ Correctness
Description
In VoiceModelDownloadModal.tsx, bytesPerSecond is computed as total loadedBytes divided by total
elapsed time since download start, not the delta of bytes since the last measurement; this yields an
average-since-start rate that will not reflect real-time speed fluctuations and, if a browser
cache/cheap warm-start delivers a near-instant partial loaded value, can produce artificially
inflated 'speed' text for a brief period. The comment in inferenceProgressEmitter.ts describes an
identical average-based (not instantaneous) approach for the WebLLM path, but this is presented to
users as a live transfer speed.
Code

components/voice/VoiceModelDownloadModal.tsx[R57-59]

+ if (loadedBytes == null) return;+ const elapsedSeconds = (Date.now() - downloadStartMsRef.current) / 1000;+ setBytesPerSecond(elapsedSeconds > 0.5 ? loadedBytes / elapsedSeconds : null);
Relevance

●● Moderate

Instantaneous vs average speed is a product choice; no close historical decision found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The effect computes elapsedSeconds as (Date.now() - downloadStartMsRef.current)/1000 (time since the
whole download started) and divides the current cumulative loadedBytes by that, rather than tracking
a previous loadedBytes/timestamp pair to compute an instantaneous rate. This average-since-start
metric can look wrong to users mid-download if throughput is uneven (e.g., a slow start then a fast
middle will show a rate lower than the actual current rate, or vice versa), which is a materially
different number than the 'speed' label implies.

components/voice/VoiceModelDownloadModal.tsx[43-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`bytesPerSecond` in `VoiceModelDownloadModal.tsx` is calculated as cumulative `loadedBytes` divided by total elapsed time since the download started, which is an average-since-start rate rather than a live/instantaneous transfer speed, and can be misleading when throughput is uneven during the download.
## Issue Context
The new speed display feature added in this PR is intended to show the user a live transfer speed (labelled 'X MB/s'), but the calculation method produces an average rather than a current rate.
## Fix Focus Areas
- components/voice/VoiceModelDownloadModal.tsx[43-60]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Estimated metrics appear measured✓ Resolved🐞 Bug≡ Correctness
Description
The WebLLM loaded bytes and speed are derived from a hand-authored approximate total, but the UI
renders ordinary byte and MB/s values without a visible estimate indicator. Users cannot distinguish
these synthetic values from the measured telemetry shown by the voice download UI.
Code

components/settings/LocalAiDownloadProgress.tsx[R52-55]

+ const sizeText =+ progress.loadedBytes != null && progress.totalBytes != null+ ? t<string>('settings.ai.localAi.downloadSize', {+ loaded: formatMegabytes(progress.loadedBytes),
Relevance

●● Moderate

UX/labeling change (estimate indicator) is subjective; no close repo precedent found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The emitter documents and computes all three metrics from progress × WEBLLM_MODEL_APPROX_MB, while
the component and English locale render them without words or symbols indicating approximation. The
changelog itself describes these metrics as approximate.

services/ai/inferenceProgressEmitter.ts[14-20]
services/ai/inferenceProgressEmitter.ts[67-71]
components/settings/LocalAiDownloadProgress.tsx[50-64]
locales/en/settings.json[162-163]
CHANGELOG.md[29-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Make the WebLLM UI visibly identify derived byte counts and speed as estimates.
## Issue Context
Source comments are not visible to users. Add localized approximation wording or symbols to both size and speed while leaving the voice UI unchanged because its values are measured.
## Fix Focus Areas
- components/settings/LocalAiDownloadProgress.tsx[50-64]
- locales/en/settings.json[162-163]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Missing QNBS in localAiFacade.test.ts📘 Rule violation§ Compliance
Description
tests/unit/localAiFacade.test.ts was modified (test expectations changed) without adding any
QNBS-v3 annotation comment in the diff.
Code

tests/unit/localAiFacade.test.ts[243]

+ expect(progSpy).toHaveBeenCalledWith(0.5, 'half', 'm');
Relevance

● Weak

Adding QNBS-v3 annotations to tests was previously rejected in similar situations.

PR-#290

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2524933 requires at least one QNBS-v3 annotation comment in the diff for each
modified source file with substantive logic changes. This file includes a modified expectation line
without any accompanying QNBS-v3 annotation added in the change.

Rule 2524933: Require QNBS-v3 annotation comments on all non-trivial code changes
tests/unit/localAiFacade.test.ts[243-243]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`tests/unit/localAiFacade.test.ts` contains a substantive test logic change but the diff does not introduce a QNBS-v3 annotation comment for this file.
## Issue Context
The assertion was updated to include an additional argument, changing test behavior/expectations.
## Fix Focus Areas
- tests/unit/localAiFacade.test.ts[239-244]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View low (2)
6. QNBS comments missing bracket format 📘 Rule violation⚙ Maintainability
Description
New // QNBS-v3 annotations added in this PR do not follow the required `// QNBS-v3: [reason /
impact / creative value]` format. This weakens auditability because annotations are not
machine-parseable per the checklist.
Code

services/downloadProgressFormat.ts[R1-3]

+// QNBS-v3 (#333 item 1): shared formatting for the two model-download progress UIs+// (LocalAiDownloadProgress, VoiceModelDownloadModal) — kept as pure functions so both can render+// identical "X MB of Y MB" / "Z MB/s" text without duplicating the rounding/formatting logic.
Relevance

● Weak

Team recently rejected enforcing stricter QNBS-v3 comment formatting changes.

PR-#339

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2524954 requires every // QNBS-v3 line comment in .ts/.tsx/.js/.jsx to exactly
match // QNBS-v3: [reason / impact / creative value]. The cited added comments use `// QNBS-v3
(#333 item 1): ...` (missing the required bracketed triple-segment format), so they violate the
rule.

Rule 2524954: Enforce QNBS-v3 annotation format in TypeScript and JavaScript files
services/downloadProgressFormat.ts[1-3]
components/settings/LocalAiDownloadProgress.tsx[50-52]
services/voice/voiceCommandService.ts[608-614]
packages/ai-core/src/index.ts[185-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
QNBS-v3 annotations added by this PR are not in the required format `// QNBS-v3: [reason / impact / creative value]`.
## Issue Context
The checklist requires the exact prefix and bracketed triple-segment content so annotations can be validated/parsed consistently.
## Fix Focus Areas
- services/downloadProgressFormat.ts[1-3]
- components/settings/LocalAiDownloadProgress.tsx[50-52]
- services/voice/voiceCommandService.ts[608-614]
- packages/ai-core/src/index.ts[185-190]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Missing QNBS in downloadProgressFormat.test.ts✓ Resolved📘 Rule violation§ Compliance
Description
A new test file with substantive logic changes was added without any // QNBS-v3 annotation comment
in the diff, violating the per-file annotation requirement.
Code

tests/unit/services/downloadProgressFormat.test.ts[R1-4]

+import { describe, expect, it } from 'vitest';+import {+ formatMegabytes,+ formatMegabytesPerSecond,
Relevance

● Weak

Per-file QNBS-v3 annotations in tests have been rejected before; likely skipped again.

PR-#290

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2524933 requires at least one // QNBS-v3 annotation comment in the diff for each
modified source file with substantive logic changes. The added test file content begins immediately
with imports and contains no QNBS-v3 annotation comment in the added lines.

Rule 2524933: Require QNBS-v3 annotation comments on all non-trivial code changes
tests/unit/services/downloadProgressFormat.test.ts[1-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`tests/unit/services/downloadProgressFormat.test.ts` was added without any QNBS-v3 annotation comment in the diff, but the compliance checklist requires at least one QNBS-v3 comment for each modified source file with substantive logic changes.
## Issue Context
This PR introduces a new unit test file with executable test logic.
## Fix Focus Areas
- tests/unit/services/downloadProgressFormat.test.ts[1-8]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Compliance rules (platform): 123 rules
Review mode: 🧠 Deep: This spans multiple independent download paths, UI/state plumbing, formatting, model-size metadata, and localization with substantial logic density and many easy-to-miss integration points.

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment threadcomponents/settings/LocalAiDownloadProgress.tsx Outdated
Comment threadservices/voice/voiceCommandService.ts
Comment threadpackages/ai-core/src/index.ts
Comment threadcomponents/voice/VoiceModelDownloadModal.tsx Outdated

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
packages/ai-core/src/index.ts (1)

185-189: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keep each QNBS-v3 comment on one physical line.

The new comments wrap across multiple physical lines. Replace each with one concise why-comment.

  • packages/ai-core/src/index.ts#L185-L189: collapse the model-size rationale into one line.
  • services/ai/inferenceProgressEmitter.ts#L14-L17: collapse the derived-byte rationale into one line.
  • services/ai/inferenceProgressEmitter.ts#L56-L57: collapse the optional-model-ID rationale into one line.
  • services/localAiFacade.ts#L159-L161: collapse the workerModelId rationale into one line.
  • types.ts#L612-L614: collapse the byte-source rationale into one line.
  • services/voice/voiceCommandService.ts#L608-L613: collapse the progress-scale rationale into one line.

As per coding guidelines, “For every non-trivial code change, add one single-line QNBS-v3 comment explaining why” and “never wrap the comment across physical lines.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ai-core/src/index.ts` around lines 185 - 189, Collapse each
multi-line QNBS-v3 rationale into one concise physical-line why-comment:
packages/ai-core/src/index.ts lines 185-189 for model-size metadata;
services/ai/inferenceProgressEmitter.ts lines 14-17 for derived byte estimates
and lines 56-57 for the optional model ID; services/localAiFacade.ts lines
159-161 for workerModelId; types.ts lines 612-614 for the byte source; and
services/voice/voiceCommandService.ts lines 608-613 for the progress scale.
Preserve each comment’s rationale and the required QNBS-v3 prefix; make no other
changes.

Source: Coding guidelines

services/downloadProgressFormat.ts (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the required single-line QNBS-v3 rationale format.

The changed TypeScript and TSX surfaces either omit the required rationale or split it across physical lines. Normalize each location to one single-line // QNBS-v3: [Grund / Impact / Kreativer Mehrwert] comment.

  • services/downloadProgressFormat.ts#L1-L3: collapse the shared-formatting rationale into one line.
  • tests/unit/services/downloadProgressFormat.test.ts#L1-L29: add one QNBS-v3 rationale before the imports.
  • components/voice/VoiceModelDownloadModal.tsx#L35-L36: collapse the byte-selector rationale into one line.
  • components/voice/VoiceModelDownloadModal.tsx#L43-L44: collapse the speed-calculation rationale into one line.
  • components/voice/VoiceModelDownloadModal.tsx#L113-L114: collapse the display-formatting rationale into one line.
  • components/settings/LocalAiDownloadProgress.tsx#L50-L51: collapse the approximate-byte rationale into one line.

As per coding guidelines, every non-trivial TypeScript or TSX change requires one single-line QNBS-v3 comment in the specified format.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@services/downloadProgressFormat.ts` around lines 1 - 3, Normalize the QNBS-v3
rationale comments to one physical line using the exact // QNBS-v3: [Grund /
Impact / Kreativer Mehrwert] format. Update services/downloadProgressFormat.ts
(lines 1-3), VoiceModelDownloadModal.tsx (lines 35-36, 43-44, and 113-114), and
LocalAiDownloadProgress.tsx (lines 50-51) by collapsing each existing rationale;
add one rationale before imports in
tests/unit/services/downloadProgressFormat.test.ts (lines 1-29).

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/voice/VoiceModelDownloadModal.tsx`:
- Around line 43-60: Update the speed-tracking effect using downloadStartMsRef
and bytesPerSecond so it stores the previous loadedBytes value and timestamp,
resets both when isDownloading becomes false, and computes bytes per second from
consecutive byte and time deltas after samples begin. Avoid dividing cumulative
loadedBytes by the total startup-inclusive elapsed time, while preserving the
existing null/initial-sample handling.
In `@public/locales/el/bundle.json`:
- Around line 1808-1809: Translate the download size and speed labels,
preserving the {{loaded}}, {{total}}, and {{speed}} interpolation tokens, in
public/locales/el/bundle.json lines 1808-1809 and 2470-2472 into Greek;
public/locales/eu/bundle.json lines 1808-1809 and 2470-2472 into Basque;
public/locales/fa/bundle.json lines 1808-1809 and 2470-2472 into Persian; and
public/locales/fi/bundle.json lines 1808-1809 and 2470-2472 into Finnish. Update
both local AI and voice entries at every listed site, with no English
user-facing values remaining.
In `@public/locales/pt/bundle.json`:
- Around line 1808-1809: Translate the English “of” connector in the affected
progress strings, updating the source locale values for
settings.ai.localAi.downloadSize and voice.modelDownload.progressBytes in
public/locales/pt/bundle.json (1808-1809, 2470-2472),
public/locales/ru/bundle.json (1808-1809, 2470-2472),
public/locales/sv/bundle.json (1808-1809, 2470-2472), and
public/locales/zh/bundle.json (1808-1809, 2470-2472); then regenerate each
corresponding runtime bundle.
In `@services/downloadProgressFormat.ts`:
- Around line 10-13: Update formatMegabytesPerSecond to accept the active locale
and format the megabytes value with Intl.NumberFormat using one fractional digit
instead of toFixed(1). Update both callers to pass their locale through, and add
a test covering comma-decimal formatting for a non-English locale.
In `@services/voice/voiceCommandService.ts`:
- Around line 621-630: Update the voice download flow around setVoiceSettings to
reset wasmModelDownloadLoadedBytes and wasmModelDownloadTotalBytes when starting
a download, and clear them again on cancellation, including byte-less downloads.
Preserve byte progress updates when p.loaded and p.total are available, and add
a test covering sequential downloads where the later download has no byte
metrics.
---
Nitpick comments:
In `@packages/ai-core/src/index.ts`:
- Around line 185-189: Collapse each multi-line QNBS-v3 rationale into one
concise physical-line why-comment: packages/ai-core/src/index.ts lines 185-189
for model-size metadata; services/ai/inferenceProgressEmitter.ts lines 14-17 for
derived byte estimates and lines 56-57 for the optional model ID;
services/localAiFacade.ts lines 159-161 for workerModelId; types.ts lines
612-614 for the byte source; and services/voice/voiceCommandService.ts lines
608-613 for the progress scale. Preserve each comment’s rationale and the
required QNBS-v3 prefix; make no other changes.
In `@services/downloadProgressFormat.ts`:
- Around line 1-3: Normalize the QNBS-v3 rationale comments to one physical line
using the exact // QNBS-v3: [Grund / Impact / Kreativer Mehrwert] format. Update
services/downloadProgressFormat.ts (lines 1-3), VoiceModelDownloadModal.tsx
(lines 35-36, 43-44, and 113-114), and LocalAiDownloadProgress.tsx (lines 50-51)
by collapsing each existing rationale; add one rationale before imports in
tests/unit/services/downloadProgressFormat.test.ts (lines 1-29).
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 91834030-57e4-4d9b-b8a3-69d188612a99

📥 Commits

Reviewing files that changed from the base of the PR and between dce75b3 and efeb639.

📒 Files selected for processing (54)
  • CHANGELOG.md
  • README.md
  • components/settings/LocalAiDownloadProgress.tsx
  • components/voice/VoiceModelDownloadModal.tsx
  • locales/ar/settings.json
  • locales/de/settings.json
  • locales/el/settings.json
  • locales/en/settings.json
  • locales/es/settings.json
  • locales/eu/settings.json
  • locales/fa/settings.json
  • locales/fi/settings.json
  • locales/fr/settings.json
  • locales/he/settings.json
  • locales/hu/settings.json
  • locales/is/settings.json
  • locales/it/settings.json
  • locales/ja/settings.json
  • locales/ko/settings.json
  • locales/pt/settings.json
  • locales/ru/settings.json
  • locales/sv/settings.json
  • locales/zh/settings.json
  • packages/ai-core/src/index.ts
  • public/locales/ar/bundle.json
  • public/locales/de/bundle.json
  • public/locales/el/bundle.json
  • public/locales/en/bundle.json
  • public/locales/es/bundle.json
  • public/locales/eu/bundle.json
  • public/locales/fa/bundle.json
  • public/locales/fi/bundle.json
  • public/locales/fr/bundle.json
  • public/locales/he/bundle.json
  • public/locales/hu/bundle.json
  • public/locales/is/bundle.json
  • public/locales/it/bundle.json
  • public/locales/ja/bundle.json
  • public/locales/ko/bundle.json
  • public/locales/pt/bundle.json
  • public/locales/ru/bundle.json
  • public/locales/sv/bundle.json
  • public/locales/zh/bundle.json
  • services/ai/inferenceProgressEmitter.ts
  • services/downloadProgressFormat.ts
  • services/localAiFacade.ts
  • services/voice/voiceCommandService.ts
  • tests/unit/LocalAiDownloadProgress.test.tsx
  • tests/unit/components/voice/VoiceModelDownloadModal.test.tsx
  • tests/unit/localAiFacade.test.ts
  • tests/unit/services/downloadProgressFormat.test.ts
  • tests/unit/services/inferenceProgressEmitter.test.ts
  • tests/unit/services/voice/voiceCommandService.test.ts
  • types.ts

Comment threadcomponents/voice/VoiceModelDownloadModal.tsx Outdated
Comment threadpublic/locales/el/bundle.json Outdated
Comment threadpublic/locales/pt/bundle.json Outdated
Comment threadservices/downloadProgressFormat.ts Outdated
Comment threadservices/voice/voiceCommandService.ts
…able)
Freshly published/reviewed 2026-08-12; latest extract-zip release (2.0.1) has
no patched version to override to. Transitive devDependency of
@puppeteer/browsers (Playwright's browser-binary downloader) — only ever
extracts Playwright/Chromium's own CDN-hosted zip releases, never a user- or
attacker-supplied archive, and ships in no production bundle. Documented in
src-tauri/osv-scanner.toml's IgnoredVulns list (existing pattern for
unfixable transitive findings) and AUDIT.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@codecov

codecovBot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.65217% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
components/voice/VoiceModelDownloadModal.tsx95.83%0 Missing and 1 partial ⚠️
services/ai/inferenceProgressEmitter.ts85.71%0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Addresses all 14 CodeRabbit/CodeAnt/Qodo/Sourcery review findings on PR #346:
- services/voice/voiceCommandService.ts + components/voice/VoiceModelDownloadModal.tsx:
explicitly clear wasmModelDownloadLoadedBytes/wasmModelDownloadTotalBytes at every
download lifecycle boundary (start, cancel, error, completion) — setVoiceSettings
merges partial state, so a prior attempt's bytes previously lingered in Redux until
the next byte-bearing progress tick arrived, showing stale size/speed on retry or
when switching models. types.ts: widened both fields to `number | undefined` so
they can be explicitly cleared under exactOptionalPropertyTypes.
- components/voice/VoiceModelDownloadModal.tsx: bytesPerSecond is now delta-based
(bytes and timestamp of the previous sample, reset when downloading stops) instead
of cumulative-loaded-bytes / elapsed-since-start, which included startup time and
smoothed real throughput changes into a misleading average presented as a live rate.
- services/downloadProgressFormat.ts: formatMegabytesPerSecond renamed to
megabytesPerSecond and now returns a number instead of a toFixed() string — the
decimal separator is locale-dependent (comma vs period), so callers now format it
via formatNumber() from useTranslation() instead of a hardcoded period.
- packages/ai-core/src/index.ts / services/ai/localModelStorageService.ts: removed
the duplicate WEBLLM_MODEL_APPROX_MB literal from localModelStorageService — it now
re-exports the canonical ai-core table instead of maintaining a second, driftable
copy of the same model-id-to-size mapping.
- services/ai/inferenceProgressEmitter.ts: replaced an unsafe `as WebLlmModelId` cast
with a proper type guard (isKnownWebLlmModel) — modelId stays `string` since real
call sites in localAiFacade.ts pass broader adaptive/fallback model ids, not just
the curated WebLlmModelId union, so narrowing the param type would have broken them.
- components/settings/LocalAiDownloadProgress.tsx: prefixes WebLLM's derived/estimated
byte and speed values with "~" so users can distinguish them from the voice download
UI's measured bytes, without adding new i18n keys (reuses the tilde convention
already used in WEBLLM_SUPPORTED_MODELS' own size labels).
- 8 locales (el, eu, fa, fi, pt, ru, sv, zh): translated the "of" connector in
settings.ai.localAi.downloadSize / voice.modelDownload.progressBytes, mirroring
this file's own already-translated storageUsage key's per-locale phrasing exactly.
MB/s stays untranslated — a universal unit abbreviation, same as every other locale
including production ones (de/es/fr/it/ar/he/...) already leave it.
- New/updated tests: voiceCommandService (3 new lifecycle-boundary byte-clearing
cases), VoiceModelDownloadModal (delta-speed test, byte-clear-on-cancel coverage),
downloadProgressFormat (renamed function, plain-number-return assertion).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@qnbs

qnbs commented Aug 13, 2026

Copy link
Copy Markdown
OwnerAuthor

@coderabbitai review

@coderabbitai

coderabbitaiBot commented Aug 13, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qnbs
qnbs enabled auto-merge (squash) August 13, 2026 03:20
qnbsand others added 2 commits August 13, 2026 07:03
Real conflicts resolved:
- CHANGELOG.md: three genuinely distinct "### Fixed" entries from different
PRs (#341/#344's AI Writing Studio readability fixes, already in main, vs.
this branch's own voice-download-progress-scale fix) — kept both.
- locales/pt/settings.json + its bundle: same providerStatusReady
("Preparar" infinitive vs. the already-fixed "Pronto") conflict already
resolved on the other branches this session — took main's already-correct
side, since this branch never touched that key.
- README.md: stale i18n key-count/test-file-count badges — regenerated via
`node scripts/sync-readme-metrics.mjs` post-merge rather than guessing
(2918 keys × 19 locales, 547 test files).
- src-tauri/osv-scanner.toml: cosmetic-only comment-header conflict (both
sides already had the identical extract-zip IgnoredVulns entry) — kept the
more current review-date comment.
Rebuilt all 19 public/locales/*/bundle.json bundles fresh via
`pnpm run i18n:check` rather than resolving bundle.json conflicts by hand.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CHANGELOG.md: two genuinely distinct "### Added" entries (#332's manual
reduced-transparency toggle, already in main, vs. this branch's own
download-progress bytes/speed entry) — kept both.
README.md: stale i18n key-count/test-file-count badges across 5 locations —
regenerated via `node scripts/sync-readme-metrics.mjs` post-merge (2919 keys
x 19 locales, 549 test files) rather than guessing.
All 19 locale source files and public/locales/*/bundle.json merged/rebuilt
cleanly with no manual conflict resolution needed this time.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@qnbs
qnbs merged commit 53f6f40 into mainAug 13, 2026
29 checks passed
@qnbs
qnbs deleted the fix/333-download-progress-metrics branch August 13, 2026 05:43
qnbs added a commit that referenced this pull request Aug 13, 2026
…reliability fixes (#351)
* docs(deepsource): log re-surfaced JS-0440 finding (local-only, not for push)
Co-Authored-By: GitHub Copilot (Claude Sonnet 5) <noreply@github.com>
* docs(deepsource): correct stale JS-0440 disposition, re-surfaced 2026-08-01 (local-only)
Co-Authored-By: GitHub Copilot (Claude Sonnet 5) <noreply@github.com>
* release: v1.27.0 — Phase 4 encryption production wiring + desktop/AI reliability fixes
Closes out issue #338 (Phase 4 of the at-rest encryption lifecycle): disable
encryption and passphrase rotation are now live in Settings › Privacy,
backed by the durable resumable migration journal built in the prior
release. Also ships three independently-diagnosed reliability/UX fixes:
- Tauri desktop cold boot never read persisted state back (#332) — every
desktop launch loaded as a brand-new user regardless of what was actually
saved to disk; boot hydration now mirrors the already-correct save path.
Quitting also now awaits any pending debounced autosave instead of risking
a mid-debounce data loss.
- AI Writing Studio manuscript text was unreadable, with the caret/selection
visually drifting from the real text (#341) — a blur/font-mismatch/
scroll-desync defect in the invisible-input-over-visible-mirror rendering
pattern used by both Writer Studio and the main manuscript editor.
- Voice and WebLLM model download progress bars showed real byte counts and
transfer speed instead of a bare percentage, and the voice download bar's
progress-scale bug (stuck at ~95% for most of the download) is fixed
(#333 item 1).
All 5 correction-loop PRs (#342-#346) ran to full quiescence before merging
— every CodeRabbit/CodeAnt/Qodo/Sourcery finding fixed or justified with
evidence, 0 unresolved review threads, full CI green (Quality Gate, E2E, E2E
Deep Coverage, Build, Storybook, Lighthouse, Visual Regression) — including
a genuine data-integrity bug found and fixed during that loop: a shared
try/catch in the rekey-resume recovery path could, on a crash immediately
after `commitRekeyMigration`, misinterpret that crash as "already committed"
and clear the migration journal while the durable sentinel still held the
old passphrase — leaving neither passphrase able to unlock the library.
2919 i18n keys × 19 locales.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: address PR #351 review findings — stale CHANGELOG claim + self-contradictory local-only doc
- CHANGELOG.md: the [1.27.0] section never documented its own headline
feature (#342/#343's disable-encryption/passphrase-rotation production
wiring) and still carried the prior release's "intentionally unavailable
... see issue #338" caveat, directly contradicting what shipped in this
same version (chatgpt-codex-connector). Added the missing entry, qualified
the stale caveat as describing that point in time, and corrected the Docs
section's "opened issue #338" line to note it closed in this release.
- docs/DEEPSOURCE-REVIEW-LOOP.md: two restored local-only tracking commits
(originally discarded by an earlier `git reset --hard`, recovered via
cherry-pick) carried "LOCAL-ONLY — not pushed to remote" / "do not push
this entry upstream" wording that became false the moment they were
committed to a pushed branch (qodo-code-review, coderabbitai). Removed the
contradictory local-only framing, keeping the content as a normal dated
log entry. Also softened the JS-0440 remediation TODO's "consider a
rule-level ignore repo-wide" suggestion per coderabbitai's security
concern — a blanket ignore would hide future unsafe
`dangerouslySetInnerHTML` uses, not just the one reviewed occurrence.
The public/sw.js QNBS-v3-comment findings (qodo-code-review, coderabbitai)
were false positives — the existing single-line QNBS-v3 comment on the line
directly above APP_VERSION was already unchanged by this PR's one-line
version-bump diff; verified via `git diff main~1 -- public/sw.js`.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: GitHub Copilot (Claude Sonnet 5) <noreply@github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLThis PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@qnbs