Skip to content
Merged
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
28 changes: 15 additions & 13 deletions desktop/src-tauri/examples/pocket_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,22 @@ fn main() {
let p = |name: &str| dir.join(name).to_string_lossy().into_owned();

let t0 = Instant::now();
let mut cfg = OfflineTtsConfig::default();
cfg.model = OfflineTtsModelConfig {
pocket: OfflineTtsPocketModelConfig {
lm_main: Some(p("lm_main.int8.onnx")),
lm_flow: Some(p("lm_flow.int8.onnx")),
encoder: Some(p("encoder.onnx")),
decoder: Some(p("decoder.int8.onnx")),
text_conditioner: Some(p("text_conditioner.onnx")),
vocab_json: Some(p("vocab.json")),
token_scores_json: Some(p("token_scores.json")),
voice_embedding_cache_capacity: 16,
let cfg = OfflineTtsConfig {
model: OfflineTtsModelConfig {
pocket: OfflineTtsPocketModelConfig {
lm_main: Some(p("lm_main.int8.onnx")),
lm_flow: Some(p("lm_flow.int8.onnx")),
encoder: Some(p("encoder.onnx")),
decoder: Some(p("decoder.int8.onnx")),
text_conditioner: Some(p("text_conditioner.onnx")),
vocab_json: Some(p("vocab.json")),
token_scores_json: Some(p("token_scores.json")),
voice_embedding_cache_capacity: 16,
},
num_threads: 1,
debug: false,
..Default::default()
},
num_threads: 1,
debug: false,
..Default::default()
};
let engine = OfflineTts::create(&cfg).expect("engine create");
Expand Down
28 changes: 15 additions & 13 deletions desktop/src-tauri/examples/pocket_onset_probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,22 @@ fn main() {
let dir = PathBuf::from(&model_dir);
let p = |name: &str| dir.join(name).to_string_lossy().into_owned();

let mut cfg = OfflineTtsConfig::default();
cfg.model = OfflineTtsModelConfig {
pocket: OfflineTtsPocketModelConfig {
lm_main: Some(p("lm_main.int8.onnx")),
lm_flow: Some(p("lm_flow.int8.onnx")),
encoder: Some(p("encoder.onnx")),
decoder: Some(p("decoder.int8.onnx")),
text_conditioner: Some(p("text_conditioner.onnx")),
vocab_json: Some(p("vocab.json")),
token_scores_json: Some(p("token_scores.json")),
voice_embedding_cache_capacity: 16,
let cfg = OfflineTtsConfig {
model: OfflineTtsModelConfig {
pocket: OfflineTtsPocketModelConfig {
lm_main: Some(p("lm_main.int8.onnx")),
lm_flow: Some(p("lm_flow.int8.onnx")),
encoder: Some(p("encoder.onnx")),
decoder: Some(p("decoder.int8.onnx")),
text_conditioner: Some(p("text_conditioner.onnx")),
vocab_json: Some(p("vocab.json")),
token_scores_json: Some(p("token_scores.json")),
voice_embedding_cache_capacity: 16,
},
num_threads: 1,
debug: false,
..Default::default()
},
num_threads: 1,
debug: false,
..Default::default()
};
let engine = OfflineTts::create(&cfg).expect("engine create");
Expand Down
32 changes: 17 additions & 15 deletions desktop/src-tauri/examples/prod_probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,22 @@ fn main() {
let dir = PathBuf::from(&model_dir);
let p = |name: &str| dir.join(name).to_string_lossy().into_owned();

let mut cfg = OfflineTtsConfig::default();
cfg.model = OfflineTtsModelConfig {
pocket: OfflineTtsPocketModelConfig {
lm_main: Some(p("lm_main.int8.onnx")),
lm_flow: Some(p("lm_flow.int8.onnx")),
encoder: Some(p("encoder.onnx")),
decoder: Some(p("decoder.int8.onnx")),
text_conditioner: Some(p("text_conditioner.onnx")),
vocab_json: Some(p("vocab.json")),
token_scores_json: Some(p("token_scores.json")),
voice_embedding_cache_capacity: 16,
let cfg = OfflineTtsConfig {
model: OfflineTtsModelConfig {
pocket: OfflineTtsPocketModelConfig {
lm_main: Some(p("lm_main.int8.onnx")),
lm_flow: Some(p("lm_flow.int8.onnx")),
encoder: Some(p("encoder.onnx")),
decoder: Some(p("decoder.int8.onnx")),
text_conditioner: Some(p("text_conditioner.onnx")),
vocab_json: Some(p("vocab.json")),
token_scores_json: Some(p("token_scores.json")),
voice_embedding_cache_capacity: 16,
},
num_threads: 1,
debug: false,
..Default::default()
},
num_threads: 1,
debug: false,
..Default::default()
};
let engine = OfflineTts::create(&cfg).expect("engine create");
Expand Down Expand Up @@ -199,8 +201,8 @@ fn find_gap(samples: &[f32], sr: u32, thresh: f32, min_ms: u32) -> String {
let scan_start = (sr as usize * 30) / 1000;
let min_samples = (sr as usize * min_ms as usize) / 1000;
let mut silence_from: Option<usize> = None;
for i in scan_start..samples.len() {
if samples[i].abs() < thresh {
for (i, sample) in samples.iter().enumerate().skip(scan_start) {
if sample.abs() < thresh {
silence_from.get_or_insert(i);
} else if let Some(start) = silence_from {
if i - start >= min_samples {
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/src/commands/identity_archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ mod tests {
let agent_hex = agent.public_key().to_hex();
let agent_compat = nostr::PublicKey::from_hex(&agent_hex).unwrap();
let owner_compat_secret =
nostr::SecretKey::from_slice(&owner.secret_key().as_secret_bytes()[..]).unwrap();
nostr::SecretKey::from_slice(owner.secret_key().as_secret_bytes()).unwrap();
let owner_compat_keys = nostr::Keys::new(owner_compat_secret);
let tag_json = sprout_sdk::nip_oa::compute_auth_tag(&owner_compat_keys, &agent_compat, "")
.expect("compute_auth_tag");
Expand Down
4 changes: 2 additions & 2 deletions desktop/src-tauri/src/commands/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ fn sign_blossom_upload_auth(
Tag::parse(vec!["expiration", &(now + expiry_secs).to_string()])
.map_err(|e| e.to_string())?,
];
if let Some(domain) = extract_server_authority(&base_url) {
if let Some(domain) = extract_server_authority(base_url) {
tags.push(Tag::parse(vec!["server".to_string(), domain]).map_err(|e| e.to_string())?);
}
EventBuilder::new(Kind::from(24242), "Upload sprout-media")
Expand Down Expand Up @@ -265,7 +265,7 @@ fn find_ffmpeg() -> Result<std::path::PathBuf, String> {

/// Detect if a file is a video based on magic bytes.
fn is_video_file(buf: &[u8]) -> bool {
infer::get(buf).map_or(false, |t| t.mime_type().starts_with("video/"))
infer::get(buf).is_some_and(|t| t.mime_type().starts_with("video/"))
}

/// Maximum wall-clock time for an ffmpeg transcode before we kill it.
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/src/commands/media_download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub async fn download_image(
.ok()
.and_then(|u| {
u.path_segments()?
.last()
.next_back()
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
})
Expand Down
13 changes: 5 additions & 8 deletions desktop/src-tauri/src/commands/pairing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,11 @@ async fn pairing_ws_task_inner(
let mut guard = session.lock().await;
let Some(s) = guard.as_mut() else { break };

match s.handle_abort(&event) {
Ok(reason) => {
let _ = app.emit("pairing-aborted", PairingAbortedPayload {
reason: format!("{reason:?}"),
});
break;
}
Err(_) => {}
if let Ok(reason) = s.handle_abort(&event) {
let _ = app.emit("pairing-aborted", PairingAbortedPayload {
reason: format!("{reason:?}"),
});
break;
}

if let Ok(sas) = s.handle_offer(&event) {
Expand Down
6 changes: 3 additions & 3 deletions desktop/src-tauri/src/commands/relay_members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub async fn add_relay_member(
) -> Result<serde_json::Value, String> {
let builder = events::build_relay_admin_add(&target_pubkey, &role)?;
let result = submit_event(builder, &state).await?;
Ok(serde_json::to_value(result).map_err(|e| e.to_string())?)
serde_json::to_value(result).map_err(|e| e.to_string())
}

#[tauri::command]
Expand All @@ -77,7 +77,7 @@ pub async fn remove_relay_member(
) -> Result<serde_json::Value, String> {
let builder = events::build_relay_admin_remove(&target_pubkey)?;
let result = submit_event(builder, &state).await?;
Ok(serde_json::to_value(result).map_err(|e| e.to_string())?)
serde_json::to_value(result).map_err(|e| e.to_string())
}

#[tauri::command]
Expand All @@ -88,5 +88,5 @@ pub async fn change_relay_member_role(
) -> Result<serde_json::Value, String> {
let builder = events::build_relay_admin_change_role(&target_pubkey, &new_role)?;
let result = submit_event(builder, &state).await?;
Ok(serde_json::to_value(result).map_err(|e| e.to_string())?)
serde_json::to_value(result).map_err(|e| e.to_string())
}
4 changes: 2 additions & 2 deletions desktop/src-tauri/src/commands/social.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ pub async fn get_liked_notes(
})],
)
.await?;
reactions.sort_by(|left, right| right.created_at.cmp(&left.created_at));
reactions.sort_by_key(|reaction| std::cmp::Reverse(reaction.created_at));

let reaction_ids: Vec<String> = reactions.iter().map(|event| event.id.to_hex()).collect();
let deletions = if reaction_ids.is_empty() {
Expand Down Expand Up @@ -395,7 +395,7 @@ pub async fn get_notes_timeline(
.collect();

// Sort newest-first.
notes.sort_by(|a, b| b.created_at.cmp(&a.created_at));
notes.sort_by_key(|note| std::cmp::Reverse(note.created_at));
notes.truncate(200);

Ok(UserNotesResponse {
Expand Down
25 changes: 12 additions & 13 deletions desktop/src-tauri/src/huddle/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,13 +664,12 @@ impl ModelManager {
MAX_STT_DOWNLOAD_BYTES,
"stt archive",
|downloaded, content_length| {
if let Some(total) = content_length {
if total > 0 {
let pct = ((downloaded * 89) / total).min(89) as u8;
slot.set_status(ModelStatus::Downloading {
progress_percent: pct,
});
}
if let Some(pct) =
content_length.and_then(|total| (downloaded * 89).checked_div(total))
{
slot.set_status(ModelStatus::Downloading {
progress_percent: pct.min(89) as u8,
});
}
},
)
Expand Down Expand Up @@ -779,10 +778,11 @@ impl ModelManager {
for (i, (url, filename)) in downloads.iter().enumerate() {
eprintln!("sprout-desktop: downloading Pocket TTS {filename} from {url}");

let response = fetch_url(&http_client, url, filename).await.map_err(|e| {
let _ = std::fs::remove_dir_all(&temp_dir);
e
})?;
let response = fetch_url(&http_client, url, filename)
.await
.inspect_err(|_| {
let _ = std::fs::remove_dir_all(&temp_dir);
})?;

let dest = temp_dir.join(filename);
let slot = self.tts.clone();
Expand All @@ -807,9 +807,8 @@ impl ModelManager {
},
)
.await
.map_err(|e| {
.inspect_err(|_| {
let _ = std::fs::remove_dir_all(&temp_dir);
e
})?;
eprintln!("sprout-desktop: downloaded {bytes} bytes ({filename}), wrote to disk");

Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/src/huddle/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub(crate) async fn maybe_start_stt_pipeline(
if !models::is_stt_ready() {
return Ok(false); // Models not downloaded yet — voice-only mode.
}
let model_dir = models::stt_model_dir().ok_or_else(|| "STT model directory not found")?;
let model_dir = models::stt_model_dir().ok_or("STT model directory not found")?;

let channel_uuid = parse_channel_uuid(ephemeral_channel_id)?;

Expand Down
20 changes: 13 additions & 7 deletions desktop/src-tauri/src/huddle/pocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
//! - `load_text_to_speech(model_dir)` → `Result<Engine, String>`
//! - `load_voice_style(path)` → `Result<VoiceStyle, String>`
//! - `Engine::synth_chunk(&self, text, lang, &VoiceStyle, steps, speed)`
//! → `Result<Vec<f32>, String>`
//! → `Result<Vec<f32>, String>`
//!
//! `lang` and `steps` are accepted for API compatibility with the previous
//! Kokoro engine but are unused — Pocket TTS does its own language ID from
Expand Down Expand Up @@ -589,10 +589,12 @@ mod tests {
assert_eq!(out.text, format!("{}Yep.", short_prefix()));
assert!(out.is_short, "1-word input is short");
assert_eq!(out.max_frames, Some(SHORT_PROMPT_MAX_FRAMES));
assert!(
SHORT_PROMPT_MAX_FRAMES < SHERPA_ONNX_MAX_FRAMES_DEFAULT,
"short cap must be tighter than the upstream default"
);
const {
assert!(
SHORT_PROMPT_MAX_FRAMES < SHERPA_ONNX_MAX_FRAMES_DEFAULT,
"short cap must be tighter than the upstream default"
);
}
}

#[test]
Expand Down Expand Up @@ -758,10 +760,14 @@ mod tests {
#[test]
fn short_prompt_max_frames_is_below_upstream_default() {
// Sanity: the override only ever *lowers* the cap, never raises it.
assert!(SHORT_PROMPT_MAX_FRAMES < SHERPA_ONNX_MAX_FRAMES_DEFAULT);
const {
assert!(SHORT_PROMPT_MAX_FRAMES < SHERPA_ONNX_MAX_FRAMES_DEFAULT);
}
// …and is still large enough for a one-to-four-word reply. At Mimi's
// 12.5 Hz frame rate, 100 frames = 8 s, which is roomy.
assert!(SHORT_PROMPT_MAX_FRAMES >= 50, "would risk truncation");
const {
assert!(SHORT_PROMPT_MAX_FRAMES >= 50, "would risk truncation");
}
}

// ── trim_leading_cold_start ──────────────────────────────────────────────
Expand Down
4 changes: 2 additions & 2 deletions desktop/src-tauri/src/huddle/preprocessing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const ABBREVIATIONS: &[&str] = &[
/// Returns non-empty, trimmed strings.
pub fn split_sentences(text: &str) -> Vec<String> {
// First, split on newlines and em-dashes to get coarse segments.
let coarse: Vec<&str> = text.split(|c: char| c == '\n' || c == '—').collect();
let coarse: Vec<&str> = text.split(['\n', '—']).collect();

let mut sentences = Vec::new();

Expand Down Expand Up @@ -240,7 +240,7 @@ fn strip_urls(text: &str) -> String {
// belongs to the surrounding sentence rather than the URL itself.
// A trailing `.`, `!`, or `?` is preserved when it is at end-of-string
// or followed by whitespace (i.e. it is a sentence boundary).
let trailing_punct = if url_token.ends_with(|c: char| matches!(c, '.' | '!' | '?')) {
let trailing_punct = if url_token.ends_with(['.', '!', '?']) {
let after = rest; // rest is already past url_end
if after.is_empty() || after.starts_with(|c: char| c.is_whitespace()) {
// Preserve the trailing punctuation.
Expand Down
Loading