fix(sync): Replace text-only progress with animated bars for docs/embed stages
Stages 3 (generate-docs) and 4 (embed) reported progress by appending
"(N/M)" text to the stage spinner message, while stages 1-2 (ingest)
used dedicated indicatif progress bars with animated [====> ] rendering
registered with the global MultiProgress. This visual inconsistency
was introduced when progress callbacks were wired through in 266ed78.
Replace the spinner.set_message() callbacks with proper ProgressBar
instances that match the ingest stage pattern:
- Create a bar-style ProgressBar registered via multi().add()
- Use the same template/progress_chars as the ingest discussion bars
- Lazy-init the tick via AtomicBool to avoid showing the bar before
the first callback fires (matching how ingest enables ticks only
at DiscussionSyncStarted)
- Update set_length on every callback for the docs stage, since the
regenerator's estimated_total can grow if new dirty items are
queued during processing (using .max() internally)
- Clean up both the sub-bar and stage spinner on completion/error
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
use console::style;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use serde::Serialize;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tracing::Instrument;
|
||||
use tracing::{info, warn};
|
||||
|
||||
@@ -160,16 +162,39 @@ pub async fn run_sync(
|
||||
options.robot_mode,
|
||||
);
|
||||
info!("Sync stage {current_stage}/{total_stages}: generating documents");
|
||||
let docs_spinner = spinner.clone();
|
||||
|
||||
// Create a dedicated progress bar matching the ingest stage style
|
||||
let docs_bar = if options.robot_mode {
|
||||
ProgressBar::hidden()
|
||||
} else {
|
||||
let b = crate::cli::progress::multi().add(ProgressBar::new(0));
|
||||
b.set_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template(
|
||||
" {spinner:.blue} Processing documents [{bar:30.cyan/dim}] {pos}/{len}",
|
||||
)
|
||||
.unwrap()
|
||||
.progress_chars("=> "),
|
||||
);
|
||||
b
|
||||
};
|
||||
let docs_bar_clone = docs_bar.clone();
|
||||
let tick_started = Arc::new(AtomicBool::new(false));
|
||||
let tick_started_clone = Arc::clone(&tick_started);
|
||||
let docs_cb: Box<dyn Fn(usize, usize)> = Box::new(move |processed, total| {
|
||||
if total > 0 {
|
||||
docs_spinner.set_message(format!(
|
||||
"Processing documents... ({processed}/{total})"
|
||||
));
|
||||
if !tick_started_clone.swap(true, Ordering::Relaxed) {
|
||||
docs_bar_clone.enable_steady_tick(std::time::Duration::from_millis(100));
|
||||
}
|
||||
// Update length every callback — the regenerator's estimated_total
|
||||
// can grow if new dirty items are queued during processing.
|
||||
docs_bar_clone.set_length(total as u64);
|
||||
docs_bar_clone.set_position(processed as u64);
|
||||
}
|
||||
});
|
||||
let docs_result = run_generate_docs(config, false, None, Some(docs_cb))?;
|
||||
result.documents_regenerated = docs_result.regenerated;
|
||||
docs_bar.finish_and_clear();
|
||||
spinner.finish_and_clear();
|
||||
} else {
|
||||
info!("Sync: skipping document generation (--no-docs)");
|
||||
@@ -185,19 +210,43 @@ pub async fn run_sync(
|
||||
options.robot_mode,
|
||||
);
|
||||
info!("Sync stage {current_stage}/{total_stages}: embedding documents");
|
||||
let embed_spinner = spinner.clone();
|
||||
|
||||
// Create a dedicated progress bar matching the ingest stage style
|
||||
let embed_bar = if options.robot_mode {
|
||||
ProgressBar::hidden()
|
||||
} else {
|
||||
let b = crate::cli::progress::multi().add(ProgressBar::new(0));
|
||||
b.set_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template(
|
||||
" {spinner:.blue} Generating embeddings [{bar:30.cyan/dim}] {pos}/{len}",
|
||||
)
|
||||
.unwrap()
|
||||
.progress_chars("=> "),
|
||||
);
|
||||
b
|
||||
};
|
||||
let embed_bar_clone = embed_bar.clone();
|
||||
let tick_started = Arc::new(AtomicBool::new(false));
|
||||
let tick_started_clone = Arc::clone(&tick_started);
|
||||
let embed_cb: Box<dyn Fn(usize, usize)> = Box::new(move |processed, total| {
|
||||
embed_spinner.set_message(format!(
|
||||
"Embedding documents... ({processed}/{total})"
|
||||
));
|
||||
if total > 0 {
|
||||
if !tick_started_clone.swap(true, Ordering::Relaxed) {
|
||||
embed_bar_clone.enable_steady_tick(std::time::Duration::from_millis(100));
|
||||
}
|
||||
embed_bar_clone.set_length(total as u64);
|
||||
embed_bar_clone.set_position(processed as u64);
|
||||
}
|
||||
});
|
||||
match run_embed(config, options.full, false, Some(embed_cb)).await {
|
||||
Ok(embed_result) => {
|
||||
result.documents_embedded = embed_result.embedded;
|
||||
embed_bar.finish_and_clear();
|
||||
spinner.finish_and_clear();
|
||||
}
|
||||
Err(e) => {
|
||||
// Graceful degradation: Ollama down is a warning, not an error
|
||||
embed_bar.finish_and_clear();
|
||||
spinner.finish_and_clear();
|
||||
if !options.robot_mode {
|
||||
eprintln!(" {} Embedding skipped ({})", style("warn").yellow(), e);
|
||||
|
||||
Reference in New Issue
Block a user