Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2024-02-18 - Pre-scaled Audio Feedback
**Learning:** AudioFeedback volume scaling was applied during every playback, causing unnecessary numpy overhead and latency.
**Action:** Pre-calculate scaled audio during `_load_and_cache` to minimize `_play_cached` latency (from ~1ms to ~0.04ms).

## 2024-05-22 - ONNX Model Warmup
**Learning:** The first inference pass with ONNX Runtime incurs a significant "cold start" penalty (buffer allocation, graph optimization) which impacts user perceived latency for the first dictation.
**Action:** Implemented `ParakeetManager.warmup()` to run a dummy inference during application startup, shifting this cost away from the user interaction loop.
1 change: 1 addition & 0 deletions src/chirp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(self, *, verbose: bool = False) -> None:
model_dir=model_dir,
timeout=self.config.model_timeout,
)
self.parakeet.warmup()
except ModelNotPreparedError as exc:
self.logger.error(str(exc))
raise SystemExit(1) from exc
Expand Down
9 changes: 9 additions & 0 deletions src/chirp/parakeet_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ def ensure_loaded(self):
self._model = self._load_model()
return self._model

def warmup(self) -> None:
"""Performs a dummy inference to initialize ONNX buffers."""
self._logger.debug("Warming up Parakeet model...")
dummy_audio = np.zeros(16_000, dtype=np.float32)
try:
self.transcribe(dummy_audio)
except Exception as exc:
self._logger.warning("Warmup failed: %s", exc)

def _resolve_providers(self, key: str) -> Sequence[str]:
normalized = key.lower()
if normalized != "cpu":
Expand Down
25 changes: 25 additions & 0 deletions tests/test_parakeet_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,31 @@ def test_transcribe_reloads_and_updates_time(self, mock_time, mock_onnx):
manager._stop_monitor.set()
time.sleep(0.05)

@patch("chirp.parakeet_manager.onnx_asr")
def test_warmup(self, mock_onnx):
"""Test that warmup performs dummy inference."""
mock_model_instance = MagicMock()
mock_onnx.load_model.return_value = mock_model_instance

manager = ParakeetManager(
model_name="test",
quantization=None,
provider_key="cpu",
threads=1,
logger=self.logger,
model_dir=self.model_dir,
timeout=100.0,
)

manager.warmup()

# Verify recognize was called with silent audio
mock_model_instance.recognize.assert_called_once()
args, _ = mock_model_instance.recognize.call_args
audio_arg = args[0]
self.assertEqual(audio_arg.shape, (16000,))
self.assertTrue(np.all(audio_arg == 0))

@patch("chirp.parakeet_manager.onnx_asr")
def test_timeout_zero_disables_monitor(self, mock_onnx):
"""Test that timeout=0 disables the monitor thread."""
Expand Down