mirror of
https://github.com/block/buzz.git
synced 2026-11-08 10:21:46 +00:00
## 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>
97 lines
3.8 KiB
Rust
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(())
|
|
}
|