Implement silero-based audio chunker(#857) & introducing adaptive VAD & fix compilations on Windows(#66)/Linux(#67)#989
Implement silero-based audio chunker(#857) & introducing adaptive VAD & fix compilations on Windows(#66)/Linux(#67)#989metaphorics wants to merge 39 commits intomainfrom
Conversation
…es`, `.code_indexer`, `.idea`) - Add comprehensive guidelines for project development and architecture in `.junie/guidelines.md` - Introduce dedicated instructions for Claude code contribution in `CLAUDE.md`
…essing - Added `ChunkConfig` for flexible chunking behavior. - Implemented adaptive VAD with `SileroConfig`, allowing dynamic threshold adjustments. - Introduced new tests covering RMS chunking, Silero chunking, and configuration scenarios. - Improved silence handling to enhance accuracy and prevent empty chunks.
- Enabled `Box<dyn Predictor>` usage for flexible predictor implementations. - Added support for dynamic VAD selection (Silero or RMS) based on environment variable. - Integrated configurable max duration for audio chunking.
📝 WalkthroughWalkthroughThis update introduces new documentation files outlining project guidelines and AI assistant instructions, updates ignore files for Git and AI assistants, and enhances the audio chunking system. The chunker now supports configurable parameters, adaptive Silero VAD-based chunking with a new SmartPredictor, improved silence trimming with multi-stage and spectral analysis, and dynamic predictor selection in the local STT plugin. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant WebSocketServer
participant Chunker
participant SmartPredictor
participant SileroVAD
participant RMSPredictor
User->>WebSocketServer: Connects and sends audio
WebSocketServer->>WebSocketServer: Check USE_SMART_PREDICTOR env
alt USE_SMART_PREDICTOR == "true"
WebSocketServer->>SmartPredictor: Try to initialize (realtime)
alt SmartPredictor init success
WebSocketServer->>Chunker: Use SmartPredictor with config
else SmartPredictor init fails
WebSocketServer->>SileroVAD: Try to initialize
alt SileroVAD init success
WebSocketServer->>Chunker: Use SileroVAD with config
else SileroVAD init fails
WebSocketServer->>RMSPredictor: Use RMS with config
end
end
else
WebSocketServer->>SileroVAD: Try to initialize
alt SileroVAD init success
WebSocketServer->>Chunker: Use SileroVAD with config
else SileroVAD init fails
WebSocketServer->>RMSPredictor: Use RMS with config
end
end
WebSocketServer->>Chunker: Stream audio chunks using selected predictor and hallucination prevention config
Chunker->>SmartPredictor/SileroVAD/RMSPredictor: Predict speech/silence per chunk
Chunker->>WebSocketServer: Emit trimmed, non-empty audio chunks
WebSocketServer->>User: Sends transcriptions/results
sequenceDiagram
participant ChunkStream
participant Predictor
participant AudioSource
loop For each audio buffer
AudioSource->>ChunkStream: Provide audio samples
ChunkStream->>Predictor: Check for silence using window
alt Silence detected or max duration reached
ChunkStream->>Predictor: Trim silence using multi-stage and spectral methods
alt Chunk is non-empty after trimming
ChunkStream->>Caller: Emit chunk
else
ChunkStream->>Caller: Skip empty chunk, continue polling
end
else
ChunkStream->>AudioSource: Wait for more data
end
end
sequenceDiagram
participant Silero
participant Vad
participant ConfidenceBuffer
Silero->>Vad: Predict speech probability for frame
Vad-->>Silero: Probability value
Silero->>ConfidenceBuffer: Update with new probability
Silero->>Silero: Calculate adaptive threshold based on confidence history
Silero->>Silero: Decide speech/silence using adaptive threshold
Silero->>Silero: Reset state if no speech for N frames
sequenceDiagram
participant SmartPredictor
participant Silero
participant NoiseProfile
participant OnsetDetector
participant SpectrumAnalyzer
SmartPredictor->>Silero: Get VAD confidence
SmartPredictor->>NoiseProfile: Update noise floor and profile during silence
SmartPredictor->>OnsetDetector: Detect onsets in audio
SmartPredictor->>SpectrumAnalyzer: Extract spectral features (centroid, spread, harmonicity)
SmartPredictor->>SmartPredictor: Fuse features with weights and hysteresis
SmartPredictor->>SmartPredictor: Adjust thresholds based on confidence decay and SNR
SmartPredictor-->>ChunkStream: Return fused speech detection result
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (9)
.junie/guidelines.md (2)
29-29: Fix code block language specification.The code block is missing a language identifier, which is flagged by markdownlint.
-``` +```bash
83-90: Minor grammatical improvements suggested.Static analysis suggests small preposition fixes for better readability.
-1. If it needs native access, create a new plugin in `plugins/` +1. If it needs native access, create a new plugin to `plugins/` 2. Implement Rust logic and expose commands 3. Run `cargo test export_types` to generate TypeScript bindings -4. Import and use in React components +4. Import and use them in React componentsCLAUDE.md (1)
87-90: Minor grammatical improvements suggested.Same issues as in guidelines.md - small preposition fixes for better readability.
-1. If it needs native access, create a new plugin in `plugins/` +1. If it needs native access, create a new plugin to `plugins/` 2. Implement Rust logic and expose commands 3. Run `cargo test export_types` to generate TypeScript bindings -4. Import and use in React components +4. Import and use them in React componentsplugins/local-stt/src/server.rs (1)
148-149: Consider using a more robust configuration approach.While environment variables work, consider moving this to a proper configuration system for better maintainability and testability.
// Consider a configuration struct approach: struct ChunkerConfig { use_silero_vad: bool, silero_max_duration: Duration, rms_max_duration: Duration, } impl Default for ChunkerConfig { fn default() -> Self { Self { use_silero_vad: true, silero_max_duration: Duration::from_secs(30), rms_max_duration: Duration::from_secs(15), } } }crates/chunker/src/lib.rs (1)
136-143: Document or make endianness configurable.The function assumes little-endian byte order which may not be portable. Consider documenting this assumption or using target-specific byte order.
fn to_f32(bytes: &[u8]) -> Vec<f32> { + // Note: Assumes little-endian PCM data let mut samples = Vec::with_capacity(bytes.len() / 2); for chunk in bytes.chunks_exact(2) { let sample = i16::from_le_bytes([chunk[0], chunk[1]]) as f32 / 32768.0; samples.push(sample); } samples }crates/chunker/src/stream.rs (1)
73-109: Improved silence trimming logic.The bidirectional trimming is a great improvement. However, the condition check on lines 92-94 appears unnecessary.
for start_idx in (0..data.len()).rev().step_by(window_size) { let end_idx = (start_idx + window_size).min(data.len()); - if start_idx >= end_idx { - continue; - } let window = &data[start_idx..end_idx];The condition
start_idx >= end_idxcan only be true whenstart_idxequalsdata.len()andwindow_sizeis 0, which shouldn't happen given the function's context.crates/chunker/src/predictor.rs (3)
5-10: Good addition for dynamic dispatch support.The implementation correctly enables
Box<dyn Predictor>usage. Minor note: the comment could be more precise.-// Allow Box<dyn Predictor> to be used as a Predictor +// Implement Predictor for Box<P> where P: Predictor
64-84: Consider optimizing mutex usage for performance.The implementation is thread-safe but uses many separate mutex lock/unlock operations in the hot path (predict method). This could impact performance in high-throughput scenarios.
Consider combining related state into a single mutex-protected struct to reduce lock contention:
struct SileroState { vad: hypr_vad::Vad, confidence_history: VecDeque<f32>, frames_since_speech: usize, } pub struct Silero { state: Mutex<SileroState>, config: SileroConfig, // Config is read-only, no mutex needed }This would reduce the number of lock operations per predict() call from 4+ to 1-2.
Also applies to: 117-147
87-95: Optimize mutex usage in reset logic.The method locks
frames_since_speechtwice unnecessarily.fn maybe_reset_state(&self) { - let frames = *self.frames_since_speech.lock().unwrap(); + let mut frames = self.frames_since_speech.lock().unwrap(); // Reset after ~3 seconds of no speech (assuming 30ms chunks) - if frames > 100 { + if *frames > 100 { self.inner.lock().unwrap().reset(); self.confidence_history.lock().unwrap().clear(); - *self.frames_since_speech.lock().unwrap() = 0; + *frames = 0; } }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.aiignore(1 hunks).gitignore(1 hunks).junie/guidelines.md(1 hunks)CLAUDE.md(1 hunks)crates/chunker/src/lib.rs(2 hunks)crates/chunker/src/predictor.rs(2 hunks)crates/chunker/src/stream.rs(3 hunks)plugins/local-stt/src/server.rs(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,ts,tsx,rs}`: 1. No error handling. 2. No unused imports, variables, or functions. 3. For comments, keep it minimal. It should be about "Why", not "What".
**/*.{js,ts,tsx,rs}: 1. No error handling.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".
plugins/local-stt/src/server.rscrates/chunker/src/lib.rscrates/chunker/src/stream.rscrates/chunker/src/predictor.rs
🪛 GitHub Actions: .github/workflows/fmt.yaml
plugins/local-stt/src/server.rs
[error] 143-179: dprint formatting check failed. The file is not properly formatted according to dprint rules. Run 'dprint fmt' to fix code style issues.
🪛 LanguageTool
CLAUDE.md
[uncategorized] ~87-~87: The preposition ‘to’ seems more likely in this position.
Context: ...eeds native access, create a new plugin in plugins/ 2. Implement Rust logic and ...
(AI_HYDRA_LEO_REPLACE_IN_TO)
[uncategorized] ~90-~90: Possible missing preposition found.
Context: ...pes` to generate TypeScript bindings 4. Import and use in React components ### Workin...
(AI_HYDRA_LEO_MISSING_TO)
[duplication] ~92-~92: Possible typo: you repeated a word.
Context: ...e in React components ### Working with Audio - Audio processing logic is in crates/audio/ ...
(ENGLISH_WORD_REPEAT_RULE)
.junie/guidelines.md
[uncategorized] ~83-~83: The preposition ‘to’ seems more likely in this position.
Context: ...eeds native access, create a new plugin in plugins/ 2. Implement Rust logic and ...
(AI_HYDRA_LEO_REPLACE_IN_TO)
[uncategorized] ~86-~86: Possible missing preposition found.
Context: ...pes` to generate TypeScript bindings 4. Import and use in React components ### Workin...
(AI_HYDRA_LEO_MISSING_TO)
[duplication] ~88-~88: Possible typo: you repeated a word.
Context: ...e in React components ### Working with Audio - Audio processing logic is in crates/audio/ ...
(ENGLISH_WORD_REPEAT_RULE)
🪛 markdownlint-cli2 (0.17.2)
.junie/guidelines.md
29-29: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci (macos, macos-latest)
- GitHub Check: ci
- GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (6)
.gitignore (1)
10-10: LGTM! Good additions to .gitignore.The recursive
**/node_modules/pattern and tool-specific directory ignores (.code_indexer/,.idea/,.serena/) are appropriate additions that will help maintain a cleaner repository.Also applies to: 18-21
.aiignore (1)
1-8: Well-documented AI ignore configuration.The file clearly explains its purpose and functionality, including important exceptions. The documentation strikes a good balance between being informative and concise.
.junie/guidelines.md (1)
1-197: Excellent comprehensive documentation.This is a thorough and well-structured guide that covers all essential aspects of the project architecture, development workflow, and conventions. It will be invaluable for onboarding new contributors.
CLAUDE.md (1)
1-201: Excellent AI-focused documentation.This comprehensive guide provides excellent context for automated code assistants working with the Hyprnote codebase. The detailed architecture overview and development patterns will be invaluable for AI-assisted development.
plugins/local-stt/src/server.rs (1)
147-168: Well-implemented dynamic predictor selection.The logic for choosing between Silero VAD and RMS predictors is sound:
- Environment variable configuration provides flexibility
- Graceful fallback when Silero initialization fails
- Appropriate different max durations (30s for Silero, 15s for RMS)
- Good logging for observability
crates/chunker/src/stream.rs (1)
13-35: Well-structured configuration with sensible defaults.The
ChunkConfigstruct provides good configurability for chunking behavior. The default values appear reasonable for audio processing.
- Introduced a detailed `README.md` for the `chunker` crate, outlining features, usage, and configuration. - Enhanced dynamic VAD selection logic in `local-stt` to improve error handling and fallback mechanism. - Refactored code for better readability and maintainability in chunking implementations.
|
Workarounds for a little issue: The Silero VAD model expects audio chunks of at least 30ms (480 samples at 16kHz), but the chunker's silence trimming is using 100-sample windows, which is only about 6ms. This is causing the invalid input shape error. |
- Refactored temporary directory handling with better error handling and clearer expectations. - Adjusted `trim_window_size` to match Silero's minimum sample requirement (480 samples for 30ms at 16kHz). - Updated test cases to verify speech detection within the first 600ms of audio. - Added zero-padding logic in Silero predictor for small chunks to improve robustness. - Improved handling for prolonged silences to prevent busy looping in audio streaming.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
crates/chunker/src/predictor.rs (1)
132-132: Consider error handling for VAD prediction.The
hypr_vad::Vad::run()call could potentially fail. While the current error propagation is correct, consider logging VAD failures for debugging purposes in production environments.- let probability = self.inner.lock().unwrap().run(samples)?; + let probability = self.inner.lock().unwrap().run(samples) + .map_err(|e| { + log::warn!("VAD prediction failed: {}", e); + e + })?;
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
crates/chunker/README.md(1 hunks)crates/chunker/src/lib.rs(2 hunks)crates/chunker/src/predictor.rs(2 hunks)crates/chunker/src/stream.rs(3 hunks)plugins/local-stt/src/server.rs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- plugins/local-stt/src/server.rs
- crates/chunker/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,ts,tsx,rs}`: 1. No error handling. 2. No unused imports, variables, or functions. 3. For comments, keep it minimal. It should be about "Why", not "What".
**/*.{js,ts,tsx,rs}: 1. No error handling.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".
crates/chunker/src/predictor.rscrates/chunker/src/stream.rs
🪛 LanguageTool
crates/chunker/README.md
[uncategorized] ~51-~51: Loose punctuation mark.
Context: ...ation ### ChunkConfig - max_duration: Maximum chunk duration (default: 30s) -...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~58-~58: Loose punctuation mark.
Context: ...s) ### SileroConfig - base_threshold: Default VAD threshold (0.0-1.0) - `conf...
(UNLIKELY_OPENING_PUNCTUATION)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci (windows, windows-latest)
- GitHub Check: ci (macos, macos-latest)
- GitHub Check: ci
🔇 Additional comments (8)
crates/chunker/src/predictor.rs (4)
5-10: LGTM! Clean trait object implementation.This enables using
Box<dyn Predictor>seamlessly, which is essential for dynamic predictor selection mentioned in the PR objectives.
37-62: Well-structured adaptive configuration.The configuration parameters provide good control over the adaptive thresholding behavior. The default values appear reasonable for general use cases.
118-126: Excellent fix for the core PR issue.This directly addresses the ONNX Runtime warnings mentioned in the PR objectives by enforcing the 480-sample minimum (30ms at 16kHz) required by Silero VAD. Returning
falsefor small chunks during silence trimming is appropriate.
88-94: ```shell
#!/bin/bashDisplay context around chunk_size in crates/chunker/src/lib.rs
rg -n -A10 -B10 "chunk_size" crates/chunker/src/lib.rs
</details> <details> <summary>crates/chunker/src/stream.rs (3)</summary> `32-32`: **Perfect alignment with Silero VAD requirements.** The default `trim_window_size: 480` (30ms at 16kHz) matches exactly with the minimum input size required by Silero VAD, preventing the ONNX Runtime warnings mentioned in the PR objectives. --- `73-109`: **Robust bidirectional silence trimming.** The enhanced trimming logic now removes silence from both ends of the audio buffer. The implementation correctly handles edge cases and uses the predictor for accurate silence detection. --- `174-177`: **Good resolution of the busy loop issue.** This addresses the past review comment about potential busy loops. By returning `Poll::Pending` without calling `wake_by_ref()` when chunks are trimmed to empty, the code avoids immediate rescheduling and allows more data to accumulate naturally. </details> <details> <summary>crates/chunker/README.md (1)</summary> `1-70`: **Excellent comprehensive documentation.** The README provides clear usage examples, detailed configuration options, and helpful implementation details. The structure makes it easy for developers to understand both basic and advanced usage patterns. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
- Updated default `trim_window_size` in `README.md` to 480 samples for better alignment with Silero requirements. - Optimized silence trimming loop in `stream.rs` to improve efficiency and maintainability.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
crates/chunker/README.md (6)
1-4: Standardize the crate title header
It’s more conventional to use the crate name as the primary header.Apply this diff if you’d like to align with typical Rust crate READMEs:
-# Audio Chunker +# chunker + +Audio Chunker: intelligent, real-time audio segmentation for Whisper STT integration.
5-13: Singularize “neural networks” for accuracy
The Silero VAD predictor uses a single neural network under the hood.- - **Silero VAD-based chunking**: Advanced voice activity detection using neural networks + - **Silero VAD-based chunking**: Advanced voice activity detection using a neural network
16-24: Clarify return type ofchunks
It’s helpful to note thatchunks(...)returns an iterator/stream of audio buffers.let chunked = audio_source.chunks(RMS::new(), Duration::from_secs(15)); + +// `chunked` implements Iterator<Item = AudioChunk> (or Stream in async contexts)
49-55: Unify duration units for clarity
Spell out seconds for consistency across all defaults.- - `max_duration`: Maximum chunk duration (default: 30s) + - `max_duration`: Maximum chunk duration (default: 30 seconds) - - `min_buffer_duration`: Minimum buffer before considering splits (default: 6s) + - `min_buffer_duration`: Minimum buffer before considering splits (default: 6 seconds)
56-63: Enhance SileroConfig field descriptions
Consider listing valid ranges or expected units for all thresholds to guide users.For example:
- `confidence_window_size`: History window in frames (default: 10, must be ≥1) - `high_confidence_threshold`: Clear-speech detection (0.0–1.0)
66-70: Capitalize “Runtime” in ONNX Runtime
Use the official product name for consistency.- - Uses ONNX runtime for efficient neural network inference + - Uses ONNX Runtime for efficient neural network inference
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
crates/chunker/README.md(1 hunks)crates/chunker/src/stream.rs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/chunker/src/stream.rs
🧰 Additional context used
🪛 LanguageTool
crates/chunker/README.md
[uncategorized] ~51-~51: Loose punctuation mark.
Context: ...ation ### ChunkConfig - max_duration: Maximum chunk duration (default: 30s) -...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~58-~58: Loose punctuation mark.
Context: ...s) ### SileroConfig - base_threshold: Default VAD threshold (0.0-1.0) - `conf...
(UNLIKELY_OPENING_PUNCTUATION)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci
- GitHub Check: ci (windows, windows-latest)
- GitHub Check: ci (macos, macos-latest)
…es`, `.code_indexer`, `.idea`) - Add comprehensive guidelines for project development and architecture in `.junie/guidelines.md` - Introduce dedicated instructions for Claude code contribution in `CLAUDE.md`
…essing - Added `ChunkConfig` for flexible chunking behavior. - Implemented adaptive VAD with `SileroConfig`, allowing dynamic threshold adjustments. - Introduced new tests covering RMS chunking, Silero chunking, and configuration scenarios. - Improved silence handling to enhance accuracy and prevent empty chunks.
- Enabled `Box<dyn Predictor>` usage for flexible predictor implementations. - Added support for dynamic VAD selection (Silero or RMS) based on environment variable. - Integrated configurable max duration for audio chunking.
- Introduced a detailed `README.md` for the `chunker` crate, outlining features, usage, and configuration. - Enhanced dynamic VAD selection logic in `local-stt` to improve error handling and fallback mechanism. - Refactored code for better readability and maintainability in chunking implementations.
- Refactored temporary directory handling with better error handling and clearer expectations. - Adjusted `trim_window_size` to match Silero's minimum sample requirement (480 samples for 30ms at 16kHz). - Updated test cases to verify speech detection within the first 600ms of audio. - Added zero-padding logic in Silero predictor for small chunks to improve robustness. - Improved handling for prolonged silences to prevent busy looping in audio streaming.
- Enhanced clarity of small chunk rejection logic with detailed explanations for ONNX model assumptions and trimming safety margins. - Simplified VAD speech likelihood computation by removing fallback prediction and relying on default average confidence. - Added explicit formatting with `dprint fmt` for consistency with `cargo fmt`.
…elopment patterns - Introduced standardized `.cursor/rules/` files detailing audio processing pipelines, code style conventions, database patterns, and plugin development recommendations. - Added comprehensive guides for essential development commands, project overview, structure, and task completion checklists. - Improved maintainability through centralized documentation of architecture and best practices. - Updated `.gitignore` and `.serena` for cache management and memory updates.
- Enhanced `.cursor/rules/` with detailed Rust style conventions aligned with the official Rust Style Guide. - Expanded audio processing guidelines to emphasize local-first privacy and backend options. - Introduced comprehensive code style, testing patterns, and performance best practices. - Added `rustfmt` compliance details and standardized project documentation structure.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (9)
crates/chunker/src/predictor.rs (3)
351-351: Handle potential prediction errors gracefully.The VAD prediction result is ignored, which could mask errors that might affect the confidence history.
Consider handling the prediction result:
- let _ = self.silero.predict(samples); + if let Err(e) = self.silero.predict(samples) { + tracing::warn!("VAD prediction failed: {}", e); + // Continue with default confidence or return early + }
373-378: Improve readability of SNR calculation.The SNR calculation could be clearer with intermediate variables.
Consider extracting intermediate variables:
- let snr = if noise_floor > 0.0 { - rms / noise_floor - } else { - 10.0 - }; + let snr = if noise_floor > 0.0 { + rms / noise_floor + } else { + 10.0 // Default high SNR when noise floor is zero + };
380-394: Consider extracting confidence calculation logic.The multi-feature fusion calculation is complex and could benefit from clearer variable names and structure.
Consider refactoring for clarity:
- // Weighted feature fusion - let mut confidence = 0.0; - confidence += speech_likelihood * VAD_WEIGHT; // VAD is primary - confidence += speech_quality * SPEECH_QUALITY_WEIGHT; // Spectral quality - confidence += (snr.min(10.0) / 10.0) * SNR_WEIGHT; // SNR contribution - - // Boost confidence if onset detected - if is_onset { - confidence = (confidence + ONSET_BOOST).min(1.0); - } - - // Hysteresis for temporal stability - let prev_confidence = self.silero.get_recent_confidence_avg(3).unwrap_or(0.5); - confidence = - confidence * HYSTERESIS_CURRENT_WEIGHT + prev_confidence * HYSTERESIS_PREVIOUS_WEIGHT; + // Weighted feature fusion + let vad_contribution = speech_likelihood * VAD_WEIGHT; + let quality_contribution = speech_quality * SPEECH_QUALITY_WEIGHT; + let snr_contribution = (snr.min(10.0) / 10.0) * SNR_WEIGHT; + + let mut base_confidence = vad_contribution + quality_contribution + snr_contribution; + + // Boost confidence if onset detected + if is_onset { + base_confidence = (base_confidence + ONSET_BOOST).min(1.0); + } + + // Apply hysteresis for temporal stability + let prev_confidence = self.silero.get_recent_confidence_avg(3).unwrap_or(0.5); + let confidence = base_confidence * HYSTERESIS_CURRENT_WEIGHT + prev_confidence * HYSTERESIS_PREVIOUS_WEIGHT;CLAUDE.md (2)
88-88: Minor grammatical improvement suggested.Static analysis suggests using "to" instead of "in" for better readability.
-1. If it needs native access, create a new plugin in `plugins/` +1. If it needs native access, create a new plugin to `plugins/`
91-91: Missing preposition in step 4.The sentence appears to be missing a preposition for proper grammar.
-4. Import and use in React components +4. Import and use in React componentsNote: The sentence could be improved as "Import and use them in React components" for clarity.
AGENTS.md (2)
1-198: Consider reducing duplication with CLAUDE.md.This documentation largely duplicates content from
CLAUDE.md. While having comprehensive documentation is valuable, the significant overlap creates maintenance burden and potential for inconsistencies.Consider:
- Creating a shared base document with common content
- Having agent-specific sections that reference the base
- Or clearly differentiating the target audiences and use cases
84-84: Minor grammatical improvement suggested.Same issue as in CLAUDE.md - static analysis suggests using "to" instead of "in".
-1. If it needs native access, create a new plugin in `plugins/` +1. If it needs native access, create a new plugin to `plugins/`.serena/memories/code_style_conventions.md (1)
104-104: Correct macOS capitalization.The operating system should be capitalized as "macOS" according to Apple's official branding.
-- Platform modules (e.g., `audio::macos`, `audio::windows`) +- Platform modules (e.g., `audio::macOS`, `audio::windows`).cursor/rules/code-style.mdc (1)
341-341: Correct macOS capitalization for consistency.Same issue as in the other style guide - macOS should be properly capitalized.
-- Platform modules (e.g., `audio::macos`, `audio::windows`) +- Platform modules (e.g., `audio::macOS`, `audio::windows`)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
.cursor/rules/audio-processing.mdc(1 hunks).cursor/rules/code-style.mdc(1 hunks).cursor/rules/database-patterns.mdc(1 hunks).cursor/rules/dev-commands.mdc(1 hunks).cursor/rules/plugin-development.mdc(1 hunks).cursor/rules/project-overview.mdc(1 hunks).cursor/rules/project-structure.mdc(1 hunks).cursor/rules/task-completion.mdc(1 hunks).gitignore(1 hunks).serena/memories/code_style_conventions.md(1 hunks).serena/memories/codebase_structure.md(1 hunks).serena/memories/project_overview.md(1 hunks).serena/memories/rust_style_guide.md(1 hunks).serena/memories/suggested_commands.md(1 hunks).serena/memories/task_completion_checklist.md(1 hunks).serena/project.yml(1 hunks)AGENTS.md(1 hunks)CLAUDE.md(1 hunks)crates/chunker/src/predictor.rs(3 hunks)
✅ Files skipped from review due to trivial changes (12)
- .serena/project.yml
- .serena/memories/project_overview.md
- .cursor/rules/task-completion.mdc
- .serena/memories/codebase_structure.md
- .cursor/rules/project-overview.mdc
- .cursor/rules/dev-commands.mdc
- .cursor/rules/audio-processing.mdc
- .cursor/rules/database-patterns.mdc
- .serena/memories/task_completion_checklist.md
- .serena/memories/suggested_commands.md
- .serena/memories/rust_style_guide.md
- .cursor/rules/project-structure.mdc
🚧 Files skipped from review as they are similar to previous changes (1)
- .gitignore
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,ts,tsx,rs}`: 1. No error handling. 2. No unused imports, variables, or functions. 3. For comments, keep it minimal. It should be about "Why", not "What".
**/*.{js,ts,tsx,rs}: 1. No error handling.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".
crates/chunker/src/predictor.rs
🪛 LanguageTool
AGENTS.md
[uncategorized] ~84-~84: The preposition ‘to’ seems more likely in this position.
Context: ...eeds native access, create a new plugin in plugins/ 2. Implement Rust logic and ...
(AI_HYDRA_LEO_REPLACE_IN_TO)
[uncategorized] ~87-~87: Possible missing preposition found.
Context: ...pes` to generate TypeScript bindings 4. Import and use in React components ### Workin...
(AI_HYDRA_LEO_MISSING_TO)
[duplication] ~89-~89: Possible typo: you repeated a word.
Context: ...e in React components ### Working with Audio - Audio processing logic is in crates/audio/ ...
(ENGLISH_WORD_REPEAT_RULE)
.serena/memories/code_style_conventions.md
[grammar] ~104-~104: The operating system from Apple is written “macOS”.
Context: ...in Cargo.toml - Platform modules (e.g., audio::macos, audio::windows) - Clear abstraction...
(MAC_OS)
CLAUDE.md
[uncategorized] ~88-~88: The preposition ‘to’ seems more likely in this position.
Context: ...eeds native access, create a new plugin in plugins/ 2. Implement Rust logic and ...
(AI_HYDRA_LEO_REPLACE_IN_TO)
[uncategorized] ~91-~91: Possible missing preposition found.
Context: ...pes` to generate TypeScript bindings 4. Import and use in React components ### Workin...
(AI_HYDRA_LEO_MISSING_TO)
[duplication] ~93-~93: Possible typo: you repeated a word.
Context: ...e in React components ### Working with Audio - Audio processing logic is in crates/audio/ ...
(ENGLISH_WORD_REPEAT_RULE)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci
- GitHub Check: ci (macos, macos-latest)
- GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (13)
crates/chunker/src/predictor.rs (9)
5-10: LGTM! Clean trait implementation for boxed predictors.The implementation correctly delegates to the inner predictor, enabling ergonomic use of boxed trait objects.
21-25: LGTM! Standard Default implementation.Properly delegates to the existing constructor.
43-68: LGTM! Well-structured configuration with sensible defaults.The configuration parameters are clearly documented and the default values appear reasonable for speech detection scenarios.
90-99: LGTM! Robust mutex error handling.The helper function provides consistent error recovery across the codebase, addressing the previous review concern about mutex poisoning.
101-108: LGTM! Constants extracted as suggested in previous reviews.The magic numbers have been properly extracted into named constants, improving maintainability.
213-220: LGTM! Well-reasoned approach to minimum sample handling.The decision to return
falsefor small chunks is well-justified by the detailed comments explaining why padding would be problematic for the ONNX model.
226-229: LGTM! Proper mutex scope management.The explicit scope block ensures the lock is dropped immediately after the VAD prediction, preventing lock contention.
419-452: LGTM! Well-documented speech quality calculation.The hard-coded thresholds are properly justified with detailed comments explaining the physiological basis for the ranges. The decision not to make these configurable is well-reasoned.
465-465: LGTM! Conservative threshold for noise profile updates.The 0.3 threshold is well-explained and appropriately conservative to prevent contaminating the noise profile with speech.
CLAUDE.md (1)
1-269: Excellent comprehensive documentation for Claude Code integration.This documentation provides thorough coverage of the Hyprnote project architecture, development workflows, and coding conventions. The structure is logical and the content is detailed enough to be valuable for onboarding.
A few minor grammatical improvements are suggested by static analysis tools, but these don't significantly impact readability.
.cursor/rules/plugin-development.mdc (1)
1-67: Well-structured plugin development guide.This documentation provides clear, actionable guidance for Tauri plugin development. The progression from creation to usage is logical, and the code examples are helpful for developers.
The integration with the broader project architecture (as described in other documentation files) is consistent and well-explained.
.serena/memories/code_style_conventions.md (1)
1-105: Comprehensive and well-organized style guide.This documentation provides excellent coverage of coding conventions for both TypeScript/React and Rust. The examples are practical and the organization makes it easy to find specific guidance.
.cursor/rules/code-style.mdc (1)
1-342: Outstanding comprehensive style guide.This is an exceptionally well-structured and comprehensive code style guide. The detailed examples, formatting tables, and coverage of advanced topics like async patterns make this an excellent reference for developers.
The integration of both TypeScript/React and Rust conventions in a single document with clear organization is particularly valuable for a polyglot codebase like Hyprnote.
- Added specific TypeScript/React and Rust review guidelines for structured code checks. - Introduced detailed instructions for audio processing and Tauri plugin development. - Updated development commands with `dprint fmt` for formatting consistency. - Adjusted dependencies for macOS-specific `apple-calendar` plugin features.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
.coderabbit.yaml (1)
9-22: Validate TypeScript/React path_instructions block.
The new entry underpath_instructionsconforms to the schema and covers.js,.ts, and.tsxfiles with clear guidelines. Consider whether you need to include other extensions (e.g.,.jsx) or adjust the glob to match your full React codebase.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.coderabbit.yaml(1 hunks).cursor/rules/dev-commands.mdc(1 hunks)plugins/apple-calendar/Cargo.toml(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- plugins/apple-calendar/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (1)
- .cursor/rules/dev-commands.mdc
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci
- GitHub Check: ci (macos, macos-latest)
- GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (3)
.coderabbit.yaml (3)
23-38: Rust review guidelines block is well-formed.
This segment correctly follows the schema for Rust files, with comprehensive checks and proper indentation. No issues found.
39-47: Audio processing instructions are accurate.
The real-time and buffer management checks align with the project’s audio crate requirements. Formatting and schema usage look correct.
48-56: Tauri plugin instructions look good.
The plugin-specific guidelines are clear, follow the schema, and cover command exposure, error serialization, and async handling.
- Updated `list_system_events` and `monitor` function signatures for alignment with updated imports and configurations. - Included `CalendarSource` in imports for enhanced feature compatibility.
There was a problem hiding this comment.
Actionable comments posted: 9
♻️ Duplicate comments (1)
plugins/apple-calendar/src/sync.rs (1)
189-209: Avoid creating a new Tokio runtime inside spawn_blocking.Same issue as
list_system_calendars- creating a new runtime is inefficient.
🧹 Nitpick comments (10)
crates/tcc/src/lib.rs (2)
7-11: Consider consistent error handling across platforms.The non-macOS implementation always returns
true, which may not reflect reality. Consider whether this should return aResulttype for consistency with potential macOS error cases, or at least document why permissions are assumed to be granted on non-macOS platforms.For consistency with potential macOS error handling:
#[cfg(not(target_os = "macos"))] -pub fn _audio_capture_permission_granted() -> bool { +pub fn audio_capture_permission_granted() -> Result<bool, String> { // On non-macOS platforms, assume permission is granted - true + Ok(true) }Or add documentation explaining the assumption:
#[cfg(not(target_os = "macos"))] +/// On non-macOS platforms, we assume audio capture permission is always granted +/// as these platforms typically don't require explicit permission requests pub fn _audio_capture_permission_granted() -> bool { // On non-macOS platforms, assume permission is granted true }
18-22: Review unsafe usage and test reliability.The
unsafecall to the Swift FFI function is necessary, but the test always assumes the result will betrue. In real scenarios, permission might be denied. Consider testing both scenarios or at least documenting why we expecttrue.Consider making the test more robust:
#[test] #[cfg(target_os = "macos")] fn test_audio_capture_permission_granted() { let result = unsafe { _audio_capture_permission_granted() }; - assert!(result); + // Note: This test may fail if audio capture permission is not granted + // In CI/testing environments, this should typically be true + assert!(result, "Audio capture permission should be granted in test environment"); }AGENTS.md (2)
84-84: Minor grammatical correction needed.The preposition should be "to" instead of "in" for better clarity.
-1. If it needs native access, create a new plugin in `plugins/` +1. If it needs native access, create a new plugin to `plugins/`
87-87: Add missing preposition for clarity.The sentence is missing a preposition "to" before "use".
-4. Import and use in React components +4. Import and use to React componentscrates/calendar-apple/src/stub.rs (2)
5-12: Consider implementing the Default trait.Since
Handlehas a simplenew()method that takes no parameters, implementingDefaultwould make it more idiomatic.+#[derive(Default)] pub struct Handle; impl Handle { pub fn new() -> Self { - Handle + Self::default() }
14-22: ```shell
#!/bin/bashDisplay the calendar-interface crate's lib.rs to locate the Error definition
echo "=== calendar-interface/src/lib.rs (first 200 lines) ==="
sed -n '1,200p' crates/calendar-interface/src/lib.rs || true</blockquote></details> <details> <summary>plugins/apple-calendar/src/ext.rs (2)</summary><blockquote> `125-129`: **Maintain consistent commenting style for no-op blocks.** Some non-macOS blocks have comments while others don't. Consider being consistent - either add comments to all no-op blocks or remove them. Also applies to: 139-143, 186-189 --- `43-46`: **Consider using the Error enum instead of String errors.** Multiple methods return `String` errors when they could use the more specific `crate::Error::NotSupported` for consistency. For example: ```diff - Err("Apple Calendar is only supported on macOS".to_string()) + Err(crate::Error::NotSupported.to_string())Also applies to: 63-66, 83-86, 168-171
crates/calendar-apple/src/macos.rs (2)
99-133: Add safety documentation for unsafe blocks.The unsafe blocks interact with Objective-C APIs. Consider adding safety comments explaining why these operations are safe.
For example:
+ // Safety: calendars() returns a retained NSArray that is memory-safe let calendars: Retained<NSArray<EKCalendar>> = unsafe { self.event_store.calendars() }
135-170: Consider refactoring the nested logic for clarity.The
transform_participantmethod has deeply nested logic that could be simplified for better readability.fn transform_participant(&self, participant: &EKParticipant) -> Participant { let name = unsafe { participant.name() } .unwrap_or_default() .to_string(); - let email = { - if !self.contacts_access_granted { - None - } else { - // ... complex nested logic - } - }; + let email = if self.contacts_access_granted { + self.fetch_participant_email(participant) + } else { + None + }; Participant { name, email } } + + fn fetch_participant_email(&self, participant: &EKParticipant) -> Option<String> { + // Extract the email fetching logic here + }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
.junie/guidelines.md(1 hunks)AGENTS.md(1 hunks)crates/calendar-apple/Cargo.toml(1 hunks)crates/calendar-apple/src/lib.rs(1 hunks)crates/calendar-apple/src/macos.rs(1 hunks)crates/calendar-apple/src/stub.rs(1 hunks)crates/detect/src/app/linux.rs(1 hunks)crates/detect/src/app/mod.rs(1 hunks)crates/detect/src/browser/linux.rs(1 hunks)crates/detect/src/browser/mod.rs(1 hunks)crates/detect/src/mic/linux.rs(1 hunks)crates/detect/src/mic/mod.rs(1 hunks)crates/tcc/Cargo.toml(1 hunks)crates/tcc/build.rs(1 hunks)crates/tcc/src/lib.rs(1 hunks)plugins/apple-calendar/Cargo.toml(1 hunks)plugins/apple-calendar/src/error.rs(1 hunks)plugins/apple-calendar/src/ext.rs(1 hunks)plugins/apple-calendar/src/sync.rs(3 hunks)plugins/apple-calendar/src/worker.rs(2 hunks)
✅ Files skipped from review due to trivial changes (4)
- plugins/apple-calendar/src/error.rs
- crates/tcc/build.rs
- crates/tcc/Cargo.toml
- plugins/apple-calendar/src/worker.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- plugins/apple-calendar/Cargo.toml
🧰 Additional context used
📓 Path-based instructions (2)
`**/*.rs`: Rust Review Guidelines: 1. Ensure proper error handling with Result/Option types 2. Check for clippy warnings and suggest fixes 3. Verify memory safety and absence of un...
**/*.rs: Rust Review Guidelines:
- Ensure proper error handling with Result/Option types
- Check for clippy warnings and suggest fixes
- Verify memory safety and absence of unnecessary unsafe blocks
- Check for proper use of lifetimes and borrowing
- Ensure efficient use of iterators over manual loops
- Verify proper async/await usage with Tokio
- Check for appropriate use of Arc/Mutex in concurrent code
- Ensure consistent error types using thiserror
- Verify proper use of tracing for logging
- Comments should explain "why" not "what"
- Check adherence to Rust Style Guide (enforced by rustfmt)
- Verify performance-critical paths use appropriate optimizations
crates/detect/src/browser/mod.rscrates/detect/src/app/mod.rscrates/detect/src/app/linux.rscrates/detect/src/mic/mod.rscrates/detect/src/browser/linux.rscrates/detect/src/mic/linux.rscrates/tcc/src/lib.rscrates/calendar-apple/src/stub.rscrates/calendar-apple/src/lib.rsplugins/apple-calendar/src/sync.rsplugins/apple-calendar/src/ext.rscrates/calendar-apple/src/macos.rs
`**/plugins/**/*.rs`: Tauri Plugin Specific: 1. Ensure commands are properly exposed with #[tauri::command] 2. Verify error types are serializable for IPC 3. Check for proper permi...
**/plugins/**/*.rs: Tauri Plugin Specific:
- Ensure commands are properly exposed with #[tauri::command]
- Verify error types are serializable for IPC
- Check for proper permission handling
- Ensure TypeScript bindings will generate correctly
- Verify async commands use proper runtime handling
plugins/apple-calendar/src/sync.rsplugins/apple-calendar/src/ext.rs
🪛 GitHub Actions: .github/workflows/fmt.yaml
plugins/apple-calendar/src/sync.rs
[error] 188-191: dprint formatting check failed. File is not properly formatted according to dprint rules.
🪛 LanguageTool
AGENTS.md
[uncategorized] ~84-~84: The preposition ‘to’ seems more likely in this position.
Context: ...eeds native access, create a new plugin in plugins/ 2. Implement Rust logic and ...
(AI_HYDRA_LEO_REPLACE_IN_TO)
[uncategorized] ~87-~87: Possible missing preposition found.
Context: ...pes` to generate TypeScript bindings 4. Import and use in React components ### Workin...
(AI_HYDRA_LEO_MISSING_TO)
[duplication] ~89-~89: Possible typo: you repeated a word.
Context: ...e in React components ### Working with Audio - Audio processing logic is in crates/audio/ ...
(ENGLISH_WORD_REPEAT_RULE)
.junie/guidelines.md
[uncategorized] ~84-~84: The preposition ‘to’ seems more likely in this position.
Context: ...eeds native access, create a new plugin in plugins/ 2. Implement Rust logic and ...
(AI_HYDRA_LEO_REPLACE_IN_TO)
[uncategorized] ~87-~87: Possible missing preposition found.
Context: ...pes` to generate TypeScript bindings 4. Import and use in React components ### Workin...
(AI_HYDRA_LEO_MISSING_TO)
[duplication] ~89-~89: Possible typo: you repeated a word.
Context: ...e in React components ### Working with Audio - Audio processing logic is in crates/audio/ ...
(ENGLISH_WORD_REPEAT_RULE)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci
- GitHub Check: ci (macos, macos-latest)
- GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (11)
crates/tcc/src/lib.rs (1)
24-29: Test implementation looks good.The non-macOS test correctly calls the safe Rust function and verifies the expected behavior. The implementation is straightforward and appropriate for the fallback case.
AGENTS.md (2)
89-89: Remove duplicate word.The word "with" appears to be duplicated.
-### Working with Audio +### Working AudioWait, actually looking at this more carefully, "Working with Audio" is correct and not a duplication. The static analysis tool seems to have a false positive here.
1-265: Excellent comprehensive documentation!This is a well-structured and thorough documentation file that provides excellent onboarding material for the Hyprnote project. It covers all the essential aspects including architecture, development workflow, coding conventions, and important considerations. The inclusion of specific examples and detailed explanations will be very valuable for new contributors.
crates/detect/src/browser/mod.rs (2)
11-14: Well-implemented platform abstraction for Linux support.The conditional compilation logic correctly adds Linux support by using a catch-all condition for non-macOS/non-Windows targets. This follows the established pattern and maintains consistency with the existing platform abstraction.
11-14: Verify Linux implementation exists.Ensure that the corresponding
linux.rsmodule has been implemented with appropriate detection logic for Linux platforms.#!/bin/bash # Description: Check if the Linux implementation file exists and examine its contents # Expected: Find linux.rs with Detector implementation find crates/detect/src -name "linux.rs" -exec echo "Found: {}" \; -exec cat {} \;crates/calendar-apple/Cargo.toml (1)
7-10: Inappropriate dependency scoping for cross-platform dependencies.Moving
anyhow,chrono, anditertoolsto the macOS-specific section may cause compilation issues if these dependencies are used in platform-agnostic code or stub implementations for other platforms.These common utility crates are typically safe to include in the general
[dependencies]section unless they're exclusively used in macOS-specific code paths.#!/bin/bash # Description: Check if the moved dependencies are used in non-macOS code # Expected: Find usage patterns to determine appropriate scoping echo "Checking usage of moved dependencies in calendar-apple crate..." echo "=== anyhow usage ===" rg "anyhow" crates/calendar-apple/src/ echo "=== chrono usage ===" rg "chrono" crates/calendar-apple/src/ echo "=== itertools usage ===" rg "itertools" crates/calendar-apple/src/ echo "=== File structure ===" find crates/calendar-apple/src -name "*.rs" -exec echo "Found: {}" \;crates/detect/src/app/mod.rs (2)
11-14: Consistent platform abstraction implementation.Excellent consistency with the browser detection module's approach to Linux support. The conditional compilation logic is correct and maintains the established platform abstraction pattern.
11-14: Verify corresponding Linux implementation.Ensure the
linux.rsmodule for app detection has been implemented with appropriate detection logic.#!/bin/bash # Description: Check for Linux app detection implementation # Expected: Find app/linux.rs with Detector implementation echo "Checking for app detection Linux implementation..." find crates/detect/src/app -name "linux.rs" -exec echo "Found: {}" \; -exec cat {} \;crates/detect/src/mic/mod.rs (1)
11-14: Platform abstraction looks good!The Linux platform support follows the established pattern and correctly handles all non-macOS/non-Windows targets.
crates/calendar-apple/src/lib.rs (1)
1-9: Excellent platform abstraction pattern!The refactoring to platform-specific modules with conditional exports provides clean separation of concerns and follows Rust best practices for cross-platform code.
plugins/apple-calendar/src/sync.rs (1)
287-308: LGTM!The platform-specific implementation correctly returns
NotSupportederror on non-macOS platforms and properly checks calendar access on macOS.
…andling - Replaced `tauri::async_runtime::spawn_blocking` in macOS-specific calendar functions with direct calls for cleaner async handling. - Renamed and updated `_audio_capture_permission_granted` to `_macos_audio_capture_permission` with a unified interface for permission checks. - Improved test structure for `audio_capture_permission_granted` to ensure function compatibility across platforms.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ync handling - Replaced direct async calls with `tauri::async_runtime::spawn_blocking` to better handle blocking operations in macOS-specific calendar functions. - Updated `list_system_calendars` and `list_system_events` for cleaner and more robust execution.
There was a problem hiding this comment.
Pull Request Overview
Adds extensive configuration and guideline files for AI assistants, project tooling, and code review processes alongside setup adjustments supporting the new Silero-based audio chunker and adaptive VAD.
- Introduce CLAUDE.md and AGENTS.md with detailed AI assistant usage guidelines
- Add Serena and Cursor configurations and memory artifacts for onboarding, task completion, and project structure
- Update coderabbit.yaml and .aiignore to refine automated review patterns
Reviewed Changes
Copilot reviewed 20 out of 48 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| CLAUDE.md | New guidelines for Claude Code assistant |
| AGENTS.md | New guidelines for generic AI agents |
| .serena/project.yml | Serena project configuration |
| .serena/memories/task_completion_checklist.md | Task completion checklist memory |
| .serena/memories/suggested_commands.md | Suggested commands memory |
| .serena/memories/rust_style_guide.md | Rust style guide memory |
| .serena/memories/project_overview.md | Project overview memory |
| .serena/memories/codebase_structure.md | Codebase structure memory |
| .serena/memories/code_style_conventions.md | Code style conventions memory |
| .junie/guidelines.md | Duplicate AI assistant guidelines |
| .cursor/rules/task-completion.mdc | Task completion rule for Cursor |
| .cursor/rules/project-structure.mdc | Project structure rule for Cursor |
| .cursor/rules/project-overview.mdc | Project overview rule for Cursor |
| .cursor/rules/plugin-development.mdc | Plugin development rule for Cursor |
| .cursor/rules/dev-commands.mdc | Development commands rule for Cursor |
| .cursor/rules/database-patterns.mdc | Database patterns rule for Cursor |
| .cursor/rules/code-style.mdc | Code style rule for Cursor |
| .cursor/rules/audio-processing.mdc | Audio processing rule for Cursor |
| .coderabbit.yaml | Update code review instruction patterns |
| .aiignore | Add .aiignore file |
Comments suppressed due to low confidence (4)
AGENTS.md:1
- [nitpick] Consider adding a top-level H1 (e.g., "# AGENTS.md") for consistency with other assistant guideline files.
## Project Overview
.coderabbit.yaml:9
- This entry’s indentation appears off and no longer includes
.rs. Verify the path patterns and indentation to ensure tool configs are applied correctly.
- path: "**/*.{js,ts,tsx}"
.serena/memories/suggested_commands.md:39
- [nitpick] Content here overlaps heavily with .serena/memories/task_completion_checklist.md and cursor rules. Consider deduplicating or referencing a single source of truth.
# Generate TypeScript bindings from Rust plugins (CRITICAL after modifying plugin commands)
CLAUDE.md:1
- The content in CLAUDE.md largely duplicates AGENTS.md and .junie/guidelines.md. Consider consolidating shared guidelines to reduce duplication.
# CLAUDE.md
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
crates/whisper/src/local/types.rs (3)
1-6: Consider usingstd::sync::OnceLockinstead oflazy_static.The
lazy_staticcrate is no longer necessary in modern Rust. You can usestd::sync::OnceLockwhich is part of the standard library since Rust 1.70.-use lazy_static::lazy_static; -use regex::Regex; - -lazy_static! { - static ref TRAILING_DOTS: Regex = Regex::new(r"\.{2,}$").unwrap(); -} +use regex::Regex; +use std::sync::OnceLock; + +static TRAILING_DOTS: OnceLock<Regex> = OnceLock::new(); + +fn trailing_dots_regex() -> &'static Regex { + TRAILING_DOTS.get_or_init(|| Regex::new(r"\.{2,}$").unwrap()) +}
18-36: Consider removing redundant accessor methods for public fields.Since the struct fields are already public, the getter methods are redundant and add unnecessary boilerplate. Direct field access is more idiomatic in Rust when fields are public.
- pub fn text(&self) -> &str { - &self.text - } - - pub fn start(&self) -> f32 { - self.start - } - - pub fn end(&self) -> f32 { - self.end - } - - pub fn confidence(&self) -> f32 { - self.confidence - }Keep the
duration()method as it provides computed functionality.
38-41: Updatetrimmethod to use modern regex pattern if refactored.If you adopt the
OnceLockapproach, update this method accordingly:pub fn trim(&mut self) { - self.text = TRAILING_DOTS.replace(&self.text, "").to_string(); + self.text = trailing_dots_regex().replace(&self.text, "").to_string(); }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
.serena/project.yml(1 hunks)crates/tcc/src/lib.rs(1 hunks)crates/tcc/swift-lib/src/lib.swift(1 hunks)crates/whisper/.gitignore(1 hunks)crates/whisper/Cargo.toml(1 hunks)crates/whisper/src/local/mod.rs(1 hunks)crates/whisper/src/local/model.rs(4 hunks)crates/whisper/src/local/reporter.rs(1 hunks)crates/whisper/src/local/types.rs(1 hunks)plugins/apple-calendar/src/sync.rs(3 hunks)plugins/local-stt/src/server.rs(3 hunks)
✅ Files skipped from review due to trivial changes (3)
- crates/whisper/.gitignore
- crates/whisper/src/local/mod.rs
- crates/whisper/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (4)
- .serena/project.yml
- crates/tcc/src/lib.rs
- plugins/local-stt/src/server.rs
- plugins/apple-calendar/src/sync.rs
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.rs`: Rust Review Guidelines: 1. Ensure proper error handling with Result/Option types 2. Check for clippy warnings and suggest fixes 3. Verify memory safety and absence of un...
**/*.rs: Rust Review Guidelines:
- Ensure proper error handling with Result/Option types
- Check for clippy warnings and suggest fixes
- Verify memory safety and absence of unnecessary unsafe blocks
- Check for proper use of lifetimes and borrowing
- Ensure efficient use of iterators over manual loops
- Verify proper async/await usage with Tokio
- Check for appropriate use of Arc/Mutex in concurrent code
- Ensure consistent error types using thiserror
- Verify proper use of tracing for logging
- Comments should explain "why" not "what"
- Check adherence to Rust Style Guide (enforced by rustfmt)
- Verify performance-critical paths use appropriate optimizations
crates/whisper/src/local/types.rscrates/whisper/src/local/reporter.rscrates/whisper/src/local/model.rs
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci (windows, windows-latest)
- GitHub Check: ci (macos, macos-latest)
- GitHub Check: ci
🔇 Additional comments (6)
crates/tcc/swift-lib/src/lib.swift (1)
13-14: LGTM! Platform-specific naming improves clarity.The function rename from
_audio_capture_permission_grantedto_macos_audio_capture_permissionmakes the platform-specific nature of this function explicit, which aligns well with the PR objectives of fixing compilation issues across different platforms. The internal logic remains unchanged, preserving the existing functionality.crates/whisper/src/local/types.rs (1)
9-15: LGTM! Well-structuredSegmentdefinition.The struct follows Rust conventions with proper derive macros for serialization and debugging. The public fields and reference to external source documentation are good practices.
crates/whisper/src/local/model.rs (4)
3-4: LGTM! Clean module imports.The imports are well-organized and follow Rust conventions.
56-60: LGTM! Proper conditional compilation pattern.The debug-only initialization of the reporter is handled correctly using
cfg!macro.
84-87: Clever use of conditional compilation for the reporter field.The approach of using different types for debug vs release builds while maintaining the same field name is elegant and avoids runtime overhead in release builds.
161-164: Add error handling for reporter save operation.If the
WhisperReporter::savemethod is updated to return aResult(as suggested in the reporter review), this code should handle potential errors gracefully to avoid breaking the transcription process.if let Some(reporter) = &mut self.reporter { - reporter.save(audio, &segments); + if let Err(e) = reporter.save(audio, &segments) { + tracing::warn!("Failed to save reporter data: {}", e); + } }This change depends on updating the
savemethod signature inWhisperReporter.
| pub fn save(&mut self, audio: &[f32], segments: &[Segment]) { | ||
| let file_path = self | ||
| .base_dir | ||
| .join(format!("{}_{}.json", self.uid, self.counter)); | ||
| let audio_path = self | ||
| .base_dir | ||
| .join(format!("{}_{}.wav", self.uid, self.counter)); | ||
|
|
||
| let mut audio_writer = hound::WavWriter::create(audio_path, self.audio_spec).unwrap(); | ||
| for sample in audio { | ||
| audio_writer.write_sample(*sample).unwrap(); | ||
| } | ||
| audio_writer.finalize().unwrap(); | ||
|
|
||
| let mut json_writer = std::fs::File::create(file_path).unwrap(); | ||
| serde_json::to_writer(&mut json_writer, &segments).unwrap(); | ||
|
|
||
| self.counter += 1; | ||
| } | ||
| } |
There was a problem hiding this comment.
Add proper error handling to the save method.
The method uses multiple unwrap() calls which can cause the application to panic. This is especially problematic for a debugging/reporting feature that shouldn't crash the main application.
- pub fn save(&mut self, audio: &[f32], segments: &[Segment]) {
+ pub fn save(&mut self, audio: &[f32], segments: &[Segment]) -> Result<(), Box<dyn std::error::Error>> {
let file_path = self
.base_dir
.join(format!("{}_{}.json", self.uid, self.counter));
let audio_path = self
.base_dir
.join(format!("{}_{}.wav", self.uid, self.counter));
- let mut audio_writer = hound::WavWriter::create(audio_path, self.audio_spec).unwrap();
+ let mut audio_writer = hound::WavWriter::create(audio_path, self.audio_spec)?;
for sample in audio {
- audio_writer.write_sample(*sample).unwrap();
+ audio_writer.write_sample(*sample)?;
}
- audio_writer.finalize().unwrap();
+ audio_writer.finalize()?;
- let mut json_writer = std::fs::File::create(file_path).unwrap();
- serde_json::to_writer(&mut json_writer, &segments).unwrap();
+ let json_writer = std::fs::File::create(file_path)?;
+ serde_json::to_writer(json_writer, segments)?;
self.counter += 1;
+ Ok(())
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In crates/whisper/src/local/reporter.rs around lines 32 to 51, the save method
uses multiple unwrap() calls that can cause panics. Refactor the method to
return a Result type and replace unwrap() calls with proper error handling using
the ? operator or match statements. This will propagate errors instead of
panicking, making the method safer and preventing the application from crashing
during save failures.
| impl Default for WhisperReporter { | ||
| fn default() -> Self { | ||
| let base_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("data"); | ||
| std::fs::create_dir_all(&base_dir).unwrap(); | ||
|
|
||
| let audio_spec = hound::WavSpec { | ||
| channels: 1, | ||
| sample_rate: 16000, | ||
| bits_per_sample: 32, | ||
| sample_format: hound::SampleFormat::Float, | ||
| }; | ||
|
|
||
| Self { | ||
| base_dir, | ||
| uid: uuid::Uuid::new_v4().to_string(), | ||
| counter: 0, | ||
| audio_spec, | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Improve error handling and avoid using CARGO_MANIFEST_DIR at runtime.
The current implementation has several issues:
- Multiple
unwrap()calls that can cause panics CARGO_MANIFEST_DIRmay not be available in production builds- No error propagation mechanism
impl Default for WhisperReporter {
- fn default() -> Self {
- let base_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("data");
- std::fs::create_dir_all(&base_dir).unwrap();
+ fn default() -> Self {
+ Self::new().expect("Failed to create WhisperReporter")
+ }
+}
+
+impl WhisperReporter {
+ pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
+ let base_dir = dirs::data_dir()
+ .or_else(|| dirs::home_dir().map(|p| p.join(".local/share")))
+ .unwrap_or_else(|| std::path::PathBuf::from("./data"))
+ .join("hyprnote")
+ .join("whisper_reports");
+
+ std::fs::create_dir_all(&base_dir)?;
let audio_spec = hound::WavSpec {
channels: 1,
sample_rate: 16000,
bits_per_sample: 32,
sample_format: hound::SampleFormat::Float,
};
- Self {
+ Ok(Self {
base_dir,
uid: uuid::Uuid::new_v4().to_string(),
counter: 0,
audio_spec,
- }
- }
+ })
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| impl Default for WhisperReporter { | |
| fn default() -> Self { | |
| let base_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("data"); | |
| std::fs::create_dir_all(&base_dir).unwrap(); | |
| let audio_spec = hound::WavSpec { | |
| channels: 1, | |
| sample_rate: 16000, | |
| bits_per_sample: 32, | |
| sample_format: hound::SampleFormat::Float, | |
| }; | |
| Self { | |
| base_dir, | |
| uid: uuid::Uuid::new_v4().to_string(), | |
| counter: 0, | |
| audio_spec, | |
| } | |
| } | |
| } | |
| impl Default for WhisperReporter { | |
| fn default() -> Self { | |
| Self::new().expect("Failed to create WhisperReporter") | |
| } | |
| } | |
| impl WhisperReporter { | |
| pub fn new() -> Result<Self, Box<dyn std::error::Error>> { | |
| let base_dir = dirs::data_dir() | |
| .or_else(|| dirs::home_dir().map(|p| p.join(".local/share"))) | |
| .unwrap_or_else(|| std::path::PathBuf::from("./data")) | |
| .join("hyprnote") | |
| .join("whisper_reports"); | |
| std::fs::create_dir_all(&base_dir)?; | |
| let audio_spec = hound::WavSpec { | |
| channels: 1, | |
| sample_rate: 16000, | |
| bits_per_sample: 32, | |
| sample_format: hound::SampleFormat::Float, | |
| }; | |
| Ok(Self { | |
| base_dir, | |
| uid: uuid::Uuid::new_v4().to_string(), | |
| counter: 0, | |
| audio_spec, | |
| }) | |
| } | |
| } |
🤖 Prompt for AI Agents
In crates/whisper/src/local/reporter.rs around lines 10 to 29, replace the use
of env!("CARGO_MANIFEST_DIR") with a runtime-available directory path, such as a
configurable or standard data directory. Remove all unwrap() calls by handling
errors gracefully, returning a Result from the default or initialization
function instead of panicking. Implement proper error propagation to allow
callers to handle failures in directory creation or other setup steps.
|
Closing due to inactivity |
No description provided.