feat(npu): engine log names the decoder EP alongside the encoder EP
This commit is contained in:
1 parent
402ffe4586
commit
7f7de48ecc
1 file changed
+83
-47
+83
-47
@@ -194,17 +194,17 @@ fn load_transcriber(
|
||||
match OnnxTranscriber::load(&dir, backend, language) {
|
||||
Ok(t) => {
|
||||
// The definitive "is the accelerator actually engaged" line
|
||||
// (visible with `npm run tauri dev`): the encoder runs on
|
||||
// the EP below; the autoregressive decoder always runs on
|
||||
// the CPU EP, which is why Task Manager shows only short
|
||||
// periodic NPU spikes alongside sustained CPU load.
|
||||
// (visible with `npm run tauri dev`): encoder EP from the
|
||||
// accel path, decoder EP from wherever its session landed
|
||||
// (OpenVINO/GPU on NPU systems, CPU otherwise/on fallback).
|
||||
tracing::info!(
|
||||
"transcription engine: ONNX {} (encoder EP: {}, decoder: CPU)",
|
||||
"transcription engine: ONNX {} (encoder EP: {}, decoder EP: {})",
|
||||
onnx_models::DEFAULT_ONNX_MODEL,
|
||||
match path {
|
||||
AccelPath::OnnxOpenVino => "OpenVINO/NPU",
|
||||
_ => "DirectML/GPU",
|
||||
}
|
||||
},
|
||||
t.decoder_ep()
|
||||
);
|
||||
return Ok((Box::new(t), backend));
|
||||
}
|
||||
@@ -347,7 +347,9 @@ async fn attribute_split(
|
||||
spans.sort_by_key(|s| s.start_ms);
|
||||
// Uniform naming: "You" -> "You", far speakers -> "Speaker 2", "Speaker 3"….
|
||||
let labels = crate::diarization::voiceprint::first_appearance_order(&spans);
|
||||
Some(crate::diarization::voiceprint::build_name_map(&labels, "You"))
|
||||
Some(crate::diarization::voiceprint::build_name_map(
|
||||
&labels, "You",
|
||||
))
|
||||
}
|
||||
|
||||
/// The distinct speakers seen in `segments` so far, in first-appearance order,
|
||||
@@ -655,28 +657,23 @@ pub async fn start_recording(
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clone();
|
||||
if attribute_split(
|
||||
diarizer.clone(),
|
||||
wav_path_for_diar.clone(),
|
||||
&mut snapshot,
|
||||
)
|
||||
.await
|
||||
.map(|auto_names| {
|
||||
let mut names = names_for_diar.lock().unwrap_or_else(|e| e.into_inner());
|
||||
// Never overwrite a name already set (user rename or a
|
||||
// prior pass) — same guard as the post-stop pass.
|
||||
for (label, name) in auto_names {
|
||||
names.entry(label).or_insert(name);
|
||||
}
|
||||
})
|
||||
.is_none()
|
||||
if attribute_split(diarizer.clone(), wav_path_for_diar.clone(), &mut snapshot)
|
||||
.await
|
||||
.map(|auto_names| {
|
||||
let mut names =
|
||||
names_for_diar.lock().unwrap_or_else(|e| e.into_inner());
|
||||
// Never overwrite a name already set (user rename or a
|
||||
// prior pass) — same guard as the post-stop pass.
|
||||
for (label, name) in auto_names {
|
||||
names.entry(label).or_insert(name);
|
||||
}
|
||||
})
|
||||
.is_none()
|
||||
{
|
||||
continue; // pass failed (already logged); retry next tick
|
||||
}
|
||||
let relabeled: HashMap<u64, String> = snapshot
|
||||
.into_iter()
|
||||
.map(|s| (s.id, s.speaker))
|
||||
.collect();
|
||||
let relabeled: HashMap<u64, String> =
|
||||
snapshot.into_iter().map(|s| (s.id, s.speaker)).collect();
|
||||
let mut segs = segments_for_diar.lock().unwrap_or_else(|e| e.into_inner());
|
||||
// Snapshot prior labels so only segments whose speaker
|
||||
// actually changed this pass get re-emitted (ids are stable;
|
||||
@@ -891,7 +888,8 @@ pub async fn stop_recording(
|
||||
if already_named {
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = state.store.rename_speaker(&meeting_id, &label, &name).await {
|
||||
if let Err(e) = state.store.rename_speaker(&meeting_id, &label, &name).await
|
||||
{
|
||||
tracing::warn!("failed to persist auto speaker name: {e}");
|
||||
continue;
|
||||
}
|
||||
@@ -1757,8 +1755,11 @@ fn pick_realtime_recommendation(
|
||||
.max_by(|a, b| {
|
||||
let sa = sizes.get(&a.model).copied().unwrap_or(0);
|
||||
let sb = sizes.get(&b.model).copied().unwrap_or(0);
|
||||
sa.cmp(&sb)
|
||||
.then(b.rtf.partial_cmp(&a.rtf).unwrap_or(std::cmp::Ordering::Equal))
|
||||
sa.cmp(&sb).then(
|
||||
b.rtf
|
||||
.partial_cmp(&a.rtf)
|
||||
.unwrap_or(std::cmp::Ordering::Equal),
|
||||
)
|
||||
})
|
||||
.map(|r| StressRecommendation {
|
||||
backend: r.backend.clone(),
|
||||
@@ -2525,10 +2526,17 @@ async fn run_import_pipeline(
|
||||
})
|
||||
.await;
|
||||
match transcode {
|
||||
Ok(Ok(())) => {
|
||||
emit_import_progress(&app, &meeting_id, "prepare", "done", Some(t.elapsed().as_millis()), None)
|
||||
Ok(Ok(())) => emit_import_progress(
|
||||
&app,
|
||||
&meeting_id,
|
||||
"prepare",
|
||||
"done",
|
||||
Some(t.elapsed().as_millis()),
|
||||
None,
|
||||
),
|
||||
Ok(Err(e)) => {
|
||||
return fail_import(&app, &store, &meeting_id, "prepare", &e.to_string()).await
|
||||
}
|
||||
Ok(Err(e)) => return fail_import(&app, &store, &meeting_id, "prepare", &e.to_string()).await,
|
||||
Err(e) => return fail_import(&app, &store, &meeting_id, "prepare", &e.to_string()).await,
|
||||
}
|
||||
|
||||
@@ -2552,11 +2560,22 @@ async fn run_import_pipeline(
|
||||
.await;
|
||||
let (mut segments, resolved_language) = match transcribed {
|
||||
Ok(Ok(v)) => {
|
||||
emit_import_progress(&app, &meeting_id, "transcribe", "done", Some(t.elapsed().as_millis()), None);
|
||||
emit_import_progress(
|
||||
&app,
|
||||
&meeting_id,
|
||||
"transcribe",
|
||||
"done",
|
||||
Some(t.elapsed().as_millis()),
|
||||
None,
|
||||
);
|
||||
v
|
||||
}
|
||||
Ok(Err(e)) => return fail_import(&app, &store, &meeting_id, "transcribe", &e.to_string()).await,
|
||||
Err(e) => return fail_import(&app, &store, &meeting_id, "transcribe", &e.to_string()).await,
|
||||
Ok(Err(e)) => {
|
||||
return fail_import(&app, &store, &meeting_id, "transcribe", &e.to_string()).await
|
||||
}
|
||||
Err(e) => {
|
||||
return fail_import(&app, &store, &meeting_id, "transcribe", &e.to_string()).await
|
||||
}
|
||||
};
|
||||
|
||||
// Phase 3: diarize — one pass if the models are installed, else every line
|
||||
@@ -2578,7 +2597,14 @@ async fn run_import_pipeline(
|
||||
Err(e) => tracing::warn!("import diarization task failed: {e}"),
|
||||
}
|
||||
}
|
||||
emit_import_progress(&app, &meeting_id, "diarize", "done", Some(t.elapsed().as_millis()), None);
|
||||
emit_import_progress(
|
||||
&app,
|
||||
&meeting_id,
|
||||
"diarize",
|
||||
"done",
|
||||
Some(t.elapsed().as_millis()),
|
||||
None,
|
||||
);
|
||||
|
||||
// Phase 4: finalize — persist segments, notes, seal audio.
|
||||
emit_import_progress(&app, &meeting_id, "finalize", "active", None, None);
|
||||
@@ -2626,7 +2652,14 @@ async fn run_import_pipeline(
|
||||
}
|
||||
}
|
||||
}
|
||||
emit_import_progress(&app, &meeting_id, "finalize", "done", Some(t.elapsed().as_millis()), None);
|
||||
emit_import_progress(
|
||||
&app,
|
||||
&meeting_id,
|
||||
"finalize",
|
||||
"done",
|
||||
Some(t.elapsed().as_millis()),
|
||||
None,
|
||||
);
|
||||
|
||||
let _ = app.emit(
|
||||
"transcript://finalized",
|
||||
@@ -2643,7 +2676,9 @@ async fn fail_import(
|
||||
phase: &str,
|
||||
message: &str,
|
||||
) {
|
||||
let _ = store.set_meeting_status(&meeting_id.to_string(), "error").await;
|
||||
let _ = store
|
||||
.set_meeting_status(&meeting_id.to_string(), "error")
|
||||
.await;
|
||||
emit_import_progress(app, meeting_id, phase, "error", None, Some(message));
|
||||
}
|
||||
|
||||
@@ -2926,8 +2961,8 @@ fn fold_wav(wav: &[u8], out_channels: u16) -> Option<Vec<u8>> {
|
||||
(hound::SampleFormat::Int, 16) => {
|
||||
let s: Vec<i16> = reader.samples::<i16>().map_while(Result::ok).collect();
|
||||
for frame in s.chunks(ch) {
|
||||
let avg = (frame.iter().map(|&x| x as i32).sum::<i32>()
|
||||
/ frame.len() as i32) as i16;
|
||||
let avg =
|
||||
(frame.iter().map(|&x| x as i32).sum::<i32>() / frame.len() as i32) as i16;
|
||||
for _ in 0..out_channels {
|
||||
writer.write_sample(avg).ok()?;
|
||||
}
|
||||
@@ -3125,10 +3160,7 @@ pub async fn bulk_export_meetings(
|
||||
/// so re-importing never collides with existing meetings. Returns the count
|
||||
/// imported.
|
||||
#[tauri::command]
|
||||
pub async fn import_meeting_bundle(
|
||||
state: State<'_, AppState>,
|
||||
dir: String,
|
||||
) -> WaResult<u32> {
|
||||
pub async fn import_meeting_bundle(state: State<'_, AppState>, dir: String) -> WaResult<u32> {
|
||||
let root = PathBuf::from(&dir);
|
||||
let mut bundles: Vec<PathBuf> = Vec::new();
|
||||
if root.join("meeting.json").exists() {
|
||||
@@ -3977,7 +4009,11 @@ only, with no preamble or commentary.";
|
||||
let notes = notes.trim();
|
||||
let user = format!(
|
||||
"My rough notes:\n{}\n\nTranscript:\n{}",
|
||||
if notes.is_empty() { "(none yet)" } else { notes },
|
||||
if notes.is_empty() {
|
||||
"(none yet)"
|
||||
} else {
|
||||
notes
|
||||
},
|
||||
prompt.transcript
|
||||
);
|
||||
provider
|
||||
@@ -4895,8 +4931,8 @@ pub(crate) fn spawn_auto_sync(
|
||||
let app = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
match enqueue_meeting_sync(store.as_ref(), &meeting_id, None, true).await {
|
||||
Ok(0) => {} // nothing changed
|
||||
Ok(_) => pump_sync(&app, store.as_ref()).await, // upload the changes
|
||||
Ok(0) => {} // nothing changed
|
||||
Ok(_) => pump_sync(&app, store.as_ref()).await, // upload the changes
|
||||
Err(e) => tracing::warn!("auto-sync enqueue failed: {e:?}"),
|
||||
}
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user