Skip to content

Add Sherpa ONNX speech models and manager - #592

Open
cogwheel0 wants to merge 14 commits into
mainfrom
t3code/add-sherpa-onnx-model-settings
Open

Add Sherpa ONNX speech models and manager#592
cogwheel0 wants to merge 14 commits into
mainfrom
t3code/add-sherpa-onnx-model-settings

Conversation

@cogwheel0

@cogwheel0 cogwheel0 commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • add Sherpa ONNX as a third STT and TTS engine with isolated long-lived workers
  • add a curated model manager for Whisper, SenseVoice, Zipformer, Parakeet, Nemotron, Piper, Mimic3, Supertonic, and Kokoro with exact archive metadata
  • replace the standalone VAD dependency with bundled Sherpa Silero VAD for server and local speech recognition
  • add resumable authenticated-isolated downloads, bounded extraction, runtime validation, no-backup storage, repair states, model/language/speaker selection, and localized settings

Testing

  • dart run build_runner build
  • flutter analyze
  • flutter test test/core/sherpa (20 passed)
  • focused Sherpa/settings/voice/TTS suite (141 passed)
  • flutter test: 4,724 passed, 4 skipped; 1 unrelated pre-existing timing-budget failure in test/core/database/fts_perf_test.dart (Budget 2 append timing)

Review

  • security, architecture, and adversarial reviews report no remaining high or medium findings
  • compact and Large physical-device model smoke tests remain opt-in and were not run
  • no STT or TTS model is bundled; only the Silero VAD asset remains bundled

Note

Add Sherpa ONNX as a local STT and TTS engine with model management

  • Introduces sherpa_onnx as a new STT and TTS engine alongside existing device and server options, replacing the vad dependency.
  • Adds a full model catalog (sherpa_catalog.dart), data model (sherpa_model.dart), storage layer (sherpa_storage.dart), and installation manager (sherpa_model_manager.dart) with download, verification, extraction, and progress streaming.
  • Adds a background-isolate STT worker (sherpa_runtime.dart) and VAD recorder (sherpa_vad_recorder.dart) that replace the previous VadHandler-based pipeline in VoiceInputService.
  • Exposes a new /profile/audio/sherpa-models route (sherpa_models_page.dart) for browsing, installing, and activating models; integrates Sherpa model/speaker/language/speed controls into audio settings and the native sheet.
  • Adds a sherpa_storage Flutter method channel on both iOS (AppDelegate.swift) and Android (MainActivity.kt) to supply no-backup storage paths and device ABI/memory info.
  • Persists new Sherpa settings keys (model IDs, language codes, speaker ID, TTS speed) and exposes activateSherpaStt/activateSherpaTts notifier methods.
  • Risk: VoiceInputService now throws StateError on post-disposal calls and no longer falls back implicitly between STT engines; callers must handle generation-mismatch early-return streams.

Macroscope summarized b154fa1.

Summary by CodeRabbit

  • New Features
    • Added offline Sherpa speech (STT/TTS) with downloadable ONNX models, VAD-based voice input, and a Sherpa Models page (install/repair/delete/activate).
    • Enabled Sherpa model selection (kind/mode/language/voice/speed) across settings, native audio sheets, routing, and chat TTS playback.
  • Bug Fixes
    • Improved Sherpa install/runtime lifecycle safety (bounded extraction, download verification, and queued install progress); added request-level cancellation for server speech synthesis.
  • Localization
    • Added Sherpa UI translations.
  • Tests
    • Expanded Sherpa, settings, voice input, TTS, and model catalog test coverage.

Greptile Summary

Adds Sherpa ONNX speech support and addresses the previously reported worker-disposal races.

  • Adds isolate-backed Sherpa STT, TTS, and bundled Silero VAD runtimes.
  • Adds curated model downloading, validation, storage, repair, and selection.
  • Integrates Sherpa settings and lifecycle management across voice input, chat TTS, native sheets, and profile UI.
  • Makes RPC disposal state monotonic and prevents disposed workers from spawning persistent replacement isolates.

Confidence Score: 5/5

The pull request appears safe to merge because the previously reported worker-disposal failures are addressed and no blocking failures remain.

The worker-disposal failures are addressed and no blocking failure remains.

T-Rex T-Rex Logs

What T-Rex did

  • The focused test attempt was captured in sherpa-focused-01-before.log and ended with 'flutter: not found' and exit code 127.
  • Environment checks were captured in sherpa-focused-02-after.log, showing PATH and Flutter/Dart availability, submodule state, and a repeated focused test attempt ending with exit code 127.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
lib/core/sherpa/sherpa_runtime.dart Implements isolate-backed speech workers with monotonic disposal guards, generation-based TTS cancellation, bounded RPC teardown, and native resource cleanup.
lib/core/sherpa/sherpa_model_manager.dart Manages Sherpa model download, verification, extraction, installation, repair, and deletion state.
lib/features/chat/services/voice_input_service.dart Integrates Sherpa recognition and VAD into voice capture while coordinating worker lifecycle operations.
lib/features/chat/services/tts_manager.dart Adds Sherpa synthesis, model loading, cancellation, playback, and lifecycle-aware unloading.
lib/features/profile/views/sherpa_models_page.dart Adds the model-management and speech-model selection interface.
test/core/sherpa/sherpa_runtime_test.dart Verifies that fully disposed STT and TTS workers reject attempts to create replacement isolates.

Reviews (14): Last reviewed commit: "fix: address adaptive Sherpa UI review" | Re-trigger Greptile

Add curated STT/TTS model management, isolate-backed speech runtimes, and Sherpa Silero VAD for server and local recognition.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Sherpa ONNX support is added across native storage bridges, model catalogs and installation, isolate-based STT/TTS workers, persisted settings, audio services, model-management screens, routing, localization, and automated tests.

Changes

Sherpa offline speech

Layer / File(s) Summary
Model contracts, catalog, and settings
lib/core/sherpa/*, lib/core/services/settings_service.dart, lib/core/persistence/persistence_keys.dart
Defines Sherpa model metadata, catalogs STT/TTS models, adds Sherpa engine preferences, and persists model, language, speaker, and speed settings.
Storage, installation, and runtime
lib/core/sherpa/*, android/app/src/main/kotlin/app/cogwheel/conduit/MainActivity.kt, ios/Runner/AppDelegate.swift, pubspec.yaml
Adds native storage channels, resumable model installation, constrained archive extraction, runtime file resolution, isolate-based STT/TTS workers, and VAD recording.
Audio execution
lib/features/chat/services/*, lib/core/utils/*
Integrates Sherpa STT/TTS configuration, model loading, speech recognition, synthesis, voice selection, cancellation, and lifecycle cleanup.
Model management and settings UI
lib/features/profile/views/*, lib/core/router/app_router.dart, lib/main.dart
Adds model browsing, filtering, installation actions, engine selectors, language and voice pickers, routing, and native-sheet activation.
Native configuration and localization
ios/Podfile, ios/Runner.xcodeproj/project.pbxproj, lib/l10n/*.arb
Updates iOS linker settings and adds Sherpa localization strings across supported locales.
Validation
test/core/*, test/features/chat/*, test/features/profile/*
Tests settings persistence, catalog constraints, archive safety, downloads, runtime disposal, VAD behavior, TTS availability, STT sequencing, native-sheet data, model browsing, and compact selectors.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AudioSettingsPage
  participant SherpaModelsPage
  participant SherpaModelManager
  participant SherpaStorage
  participant SherpaSttWorker
  User->>AudioSettingsPage: Select Sherpa engine
  AudioSettingsPage->>SherpaModelsPage: Open model selection
  SherpaModelsPage->>SherpaModelManager: Enqueue model
  SherpaModelManager->>SherpaStorage: Download, extract, validate
  SherpaSttWorker->>SherpaStorage: Load installed runtime files
  SherpaSttWorker-->>User: Emit speech events
Loading

Poem

A bunny found models beneath the moon,
Downloaded voices that hummed a tune.
STT hopped in, TTS sang bright,
VAD kept rhythm through the night.
Sherpa’s soft footsteps crossed the wire—
Offline speech now leaps higher! 🐰🎵

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding Sherpa ONNX speech models and their management support.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch t3code/add-sherpa-onnx-model-settings

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.

@macroscopeapp

macroscopeapp Bot commented Jul 25, 2026

Copy link
Copy Markdown

Macroscope has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies.

Comment thread lib/core/sherpa/sherpa_runtime.dart Outdated
@cogwheel0

Copy link
Copy Markdown
Owner Author

@macroscope-app review

@coderabbitai coderabbitai Bot 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: 23

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/chat/services/voice_input_service.dart (1)

987-1054: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Dedupe the two recorder start paths and drop the no-op try/catch.

_startServerRecording and _startSherpaRecording differ only in the Sherpa preflight and feedRecognizer; the ~20-line RecordConfig is copy-pasted, so future audio-config fixes have to be made twice. The try { ... } catch (error) { rethrow; } at Lines 987–1011 is also dead code.

♻️ Sketch
+  RecordConfig _vadRecordConfig({
+    required bool iosAudioSessionManagedExternally,
+  }) => RecordConfig(
+        encoder: AudioEncoder.pcm16bits,
+        sampleRate: _vadSampleRate,
+        numChannels: 1,
+        bitRate: 16,
+        echoCancel: true,
+        autoGain: false,
+        noiseSuppress: true,
+        androidConfig: _androidServerVadRecordConfig(
+          voiceCallSession:
+              Platform.isAndroid && iosAudioSessionManagedExternally,
+        ),
+        iosConfig: iosAudioSessionManagedExternally
+            ? _iosManagedServerVadRecordConfig
+            : _iosStandaloneServerVadRecordConfig,
+      );

Then both paths call _vadRecorder.start(..., recordConfig: _vadRecordConfig(...)) with feedRecognizer as the only difference, and the try/catch wrapper is removed.

🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 987 - 1054,
Extract the shared RecordConfig construction from _startServerRecording and
_startSherpaRecording into a reusable _vadRecordConfig helper, then have both
_vadRecorder.start calls use it while retaining their distinct feedRecognizer
values and existing preflight behavior. Remove the no-op try/catch wrapper from
_startServerRecording and allow errors to propagate directly.
🤖 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 `@lib/core/router/app_router.dart`:
- Around line 580-594: Remove the unused select query parameter from all callers
navigating to Routes.sherpaModels, while preserving the existing kind parameter
and SherpaModelsPage selectionKind behavior. Do not add parsing for select
unless a distinct selection behavior is required by the surrounding API.

In `@lib/core/sherpa/sherpa_catalog.dart`:
- Around line 461-471: Update the streaming Parakeet Unified entry in
_parakeet() to set modelType to 'nemo_transducer', matching the other Nemo
streaming models that use SherpaRuntimeAdapter.onlineTransducer and
_nemoTransducerFiles.

In `@lib/core/sherpa/sherpa_model_manager.dart`:
- Around line 117-137: Update enqueue in SherpaModelManager to reject models in
any in-flight installation phase, including downloading, verifying, extracting,
and validating, rather than checking only downloading. Ensure retry does not
remove progress and enqueue a duplicate while the model is actively being
installed, while preserving existing queue and disposed guards.

In `@lib/core/sherpa/sherpa_runtime.dart`:
- Around line 433-445: Update the timeout handler in the command flow around
_commandTimeout and _fail to invoke the same teardown path as dispose() after
killing the isolate, ensuring all three ReceivePorts and their subscriptions are
closed. Preserve the existing TimeoutException behavior while making timeout
cleanup complete even when the worker is no longer alive.
- Around line 46-49: Update the worker load paths around SherpaRuntime and the
corresponding second occurrence to reuse an injected or persistent SherpaStorage
instance instead of constructing SherpaStorage inside each load call. Thread the
existing caller-owned storage, such as voice_input_service.dart’s instance,
through the worker constructors or retain it as a field so resolveRuntimeFiles
uses the same instance across loads and tests do not require a platform-channel
mock.
- Around line 475-496: Make dispose() re-entrant-safe by removing the temporary
_disposed = false assignment around call('dispose'). Introduce or reuse a
separate internal state/flag to permit the farewell RPC while disposal is in
progress, ensuring concurrent dispose() calls return immediately and teardown
operations such as _isolate.kill() and subscription cancellation execute only
once.

In `@lib/core/sherpa/sherpa_storage.dart`:
- Around line 217-236: Update brokenModelIds() and the related installed-model
refresh flow to avoid calling installedModel() once per catalog entry after
installedModels() has already scanned the directories. Compute the installed and
broken model ID sets in a shared pass, reusing the existing scan results so each
model directory is recursively inspected only once while preserving both
methods’ current results.
- Around line 106-127: Update deviceInfo() to catch PlatformException in
addition to MissingPluginException and return the same unknown-device
SherpaDeviceInfo fallback for either platform failure. Preserve the existing
successful response parsing and fallback values.

In `@lib/core/sherpa/sherpa_vad_recorder.dart`:
- Around line 21-24: Reduce the retained VAD history configured by
SherpaVADRecorder: update bufferSizeSeconds and the resulting
bufferSizeSamples/maximumHistorySamples so they are bounded to the required
speech duration plus preRollSamples and postRollSamples, rather than 120
seconds. Preserve sufficient capacity for maxSpeechDurationSeconds and both roll
windows while avoiding the oversized List<double> allocation.

In `@lib/core/utils/native_sheet_utils.dart`:
- Around line 264-270: The native sheet configuration and _nativeVoiceSubtitle
must agree on Sherpa voice-picker behavior: either include voicePickerNav for
TtsEngine.sherpa so its subtitle branch can render, or remove the unreachable
Sherpa-specific subtitle branch if Sherpa voice selection is intentionally
hidden. Apply the chosen behavior consistently across both symbols.

In `@lib/features/chat/services/tts_manager.dart`:
- Around line 675-683: Update didChangeAppLifecycleState to enqueue the Sherpa
unload through _sherpaLoadSerial instead of invoking _sherpaTts.unload()
directly. Preserve the existing pause, inactive-session, and large-model guards,
and keep cache invalidation ordered with the serialized unload so it cannot race
_ensureSherpaLoadedSerial.
- Around line 233-236: Make TtsManager observer registration lazy by removing
registration from the private constructor TtsManager._(). Register the instance
with WidgetsBinding only after binding initialization, such as through
initialize() or a guarded initializer method, ensuring registration occurs once
before TtsManager functionality requires it.

In `@lib/features/chat/services/voice_input_service.dart`:
- Around line 1162-1173: Update the `_sherpaStt.load` catch block to mark the
model broken only for deterministic runtime or validation failures. Keep
`TimeoutException`, isolate spawn failures, and other transient startup/resource
errors retryable by skipping `_sherpaStorage.markModelBroken`, while preserving
the existing logging, availability reset, and return behavior.

In `@lib/features/profile/views/sherpa_models_page.dart`:
- Around line 176-198: Update the _ModelSection-to-_handleAction flow to pass
the broken-state information from _ModelSection into _ModelCard and the action
callback. In _handleAction, when the model is installed and marked broken, call
sherpaModelManagerProvider.retry(model) before activation; preserve the existing
_activate path for healthy installed models and all other progress states.

In `@lib/l10n/app_en.arb`:
- Around line 1595-1600: Update the localization metadata for sttSilenceDuration
so its description refers to the silence duration setting for both Server and
Sherpa speech-to-text, matching the sttSilenceDurationDescription string; leave
the localized string itself unchanged.

In `@lib/l10n/app_es.arb`:
- Around line 2538-2539: Update the sherpaChooseSpeechModel Spanish localization
to explicitly indicate a speech-recognition or transcription model,
distinguishing it from the existing synthesized-voice label in
sherpaChooseVoiceModel; leave the TTS wording unchanged.
- Line 2550: Update the sherpaInstalledSummary localization value to include an
explicit Spanish noun for the installed models while preserving the {count},
{size}, and separator formatting.

In `@lib/l10n/app_it.arb`:
- Line 2532: Translate the value of the hermesMemoryKeyDescription localization
key into Italian while preserving the original meaning and the
X-Hermes-Session-Key placeholder exactly.

In `@lib/l10n/app_ja.arb`:
- Line 3872: Translate the hermesMemoryKeyDescription value in app_ja.arb into
natural Japanese, preserving the meaning of memory scoping, the
X-Hermes-Session-Key identifier, and automatic stable-key generation when the
field is left blank.

In `@lib/l10n/app_nl.arb`:
- Line 2535: Update the Dutch localization entries for sherpaTtsDescription and
the corresponding STT description: use “spraaksynthese” instead of the broader
“spraak” for TTS, and “spraakherkenningsmodel” instead of “spraakmodel” for STT.

In `@lib/l10n/app_zh_Hant.arb`:
- Around line 2533-2551: After updating the ARB entries, regenerate the
localization Dart output using the project’s configured localization workflow
rather than editing generated files directly, then run both flutter test and
flutter analyze and resolve any failures before handoff.
- Line 2532: Translate the value of the hermesMemoryKeyDescription localization
key into natural Traditional Chinese, preserving the meaning of long-term memory
scoping, the X-Hermes-Session-Key identifier, automatic first-chat generation,
and the blank-field condition.

In `@test/core/sherpa/sherpa_model_manager_test.dart`:
- Around line 210-251: Extend the SherpaModelManager download tests around
`_download` to cover resume handling: accept and append a 206 response with a
valid matching Content-Range, restart from offset 0 when ETag or Last-Modified
validators mismatch, and reject malformed or mismatched Content-Range responses
without appending stale bytes. Reuse the existing test helpers and assertions to
verify request offsets, partial-file contents, and resulting phases.

---

Outside diff comments:
In `@lib/features/chat/services/voice_input_service.dart`:
- Around line 987-1054: Extract the shared RecordConfig construction from
_startServerRecording and _startSherpaRecording into a reusable _vadRecordConfig
helper, then have both _vadRecorder.start calls use it while retaining their
distinct feedRecognizer values and existing preflight behavior. Remove the no-op
try/catch wrapper from _startServerRecording and allow errors to propagate
directly.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: dcbb00ac-d021-4a83-a6df-629259644ac8

📥 Commits

Reviewing files that changed from the base of the PR and between 633e10b and 1c6f85c.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (46)
  • android/app/src/main/kotlin/app/cogwheel/conduit/MainActivity.kt
  • ios/Podfile
  • ios/Runner.xcodeproj/project.pbxproj
  • ios/Runner/AppDelegate.swift
  • lib/core/persistence/persistence_keys.dart
  • lib/core/router/app_router.dart
  • lib/core/services/navigation_service.dart
  • lib/core/services/settings_service.dart
  • lib/core/sherpa/sherpa_catalog.dart
  • lib/core/sherpa/sherpa_model.dart
  • lib/core/sherpa/sherpa_model_manager.dart
  • lib/core/sherpa/sherpa_runtime.dart
  • lib/core/sherpa/sherpa_storage.dart
  • lib/core/sherpa/sherpa_vad_recorder.dart
  • lib/core/utils/native_sheet_utils.dart
  • lib/core/utils/tts_voice_utils.dart
  • lib/features/chat/providers/text_to_speech_provider.dart
  • lib/features/chat/services/text_to_speech_service.dart
  • lib/features/chat/services/tts_manager.dart
  • lib/features/chat/services/voice_input_service.dart
  • lib/features/chat/voice_mode/chat_voice_mode_controller.dart
  • lib/features/profile/views/app_customization_page.dart
  • lib/features/profile/views/audio_settings_page.dart
  • lib/features/profile/views/sherpa_models_page.dart
  • lib/l10n/app_cs.arb
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_ja.arb
  • lib/l10n/app_ko.arb
  • lib/l10n/app_nl.arb
  • lib/l10n/app_ru.arb
  • lib/l10n/app_sk.arb
  • lib/l10n/app_zh.arb
  • lib/l10n/app_zh_Hant.arb
  • lib/main.dart
  • pubspec.yaml
  • test/core/services/settings_service_test.dart
  • test/core/sherpa/sherpa_catalog_test.dart
  • test/core/sherpa/sherpa_model_manager_test.dart
  • test/core/sherpa/sherpa_vad_recorder_test.dart
  • test/features/chat/services/tts_manager_test.dart
  • test/features/chat/services/voice_input_service_test.dart
  • test/features/chat/voice_mode/chat_voice_mode_controller_test.dart
💤 Files with no reviewable changes (2)
  • ios/Podfile
  • ios/Runner.xcodeproj/project.pbxproj

Comment thread lib/core/router/app_router.dart
Comment thread lib/core/sherpa/sherpa_catalog.dart
Comment thread lib/core/sherpa/sherpa_model_manager.dart
Comment thread lib/core/sherpa/sherpa_runtime.dart Outdated
Comment thread lib/core/sherpa/sherpa_runtime.dart
Comment thread lib/l10n/app_ja.arb Outdated
Comment thread lib/l10n/app_nl.arb Outdated
Comment thread lib/l10n/app_zh_Hant.arb Outdated
Comment thread lib/l10n/app_zh_Hant.arb Outdated
Comment thread test/core/sherpa/sherpa_model_manager_test.dart
@macroscopeapp

macroscopeapp Bot commented Jul 25, 2026

Copy link
Copy Markdown

Manual reviews triggered for commit 1c6f85c:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

macroscopeapp Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

Comment thread lib/core/sherpa/sherpa_storage.dart
Comment thread lib/core/services/settings_service.dart Outdated
Comment thread lib/features/chat/services/tts_manager.dart
@macroscopeapp

macroscopeapp Bot commented Jul 25, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

3 blocking correctness issues found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitai coderabbitai Bot 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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/l10n/app_it.arb (1)

2550-2550: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the installed-model summary singular-safe.

{count} installati is incorrect when {count} is 1. Use neutral wording or an ICU plural expression, for example Modelli installati: {count} • {size}.

🤖 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 `@lib/l10n/app_it.arb` at line 2550, Update the sherpaInstalledSummary
localization string to avoid the incorrect singular “installati” wording, using
neutral wording or an ICU plural expression while preserving the count and size
placeholders.
lib/features/profile/views/audio_settings_page.dart (1)

478-491: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Resolve the selected Sherpa speaker by ID, not array index.

setSherpaTtsSpeakerId stores SherpaSpeaker.id, but this code indexes model.speakers[speaker]. Non-contiguous speaker IDs show the wrong fallback or subtitle. Search model.speakers for candidate.id == speakerId, matching the native-sheet implementation.

🤖 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 `@lib/features/profile/views/audio_settings_page.dart` around lines 478 - 491,
Update the Sherpa voice metadata resolution in the ttsEngine == TtsEngine.sherpa
branch to treat sherpaTtsSpeakerId as a SherpaSpeaker.id, not an array index.
Parse the selected ID, search model.speakers for a candidate whose id matches,
and use that candidate’s name; preserve the existing choose-voice and numbered
fallback behavior when no ID or matching speaker exists.
🤖 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 `@lib/main.dart`:
- Around line 601-606: Update the Sherpa STT branch in lib/main.dart:601-606 and
the Sherpa TTS branch in lib/main.dart:641-646 to resolve the selected installed
model first; when present, activate it via
setSttPreference(SttPreference.sherpa) or
setTtsEngineSelection(TtsEngine.sherpa) respectively, then refresh the native
voice detail, and only route to the model chooser when no selected installed
model exists.

---

Outside diff comments:
In `@lib/features/profile/views/audio_settings_page.dart`:
- Around line 478-491: Update the Sherpa voice metadata resolution in the
ttsEngine == TtsEngine.sherpa branch to treat sherpaTtsSpeakerId as a
SherpaSpeaker.id, not an array index. Parse the selected ID, search
model.speakers for a candidate whose id matches, and use that candidate’s name;
preserve the existing choose-voice and numbered fallback behavior when no ID or
matching speaker exists.

In `@lib/l10n/app_it.arb`:
- Line 2550: Update the sherpaInstalledSummary localization string to avoid the
incorrect singular “installati” wording, using neutral wording or an ICU plural
expression while preserving the count and size placeholders.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: a917f304-c278-41bd-a873-76002c9f1f8e

📥 Commits

Reviewing files that changed from the base of the PR and between 1c6f85c and e761fa3.

📒 Files selected for processing (24)
  • lib/core/services/navigation_service.dart
  • lib/core/services/settings_service.dart
  • lib/core/sherpa/sherpa_catalog.dart
  • lib/core/sherpa/sherpa_model_manager.dart
  • lib/core/sherpa/sherpa_runtime.dart
  • lib/core/sherpa/sherpa_storage.dart
  • lib/core/sherpa/sherpa_vad_recorder.dart
  • lib/core/utils/native_sheet_utils.dart
  • lib/features/chat/services/tts_manager.dart
  • lib/features/chat/services/voice_input_service.dart
  • lib/features/profile/views/audio_settings_page.dart
  • lib/features/profile/views/sherpa_models_page.dart
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_ja.arb
  • lib/l10n/app_nl.arb
  • lib/l10n/app_zh_Hant.arb
  • lib/main.dart
  • test/core/services/settings_service_test.dart
  • test/core/sherpa/sherpa_catalog_test.dart
  • test/core/sherpa/sherpa_model_manager_test.dart
  • test/core/sherpa/sherpa_vad_recorder_test.dart
  • test/features/chat/services/voice_input_service_test.dart

Comment thread lib/main.dart
Comment thread lib/features/chat/services/voice_input_service.dart
Comment thread lib/features/chat/services/tts_manager.dart Outdated
Comment thread lib/core/sherpa/sherpa_catalog.dart
Comment thread lib/features/chat/services/voice_input_service.dart
Comment thread lib/features/chat/services/tts_manager.dart

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/chat/services/voice_input_service.dart (1)

1124-1189: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Sherpa model stays resident in the worker after the selection is invalidated. Both the STT and TTS availability-refresh helpers reset only their own cached model-id/available flags when the configured model becomes null, unknown, or uninstalled — neither ever calls the corresponding worker's unload(), so a previously loaded (possibly large-tier) model keeps consuming isolate memory until something unrelated eventually triggers an unload (e.g., app backgrounding, for large tier only).

  • lib/features/chat/services/voice_input_service.dart#L1124-L1189: in the id == null, invalid-model-kind, and installed == null branches of _prepareSherpaSttNow, call await _sherpaStt.unload() (guarded by _loadedSherpaSttModelId != null) before/while clearing the cache, routed through the existing _serializeSherpaLifecycle queue.
  • lib/features/chat/services/tts_manager.dart#L1627-L1641: in _refreshSherpaModelAvailability, when the engine/model/kind no longer resolves to a valid Sherpa TTS model, chain an unload of _sherpaTts (clearing _loadedSherpaModelId/_loadedSherpaLanguageCode) onto _sherpaLoadSerial instead of only flipping _sherpaModelAvailable.
🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 1124 -
1189, The Sherpa availability refresh paths must unload invalidated workers, not
only clear cache flags. In
lib/features/chat/services/voice_input_service.dart:1124-1189, update
_prepareSherpaSttNow’s null, invalid-kind, and uninstalled branches to
conditionally await _sherpaStt.unload() when _loadedSherpaSttModelId is set,
using the existing _serializeSherpaLifecycle queue. In
lib/features/chat/services/tts_manager.dart:1627-1641, update
_refreshSherpaModelAvailability to clear the loaded model/language and chain
_sherpaTts.unload() through _sherpaLoadSerial when the configured TTS model is
invalid.
🤖 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.

Outside diff comments:
In `@lib/features/chat/services/voice_input_service.dart`:
- Around line 1124-1189: The Sherpa availability refresh paths must unload
invalidated workers, not only clear cache flags. In
lib/features/chat/services/voice_input_service.dart:1124-1189, update
_prepareSherpaSttNow’s null, invalid-kind, and uninstalled branches to
conditionally await _sherpaStt.unload() when _loadedSherpaSttModelId is set,
using the existing _serializeSherpaLifecycle queue. In
lib/features/chat/services/tts_manager.dart:1627-1641, update
_refreshSherpaModelAvailability to clear the loaded model/language and chain
_sherpaTts.unload() through _sherpaLoadSerial when the configured TTS model is
invalid.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 732a513e-492f-4496-93e2-1a3bb79492b2

📥 Commits

Reviewing files that changed from the base of the PR and between e761fa3 and 6e08825.

📒 Files selected for processing (4)
  • lib/features/chat/services/tts_manager.dart
  • lib/features/chat/services/voice_input_service.dart
  • lib/main.dart
  • test/features/chat/services/tts_manager_test.dart

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
lib/features/chat/services/tts_manager.dart (5)

661-668: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not recycle session IDs in reset().

Asynchronous callbacks from an old session can still finish after reset(). Resetting _sessionCounter lets the next session reuse the same ID, causing stale callbacks to pass the new session guards and enqueue old audio or emit incorrect events.

Proposed fix
-    _sessionCounter = 0;
🤖 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 `@lib/features/chat/services/tts_manager.dart` around lines 661 - 668, Remove
the `_sessionCounter = 0` assignment from `reset()` so session IDs remain
monotonic across resets. Keep the existing playback cleanup, active-session
clearing, and idle unload scheduling unchanged, ensuring callbacks from
pre-reset sessions cannot match later sessions.

943-960: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope _serverFetchingIndices cleanup to the originating session.

The finally block removes an index unconditionally. If a new streaming session reuses that index before the old fetch completes, the old callback removes the new session’s in-flight marker and can trigger premature completion.

Proposed fix
       } finally {
-        _serverFetchingIndices.remove(index);
+        if (_activeSession?.id == session.id) {
+          _serverFetchingIndices.remove(index);
+        }
🤖 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 `@lib/features/chat/services/tts_manager.dart` around lines 943 - 960, Update
the finally block in the server chunk fetch flow to remove the index from
_serverFetchingIndices only when the originating session is still the active
session. Keep the existing completion check tied to session.id, so stale
callbacks cannot clear a newer session’s marker or trigger premature completion.

1113-1139: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Propagate startup cancellation to speak().

_startServerPlayback returns normally when the session becomes stale, but speak() then returns that inactive session as if playback started. Re-check the session ID after startup or propagate cancellation as an error.

Proposed guard
       if (engine != TtsEngine.device) {
         await _startServerPlayback(session);
+        if (_activeSession?.id != session.id) return null;
       } else {
         await _startDevicePlayback(session);
       }

Also applies to: 422-448

🤖 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 `@lib/features/chat/services/tts_manager.dart` around lines 1113 - 1139, Update
_startServerPlayback and its caller speak() so startup cancellation is
propagated instead of returning an inactive session as successfully started.
After each startup await and before speak() returns, re-check that
_activeSession?.id still matches the session ID; when stale, return the existing
cancellation result or throw the established cancellation error, preserving
normal playback behavior for active sessions.

680-694: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Join background synthesis before disposing Sherpa resources.

The prefetch and retry paths are launched with unawaited, while dispose() only waits for _sherpaLoadSerial. An in-flight _sherpaTts.synthesize() or player task can therefore continue after _sherpaTts.dispose() or _player.dispose(). Track these tasks and await/cancel them before releasing the workers.

Also applies to: 1246-1307

🤖 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 `@lib/features/chat/services/tts_manager.dart` around lines 680 - 694, The
dispose flow around TtsManager.dispose must join all background prefetch and
retry work before releasing _player and _sherpaTts. Track the unawaited
synthesis/player tasks launched by the prefetch and retry paths around the
referenced methods, then await or cancel those tracked tasks in dispose
alongside _sherpaLoadSerial before calling _player.dispose() and
_sherpaTts.dispose().

633-654: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Keep playlist mutations serialized across session changes.

An addAudioSource call can still be in flight when stop() resets _serverPlaylistSerial and returns. The next session may then clear/configure the player concurrently, after which the stale operation appends old audio and overwrites shared enqueue state. Preserve the operation queue across resets and await outstanding playlist work before starting the next session.

Also applies to: 1440-1473, 1755-1771

🤖 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 `@lib/features/chat/services/tts_manager.dart` around lines 633 - 654, Update
stop() and the related session-start/reset paths to preserve
_serverPlaylistSerial across session changes instead of resetting it while
addAudioSource work may still be pending. Await the outstanding playlist
operation before clearing or reconfiguring the player for a new session, keeping
addAudioSource mutations serialized so stale audio cannot append or overwrite
shared enqueue state.
🤖 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.

Outside diff comments:
In `@lib/features/chat/services/tts_manager.dart`:
- Around line 661-668: Remove the `_sessionCounter = 0` assignment from
`reset()` so session IDs remain monotonic across resets. Keep the existing
playback cleanup, active-session clearing, and idle unload scheduling unchanged,
ensuring callbacks from pre-reset sessions cannot match later sessions.
- Around line 943-960: Update the finally block in the server chunk fetch flow
to remove the index from _serverFetchingIndices only when the originating
session is still the active session. Keep the existing completion check tied to
session.id, so stale callbacks cannot clear a newer session’s marker or trigger
premature completion.
- Around line 1113-1139: Update _startServerPlayback and its caller speak() so
startup cancellation is propagated instead of returning an inactive session as
successfully started. After each startup await and before speak() returns,
re-check that _activeSession?.id still matches the session ID; when stale,
return the existing cancellation result or throw the established cancellation
error, preserving normal playback behavior for active sessions.
- Around line 680-694: The dispose flow around TtsManager.dispose must join all
background prefetch and retry work before releasing _player and _sherpaTts.
Track the unawaited synthesis/player tasks launched by the prefetch and retry
paths around the referenced methods, then await or cancel those tracked tasks in
dispose alongside _sherpaLoadSerial before calling _player.dispose() and
_sherpaTts.dispose().
- Around line 633-654: Update stop() and the related session-start/reset paths
to preserve _serverPlaylistSerial across session changes instead of resetting it
while addAudioSource work may still be pending. Await the outstanding playlist
operation before clearing or reconfiguring the player for a new session, keeping
addAudioSource mutations serialized so stale audio cannot append or overwrite
shared enqueue state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d6730549-0970-4514-b160-b1ce1efa306f

📥 Commits

Reviewing files that changed from the base of the PR and between 6e08825 and b869c90.

📒 Files selected for processing (2)
  • lib/features/chat/services/tts_manager.dart
  • test/features/chat/services/tts_manager_test.dart

Comment thread lib/core/sherpa/sherpa_runtime.dart

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/chat/services/tts_manager.dart (1)

1751-1758: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Cleanup failures mask the original load error.

If markModelBroken (file I/O) or unload() (RPC into a worker that just failed to initialize) throws, that secondary error propagates instead of the SherpaModelLoadException and the rethrow never executes — callers lose the "needs repair" signal.

🛡️ Preserve the original error
     } catch (error) {
       if (error is SherpaModelLoadException) {
         _sherpaModelAvailable = false;
-        await _sherpaStorage.markModelBroken(id, error);
-        await _sherpaTts.unload();
+        try {
+          await _sherpaStorage.markModelBroken(id, error);
+        } catch (_) {}
+        try {
+          await _sherpaTts.unload();
+        } catch (_) {}
       }
       rethrow;
     }
🤖 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 `@lib/features/chat/services/tts_manager.dart` around lines 1751 - 1758, Update
the Sherpa model-load catch block around _sherpaModelAvailable,
_sherpaStorage.markModelBroken, and _sherpaTts.unload so cleanup failures are
caught and suppressed, ensuring the original SherpaModelLoadException is
rethrown. Preserve the existing cleanup attempts and model-unavailable state
while preventing either cleanup operation from replacing the load error.
🤖 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.

Outside diff comments:
In `@lib/features/chat/services/tts_manager.dart`:
- Around line 1751-1758: Update the Sherpa model-load catch block around
_sherpaModelAvailable, _sherpaStorage.markModelBroken, and _sherpaTts.unload so
cleanup failures are caught and suppressed, ensuring the original
SherpaModelLoadException is rethrown. Preserve the existing cleanup attempts and
model-unavailable state while preventing either cleanup operation from replacing
the load error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4415a3a0-149e-4447-8390-1e5c70f02765

📥 Commits

Reviewing files that changed from the base of the PR and between b869c90 and e4a662e.

📒 Files selected for processing (6)
  • lib/core/services/api_service.dart
  • lib/core/sherpa/sherpa_runtime.dart
  • lib/features/chat/services/tts_manager.dart
  • lib/features/chat/services/voice_input_service.dart
  • test/core/sherpa/sherpa_runtime_test.dart
  • test/features/chat/services/tts_manager_test.dart

Comment thread lib/features/chat/services/voice_input_service.dart Outdated

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/features/chat/services/voice_input_service.dart (2)

1258-1269: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Error path in _processSherpaSamples skips the generation check.

The success path re-validates _listenGeneration != listenGeneration before calling _handleSherpaResult, but the catch block reports the error unconditionally via _reportRecognitionError, which writes to whatever _textStreamController/_transcriptEventController are currently active. If finalize() throws after a newer listening session has started (controllers replaced), a stale error would leak into the new session's streams. Given today's stop/start serialization this window looks unreachable, but the asymmetry with the success-path guard is a latent hazard if that serialization ever changes.

🛡️ Proposed guard
     } catch (error) {
-      _reportRecognitionError(error);
+      if (_listenGeneration != listenGeneration) return;
+      _reportRecognitionError(error);
     }
🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 1258 -
1269, Update _processSherpaSamples so its catch block re-checks
_listenGeneration against listenGeneration before calling
_reportRecognitionError. Preserve the existing success-path guard and suppress
errors from stale listening generations, while still reporting errors for the
current session.

1544-1553: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Large-model unload check reads stale loaded-model state outside the serialized queue.

model?.tier is evaluated against _loadedSherpaSttModelId synchronously, before the unload is enqueued via _serializeSherpaLifecycle. If a _prepareSherpaStt() load is still in-flight when the app backgrounds (_loadedSherpaSttModelId is still null at that point, since it's only set after load() succeeds), this check short-circuits and no unload is scheduled — even though the load will shortly complete with a large model that then stays resident while the app is paused. tts_manager.dart's equivalent _scheduleIdleLargeSherpaUnload avoids this by re-checking the tier inside the serialized _sherpaLoadSerial chain, after any in-flight load has settled.

♻️ Proposed fix: move the tier check inside the serialized unload
   void didChangeAppLifecycleState(AppLifecycleState state) {
     if (state != AppLifecycleState.paused || _isListening) return;
-    final model = sherpaModelById(_loadedSherpaSttModelId);
-    if (model?.tier != SherpaModelTier.large) return;
-    _loadedSherpaSttModelId = null;
-    _loadedSherpaSttLanguageCode = null;
-    _sherpaSttAvailable = false;
-    unawaited(_unloadSherpaRecognizer());
+    unawaited(_serializeSherpaLifecycle(() async {
+      if (_isListening) return;
+      final model = sherpaModelById(_loadedSherpaSttModelId);
+      if (model?.tier != SherpaModelTier.large) return;
+      await _unloadSherpaRecognizerNow();
+    }));
   }
🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 1544 -
1553, Move the large-model tier check from didChangeAppLifecycleState into the
serialized _unloadSherpaRecognizer flow, using the state after _sherpaLoadSerial
has settled so in-flight _prepareSherpaStt loads are observed. Keep the
paused-state and _isListening guards in didChangeAppLifecycleState, and only
clear the loaded-model fields and unload when the serialized check confirms a
large model.
🤖 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.

Outside diff comments:
In `@lib/features/chat/services/voice_input_service.dart`:
- Around line 1258-1269: Update _processSherpaSamples so its catch block
re-checks _listenGeneration against listenGeneration before calling
_reportRecognitionError. Preserve the existing success-path guard and suppress
errors from stale listening generations, while still reporting errors for the
current session.
- Around line 1544-1553: Move the large-model tier check from
didChangeAppLifecycleState into the serialized _unloadSherpaRecognizer flow,
using the state after _sherpaLoadSerial has settled so in-flight
_prepareSherpaStt loads are observed. Keep the paused-state and _isListening
guards in didChangeAppLifecycleState, and only clear the loaded-model fields and
unload when the serialized check confirms a large model.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 12d72055-02ad-4021-912c-4ac699507253

📥 Commits

Reviewing files that changed from the base of the PR and between e4a662e and 359e8e8.

📒 Files selected for processing (3)
  • lib/features/chat/services/tts_manager.dart
  • lib/features/chat/services/voice_input_service.dart
  • test/features/chat/services/voice_input_service_test.dart

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
lib/features/chat/services/voice_input_service.dart (4)

224-231: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Honor forceLocalStt when Sherpa is selected.

checkOnDeviceSupport() and testOnDeviceStt() explicitly call initialize(forceLocalStt: true), but this early Sherpa return bypasses _loadLocales() and _initializeNativeLocalStt(). Those APIs will therefore always report native on-device STT as unavailable whenever Sherpa is the selected preference.

Proposed fix
-    if (_preference == SttPreference.sherpa) {
+    if (_preference == SttPreference.sherpa && !forceLocalStt) {
       await _prepareSherpaStt();
       _isInitialized = true;
       return true;
     }
🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 224 - 231,
Update the initialization branch around _prepareSherpaStt so forceLocalStt takes
precedence over the Sherpa preference: when forced, continue through
_loadLocales() and _initializeNativeLocalStt() instead of returning from the
Sherpa path. Preserve the existing Sherpa-only preparation and early return when
forceLocalStt is false.

1036-1072: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Clean up resources when VAD startup is cancelled.

_stopListeningInternal() stops the recorder before awaiting _vadRecordingStartup. If cancellation wins between the generation checks and _setupVadStreams() or _vadRecorder.start(), startup can then attach subscriptions or start recording, fail the final generation check, and be silently ignored by _launchVadRecording(). No later stop is guaranteed, leaving the recorder or subscriptions active.

Proposed fix
   Future<void> _startVadRecording({
     required int generation,
     required bool iosAudioSessionManagedExternally,
     required bool feedRecognizer,
   }) async {
-    _checkListeningGeneration(generation);
-    await _stopVadRecording();
-    _checkListeningGeneration(generation);
-    _vadPendingSamples = null;
-    await _setupVadStreams();
-    _checkListeningGeneration(generation);
-    final settings = _ref?.read(appSettingsProvider);
-    ...
-    await _vadRecorder.start(...);
-    _checkListeningGeneration(generation);
+    try {
+      _checkListeningGeneration(generation);
+      await _stopVadRecording();
+      _checkListeningGeneration(generation);
+      _vadPendingSamples = null;
+      await _setupVadStreams(generation: generation);
+      _checkListeningGeneration(generation);
+      final settings = _ref?.read(appSettingsProvider);
+      ...
+      await _vadRecorder.start(...);
+      _checkListeningGeneration(generation);
+    } catch (_) {
+      await _stopVadRecording();
+      rethrow;
+    }
   }
🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 1036 -
1072, Update _startVadRecording to clean up VAD resources when a generation
check fails during startup: ensure cancellation after _setupVadStreams() or
_vadRecorder.start() stops the recorder and disposes any subscriptions before
the cancellation propagates. Preserve _launchVadRecording() behavior while
guaranteeing no recorder or stream remains active after a cancelled startup.

1074-1101: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bind VAD subscriptions to the listening generation.

The callbacks only inspect mode flags and _isListening. During teardown, _isListening is cleared before the mode flags, and subscription cancellation occurs after recorder.stop(). Queued events from an older session can therefore populate _vadPendingSamples, report an error, or stop a newly started session.

Pass generation into _setupVadStreams() and guard every callback with _isCurrentListeningGeneration(generation).

Proposed fix
-  Future<void> _setupVadStreams() async {
+  Future<void> _setupVadStreams({required int generation}) async {
     ...
     _vadSpeechEndSub = _vadRecorder.onSpeechEnd.listen((samples) {
-      if (!_usingServerStt && !_usingSherpaStt) return;
+      if (!_isCurrentListeningGeneration(generation)) return;
       if (samples.isEmpty) return;
       _vadPendingSamples = samples;
-      if (_isListening) {
-        unawaited(_stopListening());
-      }
+      unawaited(_stopListening());
     });

     ...
     _vadFrameSub = _vadRecorder.onFrameProcessed.listen((frame) {
-      if (!_isListening) return;
+      if (!_isCurrentListeningGeneration(generation)) return;
       ...
     });

     ...
     _vadErrorSub = _vadRecorder.onError.listen((message) {
+      if (!_isCurrentListeningGeneration(generation)) return;
       _reportRecognitionError(Exception(message));
-      if (_isListening) {
-        unawaited(_stopListening());
-      }
+      unawaited(_stopListening());
     });
   }
🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 1074 -
1101, Update _setupVadStreams to accept the listening generation and capture it
for all VAD event callbacks. In the onSpeechEnd, onFrameProcessed, and onError
handlers, require _isCurrentListeningGeneration(generation) before processing
events, updating _vadPendingSamples or intensity, reporting errors, or stopping
listening; retain the existing mode, sample, and listening checks after this
generation guard.

1523-1557: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not unload Sherpa while a transition or disposal is in flight.

!_isListening does not imply the recognizer is idle: stop clears that flag before Sherpa finalization, and start can still be preparing the model or VAD. A pause callback can therefore unload the worker during finalization/startup. Also remove the lifecycle observer before awaiting teardown; otherwise a callback can enqueue work after the awaited lifecycle future was captured and race _sherpaStt.dispose().

Proposed fix
   Future<void> dispose() async {
+    if (_observingLifecycle) {
+      WidgetsBinding.instance.removeObserver(this);
+      _observingLifecycle = false;
+    }
     await stopListening();
     ...
-    if (_observingLifecycle) {
-      WidgetsBinding.instance.removeObserver(this);
-      _observingLifecycle = false;
-    }
   }

   Future<void> _unloadIdleLargeSherpaRecognizerNow() async {
-    if (_lifecycleState != AppLifecycleState.paused || _isListening) return;
+    if (!_observingLifecycle ||
+        _lifecycleState != AppLifecycleState.paused ||
+        _isListening ||
+        _startListeningInFlight != null ||
+        _stopListeningInFlight != null ||
+        _vadRecordingStartup != null) {
+      return;
+    }
     ...
   }
🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 1523 -
1557, Update dispose, didChangeAppLifecycleState, and
_unloadIdleLargeSherpaRecognizerNow to coordinate with the Sherpa lifecycle
serialization and disposal state rather than relying only on _isListening.
Remove the lifecycle observer before awaiting teardown, prevent pause-triggered
unloads while start/stop/finalization or disposal is in flight, and ensure any
queued lifecycle work completes before _sherpaStt.dispose() runs.
🤖 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.

Outside diff comments:
In `@lib/features/chat/services/voice_input_service.dart`:
- Around line 224-231: Update the initialization branch around _prepareSherpaStt
so forceLocalStt takes precedence over the Sherpa preference: when forced,
continue through _loadLocales() and _initializeNativeLocalStt() instead of
returning from the Sherpa path. Preserve the existing Sherpa-only preparation
and early return when forceLocalStt is false.
- Around line 1036-1072: Update _startVadRecording to clean up VAD resources
when a generation check fails during startup: ensure cancellation after
_setupVadStreams() or _vadRecorder.start() stops the recorder and disposes any
subscriptions before the cancellation propagates. Preserve _launchVadRecording()
behavior while guaranteeing no recorder or stream remains active after a
cancelled startup.
- Around line 1074-1101: Update _setupVadStreams to accept the listening
generation and capture it for all VAD event callbacks. In the onSpeechEnd,
onFrameProcessed, and onError handlers, require
_isCurrentListeningGeneration(generation) before processing events, updating
_vadPendingSamples or intensity, reporting errors, or stopping listening; retain
the existing mode, sample, and listening checks after this generation guard.
- Around line 1523-1557: Update dispose, didChangeAppLifecycleState, and
_unloadIdleLargeSherpaRecognizerNow to coordinate with the Sherpa lifecycle
serialization and disposal state rather than relying only on _isListening.
Remove the lifecycle observer before awaiting teardown, prevent pause-triggered
unloads while start/stop/finalization or disposal is in flight, and ensure any
queued lifecycle work completes before _sherpaStt.dispose() runs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 551e4db7-7308-4050-8ffa-e2e959f14f27

📥 Commits

Reviewing files that changed from the base of the PR and between 359e8e8 and 2c75020.

📒 Files selected for processing (1)
  • lib/features/chat/services/voice_input_service.dart

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/chat/services/voice_input_service.dart (1)

687-707: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

A stopListening() during Sherpa model preparation is silently dropped, so recording starts anyway.

_startListeningInternal awaits _prepareSherpaStt() (isolate spawn + model load, can take seconds) at Line 694-696 before _listenGeneration is advanced and _isListening is set to true (Line 707/713). If stopListening() is called during this window, _stopListeningInternal reads wasListening = _isListening as false and returns early as a no-op — even though it unconditionally bumps _listenGeneration first. The pending start never re-checks the generation after its await _prepareSherpaStt(), so once the model finishes loading it proceeds straight into _startSherpaRecording, which gets a brand-new (unaffected) generation number at Line 707 and starts recording — despite the caller having already received a completed stopListening().

Net effect: caller believes they cancelled the request to listen, but the microphone starts anyway once the Sherpa model finishes loading.

🐛 Proposed fix: honor cancellation requested during Sherpa prep
-    if (_preference == SttPreference.sherpa && !_sherpaSttAvailable) {
-      await _prepareSherpaStt();
-    }
+    if (_preference == SttPreference.sherpa && !_sherpaSttAvailable) {
+      final prepGeneration = _listenGeneration;
+      await _prepareSherpaStt();
+      if (_listenGeneration != prepGeneration) {
+        // A stop() call raced in while the model was loading; honor it
+        // instead of starting a session nobody asked for anymore.
+        return const Stream<String>.empty();
+      }
+    }

Worth adding a regression test mirroring the existing "waits for an in-flight stop before starting a new session" test in test/features/chat/services/voice_input_service_test.dart, but gated on Sherpa model preparation instead of native STT's stopListening(). Want me to draft it?

🤖 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 `@lib/features/chat/services/voice_input_service.dart` around lines 687 - 707,
Update _startListeningInternal to re-check the listen-generation cancellation
state immediately after awaiting _prepareSherpaStt(), and abort without starting
recording when stopListening() advanced the generation during preparation.
Preserve normal startup when the generation is unchanged, ensuring
_startSherpaRecording is not invoked after a completed cancellation.
🤖 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.

Outside diff comments:
In `@lib/features/chat/services/voice_input_service.dart`:
- Around line 687-707: Update _startListeningInternal to re-check the
listen-generation cancellation state immediately after awaiting
_prepareSherpaStt(), and abort without starting recording when stopListening()
advanced the generation during preparation. Preserve normal startup when the
generation is unchanged, ensuring _startSherpaRecording is not invoked after a
completed cancellation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1a8b1d0e-d06e-4e6e-a824-ccb126389b13

📥 Commits

Reviewing files that changed from the base of the PR and between 2c75020 and dbf0178.

📒 Files selected for processing (2)
  • lib/features/chat/services/voice_input_service.dart
  • test/features/chat/services/voice_input_service_test.dart

Comment thread lib/features/chat/services/voice_input_service.dart
Comment thread lib/features/profile/views/sherpa_models_page.dart
…x-model-settings

# Conflicts:
#	lib/l10n/app_cs.arb
#	lib/l10n/app_de.arb
#	lib/l10n/app_es.arb
#	lib/l10n/app_fr.arb
#	lib/l10n/app_it.arb
#	lib/l10n/app_ja.arb
#	lib/l10n/app_ko.arb
#	lib/l10n/app_nl.arb
#	lib/l10n/app_ru.arb
#	lib/l10n/app_sk.arb
#	lib/l10n/app_zh.arb
#	lib/l10n/app_zh_Hant.arb
#	test/core/services/settings_service_test.dart
#	test/core/utils/native_sheet_utils_test.dart
Comment thread lib/core/services/settings_service.dart

@coderabbitai coderabbitai Bot 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: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
lib/l10n/app_nl.arb (1)

2532-2532: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate this string for the Dutch locale.

hermesMemoryKeyDescription is still English, so Dutch users will see an untranslated description in the settings UI.

Proposed fix
-  "hermesMemoryKeyDescription": "The memory key scopes the agent's long-term memory to you (X-Hermes-Session-Key). A stable key is generated automatically the first time you chat if you leave this blank.",
+  "hermesMemoryKeyDescription": "<Dutch translation preserving X-Hermes-Session-Key and the blank-value behavior>",
🤖 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 `@lib/l10n/app_nl.arb` at line 2532, Translate the hermesMemoryKeyDescription
value in the Dutch ARB locale into natural Dutch, preserving the meaning,
placeholder text “X-Hermes-Session-Key,” and blank-value behavior description.
lib/l10n/app_ru.arb (1)

2532-2532: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the Hermes memory-key description.

This Russian locale entry remains entirely in English, so the Hermes settings screen displays untranslated copy to Russian users.

Proposed translation
-  "hermesMemoryKeyDescription": "The memory key scopes the agent's long-term memory to you (X-Hermes-Session-Key). A stable key is generated automatically the first time you chat if you leave this blank.",
+  "hermesMemoryKeyDescription": "Ключ памяти ограничивает долгосрочную память агента вашими данными (X-Hermes-Session-Key). Если оставить поле пустым, при первом чате ключ будет создан автоматически.",
🤖 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 `@lib/l10n/app_ru.arb` at line 2532, Translate the hermesMemoryKeyDescription
value in the Russian ARB locale into natural Russian while preserving the
meaning, placeholder text “X-Hermes-Session-Key,” and blank-line behavior.

Source: Coding guidelines

lib/core/utils/native_sheet_utils.dart (1)

321-335: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Sherpa speaker resolution is duplicated in three places.

The same "parse sherpaTtsSpeakerId, look up the model, match candidate.id, fall back to sherpaVoiceNumber(id + 1)" logic exists here, in _voiceSubtitle (lib/features/profile/views/audio_settings_page.dart lines 509-522), and partially in _handleNativeTtsVoiceSelection (lib/main.dart lines 919-930). Extract a single helper (e.g. alongside formatTtsVoiceDisplayName in lib/core/utils/tts_voice_utils.dart) so the fallback text and the off-by-one + 1 stay in sync.

🤖 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 `@lib/core/utils/native_sheet_utils.dart` around lines 321 - 335, Extract the
duplicated Sherpa speaker resolution into a shared helper near
formatTtsVoiceDisplayName, then update this code and _voiceSubtitle to use it
while refactoring _handleNativeTtsVoiceSelection to reuse the same lookup.
Preserve null handling and the fallback sherpaVoiceNumber(id + 1) behavior so
all call sites remain consistent.
lib/main.dart (1)

660-666: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Sherpa STT language code is persisted without validation.

The sibling stt-language-code case normalizes and rejects invalid input via SettingsService.normalizeSttLanguageCode; this branch stores whatever string the native sheet sends. Validate against the configured Sherpa model's languages before persisting so a stale/unsupported tag cannot reach the recognizer.

🛡️ Proposed fix
         case 'sherpa-stt-language-code':
           if (value is String) {
+            final model = sherpaModelById(
+              ref.read(appSettingsProvider).sherpaSttModelId,
+            );
+            if (value.isNotEmpty &&
+                !(model?.supportsLanguage(value) ?? false)) {
+              DebugLogger.validation(
+                'Ignoring unsupported Sherpa STT language code',
+                scope: 'native/sheet',
+                data: {'value': value},
+              );
+              await _refreshNativeVoiceDetail();
+              return;
+            }
             await ref
                 .read(appSettingsProvider.notifier)
                 .setSherpaSttLanguageCode(value.isEmpty ? null : value);
             await _refreshNativeVoiceDetail();
           }
🤖 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 `@lib/main.dart` around lines 660 - 666, Update the sherpa-stt-language-code
handling in the settings change switch to validate non-empty values against the
configured Sherpa model’s languages using the existing
SettingsService.normalizeSttLanguageCode flow or equivalent sibling logic.
Persist only a supported normalized language code, retain null for empty input,
and avoid updating settings or refreshing native voice details for invalid
values.
🤖 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 `@ios/Runner/NativeSheetBridge.swift`:
- Line 4500: Update the showsDisclosure predicate in the native sheet row
configuration so dismissOnSelect-only actions remain action-only without a
chevron. Restrict the added disclosure behavior to Sherpa rows, or require an
actual navigation target such as item.url or canNavigate(item), while preserving
disclosure for genuinely navigable rows.

In `@lib/core/services/native_sheet_hydration_service.dart`:
- Around line 724-744: Update the hydration flow around the ttsService voice
load and sherpaInstalledModelsProvider inventory load to start both independent
operations concurrently, then await their results together. Preserve the
existing per-source try/catch behavior and warning identifiers, including null
or fallback results when either operation fails.

In `@lib/features/profile/views/sherpa_models_page.dart`:
- Around line 846-862: Update the failed-install banner in the Sherpa install
progress UI to display a concise message derived from progress?.error when
available, falling back to _phaseLabel(l10n, SherpaInstallPhase.failed) when no
error exists. Preserve the existing styling, truncation, and layout around the
failed phase.
- Around line 96-108: Update the model categorization around active and
available so available excludes every model already included in active, while
retaining the existing installed-model exclusion. Use the existing activeIds or
active collection consistently, ensuring settings-referenced but uninstalled
models render only in active.
- Around line 375-386: After awaiting ThemedDialogs.confirm in the model
deletion handler, check that the ConsumerState is still mounted before using
ref. Return immediately when disposed, then perform
ref.read(sherpaModelManagerProvider).delete and
ref.invalidate(sherpaInstalledModelsProvider) only while mounted.

In `@lib/features/profile/widgets/adaptive_segmented_selector.dart`:
- Around line 34-47: Update the useIcons calculation in the LayoutBuilder
builder to incorporate MediaQuery.textScalerOf(context).scale(1) into the width
threshold so larger accessibility text disables decorative icons on narrow
layouts. Remove the redundant !constraints.maxWidth.isFinite branch, while
preserving showIcons and the options.length < 3 behavior.

In `@lib/l10n/app_es.arb`:
- Line 2565: Update the sherpaMeteredNetworkWarning localization value to the
clearer Spanish wording “La red actual es de uso medido.”
- Line 2571: Update the sherpaDownloadSizeSummary translation so the
installed-size placeholder uses grammatically correct storage wording, such as
indicating the amount after installation, while preserving the download-size
portion and placeholders.

In `@lib/l10n/app_fr.arb`:
- Line 2551: Update the ARB entries sherpaInstalledSummary and
sherpaDownloadSizeSummary to use ICU pluralization for correct singular/plural
grammar and explicit wording around the size value. Keep the changes in the ARB
source, regenerate the localization outputs, then run the Flutter analysis and
test gates.

In `@lib/l10n/app_ja.arb`:
- Line 3901: Update the sherpaVoices localization value to use the natural
Japanese counter phrase "{count} 種類の音声", preserving the existing {count}
placeholder.

In `@lib/l10n/app_ru.arb`:
- Line 2561: Update the sherpaVoices localization message in app_ru.arb to use
ICU pluralization for the integer count, with grammatically correct Russian
forms for the relevant numeric ranges. Declare count as an integer placeholder
in the message metadata, then regenerate the localization output.

In `@test/core/utils/native_sheet_utils_test.dart`:
- Around line 12-25: Add a test case covering buildNativeAudioSheetParts when
installedModelCount is null, and assert the sherpa-models manager subtitle
equals l10n.sherpaModelsSubtitle. Keep the existing populated-summary test
unchanged and verify the manager remains discoverable through parts.mainItems.

In `@test/features/profile/views/sherpa_models_page_test.dart`:
- Around line 78-154: Extend the sherpa model page widget tests to cover both
stateful branches: emit a pending-to-installed transition through the overridden
sherpaInstallProgressProvider and verify auto-activation calls the
installed-model flow and pops the page, and add a broken-model scenario that
exercises retry through _handleAction. Reuse the existing router and provider
setup, asserting the page is dismissed and no exception occurs after each path.

In `@test/features/profile/widgets/adaptive_segmented_selector_test.dart`:
- Around line 60-111: Add a sibling widget test for the wide-layout threshold
using a width of at least 360, reusing the AdaptiveSegmentedSelector options and
platform setup from the compact test. Assert all labels remain present and each
platform-appropriate icon is found, so the useIcons threshold behavior is
verified in both layouts.

---

Outside diff comments:
In `@lib/core/utils/native_sheet_utils.dart`:
- Around line 321-335: Extract the duplicated Sherpa speaker resolution into a
shared helper near formatTtsVoiceDisplayName, then update this code and
_voiceSubtitle to use it while refactoring _handleNativeTtsVoiceSelection to
reuse the same lookup. Preserve null handling and the fallback
sherpaVoiceNumber(id + 1) behavior so all call sites remain consistent.

In `@lib/l10n/app_nl.arb`:
- Line 2532: Translate the hermesMemoryKeyDescription value in the Dutch ARB
locale into natural Dutch, preserving the meaning, placeholder text
“X-Hermes-Session-Key,” and blank-value behavior description.

In `@lib/l10n/app_ru.arb`:
- Line 2532: Translate the hermesMemoryKeyDescription value in the Russian ARB
locale into natural Russian while preserving the meaning, placeholder text
“X-Hermes-Session-Key,” and blank-line behavior.

In `@lib/main.dart`:
- Around line 660-666: Update the sherpa-stt-language-code handling in the
settings change switch to validate non-empty values against the configured
Sherpa model’s languages using the existing
SettingsService.normalizeSttLanguageCode flow or equivalent sibling logic.
Persist only a supported normalized language code, retain null for empty input,
and avoid updating settings or refreshing native voice details for invalid
values.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: f98268fc-3837-4f53-bf0a-eab652eb4b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 328961f and e8b83b4.

📒 Files selected for processing (25)
  • ios/Runner/NativeSheetBridge.swift
  • lib/core/services/native_sheet_hydration_service.dart
  • lib/core/utils/native_sheet_utils.dart
  • lib/features/profile/views/audio_settings_page.dart
  • lib/features/profile/views/sherpa_models_page.dart
  • lib/features/profile/widgets/adaptive_segmented_selector.dart
  • lib/features/profile/widgets/settings_page_scaffold.dart
  • lib/l10n/app_cs.arb
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_ja.arb
  • lib/l10n/app_ko.arb
  • lib/l10n/app_nl.arb
  • lib/l10n/app_ru.arb
  • lib/l10n/app_sk.arb
  • lib/l10n/app_zh.arb
  • lib/l10n/app_zh_Hant.arb
  • lib/main.dart
  • lib/shared/widgets/conduit_components.dart
  • test/core/utils/native_sheet_utils_test.dart
  • test/features/profile/views/sherpa_models_page_test.dart
  • test/features/profile/widgets/adaptive_segmented_selector_test.dart

Comment thread ios/Runner/NativeSheetBridge.swift Outdated
Comment thread lib/core/services/native_sheet_hydration_service.dart Outdated
Comment thread lib/features/profile/views/sherpa_models_page.dart
Comment thread lib/features/profile/views/sherpa_models_page.dart
Comment thread lib/features/profile/views/sherpa_models_page.dart
Comment thread lib/l10n/app_ja.arb Outdated
Comment thread lib/l10n/app_ru.arb Outdated
Comment thread test/core/utils/native_sheet_utils_test.dart
Comment thread test/features/profile/views/sherpa_models_page_test.dart
Comment thread test/features/profile/widgets/adaptive_segmented_selector_test.dart

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
test/core/services/settings_service_test.dart (1)

574-590: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert lower-bound clamping is persisted.

Line 587 verifies only state after 0.1; a regression that persists 0.1 instead of the clamped 0.5 still passes.

Proposed test addition
         await notifier.setSherpaTtsSpeed(0.1);
         check(container.read(appSettingsProvider).sherpaTtsSpeed).equals(0.5);
+        check(
+          PreferencesStore.get<double>(PreferenceKeys.sherpaTtsSpeed),
+        ).equals(0.5);
🤖 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 `@test/core/services/settings_service_test.dart` around lines 574 - 590, Extend
the test for setSherpaTtsSpeed to also read PreferenceKeys.sherpaTtsSpeed after
setting 0.1 and assert that persistence contains the clamped value 0.5, matching
the existing state assertion.
lib/core/services/settings_service.dart (1)

1414-1500: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Wait for settings hydration before writing Sherpa selections.

PreferencesStore.put skips writes before initialization. These APIs can update state during _pendingLoad, after which _hydrateFromPrefs() replaces it with stale storage. Mirror Lines 1251-1255 before every new Sherpa mutation API.

Proposed fix
+  Future<bool> _awaitSettingsHydration() async {
+    final pendingLoad = _pendingLoad;
+    if (pendingLoad != null) await pendingLoad;
+    return ref.mounted;
+  }
+
   Future<void> activateSherpaStt({
     required String modelId,
     String? languageCode,
   }) async {
+    if (!await _awaitSettingsHydration()) return;
     final normalizedLanguageCode = SettingsService.normalizeSherpaLanguageCode(
       languageCode,
     );

Apply the same guard to activateSherpaTts, setSherpaSttLanguageCode, setSherpaTtsLanguageCode, setSherpaTtsSpeakerId, and setSherpaTtsSpeed.

🤖 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 `@lib/core/services/settings_service.dart` around lines 1414 - 1500, Ensure all
Sherpa mutation APIs wait for settings hydration before updating preferences or
state, matching the existing guard pattern around lines 1251-1255. Add the guard
at the start of activateSherpaTts, setSherpaSttLanguageCode,
setSherpaTtsLanguageCode, setSherpaTtsSpeakerId, and setSherpaTtsSpeed; preserve
the existing normalization, clamping, persistence, and state updates afterward.
lib/l10n/app_cs.arb (1)

4154-4156: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate all newly added English strings in lib/l10n/app_cs.arb.

The changed entries introduce untranslated English into the Czech locale.

  • lib/l10n/app_cs.arb#L4154-L4156: translate the OpenRouter validation and description strings.
  • lib/l10n/app_cs.arb#L4237-L4290: translate the model, reasoning, sync, preview, and Ollama strings.
🤖 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 `@lib/l10n/app_cs.arb` around lines 4154 - 4156, Translate every newly added
English string in lib/l10n/app_cs.arb into Czech, covering the OpenRouter
entries at lines 4154-4156 and the model, reasoning, sync, preview, and Ollama
entries at lines 4237-4290; preserve all ARB keys and placeholders while leaving
already translated content unchanged.

Source: Coding guidelines

lib/l10n/app_zh.arb (1)

2499-2501: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate all newly added UI strings in the Chinese locales.

  • lib/l10n/app_zh.arb#L2499-L2501: Translate the OpenRouter validation and description strings.
  • lib/l10n/app_zh.arb#L2582-L2635: Translate the reasoning, proxy, sync, preview, and Ollama strings.
  • lib/l10n/app_zh_Hant.arb#L2499-L2501: Translate the OpenRouter validation and description strings.
  • lib/l10n/app_zh_Hant.arb#L2582-L2635: Translate the reasoning, proxy, sync, preview, and Ollama strings.
🤖 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 `@lib/l10n/app_zh.arb` around lines 2499 - 2501, Translate all newly added UI
strings in lib/l10n/app_zh.arb at lines 2499-2501 and 2582-2635, including the
OpenRouter, reasoning, proxy, sync, preview, and Ollama strings. Apply
equivalent Simplified Chinese translations in lib/l10n/app_zh_Hant.arb at lines
2499-2501 and 2582-2635 using Traditional Chinese appropriate for that locale,
while preserving the existing ARB keys and formatting.
🤖 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.

Outside diff comments:
In `@lib/core/services/settings_service.dart`:
- Around line 1414-1500: Ensure all Sherpa mutation APIs wait for settings
hydration before updating preferences or state, matching the existing guard
pattern around lines 1251-1255. Add the guard at the start of activateSherpaTts,
setSherpaSttLanguageCode, setSherpaTtsLanguageCode, setSherpaTtsSpeakerId, and
setSherpaTtsSpeed; preserve the existing normalization, clamping, persistence,
and state updates afterward.

In `@lib/l10n/app_cs.arb`:
- Around line 4154-4156: Translate every newly added English string in
lib/l10n/app_cs.arb into Czech, covering the OpenRouter entries at lines
4154-4156 and the model, reasoning, sync, preview, and Ollama entries at lines
4237-4290; preserve all ARB keys and placeholders while leaving already
translated content unchanged.

In `@lib/l10n/app_zh.arb`:
- Around line 2499-2501: Translate all newly added UI strings in
lib/l10n/app_zh.arb at lines 2499-2501 and 2582-2635, including the OpenRouter,
reasoning, proxy, sync, preview, and Ollama strings. Apply equivalent Simplified
Chinese translations in lib/l10n/app_zh_Hant.arb at lines 2499-2501 and
2582-2635 using Traditional Chinese appropriate for that locale, while
preserving the existing ARB keys and formatting.

In `@test/core/services/settings_service_test.dart`:
- Around line 574-590: Extend the test for setSherpaTtsSpeed to also read
PreferenceKeys.sherpaTtsSpeed after setting 0.1 and assert that persistence
contains the clamped value 0.5, matching the existing state assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8a09f0f1-70e4-4adc-a5c8-173904bb7397

📥 Commits

Reviewing files that changed from the base of the PR and between e8b83b4 and a8616bb.

📒 Files selected for processing (21)
  • ios/Runner/NativeSheetBridge.swift
  • lib/core/persistence/persistence_keys.dart
  • lib/core/services/native_sheet_hydration_service.dart
  • lib/core/services/settings_service.dart
  • lib/core/utils/native_sheet_utils.dart
  • lib/l10n/app_cs.arb
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_ja.arb
  • lib/l10n/app_ko.arb
  • lib/l10n/app_nl.arb
  • lib/l10n/app_ru.arb
  • lib/l10n/app_sk.arb
  • lib/l10n/app_zh.arb
  • lib/l10n/app_zh_Hant.arb
  • lib/main.dart
  • test/core/services/settings_service_test.dart
  • test/core/utils/native_sheet_utils_test.dart

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant