From 9867cfe93836bbb75ad3271a1b5ffa25458f3696 Mon Sep 17 00:00:00 2001 From: Nick DiZazzo Date: Sun, 13 Sep 2026 15:04:04 -0400 Subject: [PATCH 1/4] refactor(runtime): route runtime console output through the sink-aware writer Model maintenance, catalog downloads, layer-package progress, key rotation, model resolution/search, the interactive prompt, the skippy-server startup banners, and the benchmark prompt importer now write through the sink-aware console writer instead of printing directly. Output is byte-identical, but it is now suppressed in JSON mode and while the interactive dashboard owns the terminal, so stray lines can no longer paint over the frame. `skippy-server example-config` writes through the machine-output handle so its JSON always reaches stdout. The client model list writes to stderr directly to keep the embedded client free of the events dependency. --- Cargo.lock | 3 + crates/mesh-client/src/models/catalog.rs | 13 +- .../materialization/package_download.rs | 35 +++-- .../src/models/catalog.rs | 40 ++++-- .../src/models/maintenance.rs | 120 ++++++++++++------ .../src/models/resolve/mod.rs | 7 +- .../src/models/search.rs | 4 +- .../src/network/nostr/keys.rs | 17 ++- .../src/runtime/interactive.rs | 10 +- crates/mesh-llm-system/Cargo.toml | 1 + .../mesh-llm-system/src/benchmark_prompts.rs | 6 +- .../src/frontend/generation/server.rs | 13 +- crates/skippy-server/src/http.rs | 7 +- crates/skippy-server/src/main.rs | 5 +- 14 files changed, 195 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53bd2af5ac..fb912446a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4117,6 +4117,7 @@ dependencies = [ "tar", "tempfile", "tokio", + "tracing", ] [[package]] @@ -4157,6 +4158,7 @@ dependencies = [ "libc", "libloading", "mesh-llm-build-info", + "mesh-llm-events", "mesh-llm-gpu-bench", "mesh-llm-native-runtime", "mesh-llm-release-footer", @@ -4240,6 +4242,7 @@ dependencies = [ "mesh-native-serving-plugin-api", "skippy-server", "skippy-tokenizer", + "tracing", ] [[package]] diff --git a/crates/mesh-client/src/models/catalog.rs b/crates/mesh-client/src/models/catalog.rs index 089bd97176..27126999af 100644 --- a/crates/mesh-client/src/models/catalog.rs +++ b/crates/mesh-client/src/models/catalog.rs @@ -1,4 +1,5 @@ use serde::Deserialize; +use std::io::Write; use std::sync::LazyLock; #[derive(Clone, Debug, Deserialize)] @@ -106,17 +107,21 @@ pub fn huggingface_repo_url(url: &str) -> Option { } pub fn list_models() { - eprintln!("Available models:"); - eprintln!(); + let _ = writeln!(std::io::stderr(), "Available models:"); + let _ = writeln!(std::io::stderr()); for m in MODEL_CATALOG.iter() { let draft_info = if let Some(d) = m.draft.as_deref() { format!(" (draft: {})", d) } else { String::new() }; - eprintln!( + let _ = writeln!( + std::io::stderr(), " {:40} {:>6} {}{}", - m.name, m.size, m.description, draft_info + m.name, + m.size, + m.description, + draft_info ); } } diff --git a/crates/mesh-llm-host-runtime/src/inference/skippy/materialization/package_download.rs b/crates/mesh-llm-host-runtime/src/inference/skippy/materialization/package_download.rs index 9ecc1989df..27197703be 100644 --- a/crates/mesh-llm-host-runtime/src/inference/skippy/materialization/package_download.rs +++ b/crates/mesh-llm-host-runtime/src/inference/skippy/materialization/package_download.rs @@ -156,11 +156,13 @@ impl LayerPackageDownloadScope { bytes_per_sec: Option, force: bool, ) { + let mut err = mesh_llm_events::console_err(); let Ok(mut scope_state) = self.state.lock() else { return; }; if !scope_state.announced { - eprintln!( + let _ = writeln!( + err, "\r\x1b[KšŸ“¦ Downloading layer package {} ({} file(s))", self.package, self.total_files ); @@ -204,11 +206,11 @@ impl LayerPackageDownloadScope { ), 3, ); - eprint!("\r\x1b[K {gauge}"); - let _ = std::io::stderr().flush(); + let _ = write!(err, "\r\x1b[K {gauge}"); + let _ = err.flush(); scope_state.drawn_line = true; if force { - eprintln!(); + let _ = writeln!(err); scope_state.drawn_line = false; } } @@ -275,6 +277,7 @@ impl LayerPackageDownloadProgress { } fn emit_ready(&self, path: &Path) { + let mut err = mesh_llm_events::console_err(); let total = fs::metadata(path) .ok() .map(|metadata| metadata.len()) @@ -313,12 +316,17 @@ impl LayerPackageDownloadProgress { if !showed_progress { let file = layer_package_artifact_display(&self.label, &self.file); match total { - Some(total) if total > 0 => eprintln!( - " āœ… Ready {} ({})", - file, - format_layer_package_download_bytes(total) - ), - _ => eprintln!(" āœ… Ready {}", file), + Some(total) if total > 0 => { + let _ = writeln!( + err, + " āœ… Ready {} ({})", + file, + format_layer_package_download_bytes(total) + ); + } + _ => { + let _ = writeln!(err, " āœ… Ready {}", file); + } } } } @@ -479,6 +487,7 @@ fn draw_layer_package_file_progress( bytes_per_sec: Option, force: bool, ) { + let mut err = mesh_llm_events::console_err(); let percent = if total == 0 { 0 } else { @@ -515,10 +524,10 @@ fn draw_layer_package_file_progress( ), 3, ); - eprint!("\r\x1b[K {gauge}"); - let _ = std::io::stderr().flush(); + let _ = write!(err, "\r\x1b[K {gauge}"); + let _ = err.flush(); if force { - eprintln!(); + let _ = writeln!(err); } } diff --git a/crates/mesh-llm-host-runtime/src/models/catalog.rs b/crates/mesh-llm-host-runtime/src/models/catalog.rs index 8a1e0834d7..6a682521d1 100644 --- a/crates/mesh-llm-host-runtime/src/models/catalog.rs +++ b/crates/mesh-llm-host-runtime/src/models/catalog.rs @@ -357,6 +357,7 @@ impl MeshDownloadProgress { } fn draw(state: &mut MeshDownloadProgressState, force: bool) { + let mut err = mesh_llm_events::console_err(); if !force && state.downloaded == 0 && state.total == 0 { return; } @@ -408,7 +409,8 @@ impl MeshDownloadProgress { ), }; let bar = render_inline_progress_bar(ratio, DOWNLOAD_PROGRESS_BAR_WIDTH); - eprint!( + let _ = write!( + err, "\r\x1b[KDownloading {:>3}.{:01}% {} {} / {}{}", percent_major, percent_minor, @@ -418,7 +420,7 @@ impl MeshDownloadProgress { speed_suffix, ); state.drawn_line = true; - let _ = std::io::stderr().flush(); + let _ = err.flush(); } fn apply_download_event(state: &mut MeshDownloadProgressState, event: &DownloadEvent) { @@ -566,6 +568,7 @@ fn download_hf_assets_sync( assets: Vec, progress: bool, ) -> Result { + let mut console = mesh_llm_events::console_err(); let api = super::build_hf_api(false)?; let mut download_plan = initial_download_plan_for_assets(assets)?; let current_plan: Vec<(bool, HfAsset)> = download_plan.iter().cloned().collect(); @@ -592,7 +595,9 @@ fn download_hf_assets_sync( None, None, ModelProgressStatus::Ensuring, - || eprintln!("šŸ“„ Ensuring {} is available locally...", label), + || { + let _ = writeln!(console, "šŸ“„ Ensuring {} is available locally...", label); + }, ); } @@ -649,7 +654,9 @@ fn download_hf_assets_sync( None, None, ModelProgressStatus::Ensuring, - || eprintln!(" šŸ“„ Ensuring model {}", asset.file), + || { + let _ = writeln!(console, " šŸ“„ Ensuring model {}", asset.file); + }, ); } let visible_tracker = if progress && required { @@ -743,6 +750,7 @@ fn emit_completed_asset_progress( path: &Path, visible_tracker: Option<&Arc>, ) { + let mut err = mesh_llm_events::console_err(); if required && interactive_tui_active() { emit_required_asset_ready_progress(label, asset_file, path, visible_tracker); } else if interactive_tui_active() { @@ -752,7 +760,9 @@ fn emit_completed_asset_progress( None, None, ModelProgressStatus::Ready, - || eprintln!(" Downloaded model metadata {asset_file}"), + || { + let _ = writeln!(err, " Downloaded model metadata {asset_file}"); + }, ); } } @@ -763,6 +773,7 @@ fn emit_required_asset_ready_progress( path: &Path, visible_tracker: Option<&Arc>, ) { + let mut err = mesh_llm_events::console_err(); let showed_progress = visible_tracker.is_some_and(|tracker| tracker.showed_meaningful_progress()); if showed_progress { @@ -772,7 +783,9 @@ fn emit_required_asset_ready_progress( None, None, ModelProgressStatus::Ready, - || eprintln!(" āœ… Ready {asset_file}"), + || { + let _ = writeln!(err, " āœ… Ready {asset_file}"); + }, ); } else if let Ok(meta) = std::fs::metadata(path) { emit_or_print_model_progress( @@ -782,11 +795,12 @@ fn emit_required_asset_ready_progress( Some(meta.len()), ModelProgressStatus::Ready, || { - eprintln!( + let _ = writeln!( + err, " āœ… Ready {} ({})", asset_file, format_download_bytes(meta.len()) - ) + ); }, ); } else { @@ -796,7 +810,9 @@ fn emit_required_asset_ready_progress( None, None, ModelProgressStatus::Ready, - || eprintln!(" āœ… Ready {asset_file}"), + || { + let _ = writeln!(err, " āœ… Ready {asset_file}"); + }, ); } } @@ -839,11 +855,13 @@ fn print_multipart_terminal_progress( progress: &MultipartDownloadProgress, terminal_frame_mode: MultipartTerminalFrameMode, ) { - eprint!( + let mut err = mesh_llm_events::console_err(); + let _ = write!( + err, "{}", multipart_progress_terminal_frame(progress, terminal_frame_mode) ); - let _ = std::io::stderr().flush(); + let _ = err.flush(); } fn multipart_progress_terminal_frame( diff --git a/crates/mesh-llm-host-runtime/src/models/maintenance.rs b/crates/mesh-llm-host-runtime/src/models/maintenance.rs index dc17b9e175..bcbc6ccc15 100644 --- a/crates/mesh-llm-host-runtime/src/models/maintenance.rs +++ b/crates/mesh-llm-host-runtime/src/models/maintenance.rs @@ -3,6 +3,7 @@ use anyhow::{Context, Result}; use hf_hub::{RepoTypeModel, repository::ModelInfo}; use mesh_llm_events::terminal_progress::{DeterminateProgressLine, clear_stderr_line}; use std::collections::BTreeSet; +use std::io::Write; use std::path::{Path, PathBuf}; struct CachedRepo { @@ -23,11 +24,12 @@ pub fn run_update(repo: Option<&str>, all: bool, check: bool) -> Result<()> { } fn run_update_sync(repo: Option<&str>, all: bool, check: bool) -> Result<()> { + let mut err = mesh_llm_events::console_err(); let api = build_hf_api(!check)?; let repos = cached_repos()?; if repos.is_empty() { - eprintln!("šŸ“¦ No cached Hugging Face model repos found"); - eprintln!(" {}", huggingface_hub_cache_dir().display()); + writeln!(err, "šŸ“¦ No cached Hugging Face model repos found")?; + writeln!(err, " {}", huggingface_hub_cache_dir().display())?; return Ok(()); } @@ -59,10 +61,10 @@ fn run_update_sync(repo: Option<&str>, all: bool, check: bool) -> Result<()> { }; if !check { - eprintln!("šŸ”„ Updating cached Hugging Face repos"); - eprintln!("šŸ“ Cache: {}", huggingface_hub_cache_dir().display()); - eprintln!("šŸ“¦ Selected: {}", selected.len()); - eprintln!(); + writeln!(err, "šŸ”„ Updating cached Hugging Face repos")?; + writeln!(err, "šŸ“ Cache: {}", huggingface_hub_cache_dir().display())?; + writeln!(err, "šŸ“¦ Selected: {}", selected.len())?; + writeln!(err)?; } let mut updates = 0usize; let total_selected = selected.len(); @@ -73,48 +75,66 @@ fn run_update_sync(repo: Option<&str>, all: bool, check: bool) -> Result<()> { if let Some(remote_revision) = check_repo_update(&api, &repo)? { updates += 1; clear_progress_line()?; - eprintln!("šŸ†• [{}/{}] {}", index + 1, total_selected, repo.repo_id); - eprintln!(" ref: {}", repo.ref_name); - eprintln!(" local: {}", short_revision(&repo.local_revision)); - eprintln!(" latest: {}", short_revision(&remote_revision)); - eprintln!(" update: mesh-llm models updates {}", repo.repo_id); - eprintln!(); + writeln!( + err, + "šŸ†• [{}/{}] {}", + index + 1, + total_selected, + repo.repo_id + )?; + writeln!(err, " ref: {}", repo.ref_name)?; + writeln!(err, " local: {}", short_revision(&repo.local_revision))?; + writeln!(err, " latest: {}", short_revision(&remote_revision))?; + writeln!(err, " update: mesh-llm models updates {}", repo.repo_id)?; + writeln!(err)?; } } else { - eprintln!("🧭 [{}/{}] {}", index + 1, total_selected, repo.repo_id); + writeln!( + err, + "🧭 [{}/{}] {}", + index + 1, + total_selected, + repo.repo_id + )?; let counts = update_cached_repo(&api, &repo)?; refresh_totals.refreshed += counts.refreshed; refresh_totals.missing_meta += counts.missing_meta; - eprintln!(); + writeln!(err)?; } } if check { clear_progress_line()?; if updates > 0 { - eprintln!("šŸ“¬ Update summary"); - eprintln!(" repos with updates: {updates}"); - eprintln!(" update one: mesh-llm models updates "); - eprintln!(" update all: mesh-llm models updates --all"); + writeln!(err, "šŸ“¬ Update summary")?; + writeln!(err, " repos with updates: {updates}")?; + writeln!(err, " update one: mesh-llm models updates ")?; + writeln!(err, " update all: mesh-llm models updates --all")?; } } else { - eprintln!(); - eprintln!("āœ… Update complete"); - eprintln!(" refreshed files: {}", refresh_totals.refreshed); + writeln!(err)?; + writeln!(err, "āœ… Update complete")?; + writeln!(err, " refreshed files: {}", refresh_totals.refreshed)?; if refresh_totals.missing_meta > 0 { - eprintln!(" missing config.json: {}", refresh_totals.missing_meta); + writeln!( + err, + " missing config.json: {}", + refresh_totals.missing_meta + )?; } } Ok(()) } pub fn warn_about_updates_for_paths(paths: &[PathBuf]) { + let mut console = mesh_llm_events::console_err(); let mut cache_models = Vec::new(); let mut seen = BTreeSet::new(); for path in paths { let Some(repo) = (match cached_repo_for_path(path) { Ok(repo) => repo, Err(err) => { - eprintln!( + let _ = writeln!( + console, "Warning: could not inspect cached Hugging Face repo for {}: {err}", path.display() ); @@ -132,19 +152,29 @@ pub fn warn_about_updates_for_paths(paths: &[PathBuf]) { } let result = run_hf_sync(move || { + let mut console = mesh_llm_events::console_err(); let api = build_hf_api(false)?; for repo in cache_models { match check_repo_update(&api, &repo) { Ok(Some(remote_revision)) => { - eprintln!("šŸ†• Update available for {}", repo.repo_id); - eprintln!(" local: {}", short_revision(&repo.local_revision)); - eprintln!(" latest: {}", short_revision(&remote_revision)); - eprintln!(" continuing with pinned local snapshot"); - eprintln!(" update: mesh-llm models updates {}", repo.repo_id); + let _ = writeln!(console, "šŸ†• Update available for {}", repo.repo_id); + let _ = writeln!( + console, + " local: {}", + short_revision(&repo.local_revision) + ); + let _ = writeln!(console, " latest: {}", short_revision(&remote_revision)); + let _ = writeln!(console, " continuing with pinned local snapshot"); + let _ = writeln!( + console, + " update: mesh-llm models updates {}", + repo.repo_id + ); } Ok(None) => {} Err(err) => { - eprintln!( + let _ = writeln!( + console, "Warning: could not check for updates for {}: {err}", repo.repo_id ); @@ -154,7 +184,10 @@ pub fn warn_about_updates_for_paths(paths: &[PathBuf]) { Ok(()) }); if let Err(err) = result { - eprintln!("Warning: could not initialize Hugging Face update checks: {err}"); + let _ = writeln!( + console, + "Warning: could not initialize Hugging Face update checks: {err}" + ); } } @@ -340,6 +373,7 @@ fn check_repo_update(api: &hf_hub::HFClientSync, repo: &CachedRepo) -> Result Result { + let mut console = mesh_llm_events::console_err(); let (owner, name) = repo .repo_id .split_once('/') @@ -347,12 +381,20 @@ fn update_cached_repo(api: &hf_hub::HFClientSync, repo: &CachedRepo) -> Result Result Result { - eprintln!(" āœ… {}", path.display()); + writeln!(console, " āœ… {}", path.display())?; counts.refreshed += 1; } Err(err) if file == "config.json" => { if is_not_found_error(&err.to_string()) { - eprintln!(" ā„¹ļø no config.json published for {}", repo.repo_id); + writeln!( + console, + " ā„¹ļø no config.json published for {}", + repo.repo_id + )?; } else { - eprintln!(" āš ļø config.json: {err}"); + writeln!(console, " āš ļø config.json: {err}")?; } counts.missing_meta += 1; } diff --git a/crates/mesh-llm-host-runtime/src/models/resolve/mod.rs b/crates/mesh-llm-host-runtime/src/models/resolve/mod.rs index 5323bf8407..e487d20d84 100644 --- a/crates/mesh-llm-host-runtime/src/models/resolve/mod.rs +++ b/crates/mesh-llm-host-runtime/src/models/resolve/mod.rs @@ -11,6 +11,7 @@ use model_artifact::{ModelArtifactFile, select_primary_artifact_file}; use serde::Deserialize; use std::cmp::Ordering; use std::collections::HashSet; +use std::io::Write; // std imports kept minimal; filesystem ops via std::fs::read_dir used in helper use std::path::{Path, PathBuf}; #[cfg(test)] @@ -195,6 +196,7 @@ async fn download_exact_ref_with_progress_direct( progress: bool, direct: bool, ) -> Result { + let mut err = mesh_llm_events::console_err(); let input = canonicalize_model_ref_input(input).await?; match parse_exact_model_ref(&input)? { ExactModelRef::Catalog(model) => download_remote_catalog_model(&model, progress).await, @@ -212,7 +214,7 @@ async fn download_exact_ref_with_progress_direct( ) { if progress { - eprintln!("ℹ Using repackaged model from catalog: {}", model.name); + writeln!(err, "ℹ Using repackaged model from catalog: {}", model.name)?; } return download_remote_catalog_model(&model, progress).await; } @@ -233,6 +235,7 @@ pub async fn resolve_model_spec(input: &Path) -> Result { } pub async fn resolve_model_spec_with_progress(input: &Path, progress: bool) -> Result { + let mut err = mesh_llm_events::console_err(); let raw = input.to_string_lossy(); if raw.starts_with("hf://") { @@ -265,7 +268,7 @@ pub async fn resolve_model_spec_with_progress(input: &Path, progress: bool) -> R .context("join remote catalog resolve task")? { if progress { - eprintln!("šŸ“„ Found in remote catalog: {}", hf_ref.name); + writeln!(err, "šŸ“„ Found in remote catalog: {}", hf_ref.name)?; } return catalog::download_hf_repo_file_with_progress_label( &hf_ref.repo, diff --git a/crates/mesh-llm-host-runtime/src/models/search.rs b/crates/mesh-llm-host-runtime/src/models/search.rs index 5546de870e..f98076ec77 100644 --- a/crates/mesh-llm-host-runtime/src/models/search.rs +++ b/crates/mesh-llm-host-runtime/src/models/search.rs @@ -12,6 +12,7 @@ use hf_hub::repository::ModelInfo; use regex_lite::Regex; use serde_json::{Value, json}; use std::collections::HashSet; +use std::io::Write; use std::sync::LazyLock; use tokio::task::JoinSet; use tokio_stream::StreamExt; @@ -179,6 +180,7 @@ pub async fn search_huggingface( where F: FnMut(SearchProgress), { + let mut console = mesh_llm_events::console_err(); const SEARCH_CONCURRENCY: usize = 10; let repo_limit = match sort { @@ -250,7 +252,7 @@ where } Ok(None) => {} Err(err) => { - eprintln!("āš ļø Failed to inspect Hugging Face repo: {err:#}"); + writeln!(console, "āš ļø Failed to inspect Hugging Face repo: {err:#}")?; } } if let Some((next_index, repo)) = pending.next() { diff --git a/crates/mesh-llm-host-runtime/src/network/nostr/keys.rs b/crates/mesh-llm-host-runtime/src/network/nostr/keys.rs index 1f26499cb4..53971d52a3 100644 --- a/crates/mesh-llm-host-runtime/src/network/nostr/keys.rs +++ b/crates/mesh-llm-host-runtime/src/network/nostr/keys.rs @@ -2,6 +2,7 @@ use anyhow::Result; use nostr_sdk::prelude::*; +use std::io::Write; // --------------------------------------------------------------------------- // Keys — stored in ~/.mesh-llm/nostr.nsec for the default node key, or in the @@ -80,24 +81,28 @@ fn ensure_private_nostr_key_file(_path: &std::path::Path) -> Result<()> { /// Delete the Nostr key and node identity key. After rotation the /// node gets a fresh identity on next start. pub fn rotate_keys() -> Result<()> { + let mut err = mesh_llm_events::console_err(); let nostr_path = nostr_key_path()?; if nostr_path.exists() { std::fs::remove_file(&nostr_path)?; - eprintln!("šŸ”‘ Deleted {}", nostr_path.display()); + writeln!(err, "šŸ”‘ Deleted {}", nostr_path.display())?; } else { - eprintln!("No Nostr key to rotate (none exists yet)."); + writeln!(err, "No Nostr key to rotate (none exists yet).")?; } let node_key_path = crate::mesh::default_node_key_path()?; if node_key_path.exists() { std::fs::remove_file(&node_key_path)?; - eprintln!("šŸ”‘ Deleted {}", node_key_path.display()); + writeln!(err, "šŸ”‘ Deleted {}", node_key_path.display())?; } else { - eprintln!("No node key to rotate (none exists yet)."); + writeln!(err, "No node key to rotate (none exists yet).")?; } - eprintln!(); - eprintln!("āœ… Keys rotated. New identities will be generated on next start."); + writeln!(err)?; + writeln!( + err, + "āœ… Keys rotated. New identities will be generated on next start." + )?; Ok(()) } diff --git a/crates/mesh-llm-host-runtime/src/runtime/interactive.rs b/crates/mesh-llm-host-runtime/src/runtime/interactive.rs index 75bdc5ace8..70c7ddbc86 100644 --- a/crates/mesh-llm-host-runtime/src/runtime/interactive.rs +++ b/crates/mesh-llm-host-runtime/src/runtime/interactive.rs @@ -7,7 +7,6 @@ use crossterm::terminal::{disable_raw_mode, enable_raw_mode, size}; use mesh_llm_events::{ConsoleSessionMode, OutputSink, TuiControlFlow, TuiEvent, TuiKeyEvent}; use std::fmt; use std::io::BufRead; -#[cfg(test)] use std::io::Write; use std::sync::Arc; use std::time::Duration; @@ -172,6 +171,7 @@ fn spawn_line_handler_with_runtime( output_sink: Arc, initial_prompt_mode: InitialPromptMode, ) { + let mut console = mesh_llm_events::console_err(); if matches!(initial_prompt_mode, InitialPromptMode::Immediate) { let _ = output_sink.write_ready_prompt(); } @@ -209,7 +209,7 @@ fn spawn_line_handler_with_runtime( match line_rx.recv().await { Some(Ok(line)) => match parse_command(&line) { Some(InteractiveCommand::Help) => { - eprintln!("{HELP_TEXT}"); + let _ = writeln!(console, "{HELP_TEXT}"); } Some(InteractiveCommand::Quit) => { if control_tx @@ -223,7 +223,11 @@ fn spawn_line_handler_with_runtime( break; } Some(InteractiveCommand::Info) => { - eprintln!("{}", console_state.status_snapshot_string().await); + let _ = writeln!( + console, + "{}", + console_state.status_snapshot_string().await + ); } None => {} }, diff --git a/crates/mesh-llm-system/Cargo.toml b/crates/mesh-llm-system/Cargo.toml index bc6be641bb..66e20401c0 100644 --- a/crates/mesh-llm-system/Cargo.toml +++ b/crates/mesh-llm-system/Cargo.toml @@ -16,6 +16,7 @@ hex = "0.4.3" libc = "0.2.183" libloading = "0.9" mesh-llm-build-info.workspace = true +mesh-llm-events = { path = "../mesh-llm-events", version = "0.76.1" } mesh-llm-gpu-bench = { path = "../mesh-llm-gpu-bench", version = "0.76.1" } mesh-llm-native-runtime = { path = "../mesh-llm-native-runtime", version = "0.76.1" } mesh-llm-release-footer.workspace = true diff --git a/crates/mesh-llm-system/src/benchmark_prompts.rs b/crates/mesh-llm-system/src/benchmark_prompts.rs index 1b6e504c62..a63933263b 100644 --- a/crates/mesh-llm-system/src/benchmark_prompts.rs +++ b/crates/mesh-llm-system/src/benchmark_prompts.rs @@ -79,6 +79,7 @@ struct DatasetSplit { } pub async fn import_prompt_corpus(args: ImportPromptsArgs) -> Result<()> { + let mut err = mesh_llm_events::console_err(); if args.limit == 0 { bail!("--limit must be at least 1"); } @@ -125,12 +126,13 @@ pub async fn import_prompt_corpus(args: ImportPromptsArgs) -> Result<()> { writer.flush().context("Flush prompt corpus")?; let summary = summarize_prompts(&prompts, &args.output); - eprintln!( + writeln!( + err, "šŸ“ Imported {} prompts from {} to {}", summary.prompt_count, source_spec.dataset, args.output.display() - ); + )?; Ok(()) } diff --git a/crates/skippy-server/src/frontend/generation/server.rs b/crates/skippy-server/src/frontend/generation/server.rs index ab0a5b9b4a..76e6894a8a 100644 --- a/crates/skippy-server/src/frontend/generation/server.rs +++ b/crates/skippy-server/src/frontend/generation/server.rs @@ -55,6 +55,7 @@ use skippy_protocol::StageConfig; use skippy_protocol::StageTopology; use std::collections::BTreeMap; use std::future::Future; +use std::io::Write; use std::net::SocketAddr; use std::path::PathBuf; use std::sync::Arc; @@ -63,6 +64,7 @@ use std::sync::atomic::AtomicUsize; use std::time::Duration; pub async fn serve_openai(args: ServeOpenAiArgs) -> Result<()> { + let mut out = mesh_llm_events::console_out(); let config = load_json::(&args.config) .with_context(|| format!("load stage config {}", args.config.display()))?; let topology = match args.topology.as_ref() { @@ -195,7 +197,8 @@ pub async fn serve_openai(args: ServeOpenAiArgs) -> Result<()> { .wrap_backend_with_context_limit(backend, Some(ctx_size)); let app: Router = instrumented_openai_router(backend, tokenizer, telemetry.clone()); - println!( + writeln!( + out, "skippy-server listening: openai={} model_id={} backend={} generation_concurrency={} generation_queue_capacity={} generation_admission_timeout_secs={}", args.bind_addr, model_id, @@ -203,7 +206,7 @@ pub async fn serve_openai(args: ServeOpenAiArgs) -> Result<()> { generation_concurrency, generation_queue_capacity, args.generation_admission_timeout_secs, - ); + )?; let listener = bind_serve_listener(args.bind_addr)?; axum::serve(listener, app).await?; @@ -341,17 +344,19 @@ async fn serve_embedded_openai_with_shutdown_and_scheduler( shutdown: impl Future + Send + 'static, iteration_scheduler: Option, ) -> Result<()> { + let mut out = mesh_llm_events::console_out(); let bind_addr = args.bind_addr; let binding = embedded_openai_router_with_scheduler(args, iteration_scheduler)?; - println!( + writeln!( + out, "skippy-server listening: openai={} model_id={} backend=embedded-stage0 generation_concurrency={} generation_queue_capacity={} generation_admission_timeout_secs={}", bind_addr, binding.model_id, binding.generation_concurrency, binding.generation_queue_capacity, binding.generation_admission_timeout_secs, - ); + )?; let listener = bind_serve_listener(bind_addr)?; axum::serve(listener, binding.router) diff --git a/crates/skippy-server/src/http.rs b/crates/skippy-server/src/http.rs index 0a399068cd..35f0b9f02e 100644 --- a/crates/skippy-server/src/http.rs +++ b/crates/skippy-server/src/http.rs @@ -1,6 +1,7 @@ use std::{ collections::BTreeMap, future::Future, + io::Write, net::SocketAddr, sync::{Arc, Mutex}, time::Instant, @@ -206,6 +207,7 @@ pub async fn serve_stage_http_with_shutdown( options: StageHttpOptions, shutdown: impl Future + Send + 'static, ) -> Result<()> { + let mut out = mesh_llm_events::console_out(); let bind_addr = options.bind_addr; let stage_id = options.config.stage_id.clone(); let layer_start = options.config.layer_start; @@ -213,10 +215,11 @@ pub async fn serve_stage_http_with_shutdown( let load_mode = options.config.load_mode.clone(); let app = stage_http_router(options)?; - println!( + writeln!( + out, "skippy-server listening: http={} stage_id={} layer_range={}..{} load_mode={:?}", bind_addr, stage_id, layer_start, layer_end, load_mode, - ); + )?; let listener = bind_serve_listener(bind_addr)?; axum::serve(listener, app) diff --git a/crates/skippy-server/src/main.rs b/crates/skippy-server/src/main.rs index 26cc3ead7a..beba9a9347 100644 --- a/crates/skippy-server/src/main.rs +++ b/crates/skippy-server/src/main.rs @@ -1,3 +1,5 @@ +use std::io::Write; + use anyhow::Result; use clap::Parser; @@ -13,7 +15,8 @@ async fn main() -> Result<()> { Command::ServeBinary(args) => serve_binary(args).await, Command::ServeOpenAi(args) => serve_openai(args).await, Command::ExampleConfig => { - println!("{}", serde_json::to_string_pretty(&example_config())?); + let mut out = mesh_llm_events::machine_out(); + writeln!(out, "{}", serde_json::to_string_pretty(&example_config())?)?; Ok(()) } } From dbd0ca0608a8e59c27eaf837cad8e4573780968f Mon Sep 17 00:00:00 2001 From: Nick DiZazzo Date: Sun, 13 Sep 2026 15:04:15 -0400 Subject: [PATCH 2/4] refactor(runtime): report transport and plugin diagnostics through tracing Binary-transport lifecycle, lane handshakes, native serving plugin dispatch, and native runtime discovery now log through `tracing` instead of writing to stderr, so each line carries a level and a target and can be filtered with `RUST_LOG`. Failures log at warn, lifecycle milestones at info, and per-request chatter at debug. The runtime subscriber gains directives for `skippy_server`, `mesh_native_serving_plugin_host`, and `mesh_llm_runtime_install` so their warnings still reach the dashboard; without them the default ERROR filter would drop the messages that used to print unconditionally. --- .../src/runtime/tracing_writer.rs | 5 +- crates/mesh-llm-runtime-install/Cargo.toml | 1 + .../mesh-llm-runtime-install/src/discovery.rs | 4 +- .../Cargo.toml | 1 + .../src/lib.rs | 2 +- .../src/plugin_dispatch.rs | 102 +++++++++--------- .../src/binary_transport/binary_messaging.rs | 12 +-- .../binary_messaging/reply.rs | 6 +- .../src/binary_transport/direct_return.rs | 10 +- .../src/binary_transport/preconnect.rs | 4 +- .../src/binary_transport/socket.rs | 6 +- .../src/binary_transport/stage_execution.rs | 2 +- .../frontend/embedded_generation/lifecycle.rs | 2 +- .../frontend/generation/persistent_lanes.rs | 6 +- .../src/frontend/linear_proposal.rs | 2 +- 15 files changed, 83 insertions(+), 82 deletions(-) diff --git a/crates/mesh-llm-host-runtime/src/runtime/tracing_writer.rs b/crates/mesh-llm-host-runtime/src/runtime/tracing_writer.rs index f82ee103d0..d2935ec4b8 100644 --- a/crates/mesh-llm-host-runtime/src/runtime/tracing_writer.rs +++ b/crates/mesh-llm-host-runtime/src/runtime/tracing_writer.rs @@ -303,7 +303,10 @@ pub(super) fn runtime_tracing_subscriber() .add_directive("mesh_inference=info".parse()?) .add_directive("nostr_relay_pool=off".parse()?) .add_directive("nostr_sdk=warn".parse()?) - .add_directive("noq_proto::connection=warn".parse()?), + .add_directive("noq_proto::connection=warn".parse()?) + .add_directive("skippy_server=warn".parse()?) + .add_directive("mesh_native_serving_plugin_host=warn".parse()?) + .add_directive("mesh_llm_runtime_install=warn".parse()?), ) .with_writer(MeshTracingStderr) .finish()) diff --git a/crates/mesh-llm-runtime-install/Cargo.toml b/crates/mesh-llm-runtime-install/Cargo.toml index 348af5e67c..4f8629be60 100644 --- a/crates/mesh-llm-runtime-install/Cargo.toml +++ b/crates/mesh-llm-runtime-install/Cargo.toml @@ -28,3 +28,4 @@ skippy-ffi = { path = "../skippy-ffi", version = "0.76.1", default-features = fa tar = "0.4" tempfile = "3" tokio = { version = "1", features = ["fs", "io-util", "rt"] } +tracing = "0.1" diff --git a/crates/mesh-llm-runtime-install/src/discovery.rs b/crates/mesh-llm-runtime-install/src/discovery.rs index e2f0cf3d4a..2b6da041ed 100644 --- a/crates/mesh-llm-runtime-install/src/discovery.rs +++ b/crates/mesh-llm-runtime-install/src/discovery.rs @@ -97,7 +97,7 @@ fn read_installed_runtime_lenient(path: &Path) -> Option let manifest = match NativeRuntimeManifest::read_from_dir(path) { Ok(manifest) => manifest, Err(error) => { - eprintln!( + tracing::warn!( "warning: skipping malformed native runtime {}: {error:#}", path.display() ); @@ -268,7 +268,7 @@ fn append_runtime_dir( .with_context(|| format!("validate native runtime {}", runtime_dir.display())); } InvalidManifestPolicy::WarnAndSkip => { - eprintln!( + tracing::warn!( "warning: skipping malformed native runtime {}: {error:#}", runtime_dir.display() ); diff --git a/crates/mesh-native-serving-plugin-host/Cargo.toml b/crates/mesh-native-serving-plugin-host/Cargo.toml index 77932a1d3e..a47a1e725d 100644 --- a/crates/mesh-native-serving-plugin-host/Cargo.toml +++ b/crates/mesh-native-serving-plugin-host/Cargo.toml @@ -14,6 +14,7 @@ libloading = "0.9" mesh-native-serving-plugin-api = { path = "../mesh-native-serving-plugin-api", version = "0.76.1" } skippy-server = { path = "../skippy-server", version = "0.76.1" } skippy-tokenizer = { path = "../skippy-tokenizer", version = "0.76.1" } +tracing = "0.1" [lints] workspace = true diff --git a/crates/mesh-native-serving-plugin-host/src/lib.rs b/crates/mesh-native-serving-plugin-host/src/lib.rs index 959fede680..78b8d5ddd5 100644 --- a/crates/mesh-native-serving-plugin-host/src/lib.rs +++ b/crates/mesh-native-serving-plugin-host/src/lib.rs @@ -613,7 +613,7 @@ impl ActivePlugin { impl Drop for ActivePlugin { fn drop(&mut self) { if let Err(error) = self.shutdown() { - eprintln!("native serving plugin shutdown failed: {error:#}"); + tracing::warn!("native serving plugin shutdown failed: {error:#}"); } } } diff --git a/crates/mesh-native-serving-plugin-host/src/plugin_dispatch.rs b/crates/mesh-native-serving-plugin-host/src/plugin_dispatch.rs index 84e55650b1..2b336f308b 100644 --- a/crates/mesh-native-serving-plugin-host/src/plugin_dispatch.rs +++ b/crates/mesh-native-serving-plugin-host/src/plugin_dispatch.rs @@ -442,7 +442,7 @@ impl Drop for PluginDriver { if let Some(active) = Arc::get_mut(&mut self.active) && let Err(error) = active.shutdown() { - eprintln!("native serving plugin shutdown failed: {error:#}"); + tracing::warn!("native serving plugin shutdown failed: {error:#}"); } } } @@ -455,7 +455,7 @@ impl Drop for PluginDriver { fn stop_worker(queue: &Arc, worker: &WorkerHandle, label: &str) { queue.close(); if !worker.exit.wait_for_exit(CLEAN_SHUTDOWN_TIMEOUT) { - eprintln!( + tracing::warn!( "native serving plugin {label} worker did not stop within {CLEAN_SHUTDOWN_TIMEOUT:?}; \ deferring plugin shutdown to that thread" ); @@ -512,31 +512,24 @@ fn plugin_worker( lifecycle_delivery_failures.fetch_add(1, Ordering::Relaxed); } else { report_delivery_failures.fetch_add(1, Ordering::Relaxed); - eprintln!("native serving plugin report handoff failed: {error:#}"); + tracing::warn!("native serving plugin report handoff failed: {error:#}"); } } } } -fn run_proposal( - active: &ActivePlugin, +/// `Some(_)` is the response to send *instead of* dispatching the proposal; +/// `None` means the proposal may run. +fn proposal_predispatch_gate( passive_queue: &PluginCommandQueue, - enqueued_at: Instant, - query: LinearProposalQuery, - reply: &SyncSender, -) { - let deadline = query.deadline; - let queue_wait_us = elapsed_us(enqueued_at); + deadline: Instant, + queue_wait_us: u64, +) -> Option { if Instant::now() >= deadline { - send_proposal_response( - passive_queue, - reply, - abstention( - queue_wait_us, - LinearProposalSourceOutcome::DeadlineExceededBeforeDispatch, - ), - ); - return; + return Some(abstention( + queue_wait_us, + LinearProposalSourceOutcome::DeadlineExceededBeforeDispatch, + )); } // Reports and discards run on the passive worker so they cannot consume @@ -548,40 +541,41 @@ fn run_proposal( let fence_consumes_query_deadline = remaining <= CLEAN_SHUTDOWN_TIMEOUT; if let Err(error) = fence_passive(passive_queue, fence_timeout) { if fence_consumes_query_deadline && matches!(&error, PassiveFenceError::Timeout(_)) { - send_proposal_response( - passive_queue, - reply, - abstention( - queue_wait_us, - LinearProposalSourceOutcome::HostDeadlineExceeded, - ), - ); - return; + return Some(abstention( + queue_wait_us, + LinearProposalSourceOutcome::HostDeadlineExceeded, + )); } - eprintln!("native serving plugin proposal fence failed: {error}"); - send_proposal_response( - passive_queue, - reply, - ProposalResponse { - proposal: Err(error.to_string()), - telemetry: LinearProposalSourceTelemetry { - queue_wait_us, - callback_elapsed_us: 0, - outcome: LinearProposalSourceOutcome::SourceError, - }, + tracing::warn!("native serving plugin proposal fence failed: {error}"); + return Some(ProposalResponse { + proposal: Err(error.to_string()), + telemetry: LinearProposalSourceTelemetry { + queue_wait_us, + callback_elapsed_us: 0, + outcome: LinearProposalSourceOutcome::SourceError, }, - ); - return; + }); } if Instant::now() >= deadline { - send_proposal_response( - passive_queue, - reply, - abstention( - queue_wait_us, - LinearProposalSourceOutcome::DeadlineExceededBeforeDispatch, - ), - ); + return Some(abstention( + queue_wait_us, + LinearProposalSourceOutcome::DeadlineExceededBeforeDispatch, + )); + } + None +} + +fn run_proposal( + active: &ActivePlugin, + passive_queue: &PluginCommandQueue, + enqueued_at: Instant, + query: LinearProposalQuery, + reply: &SyncSender, +) { + let deadline = query.deadline; + let queue_wait_us = elapsed_us(enqueued_at); + if let Some(response) = proposal_predispatch_gate(passive_queue, deadline, queue_wait_us) { + send_proposal_response(passive_queue, reply, response); return; } @@ -610,7 +604,7 @@ fn run_proposal( Err(error) => { // Fail open, but surface the plugin's message instead of // silently degrading a failure into an abstention. - eprintln!("native serving plugin proposal failed: {error:#}"); + tracing::warn!("native serving plugin proposal failed: {error:#}"); ( Err(format!("{error:#}")), LinearProposalSourceOutcome::SourceError, @@ -655,7 +649,7 @@ fn send_proposal_response( LinearProposalDiscardReason::DeadlineExceeded, )) { - eprintln!( + tracing::warn!( "native serving plugin could not deliver the terminal discard for a detached proposal reply: {error:?}" ); } @@ -667,7 +661,7 @@ fn discard_late_candidate(passive_queue: &PluginCommandQueue, proposal: &LinearP proposal.decision_id.as_bytes().to_vec(), LinearProposalDiscardReason::DeadlineExceeded, )) { - eprintln!( + tracing::warn!( "native serving plugin could not deliver the terminal discard for a late proposal: \ {error:?}" ); @@ -775,7 +769,7 @@ fn plugin_passive_worker( PluginCommand::Report(event, ack) => { let result = active.report(&event); if ack.send(result).is_err() { - eprintln!( + tracing::warn!( "native serving plugin report callback acknowledgement receiver dropped" ); } diff --git a/crates/skippy-server/src/binary_transport/binary_messaging.rs b/crates/skippy-server/src/binary_transport/binary_messaging.rs index 6a1a314dc8..c03ac677c8 100644 --- a/crates/skippy-server/src/binary_transport/binary_messaging.rs +++ b/crates/skippy-server/src/binary_transport/binary_messaging.rs @@ -422,7 +422,7 @@ fn run_binary_stage( ) .await { - eprintln!("embedded OpenAI server failed: {error:#}"); + tracing::warn!("embedded OpenAI server failed: {error:#}"); } }); } @@ -432,7 +432,7 @@ fn run_binary_stage( }) .transpose() .context("spawn downstream preconnector")?; - println!( + tracing::info!( "skippy-server listening: binary={} stage_id={} layer_range={}..{} input_activation_width={} output_activation_width={}", bind_addr, config.stage_id, @@ -467,7 +467,7 @@ fn run_binary_stage( }; prepare_binary_stage_connection(&upstream)?; let peer_addr = upstream.peer_addr().ok(); - eprintln!( + tracing::debug!( "binary accepted connection: stage_id={} peer={peer_addr:?}", config.stage_id ); @@ -488,7 +488,7 @@ fn run_binary_stage( let task_control = worker_control.clone(); let task = thread::spawn(move || { let connection_result = (|| -> Result<()> { - eprintln!( + tracing::debug!( "binary sending ready: stage_id={} peer={peer_addr:?}", config.stage_id ); @@ -496,7 +496,7 @@ fn run_binary_stage( .context("consume optional client ready hello")?; send_ready(&mut upstream).context("failed to send binary ready")?; upstream.flush().ok(); - eprintln!( + tracing::debug!( "binary sent ready: stage_id={} peer={peer_addr:?}", config.stage_id ); @@ -565,7 +565,7 @@ fn run_binary_stage( attrs.insert("llama_stage.peer_addr".to_string(), json!(peer_addr)); } attrs.insert("llama_stage.error".to_string(), json!(error.to_string())); - eprintln!("{error:#}"); + tracing::warn!("{error:#}"); telemetry.emit("stage.binary_connection_error", attrs); } task_control.clear(); diff --git a/crates/skippy-server/src/binary_transport/binary_messaging/reply.rs b/crates/skippy-server/src/binary_transport/binary_messaging/reply.rs index 251ae0b4e6..59b61a23b4 100644 --- a/crates/skippy-server/src/binary_transport/binary_messaging/reply.rs +++ b/crates/skippy-server/src/binary_transport/binary_messaging/reply.rs @@ -52,12 +52,12 @@ pub(in crate::binary_transport) fn configure_prediction_return_stream( match prediction_return_sinks.take_wait(request_id, session_id, Duration::from_millis(250)) { Ok(Some(stream)) => { prediction_return_streams.insert((request_id, session_id), stream); - eprintln!("direct prediction return using upstream-opened sink"); + tracing::debug!("direct prediction return using upstream-opened sink"); return; } Ok(None) => {} Err(error) => { - eprintln!("direct prediction return sink lookup failed: {error:#}"); + tracing::warn!("direct prediction return sink lookup failed: {error:#}"); } } @@ -72,7 +72,7 @@ pub(in crate::binary_transport) fn configure_prediction_return_stream( prediction_return_streams.insert((request_id, session_id), stream); } Err(error) => { - eprintln!( + tracing::warn!( "direct prediction return unavailable; falling back to upstream reply: {error:#}" ); } diff --git a/crates/skippy-server/src/binary_transport/direct_return.rs b/crates/skippy-server/src/binary_transport/direct_return.rs index 5776fe3da8..10ad236c7a 100644 --- a/crates/skippy-server/src/binary_transport/direct_return.rs +++ b/crates/skippy-server/src/binary_transport/direct_return.rs @@ -86,7 +86,7 @@ impl PredictionReturnListener { match listener.accept() { Ok((stream, _)) => { if let Err(error) = stream.set_nonblocking(false) { - eprintln!( + tracing::warn!( "direct prediction return connection failed: set blocking: {error}" ); continue; @@ -94,7 +94,9 @@ impl PredictionReturnListener { let hub = thread_hub.clone(); thread::spawn(move || { if let Err(error) = handle_prediction_return_connection(hub, stream) { - eprintln!("direct prediction return connection failed: {error:#}"); + tracing::warn!( + "direct prediction return connection failed: {error:#}" + ); } }); } @@ -103,7 +105,7 @@ impl PredictionReturnListener { } Err(error) if error.kind() == io::ErrorKind::Interrupted => {} Err(error) => { - eprintln!("direct prediction return listener failed: {error}"); + tracing::warn!("direct prediction return listener failed: {error}"); break; } } @@ -254,7 +256,7 @@ impl PredictionReturnReceiver { let key = self.key; thread::spawn(move || { if let Err(error) = hub.handle_return_stream(key, stream) { - eprintln!("direct prediction return reader failed: {error:#}"); + tracing::warn!("direct prediction return reader failed: {error:#}"); } }); } diff --git a/crates/skippy-server/src/binary_transport/preconnect.rs b/crates/skippy-server/src/binary_transport/preconnect.rs index 84150db07f..2a0eee1775 100644 --- a/crates/skippy-server/src/binary_transport/preconnect.rs +++ b/crates/skippy-server/src/binary_transport/preconnect.rs @@ -71,7 +71,7 @@ fn run_downstream_preconnector( &shutdown, ) { Ok(Some(stream)) => { - eprintln!( + tracing::info!( "downstream warm preconnect ready: stage_id={} local={:?} remote={:?}", config.stage_id, stream.local_addr().ok(), @@ -84,7 +84,7 @@ fn run_downstream_preconnector( if shutdown.load(Ordering::SeqCst) { return; } - eprintln!( + tracing::warn!( "downstream warm preconnect failed: stage_id={} error={error:#}", config.stage_id, ); diff --git a/crates/skippy-server/src/binary_transport/socket.rs b/crates/skippy-server/src/binary_transport/socket.rs index 74635766b3..62a4a53ce1 100644 --- a/crates/skippy-server/src/binary_transport/socket.rs +++ b/crates/skippy-server/src/binary_transport/socket.rs @@ -147,7 +147,7 @@ fn connect_downstream_socket_inner( match $connect { Ok(stream) => return Ok(stream), Err(error) => { - eprintln!( + tracing::debug!( "downstream connect retry: source={source_ip:?} remote={downstream_addr} mode={} error={error}", $mode ); @@ -201,7 +201,7 @@ pub(super) fn connect_route_selected_with_timeout( )); } } - eprintln!( + tracing::debug!( "downstream connect succeeded: source={source_ip:?} remote={downstream_addr} mode=route-selected" ); Ok(stream) @@ -239,7 +239,7 @@ pub(super) fn validate_route_selected_stream( )); } } - eprintln!( + tracing::debug!( "downstream connect retry succeeded: source={source_ip:?} mode=blocking-route-selected" ); Ok(stream) diff --git a/crates/skippy-server/src/binary_transport/stage_execution.rs b/crates/skippy-server/src/binary_transport/stage_execution.rs index 80367f040e..5a2bde2c34 100644 --- a/crates/skippy-server/src/binary_transport/stage_execution.rs +++ b/crates/skippy-server/src/binary_transport/stage_execution.rs @@ -288,7 +288,7 @@ pub(in crate::binary_transport) fn consume_optional_client_ready_hello( Ok(4) if i32::from_le_bytes(bytes) == READY_MAGIC => { skippy_protocol::binary::recv_ready(&mut *stream) .context("consume client ready hello")?; - eprintln!("binary consumed client ready hello"); + tracing::debug!("binary consumed client ready hello"); } Ok(_) => {} Err(error) diff --git a/crates/skippy-server/src/frontend/embedded_generation/lifecycle.rs b/crates/skippy-server/src/frontend/embedded_generation/lifecycle.rs index b1e9fecd56..413cb2b610 100644 --- a/crates/skippy-server/src/frontend/embedded_generation/lifecycle.rs +++ b/crates/skippy-server/src/frontend/embedded_generation/lifecycle.rs @@ -198,7 +198,7 @@ pub(super) fn open_upstream_prediction_return(request: &EmbeddedStageZeroGenerat true } Err(error) => { - eprintln!("direct prediction return upstream-opened sink unavailable: {error:#}"); + tracing::warn!("direct prediction return upstream-opened sink unavailable: {error:#}"); false } } diff --git a/crates/skippy-server/src/frontend/generation/persistent_lanes.rs b/crates/skippy-server/src/frontend/generation/persistent_lanes.rs index b641c9481b..f5ad0a4112 100644 --- a/crates/skippy-server/src/frontend/generation/persistent_lanes.rs +++ b/crates/skippy-server/src/frontend/generation/persistent_lanes.rs @@ -328,7 +328,7 @@ impl PersistentStageLanePool { let stream = self .connect_lane_once(lane_id, connect_timeout, ready_timeout) .inspect_err(|error| { - eprintln!( + tracing::warn!( "openai downstream lane handshake failed: stage_id={} lane_id={lane_id}: {error:#}", self.config.stage_id, ); @@ -368,7 +368,7 @@ impl PersistentStageLanePool { .ok_or_else(|| anyhow!("embedded stage0 has no downstream"))?; let local_addr = stream.local_addr().ok(); let peer_addr = stream.peer_addr().ok(); - eprintln!( + tracing::debug!( "openai downstream lane waiting ready: stage_id={} lane_id={lane_id} local={local_addr:?} peer={peer_addr:?}", self.config.stage_id ); @@ -376,7 +376,7 @@ impl PersistentStageLanePool { .context("send persistent downstream lane client ready hello")?; receive_persistent_lane_ready(&mut stream, ready_timeout)?; configure_persistent_lane_io_deadlines(&stream)?; - eprintln!( + tracing::debug!( "openai downstream lane received ready: stage_id={} lane_id={lane_id} local={local_addr:?} peer={peer_addr:?}", self.config.stage_id ); diff --git a/crates/skippy-server/src/frontend/linear_proposal.rs b/crates/skippy-server/src/frontend/linear_proposal.rs index 00753b0daa..1f8c05b992 100644 --- a/crates/skippy-server/src/frontend/linear_proposal.rs +++ b/crates/skippy-server/src/frontend/linear_proposal.rs @@ -661,7 +661,7 @@ pub(crate) fn execute_linear_proposal_with_terminal_discard( .discard(decision_id, LinearProposalDiscardReason::ExecutionFailed) .is_err() { - eprintln!( + tracing::warn!( "linear proposal terminal discard failed; preserving the primary execution error" ); } From 142efa85333e7f472b3078525b4fdad8bd51665d Mon Sep 17 00:00:00 2001 From: Nick DiZazzo Date: Sun, 13 Sep 2026 15:04:21 -0400 Subject: [PATCH 3/4] feat(events): publish mesh and auto-update progress as typed events Mesh publishing and auto-update progress now travel as structured `nostr_publishing` and `auto_update` events instead of raw stderr writes, so the dashboard, the JSON log, and any future consumer see them with a level and an event name rather than as unattributed `stdout` lines. The auto-update event carries the release version when one is known. `mesh-llm update` still writes through the console writer, because a one-shot command installs no event sink and the emitted events would be dropped. With these converted the console-print ratchet drops from 133 approved occurrences to 6, all in the two terminal-progress writers. --- crates/mesh-llm-events/src/lib.rs | 13 + .../src/network/discovery.rs | 5 +- .../src/network/nostr/publish.rs | 69 ++- crates/mesh-llm-system/src/autoupdate.rs | 66 +- tools/xtask/data/console_print_allowlist.json | 562 ------------------ 5 files changed, 107 insertions(+), 608 deletions(-) diff --git a/crates/mesh-llm-events/src/lib.rs b/crates/mesh-llm-events/src/lib.rs index ba29786c88..c9530c87d7 100644 --- a/crates/mesh-llm-events/src/lib.rs +++ b/crates/mesh-llm-events/src/lib.rs @@ -571,6 +571,13 @@ pub enum OutputEvent { model: String, target: String, }, + NostrPublishing { + message: String, + }, + AutoUpdate { + message: String, + version: Option, + }, Warning { message: String, context: Option, @@ -636,6 +643,8 @@ impl OutputEvent { OutputEvent::RuntimeReady { .. } => "ready", OutputEvent::ModelDownloadProgress { .. } => "model_download_progress", OutputEvent::RequestRouted { .. } => "request_routed", + OutputEvent::NostrPublishing { .. } => "nostr_publishing", + OutputEvent::AutoUpdate { .. } => "auto_update", OutputEvent::Warning { .. } => "warning", OutputEvent::Error { .. } => "error", OutputEvent::Fatal { .. } => "fatal", @@ -657,6 +666,8 @@ impl OutputEvent { OutputEvent::Warning { .. } => OutputLevel::Warn, OutputEvent::Error { .. } => OutputLevel::Error, OutputEvent::Fatal { .. } => OutputLevel::Fatal, + OutputEvent::NostrPublishing { .. } => OutputLevel::Info, + OutputEvent::AutoUpdate { .. } => OutputLevel::Info, _ => OutputLevel::Info, } } @@ -815,6 +826,8 @@ impl OutputEvent { OutputEvent::RequestRouted { model, target } => { format!("routed request for {model} to {target}") } + OutputEvent::NostrPublishing { message } => message.clone(), + OutputEvent::AutoUpdate { message, .. } => message.clone(), OutputEvent::Warning { message, .. } => message.clone(), OutputEvent::Error { message, .. } => message.clone(), OutputEvent::Fatal { message, .. } => message.clone(), diff --git a/crates/mesh-llm-host-runtime/src/network/discovery.rs b/crates/mesh-llm-host-runtime/src/network/discovery.rs index aa4431a449..3042a85078 100644 --- a/crates/mesh-llm-host-runtime/src/network/discovery.rs +++ b/crates/mesh-llm-host-runtime/src/network/discovery.rs @@ -1,5 +1,6 @@ use anyhow::{Context, Result}; use mdns_sd::{DaemonStatus, ResolvedService, ServiceDaemon, ServiceEvent, ServiceInfo}; +use mesh_llm_events::{OutputEvent, emit_event}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::collections::HashMap; @@ -336,7 +337,9 @@ pub(crate) async fn publish_lan_loop(node: crate::mesh::Node, config: LanPublish let instance_name = lan_instance_name(&node).await; let host_name = format!("{instance_name}.local."); - eprintln!("Publishing mesh on local LAN via mDNS ({LAN_SERVICE_TYPE})"); + let _ = emit_event(OutputEvent::NostrPublishing { + message: format!("Publishing mesh on local LAN via mDNS ({LAN_SERVICE_TYPE})"), + }); let mut last_reported = None; loop { diff --git a/crates/mesh-llm-host-runtime/src/network/nostr/publish.rs b/crates/mesh-llm-host-runtime/src/network/nostr/publish.rs index 3a08a02b22..d2e27e8a4b 100644 --- a/crates/mesh-llm-host-runtime/src/network/nostr/publish.rs +++ b/crates/mesh-llm-host-runtime/src/network/nostr/publish.rs @@ -4,6 +4,7 @@ use super::contracts::{DiscoveredMesh, MESH_SERVICE_KIND, MeshListing}; use super::discovery::{DiscoveryClient, MeshFilter, discover}; use super::keys::load_or_create_keys; use anyhow::Result; +use mesh_llm_events::{OutputEvent, emit_event}; use nostr_sdk::prelude::*; use std::time::Duration; @@ -127,11 +128,13 @@ pub async fn publish_loop(node: crate::mesh::Node, keys: Keys, config: PublishLo // Wait for local serving to be ready before first publish (up to 60s). wait_for_local_serving_ready(&node).await; - eprintln!( - "šŸ“” Publishing mesh to Nostr (npub: {}...{})", - &npub[..12], - &npub[npub.len() - 8..] - ); + let _ = emit_event(OutputEvent::NostrPublishing { + message: format!( + "šŸ“” Publishing mesh to Nostr (npub: {}...{})", + &npub[..12], + &npub[npub.len() - 8..] + ), + }); let mut delisted = false; @@ -237,7 +240,9 @@ pub async fn publish_watchdog( continue; } - eprintln!("šŸ“” Taking over Nostr publishing for the mesh"); + let _ = emit_event(OutputEvent::NostrPublishing { + message: "šŸ“” Taking over Nostr publishing for the mesh".to_string(), + }); let Some(keys) = load_watchdog_publish_keys(check_interval_secs).await else { continue; }; @@ -338,7 +343,9 @@ async fn create_publish_loop_publisher( fn log_publish_client_cap(max_clients: Option) { if let Some(cap) = max_clients { - eprintln!(" Will delist when {} clients connected", cap); + let _ = emit_event(OutputEvent::NostrPublishing { + message: format!(" Will delist when {} clients connected", cap), + }); } } @@ -396,7 +403,11 @@ async fn confirm_missing_listing_after_backoff( node: &crate::mesh::Node, ) -> bool { let backoff = (rand::random::() % 7) + 3; - eprintln!("šŸ“” Mesh listing missing from Nostr — waiting {backoff}s before taking over..."); + let _ = emit_event(OutputEvent::NostrPublishing { + message: format!( + "šŸ“” Mesh listing missing from Nostr — waiting {backoff}s before taking over..." + ), + }); tokio::time::sleep(Duration::from_secs(backoff)).await; let Ok(recheck) = discover(relays, filter, disco).await else { @@ -406,7 +417,9 @@ async fn confirm_missing_listing_after_backoff( let our_mesh_id = node.mesh_id().await; let still_missing = !mesh_listing_present(&recheck, our_mesh_id.as_deref(), &served); if !still_missing { - eprintln!("šŸ“” Someone else took over publishing — standing down"); + let _ = emit_event(OutputEvent::NostrPublishing { + message: "šŸ“” Someone else took over publishing — standing down".to_string(), + }); } still_missing } @@ -451,18 +464,22 @@ async fn rejoin_larger_mesh_target( my_node_count: usize, interval_secs: u64, ) -> bool { - eprintln!( - "šŸ“” Found larger mesh '{}' ({} nodes vs our {}) — rejoining", - target.listing.name.as_deref().unwrap_or("unnamed"), - target.listing.node_count, - my_node_count - ); + let _ = emit_event(OutputEvent::NostrPublishing { + message: format!( + "šŸ“” Found larger mesh '{}' ({} nodes vs our {}) — rejoining", + target.listing.name.as_deref().unwrap_or("unnamed"), + target.listing.node_count, + my_node_count + ), + }); unpublish_before_rejoin(publisher).await; if join_larger_mesh(node, target).await.is_err() { tokio::time::sleep(Duration::from_secs(interval_secs)).await; return true; } - eprintln!("šŸ“” Merged into mesh — resuming publish as member"); + let _ = emit_event(OutputEvent::NostrPublishing { + message: "šŸ“” Merged into mesh — resuming publish as member".to_string(), + }); tokio::time::sleep(Duration::from_secs(30)).await; true } @@ -634,19 +651,23 @@ async fn update_delisted_state( if let Err(e) = publisher.unpublish().await { tracing::warn!("Failed to unpublish from Nostr: {e}"); } - eprintln!( - "šŸ“” Delisted from Nostr ({} clients, cap is {})", - client_count, cap - ); + let _ = emit_event(OutputEvent::NostrPublishing { + message: format!( + "šŸ“” Delisted from Nostr ({} clients, cap is {})", + client_count, cap + ), + }); *delisted = true; tokio::time::sleep(Duration::from_secs(interval_secs)).await; return true; } if client_count < cap && *delisted { - eprintln!( - "šŸ“” Re-publishing to Nostr ({} clients, cap is {})", - client_count, cap - ); + let _ = emit_event(OutputEvent::NostrPublishing { + message: format!( + "šŸ“” Re-publishing to Nostr ({} clients, cap is {})", + client_count, cap + ), + }); *delisted = false; } false diff --git a/crates/mesh-llm-system/src/autoupdate.rs b/crates/mesh-llm-system/src/autoupdate.rs index 1434f45153..b8af777fd7 100644 --- a/crates/mesh-llm-system/src/autoupdate.rs +++ b/crates/mesh-llm-system/src/autoupdate.rs @@ -1,4 +1,6 @@ use anyhow::{Context, Result, bail}; +use mesh_llm_events::{OutputEvent, emit_event}; +use std::io::Write; use std::path::{Path, PathBuf}; use crate::backend; @@ -141,16 +143,18 @@ pub async fn maybe_auto_update(options: AutoUpdateOptions) -> Result { } pub async fn run_update_command(options: UpdateCommandOptions<'_>) -> Result<()> { + let mut console = mesh_llm_events::console_err(); let target = require_update_target(options.flavor, options.detect_flavor)?; let requested_version = options.requested_version; let Some(release) = resolve_release_info(requested_version).await? else { bail!("Could not check for a release right now. Try again shortly."); }; if requested_version.is_none() && !version_newer(&release.version, options.current_version) { - eprintln!( + writeln!( + console, "mesh-llm is already up to date (v{}).", options.current_version - ); + )?; return Ok(()); } let asset_preference = if requested_version.is_some() { @@ -172,7 +176,8 @@ pub async fn run_update_command(options: UpdateCommandOptions<'_>) -> Result<()> bail!("{} is not writable.", target.exe.display()); } - eprintln!( + writeln!( + console, "ā¬‡ļø {} mesh-llm v{} -> v{} ({})...", describe_requested_update( &release.version, @@ -182,7 +187,7 @@ pub async fn run_update_command(options: UpdateCommandOptions<'_>) -> Result<()> options.current_version, release.version, target.bundle_flavor.suffix() - ); + )?; match install_latest_bundle( &target.exe, &target.install_dir, @@ -194,18 +199,19 @@ pub async fn run_update_command(options: UpdateCommandOptions<'_>) -> Result<()> .await { Ok(InstallOutcome::ExitNow) => { - eprintln!("āœ… Updated to v{}", release.version); + writeln!(console, "āœ… Updated to v{}", release.version)?; Ok(()) } Ok(InstallOutcome::HandoffAndExit) => { - eprintln!( + writeln!( + console, "āœ… Applying update to v{}; exiting so the installer can finish", release.version - ); + )?; std::process::exit(0); } Ok(InstallOutcome::RestartNow) => { - eprintln!("āœ… Updated to v{}", release.version); + writeln!(console, "āœ… Updated to v{}", release.version)?; Ok(()) } Err(err) => Err(err), @@ -294,18 +300,24 @@ async fn apply_update_if_available( return Ok(false); }; if !path_is_writable(&target.exe) { - eprintln!( - "āš ļø Auto-update skipped: {} is not writable", - target.exe.display() - ); + let _ = emit_event(OutputEvent::AutoUpdate { + message: format!( + "āš ļø Auto-update skipped: {} is not writable", + target.exe.display() + ), + version: None, + }); return Ok(true); } - eprintln!( - "ā¬‡ļø Updating mesh-llm v{current_version} -> v{} ({})...", - release.version, - target.bundle_flavor.suffix() - ); + let _ = emit_event(OutputEvent::AutoUpdate { + message: format!( + "ā¬‡ļø Updating mesh-llm v{current_version} -> v{} ({})...", + release.version, + target.bundle_flavor.suffix() + ), + version: Some(release.version.clone()), + }); match install_latest_bundle( &target.exe, &target.install_dir, @@ -317,18 +329,30 @@ async fn apply_update_if_available( .await { Ok(InstallOutcome::RestartNow) => { - eprintln!("āœ… Updated to v{}; restarting", release.version); + let _ = emit_event(OutputEvent::AutoUpdate { + message: format!("āœ… Updated to v{}; restarting", release.version), + version: Some(release.version.clone()), + }); exec_current_binary(&target.exe, SELF_UPDATE_ATTEMPTED_ENV, "1")?; } Ok(InstallOutcome::ExitNow) => { - eprintln!("āœ… Updated to v{}", release.version); + let _ = emit_event(OutputEvent::AutoUpdate { + message: format!("āœ… Updated to v{}", release.version), + version: Some(release.version.clone()), + }); } Ok(InstallOutcome::HandoffAndExit) => { - eprintln!("āœ… Updated to v{}; restarting", release.version); + let _ = emit_event(OutputEvent::AutoUpdate { + message: format!("āœ… Updated to v{}; restarting", release.version), + version: Some(release.version.clone()), + }); std::process::exit(0); } Err(err) => { - eprintln!("āš ļø Auto-update failed: {err}"); + let _ = emit_event(OutputEvent::AutoUpdate { + message: format!("āš ļø Auto-update failed: {err}"), + version: None, + }); } } diff --git a/tools/xtask/data/console_print_allowlist.json b/tools/xtask/data/console_print_allowlist.json index 962ea53fc5..2e24e06792 100644 --- a/tools/xtask/data/console_print_allowlist.json +++ b/tools/xtask/data/console_print_allowlist.json @@ -1,18 +1,4 @@ { - "crates/mesh-client/src/models/catalog.rs": [ - { - "line": 109, - "macro_name": "eprintln!" - }, - { - "line": 110, - "macro_name": "eprintln!" - }, - { - "line": 117, - "macro_name": "eprintln!" - } - ], "crates/mesh-llm-events/src/terminal_progress.rs": [ { "line": 25, @@ -27,378 +13,6 @@ "macro_name": "eprint!" } ], - "crates/mesh-llm-host-runtime/src/inference/skippy/materialization/package_download.rs": [ - { - "line": 163, - "macro_name": "eprintln!" - }, - { - "line": 207, - "macro_name": "eprint!" - }, - { - "line": 211, - "macro_name": "eprintln!" - }, - { - "line": 316, - "macro_name": "eprintln!" - }, - { - "line": 321, - "macro_name": "eprintln!" - }, - { - "line": 518, - "macro_name": "eprint!" - }, - { - "line": 521, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-host-runtime/src/models/catalog.rs": [ - { - "line": 411, - "macro_name": "eprint!" - }, - { - "line": 595, - "macro_name": "eprintln!" - }, - { - "line": 652, - "macro_name": "eprintln!" - }, - { - "line": 755, - "macro_name": "eprintln!" - }, - { - "line": 775, - "macro_name": "eprintln!" - }, - { - "line": 785, - "macro_name": "eprintln!" - }, - { - "line": 799, - "macro_name": "eprintln!" - }, - { - "line": 842, - "macro_name": "eprint!" - } - ], - "crates/mesh-llm-host-runtime/src/models/maintenance.rs": [ - { - "line": 29, - "macro_name": "eprintln!" - }, - { - "line": 30, - "macro_name": "eprintln!" - }, - { - "line": 62, - "macro_name": "eprintln!" - }, - { - "line": 63, - "macro_name": "eprintln!" - }, - { - "line": 64, - "macro_name": "eprintln!" - }, - { - "line": 65, - "macro_name": "eprintln!" - }, - { - "line": 76, - "macro_name": "eprintln!" - }, - { - "line": 77, - "macro_name": "eprintln!" - }, - { - "line": 78, - "macro_name": "eprintln!" - }, - { - "line": 79, - "macro_name": "eprintln!" - }, - { - "line": 80, - "macro_name": "eprintln!" - }, - { - "line": 81, - "macro_name": "eprintln!" - }, - { - "line": 84, - "macro_name": "eprintln!" - }, - { - "line": 88, - "macro_name": "eprintln!" - }, - { - "line": 94, - "macro_name": "eprintln!" - }, - { - "line": 95, - "macro_name": "eprintln!" - }, - { - "line": 96, - "macro_name": "eprintln!" - }, - { - "line": 97, - "macro_name": "eprintln!" - }, - { - "line": 100, - "macro_name": "eprintln!" - }, - { - "line": 101, - "macro_name": "eprintln!" - }, - { - "line": 102, - "macro_name": "eprintln!" - }, - { - "line": 104, - "macro_name": "eprintln!" - }, - { - "line": 117, - "macro_name": "eprintln!" - }, - { - "line": 139, - "macro_name": "eprintln!" - }, - { - "line": 140, - "macro_name": "eprintln!" - }, - { - "line": 141, - "macro_name": "eprintln!" - }, - { - "line": 142, - "macro_name": "eprintln!" - }, - { - "line": 143, - "macro_name": "eprintln!" - }, - { - "line": 147, - "macro_name": "eprintln!" - }, - { - "line": 157, - "macro_name": "eprintln!" - }, - { - "line": 350, - "macro_name": "eprintln!" - }, - { - "line": 354, - "macro_name": "eprintln!" - }, - { - "line": 355, - "macro_name": "eprintln!" - }, - { - "line": 368, - "macro_name": "eprintln!" - }, - { - "line": 376, - "macro_name": "eprintln!" - }, - { - "line": 381, - "macro_name": "eprintln!" - }, - { - "line": 383, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-host-runtime/src/models/resolve/mod.rs": [ - { - "line": 215, - "macro_name": "eprintln!" - }, - { - "line": 268, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-host-runtime/src/models/search.rs": [ - { - "line": 253, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-host-runtime/src/network/discovery.rs": [ - { - "line": 339, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-host-runtime/src/network/nostr/keys.rs": [ - { - "line": 86, - "macro_name": "eprintln!" - }, - { - "line": 88, - "macro_name": "eprintln!" - }, - { - "line": 94, - "macro_name": "eprintln!" - }, - { - "line": 96, - "macro_name": "eprintln!" - }, - { - "line": 99, - "macro_name": "eprintln!" - }, - { - "line": 100, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-host-runtime/src/network/nostr/publish.rs": [ - { - "line": 130, - "macro_name": "eprintln!" - }, - { - "line": 240, - "macro_name": "eprintln!" - }, - { - "line": 341, - "macro_name": "eprintln!" - }, - { - "line": 399, - "macro_name": "eprintln!" - }, - { - "line": 409, - "macro_name": "eprintln!" - }, - { - "line": 454, - "macro_name": "eprintln!" - }, - { - "line": 465, - "macro_name": "eprintln!" - }, - { - "line": 637, - "macro_name": "eprintln!" - }, - { - "line": 646, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-host-runtime/src/runtime/interactive.rs": [ - { - "line": 212, - "macro_name": "eprintln!" - }, - { - "line": 226, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-runtime-install/src/discovery.rs": [ - { - "line": 100, - "macro_name": "eprintln!" - }, - { - "line": 271, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-system/src/autoupdate.rs": [ - { - "line": 150, - "macro_name": "eprintln!" - }, - { - "line": 175, - "macro_name": "eprintln!" - }, - { - "line": 197, - "macro_name": "eprintln!" - }, - { - "line": 201, - "macro_name": "eprintln!" - }, - { - "line": 208, - "macro_name": "eprintln!" - }, - { - "line": 297, - "macro_name": "eprintln!" - }, - { - "line": 304, - "macro_name": "eprintln!" - }, - { - "line": 320, - "macro_name": "eprintln!" - }, - { - "line": 324, - "macro_name": "eprintln!" - }, - { - "line": 327, - "macro_name": "eprintln!" - }, - { - "line": 331, - "macro_name": "eprintln!" - } - ], - "crates/mesh-llm-system/src/benchmark_prompts.rs": [ - { - "line": 128, - "macro_name": "eprintln!" - } - ], "crates/mesh-llm-tui/src/terminal_progress.rs": [ { "line": 25, @@ -412,181 +26,5 @@ "line": 118, "macro_name": "eprint!" } - ], - "crates/mesh-native-serving-plugin-host/src/lib.rs": [ - { - "line": 616, - "macro_name": "eprintln!" - } - ], - "crates/mesh-native-serving-plugin-host/src/plugin_dispatch.rs": [ - { - "line": 445, - "macro_name": "eprintln!" - }, - { - "line": 458, - "macro_name": "eprintln!" - }, - { - "line": 515, - "macro_name": "eprintln!" - }, - { - "line": 561, - "macro_name": "eprintln!" - }, - { - "line": 613, - "macro_name": "eprintln!" - }, - { - "line": 658, - "macro_name": "eprintln!" - }, - { - "line": 670, - "macro_name": "eprintln!" - }, - { - "line": 778, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/binary_transport/binary_messaging.rs": [ - { - "line": 425, - "macro_name": "eprintln!" - }, - { - "line": 435, - "macro_name": "println!" - }, - { - "line": 470, - "macro_name": "eprintln!" - }, - { - "line": 491, - "macro_name": "eprintln!" - }, - { - "line": 499, - "macro_name": "eprintln!" - }, - { - "line": 568, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/binary_transport/binary_messaging/reply.rs": [ - { - "line": 55, - "macro_name": "eprintln!" - }, - { - "line": 60, - "macro_name": "eprintln!" - }, - { - "line": 75, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/binary_transport/direct_return.rs": [ - { - "line": 89, - "macro_name": "eprintln!" - }, - { - "line": 97, - "macro_name": "eprintln!" - }, - { - "line": 106, - "macro_name": "eprintln!" - }, - { - "line": 257, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/binary_transport/preconnect.rs": [ - { - "line": 74, - "macro_name": "eprintln!" - }, - { - "line": 87, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/binary_transport/socket.rs": [ - { - "line": 150, - "macro_name": "eprintln!" - }, - { - "line": 204, - "macro_name": "eprintln!" - }, - { - "line": 242, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/binary_transport/stage_execution.rs": [ - { - "line": 291, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/frontend/embedded_generation/lifecycle.rs": [ - { - "line": 201, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/frontend/generation/persistent_lanes.rs": [ - { - "line": 331, - "macro_name": "eprintln!" - }, - { - "line": 371, - "macro_name": "eprintln!" - }, - { - "line": 379, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/frontend/generation/server.rs": [ - { - "line": 198, - "macro_name": "println!" - }, - { - "line": 347, - "macro_name": "println!" - } - ], - "crates/skippy-server/src/frontend/linear_proposal.rs": [ - { - "line": 664, - "macro_name": "eprintln!" - } - ], - "crates/skippy-server/src/http.rs": [ - { - "line": 216, - "macro_name": "println!" - } - ], - "crates/skippy-server/src/main.rs": [ - { - "line": 16, - "macro_name": "println!" - } ] } \ No newline at end of file From 95fead4c9cbadd56605f684554e575cc8ac37a1b Mon Sep 17 00:00:00 2001 From: Nick DiZazzo Date: Sun, 13 Sep 2026 16:51:34 -0400 Subject: [PATCH 4/4] fix(tui): render the new publishing and auto-update events as JSON fields --- crates/mesh-llm-tui/src/output/formatting.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/mesh-llm-tui/src/output/formatting.rs b/crates/mesh-llm-tui/src/output/formatting.rs index f86c646aa2..27d9aaa667 100644 --- a/crates/mesh-llm-tui/src/output/formatting.rs +++ b/crates/mesh-llm-tui/src/output/formatting.rs @@ -439,6 +439,10 @@ impl OutputEventPresentation for OutputEvent { OutputEvent::RequestRouted { model, target } => { json!({ "model": model, "target": target }) } + OutputEvent::NostrPublishing { message } => json!({ "message": message }), + OutputEvent::AutoUpdate { message, version } => { + json!({ "message": message, "version": version }) + } OutputEvent::Warning { message, context } => { json!({ "warning": message, "context": context }) }