09690e5c779e88a80f5ece11f2d7ed9f5b96b407
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e66bd218d9 |
fix(audio): prevent dictation hangs on stalled microphones (#974)
* fix(audio): bound capture reads and support cancellation * fix(recognition): bound and serialize capture shutdown * fix(recognition): cancel retries and report stalled microphones * fix(audio): prefer pulse when the ALSA default is unavailable * fix(recognition): guard late callbacks and finalize capture tests * docs(audio): explain input selection and stall recovery * fix(speech): enforce auto-pause across buffered reload timeouts * fix(speech): recover auto-resume after reload timeout * test(speech): fix auto-pause follow-up test regressions * fix(speech): retain auto-resume after reconfigure timeout * fix(tray): preserve auto-pause recovery across suspend * test(capture-shutdown): give the bare tray the suspend path's shortcut managers _on_system_suspend now releases the main and per-language hotkey listeners before sleep (#1025); the fixture builds TrayIndicator without __init__, so it needs the two attributes present. * fix(tray): release hotkey listeners on suspend even while auto-paused Merging #1025's suspend cleanup with this branch's auto-pause handoff left an early return that skipped shortcut_manager.stop() whenever a configured game was active, keeping EVIOCGRABs held through sleep. The paused branch now only owns the model-lifecycle work; listener release and stopping active recognition run for every suspend. * fix(tray): hold the apply lock only around the download's verify and save Wrapping the whole recommended-model download in _apply_settings_lock blocked every GTK-thread settings apply (e.g. Test Dictation) for the length of the transfer. The engine already serializes reconfigures, so the lock only needs to cover verifying the download still owns the receipt and saving it; an apply that lands mid-download now proceeds instead of freezing the app. --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> |
||
|
|
9646011e77 |
fix(settings): speech model follow-ups from QA (#836)
* fix(settings): speech model follow-ups from #834 Keep the Unused downloads expander in sync with search, recommend Faster Whisper .en weights for English, parse nvidia-smi MiB VRAM, stop simple mode from clobbering Faster Whisper/Parakeet, and ceil RAM so ~7.4 GiB counts as 8. * fix(settings): keep FW .en picker ids and test unused search Represent Faster Whisper English catalog variants in the Settings size combo so tray-saved small.en is not rejected and overwritten. Add behavioral unused-downloads search coverage for match/nonmatch/clear. |
||
|
|
cdfe541c25 |
feat(speech_recognition): add faster-whisper engine backend (#543)
* feat(speech_recognition): add EngineType enum and Engine Protocol Introduce a typed EngineType enum and an Engine Protocol in common_types.py, plus an engines/ registry so new speech recognition backends can be plugged in without scattering string comparisons across the manager. * feat(speech_recognition): add faster-whisper engine backend Add a faster-whisper engine implementation using CTranslate2/INT8 for fast CPU inference. Includes model metadata utility, wiring through the recognition manager, CLI, settings UI, config, optional dependency, and unit tests. Also updates the suspend-handler test fixture for the new engine attribute. * style(ui): format faster-whisper imports for isort 8 * test(speech_recognition): expand faster-whisper coverage for codecov * test(speech_recognition): fix failing faster-whisper coverage tests - Import missing get_compute_type helper - Add tests for English-only detection, cache exceptions, and import-error registry path - Clear _has_torch_cuda lru_cache around exception test to avoid cached True - Replace broad builtins.__import__ patch with a fake engine module for registry test - Restore registry module state after test * test(speech_recognition): add coverage for faster-whisper edge cases Cover previously missing lines in: - faster_whisper_model_info.py: HF cache path/none, hf_hub lookup, fallback traversal, torch CUDA true, psutil exception - faster_whisper_engine.py: model None guard after is_ready() - engine registry import-error path * feat(install): add faster_whisper engine option to install.sh - Add faster_whisper to --engine help text and examples - Add faster-whisper as interactive option 5 with description - Install faster-whisper package and verify import at install time - Add fallback to whisper.cpp if faster-whisper installation fails - Add smoke test for faster-whisper import - Update welcome message engine display name - Note: faster-whisper models download automatically on first use * refactor(faster_whisper): apply ponytail review cleanups - Inline FASTER_WHISPER_MODEL_INFO dict literal; drop unused AVAILABLE_MODELS/MODEL_SIZES - Reuse _has_torch_cuda() in FasterWhisperEngine.device property - Remove redundant model None guard after is_ready() verification - Shrink faster-whisper default config in install.sh to engine + model_size - Update device tests to manage _has_torch_cuda lru_cache - Remove test for deleted dead-code guard * fix(faster_whisper): integrate optional engine with current main Keep whisper_cpp as default and Parakeet as an extra. Put engine selection in the Advanced island, install faster-whisper via the optional extra, and refuse unpinned Hugging Face downloads until model_checksums.txt pins them. * fix(faster_whisper): download verified models and refresh uv.lock Settings already opened a download dialog for uncached faster-whisper, but init only raised ChecksumError. Wire a Parakeet-style downloader that stores bundles under models_dir, verifies each file against model_checksums.txt, and loads WhisperModel from that path. Regenerate uv.lock for the optional faster-whisper extra, pin digests for all eleven Systran models, and add the missing -> None return annotations. * fix(faster_whisper): clear engine on unload and fix auto-pause fixture Auto-pause tests stub __init__, so they never set _faster_whisper_engine. reinitialize_after_resume then AttributeErrors once Test Python runs. Mirror the suspend-handler fixture, and release the faster-whisper engine inside unload_model the same way resume already does. * fix(faster_whisper): restore previous engine after a failed switch reconfigure() selected the new engine and released the live model before init. A failed faster-whisper download then left the manager in ERROR on the new backend while Settings reverted the pickers to the saved engine, so dictation stayed dead until restart. Reload the previous engine from disk (no download) before re-raising so the UI and live manager agree. * fix(speech_recognition): restore all live settings after failed switch Failed reconfigure already put the previous engine back, but VAD, device, remote API, and whisper.cpp knobs stayed on the attempted values while config on disk did not. Snapshot every field reconfigure writes and restore the lot before reloading the old backend. Also bind the real _init_selected_engine onto the MagicMock in the force_reinit test. * fix(speech_recognition): restore every live field after failed reconfigure A failed engine switch already reloaded the previous backend, but Greptile could still miss that VAD, audio device, remote API, and whisper.cpp knobs were part of the snapshot. Keep one explicit attr list for snapshot/restore, including the derived voice-commands flag, so live state matches persisted config after a failed Settings apply. * chore(speech_recognition): sign tip after rollback follow-up |
||
|
|
6b14338d1e |
feat(tray): offer the recommended model from the missing-model notification (#687)
* feat(tray): offer the recommended model from the missing-model notification Dictation without a model on disk showed a dead-end notification telling the user to open Settings, find the model picker and start the download by hand, at the moment they wanted to talk. There was no Download button anywhere: downloads only happen as a side effect of changing a selection. The notification now carries a "Download <model> (<size>)" button for the model the picker already stars for this machine, so recommendations stay in one place: get_recommended_model() for whisper.cpp, including the English-only variant when an English language is selected, and the RAM based recommendations for VOSK and Whisper. Clicking it downloads in the background, reports progress in the notification, and saves the model for that engine only after it loaded. Notification servers that do not advertise the actions capability, and systems without libnotify, still get the plain text they got before. Dictation is not started for the user afterwards: a download can take several minutes, and grabbing the microphone unannounced would surprise someone who has long since moved on. * feat(tray): actually fetch the offered model, and only claim it when loaded Review follow-ups on #687. reconfigure() only re-initializes when engine, model or language change, and force_download is read during that re-init. First-run defaults are whisper_cpp + tiny, and tiny is what get_recommended_model() returns on a CPU-only machine under 16GB, so the click was a no-op that logged "Applied VAD/silence timeout changes.", saved the model, said "Speech model ready", and left the next shortcut showing the same missing-model notification. Add force_reinit to reconfigure() and pass it here, then gate the save and the success notification on speech_engine.model_ready instead of on reconfigure() not raising. The "starting..." notification is now created in _download_recommended_model, which runs on the main loop where libnotify dispatches the action; only the handle travels to the worker, which already routes update/close/notify through _idle_once. The offer body no longer promises a button the server cannot draw: with no action it points at Settings, like the notification it replaced. Tests cover the case that breaks the feature — the recommendation equal to the configured model — behaviourally, through a stand-in engine that gates re-init the way reconfigure() does, plus the real reconfigure() contract in both directions. They also stop touching sys.modules at collection time, which is the desync #686 hit, and black is clean. * test(notifications): cover the libnotify/notify-send boundary directly Every test that touches notifications so far patches the whole module away, so the module itself had no coverage: the fallback to notify-send when the server has no action support or libnotify fails to initialize, the handle kept alive in _pending until the user answers, and update() refusing a fallback handle. These drive it with a fake Notify installed through the module's own lazy cache rather than by patching gi.repository, which is shared with every other test in the process. * fix(download): allow only one model download at a time across tray and Settings The tray's download worker and the Settings dialog's both drive the same engine, which has a single progress callback slot and a single configuration. Started together, the later one overwrote the earlier one's progress callback, the earlier one's finally block then cleared it mid-download, and whichever finished last became the persisted model. Give the engine a non-blocking claim. Both Settings entry points and the tray take it before starting a download and release it when the worker ends; whoever loses the race is told a download is already running and the Settings picker goes back to the saved model. --------- Co-authored-by: Kacper Paczos <23406555+kacperpaczos@users.noreply.github.com> |