Files
buzz/crates/buzz-mesh-smoke/examples/mesh_serve_smoke.rs
Mic Neale 4544ba1713 chore(mesh): upgrade MeshLLM to 0.76.2 (#7559)
## Summary

Upgrade Buzz from MeshLLM `v0.76.0-rc9` to the latest stable [v0.76.2
release](https://github.com/Mesh-LLM/mesh-llm/releases/tag/v0.76.2) (via
v0.76.1).

- Update all eight Mesh git dependencies across desktop and relay smoke
examples; both lockfiles resolve all 47 Mesh packages to
`a0c1e66b0ac037dd56544b9e2d94969ea43d694f` (tag `v0.76.2`). All non-Mesh
lock records are unchanged by the 0.76.1 → 0.76.2 bump.
- Match Mesh's `ed25519-dalek = 3.0.0` and `rusqlite = 0.40`
dependencies.
- Explicitly enable rusqlite's `fallible_uint` feature, preserving
Buzz's existing `u64` SQL conversions when **mesh is disabled**.
Mesh-enabled builds previously supplied this transitively, so testing
both configurations matters.
- Temporarily pin **only** the optional `sqlx-sqlite` package to the
merged upstream compatibility fix
[transact-rs/sqlx#4402](https://github.com/transact-rs/sqlx/pull/4402),
revision `94aafe3a68884d923b0798a767c8d7f6cfda89d2`. SQLx 0.9.0 caps
`libsqlite3-sys` below 0.38, while Mesh 0.76 requires 0.38. Cargo
resolves the conflict even with SQLite disabled. The fix raises that
upper bound; remove the patch once released. Buzz's active PostgreSQL
driver stays on crates.io SQLx 0.9.0.
- Preserve the explicit Linux keyring 3 `crypto-rust` feature so Secret
Service sessions retain DH encryption independently of Mesh keyring 4.
- Preserve the dedicated examples-only `buzz-mesh-smoke` crate and
lifecycle workflow wiring, keeping Mesh/native-runtime dev dependencies
out of relay CI builds.
- v0.76.1 → v0.76.2 upstream delta: self-updater fix for composed
product-v2 bundles (Mesh-LLM/mesh-llm#1844) plus release plumbing — no
API changes affecting Buzz.
- Merged `origin/main` (`7c789dee0`) without rewriting shared PR
history.

### Validation at `ae4adba15` (local macOS ARM64, Hermit toolchain)

- **Passed:** `cargo test --locked -p buzz-mesh-smoke --all-targets`
(all six example harnesses compile; no live harness execution).
- **Passed:** desktop Tauri workspace tests, both default and
`--features mesh-llm` graphs (3,296 tests with mesh enabled, 0
failures).
- **Passed:** `just desktop-tauri-clippy` (both feature graphs, `-D
warnings`), root workspace clippy `-D warnings`, `cargo fmt --all
--check`.
- **Passed:** `--locked` metadata resolution for both workspaces;
lockfile diff audited to touch only Mesh package records.
- **Passed:** full pre-push hook suite on the final push (branch-skew,
rust-tests, desktop-tauri-checks, file-size-check) — no hooks bypassed.
- **Not run:** live Mesh inference/admission smoke or installed-app UI
exercise. CI lifecycle smoke coverage applies.

Originating Buzz conversation:
buzz://message?channel=dffbc117-845a-4c02-bc3f-eaeeefc4deaa&id=670f9c262e1ec0a4df5600b3173a0c5de2abe17e08ba9ccfdd8eff109fadf1a8

---------

Signed-off-by: Thinker <75d8a808fa21bb8d1812e080cf471db601c6e8e6a62dcd516d37531e83a7bb77@meshllm.communities.buzz.xyz>
Signed-off-by: Michael Neale <michael.neale@gmail.com>
Co-authored-by: Thinker <75d8a808fa21bb8d1812e080cf471db601c6e8e6a62dcd516d37531e83a7bb77@meshllm.communities.buzz.xyz>
Co-authored-by: Michael Neale <michael.neale@gmail.com>
Co-authored-by: Galadriel <b81c841ed20a8e17caf4e6fac2f6f3bc61d304c84b8394720f6d00f37538c47e@meshllm.communities.buzz.xyz>
Co-authored-by: Gu <gu@buzz>
2026-09-24 07:32:07 -06:00

97 lines
3.8 KiB
Rust

//! Local mesh-serve inference smoke test.
//!
//! Serves a GGUF model through the same `mesh_llm_sdk::serve` path Buzz
//! desktop uses in "Share compute" mode, then drives one chat completion
//! against the node's local OpenAI-compatible endpoint. No mesh publish, no
//! auto-join, no Nostr discovery — pure single-node serve-and-self-consume,
//! which is exactly the loopback variant we can prove on one box.
//!
//! Usage:
//! cargo run -p buzz-relay --example mesh_serve_smoke -- <path-to.gguf>
use std::time::Duration;
use mesh_llm_sdk::{serve, MeshDiscoveryMode};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let model = std::env::args()
.nth(1)
.ok_or_else(|| anyhow::anyhow!("usage: mesh_serve_smoke <path-to.gguf>"))?;
eprintln!("[smoke] serving model: {model}");
let config = serve::EmbeddedServeConfig::builder()
.model(&model)
.api_port(19337)
.console_port(13131)
.publish(false)
.auto_join(false)
.discovery_mode(MeshDiscoveryMode::Mdns)
// NOTE: console_ui(true) is required here, not cosmetic. In mesh rev
// bd16da4, serve::start always polls `:console_port/api/status` to
// confirm readiness, but the console HTTP server only binds when
// !headless (i.e. console_ui == true). With console_ui(false) the
// poll never succeeds and startup times out after 30s. Desktop's
// Share-compute path sets console_ui(false) — likely hits the same
// wall. Flagged to the team.
.console_ui(true)
.build();
let node = serve::start(config).await?;
let base = node.api_base_url().to_string();
eprintln!("[smoke] node up, api_base_url = {base}");
// Poll until the model reports loaded/ready (give it generous time — first
// load of a 17GB GGUF into Metal can take a while).
let http = reqwest::Client::new();
let models_url = format!("{base}/models");
let mut model_id = String::new();
for i in 0..120 {
tokio::time::sleep(Duration::from_secs(5)).await;
match http.get(&models_url).send().await {
Ok(r) => {
let body = r.text().await.unwrap_or_default();
// The serve node assigns its own id (e.g. local-gguf/sha256-…),
// not the file path we passed — pull it from /models.
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&body) {
if let Some(id) = json["data"].get(0).and_then(|m| m["id"].as_str()) {
model_id = id.to_string();
eprintln!(
"[smoke] /models after {}s, served id = {model_id}",
(i + 1) * 5
);
break;
}
}
eprintln!("[smoke] waiting ({}s) /models -> {body}", (i + 1) * 5);
}
Err(e) => eprintln!("[smoke] waiting ({}s) /models err: {e}", (i + 1) * 5),
}
}
if model_id.is_empty() {
anyhow::bail!("model never became ready within timeout");
}
// One real completion — use the server's own model id.
let chat_url = format!("{base}/chat/completions");
let req = serde_json::json!({
"model": model_id,
"messages": [
{"role": "user", "content": "Reply with exactly one word: PONG"}
],
"max_tokens": 512,
"temperature": 0.0
});
eprintln!("[smoke] POST {chat_url}");
let resp = http.post(&chat_url).json(&req).send().await?;
let status = resp.status();
let body = resp.text().await?;
println!("[smoke] completion status={status}");
println!("[smoke] completion body={body}");
node.stop().await?;
if !status.is_success() {
anyhow::bail!("completion request failed: {status}");
}
Ok(())
}