From f2801190ed26955f8b21c6a6dac2471dc712cad0 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Sat, 8 Aug 2026 15:58:08 +1000 Subject: [PATCH 01/12] chore(mesh): move to MeshLLM v0.75.0 and stop pre-seeding the runtime Bumps the eight mesh-llm pins (six in the desktop Tauri crate, two in buzz-relay) from v0.74.0 to v0.75.0 and refreshes both lockfiles. Also deletes scripts/ensure-mesh-native-runtime.sh and its six call sites. The script built llama.cpp from source locally to pre-seed a native-runtime cache, but nothing needs that: - The app installs the signed runtime itself. `initialize_host_runtime()` reaches `install_native_runtime()`, whose `allow_download` defaults to true, and v0.75.0 publishes `native-runtimes.json` (13 artifacts, including darwin-aarch64-metal) alongside the release. Verified by fetching the manifest and artifact and checking both sha256 sidecars. - CI never called it. The mesh lifecycle workflow caches the app's own `~/.cache/mesh-llm/native-runtimes` and says so in a comment: "the mesh-llm SDK downloads a signed native runtime on first init". The desktop build job compiles llama.cpp through mesh-llm's own prepare-llama.sh / build-llama.sh. - The three `mesh-e2e-*` recipes that also used it are hand-run only, referenced nowhere else in the repo. `mesh=1` behaviour is unchanged; only the pre-seed line is gone from dev / staging / production. KNOWN ISSUE (do not merge before resolving): on a machine that already has a pre-0.75 runtime cache, startup fails with "native runtime artifact ... does not declare file checksums". v0.75.0 added `NativeRuntimeManifest::verify_contents`, which requires per-file checksums; caches written by older loaders have none, and the startup path enumerates the whole cache strictly, so one stale entry aborts the scan even when a valid 0.75.0 entry is present. Reproduced and bisected here: failing with the stale entries present, passing with only 0.75.0 cached, and passing with the stale entries present once a bundle dir is supplied. That is why the installed CLI is unaffected -- it ships a runtime directory beside its binary, which is discovered leniently. Signed-off-by: Michael Neale --- Cargo.lock | 391 +++++++++++++------------- Justfile | 8 - crates/buzz-relay/Cargo.toml | 4 +- desktop/src-tauri/Cargo.lock | 254 +++++++++-------- desktop/src-tauri/Cargo.toml | 12 +- scripts/ensure-mesh-native-runtime.sh | 48 ---- 6 files changed, 355 insertions(+), 362 deletions(-) delete mode 100755 scripts/ensure-mesh-native-runtime.sh diff --git a/Cargo.lock b/Cargo.lock index 83d2b9e3e3..8f87591c54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2070,43 +2070,16 @@ dependencies = [ "phf", ] -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - [[package]] name = "ctor" -version = "0.6.3" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e" +checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f" dependencies = [ - "ctor-proc-macro", - "dtor", + "link-section", + "linktime-proc-macro", ] -[[package]] -name = "ctor-proc-macro" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" - [[package]] name = "ctr" version = "0.9.2" @@ -2557,21 +2530,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dtor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - [[package]] name = "dunce" version = "1.0.5" @@ -3397,46 +3355,23 @@ dependencies = [ "arrayvec", ] -[[package]] -name = "hf-hub" -version = "1.0.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89305dc8fe34e165eaf0eb12b6e294e12381d9df9a431bcc52a5809bab4319" -dependencies = [ - "base64 0.22.1", - "bon", - "bytes", - "futures", - "globset", - "hf-xet", - "hyper", - "pathdiff", - "reqwest 0.13.4", - "serde", - "serde_json", - "sha2 0.11.0", - "thiserror 2.0.18", - "tokio", - "tokio-retry", - "tokio-util", - "tracing", - "url", -] - [[package]] name = "hf-xet" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430b33fa84f92796d4d263070b6c0d3ca219df7b9a0e1853ee431029b1612bcd" +checksum = "c237ef4fb0ce1962a5117f8bd8c74454b41629826a9df17d14a1840ca18f0754" dependencies = [ + "anyhow", "async-trait", "bytes", "http", "more-asserts", "serde", + "serde_json", "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "uuid", "xet-client", @@ -4544,6 +4479,18 @@ dependencies = [ "bitflags 2.13.0", ] +[[package]] +name = "link-section" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee1a0d6e252afe82e7bc2db42fba60e02ddf3b1accaf8cb21d96e34ba61f3d4" + +[[package]] +name = "linktime-proc-macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348d0075b1fc163b26d72a7f75fc5141daf2fd1bdf128d873cbaf6785d495bdf" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4781,8 +4728,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "hex", "mesh-llm-client", @@ -4791,8 +4738,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -4802,13 +4749,13 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" [[package]] name = "mesh-llm-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", @@ -4839,8 +4786,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -4855,8 +4802,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -4865,8 +4812,8 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "clap", @@ -4877,12 +4824,9 @@ dependencies = [ [[package]] name = "mesh-llm-gpu-bench" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ - "anyhow", - "cc", - "libc", "serde", "serde_json", "tracing", @@ -4890,8 +4834,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "serde", "serde_json", @@ -4899,16 +4843,45 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "mesh-llm-native-runtime", ] +[[package]] +name = "mesh-llm-hf-hub" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43088a838cf0c6715c65f65a5ac99045fd6d6e90949a8a4183b8104ab791e96b" +dependencies = [ + "base64 0.22.1", + "bon", + "bytes", + "futures", + "getrandom 0.2.17", + "globset", + "hf-xet", + "hyper", + "pathdiff", + "percent-encoding", + "reqwest 0.13.4", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "tokio-retry", + "tokio-util", + "tracing", + "url", + "wasm-bindgen-futures", +] + [[package]] name = "mesh-llm-host-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "argon2", @@ -4926,7 +4899,6 @@ dependencies = [ "flate2", "futures-util", "hex", - "hf-hub", "http", "http-body-util", "httparse", @@ -4941,6 +4913,7 @@ dependencies = [ "mesh-llm-config", "mesh-llm-events", "mesh-llm-guardrails", + "mesh-llm-hf-hub", "mesh-llm-identity", "mesh-llm-native-runtime", "mesh-llm-node", @@ -4953,6 +4926,7 @@ dependencies = [ "mesh-llm-types", "mesh-llm-ui", "mesh-mixture-of-agents", + "mesh-native-serving-plugin-host", "model-artifact", "model-hf", "model-package", @@ -5000,8 +4974,8 @@ dependencies = [ [[package]] name = "mesh-llm-identity" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "argon2", "base64 0.22.1", @@ -5022,8 +4996,8 @@ dependencies = [ [[package]] name = "mesh-llm-native-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "serde", @@ -5033,8 +5007,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-types", @@ -5047,8 +5021,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", @@ -5064,8 +5038,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -5083,8 +5057,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "hex", @@ -5094,18 +5068,27 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "mesh-llm-release-footer" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +dependencies = [ + "hex", + "sha2 0.10.9", +] + [[package]] name = "mesh-llm-routing" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "iroh", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -5127,8 +5110,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5142,8 +5125,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -5153,8 +5136,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "chrono", @@ -5162,8 +5145,12 @@ dependencies = [ "dirs", "hex", "libc", + "libloading", "mesh-llm-build-info", "mesh-llm-gpu-bench", + "mesh-llm-native-runtime", + "mesh-llm-release-footer", + "mesh-llm-runtime-install", "reqwest 0.12.28", "semver", "serde", @@ -5176,8 +5163,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "hex", "serde", @@ -5187,13 +5174,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" [[package]] name = "mesh-mixture-of-agents" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5204,6 +5191,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "mesh-native-serving-plugin-api" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" + +[[package]] +name = "mesh-native-serving-plugin-host" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +dependencies = [ + "anyhow", + "libloading", + "mesh-native-serving-plugin-api", + "skippy-server", +] + [[package]] name = "metrics" version = "0.24.6" @@ -5341,8 +5344,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", @@ -5352,14 +5355,14 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", - "hf-hub", + "mesh-llm-hf-hub", "model-artifact", "model-ref", "serde", @@ -5370,14 +5373,14 @@ dependencies = [ [[package]] name = "model-package" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "bytes", "chrono", "futures", - "hf-hub", + "mesh-llm-hf-hub", "model-hf", "model-ref", "reqwest 0.12.28", @@ -5390,16 +5393,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "model-artifact", @@ -5777,6 +5780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" dependencies = [ "aes-gcm", + "aws-lc-rs", "bytes", "derive_more", "enum-assoc", @@ -6167,8 +6171,8 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openai-frontend" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "async-trait", "axum", @@ -8577,7 +8581,6 @@ dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.17", "digest 0.10.7", - "sha2-asm", ] [[package]] @@ -8591,15 +8594,6 @@ dependencies = [ "digest 0.11.3", ] -[[package]] -name = "sha2-asm" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b845214d6175804686b2bd482bcffe96651bb2d1200742b712003504a2dac1ab" -dependencies = [ - "cc", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -8743,8 +8737,8 @@ checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "skippy-cache" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "blake3", @@ -8753,29 +8747,29 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "libloading", ] [[package]] name = "skippy-metrics" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" [[package]] name = "skippy-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "prost 0.14.3", "prost-build 0.14.3", @@ -8785,8 +8779,8 @@ dependencies = [ [[package]] name = "skippy-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "libc", @@ -8799,8 +8793,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "ahash", "anyhow", @@ -8811,6 +8805,8 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-native-serving-plugin-api", + "model-artifact", "openai-frontend", "opentelemetry-proto 0.31.0", "serde", @@ -8828,8 +8824,8 @@ dependencies = [ [[package]] name = "skippy-topology" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "serde", "serde_json", @@ -9849,6 +9845,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad543404f98bfc969aeb71994105c592acfc6c43323fddcd016bb208d1c65cb" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "bytes", "futures-core", @@ -9866,6 +9863,30 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio_with_wasm" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e40fbbbd95441133fe9483f522db15dbfd26dc636164ebd8f2dd28759a6aa6" +dependencies = [ + "js-sys", + "tokio", + "tokio_with_wasm_proc", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "tokio_with_wasm_proc" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01145a2c788d6aae4cd653afec1e8332534d7d783d01897cefcafe4428de992" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -11359,20 +11380,18 @@ dependencies = [ [[package]] name = "xet-client" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1e496dcbe6a09017acdfaf48e1a646735e7ff5b2a49e2c7e081cca77a59bc8" +checksum = "c3b8da8cc70aa2e3c500c0400e012df82c656ab9fca47f9f939fffc5afd89aca" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "clap", "crc32fast", "futures", "http", "hyper", - "lazy_static", "more-asserts", "rand 0.10.1", "redb", @@ -11386,8 +11405,8 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-retry", + "tokio_with_wasm", "tracing", - "tracing-subscriber", "url", "urlencoding", "web-time", @@ -11397,24 +11416,21 @@ dependencies = [ [[package]] name = "xet-core-structures" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb838aa8eb67d730af301584cf003caad407487606058292a6750711b603fbee" +checksum = "73503c223783dccc864abde22115e09d12f190448a0baf58ab2c54bc709e2f99" dependencies = [ "async-trait", "base64 0.22.1", "blake3", "bytemuck", "bytes", - "clap", "countio", - "csv", "futures", "futures-util", "getrandom 0.4.3", "heapify", "itertools", - "lazy_static", "lz4_flex", "more-asserts", "rand 0.10.1", @@ -11422,7 +11438,6 @@ dependencies = [ "safe-transmute", "serde", "static_assertions", - "tempfile", "thiserror 2.0.18", "tokio", "tokio-util", @@ -11434,32 +11449,31 @@ dependencies = [ [[package]] name = "xet-data" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fd409bef621411a9d9013798540bb8036cb2678f03ab39af89a5e88034ed8c" +checksum = "c89052ec5dec2187cad30b86af92cc24fd61c4a57a795f1ff7ff5f38d49184eb" dependencies = [ "anyhow", "async-trait", "bytes", "chrono", - "clap", "gearhash", "http", "itertools", - "lazy_static", "more-asserts", "rand 0.10.1", "serde", "serde_json", - "sha2 0.10.9", + "sha2 0.11.0", "tempfile", "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "url", "uuid", - "walkdir", + "web-time", "xet-client", "xet-core-structures", "xet-runtime", @@ -11467,9 +11481,9 @@ dependencies = [ [[package]] name = "xet-runtime" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d8f121c33866f7648b737abe70d0e2dd9c0af4ffdd7219207531d0283aa63d" +checksum = "af5c60d5eed38ab4c576f4421bae835e7bd07631fb381705605529d2015c106b" dependencies = [ "anyhow", "async-trait", @@ -11483,7 +11497,6 @@ dependencies = [ "git-version", "humantime", "konst", - "lazy_static", "libc", "more-asserts", "oneshot", @@ -11497,9 +11510,11 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "tracing-appender", "tracing-subscriber", + "web-time", "whoami", "winapi", ] diff --git a/Justfile b/Justfile index 0a43249d5f..2e62599dac 100644 --- a/Justfile +++ b/Justfile @@ -355,7 +355,6 @@ mesh-dev-fresh: mesh-e2e-hardware: #!/usr/bin/env bash set -euo pipefail - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" cargo run -p buzz-relay --example mesh_serve_client_smoke # Three isolated node processes: trusted member joins and infers; stranger is rejected. @@ -363,14 +362,12 @@ mesh-e2e-hardware: mesh-e2e-admission: #!/usr/bin/env bash set -euo pipefail - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" cargo run -p buzz-relay --example mesh_admission_smoke # Full hardware confidence suite: routing, owner admission, and real agent inference. mesh-e2e-confidence: #!/usr/bin/env bash set -euo pipefail - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" cargo build --release -p buzz-agent -p buzz-dev-mcp cargo run -p buzz-relay --example mesh_serve_client_smoke cargo run -p buzz-relay --example mesh_admission_smoke @@ -457,9 +454,6 @@ dev *ARGS: bootstrap _ensure-sidecar-stubs _ensure-migrations done fi cargo build -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr -p buzz-relay - if [[ -n "{{mesh}}" ]]; then - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" - fi # Docker Desktop's forwarded MinIO port can stall under the deployment # probe's 32 concurrent writers. Keep the gate enabled in local dev, using # the bounded profile already used by the relay test launcher. @@ -536,7 +530,6 @@ staging *ARGS: bootstrap _ensure-sidecar-stubs FEATURES=() if [[ -n "{{mesh}}" ]]; then FEATURES=(--features mesh-llm) - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" fi # Replace 0-byte sidecar stubs with real binaries so tauri dev picks them up. # buzz: the CLI sidecar. buzz-backend-kubernetes: provider discovery scans the @@ -572,7 +565,6 @@ production *ARGS: bootstrap _ensure-sidecar-stubs FEATURES=() if [[ -n "{{mesh}}" ]]; then FEATURES=(--features mesh-llm) - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" fi # Replace 0-byte sidecar stubs with real binaries so tauri dev picks them up. # buzz: the CLI sidecar. buzz-backend-kubernetes: provider discovery scans the diff --git a/crates/buzz-relay/Cargo.toml b/crates/buzz-relay/Cargo.toml index cbad2a3b29..3644b3c515 100644 --- a/crates/buzz-relay/Cargo.toml +++ b/crates/buzz-relay/Cargo.toml @@ -84,8 +84,8 @@ async-compression = { version = "0.4.42", features = ["tokio", "gzip"] } dev = ["buzz-auth/dev"] [dev-dependencies] -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } # Relay-driven mesh lifecycle smoke (examples/mesh_relay_lifecycle_smoke.rs): # the relay client for discovery notes and the exact ed25519 the mesh owner # keys use for binding verification. diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index 94504f970d..fd1b932e48 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -3662,32 +3662,6 @@ dependencies = [ "arrayvec", ] -[[package]] -name = "hf-hub" -version = "1.0.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89305dc8fe34e165eaf0eb12b6e294e12381d9df9a431bcc52a5809bab4319" -dependencies = [ - "base64 0.22.1", - "bon", - "bytes", - "futures", - "globset", - "hf-xet", - "hyper", - "pathdiff", - "reqwest 0.13.4", - "serde", - "serde_json", - "sha2 0.11.0", - "thiserror 2.0.18", - "tokio", - "tokio-retry", - "tokio-util", - "tracing", - "url", -] - [[package]] name = "hf-xet" version = "1.5.3" @@ -5133,8 +5107,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "hex", "mesh-llm-client", @@ -5143,8 +5117,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5154,13 +5128,13 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" [[package]] name = "mesh-llm-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", @@ -5191,8 +5165,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -5207,8 +5181,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -5217,8 +5191,8 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "clap", @@ -5229,12 +5203,9 @@ dependencies = [ [[package]] name = "mesh-llm-gpu-bench" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ - "anyhow", - "cc", - "libc", "serde", "serde_json", "tracing", @@ -5242,8 +5213,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "serde", "serde_json", @@ -5251,16 +5222,45 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "mesh-llm-native-runtime", ] +[[package]] +name = "mesh-llm-hf-hub" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43088a838cf0c6715c65f65a5ac99045fd6d6e90949a8a4183b8104ab791e96b" +dependencies = [ + "base64 0.22.1", + "bon", + "bytes", + "futures", + "getrandom 0.2.17", + "globset", + "hf-xet", + "hyper", + "pathdiff", + "percent-encoding", + "reqwest 0.13.4", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "tokio-retry", + "tokio-util", + "tracing", + "url", + "wasm-bindgen-futures", +] + [[package]] name = "mesh-llm-host-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "argon2", @@ -5278,7 +5278,6 @@ dependencies = [ "flate2", "futures-util", "hex", - "hf-hub", "http", "http-body-util", "httparse", @@ -5293,6 +5292,7 @@ dependencies = [ "mesh-llm-config", "mesh-llm-events", "mesh-llm-guardrails", + "mesh-llm-hf-hub", "mesh-llm-identity", "mesh-llm-native-runtime", "mesh-llm-node", @@ -5305,6 +5305,7 @@ dependencies = [ "mesh-llm-types", "mesh-llm-ui", "mesh-mixture-of-agents", + "mesh-native-serving-plugin-host", "model-artifact", "model-hf", "model-package", @@ -5352,8 +5353,8 @@ dependencies = [ [[package]] name = "mesh-llm-identity" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "argon2", "base64 0.22.1", @@ -5374,8 +5375,8 @@ dependencies = [ [[package]] name = "mesh-llm-native-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "serde", @@ -5385,8 +5386,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-types", @@ -5399,8 +5400,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", @@ -5416,8 +5417,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -5435,8 +5436,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "hex", @@ -5446,18 +5447,27 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "mesh-llm-release-footer" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +dependencies = [ + "hex", + "sha2 0.10.9", +] + [[package]] name = "mesh-llm-routing" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "iroh", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -5479,8 +5489,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5494,8 +5504,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "dirs", @@ -5505,8 +5515,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "chrono", @@ -5514,8 +5524,12 @@ dependencies = [ "dirs", "hex", "libc", + "libloading 0.8.9", "mesh-llm-build-info", "mesh-llm-gpu-bench", + "mesh-llm-native-runtime", + "mesh-llm-release-footer", + "mesh-llm-runtime-install", "reqwest 0.12.28", "semver", "serde", @@ -5528,8 +5542,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "hex", "serde", @@ -5539,13 +5553,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" [[package]] name = "mesh-mixture-of-agents" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5556,6 +5570,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "mesh-native-serving-plugin-api" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" + +[[package]] +name = "mesh-native-serving-plugin-host" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +dependencies = [ + "anyhow", + "libloading 0.8.9", + "mesh-native-serving-plugin-api", + "skippy-server", +] + [[package]] name = "miette" version = "7.6.0" @@ -5648,8 +5678,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", @@ -5659,14 +5689,14 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", - "hf-hub", + "mesh-llm-hf-hub", "model-artifact", "model-ref", "serde", @@ -5677,14 +5707,14 @@ dependencies = [ [[package]] name = "model-package" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "bytes", "chrono", "futures", - "hf-hub", + "mesh-llm-hf-hub", "model-hf", "model-ref", "reqwest 0.12.28", @@ -5697,16 +5727,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "model-artifact", @@ -6160,6 +6190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" dependencies = [ "aes-gcm", + "aws-lc-rs", "bytes", "derive_more", "enum-assoc", @@ -6785,8 +6816,8 @@ dependencies = [ [[package]] name = "openai-frontend" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "async-trait", "axum", @@ -9565,8 +9596,8 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skippy-cache" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "blake3", @@ -9575,29 +9606,29 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "libloading 0.8.9", ] [[package]] name = "skippy-metrics" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" [[package]] name = "skippy-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "prost 0.14.4", "prost-build 0.14.4", @@ -9607,8 +9638,8 @@ dependencies = [ [[package]] name = "skippy-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "anyhow", "libc", @@ -9621,8 +9652,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "ahash", "anyhow", @@ -9633,6 +9664,8 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-native-serving-plugin-api", + "model-artifact", "openai-frontend", "opentelemetry-proto", "serde", @@ -9650,8 +9683,8 @@ dependencies = [ [[package]] name = "skippy-topology" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.0" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" dependencies = [ "serde", "serde_json", @@ -11090,6 +11123,7 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52efb639344a7c6adb8e62c6f3d2c19c001ff1b79a5041ba1c6ed42e19c6aa5" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "bytes", "futures-core", diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index b8f58568b4..477a189cc8 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -109,14 +109,14 @@ buzz_voice_pkg = { package = "buzz-voice", path = "../../crates/buzz-voice" } buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" } portable-pty = "0.9" iroh = { version = "1.0.2", optional = true } -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } # Model catalog + hardware survey for the Share-compute model picker (same # diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client. -mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-client", optional = true } -mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-node", optional = true } -mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-system", optional = true } -mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-events", optional = true } +mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-client", optional = true } +mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-node", optional = true } +mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-system", optional = true } +mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-events", optional = true } base64 = "0.22" sha2 = "0.11" tar = "0.4" diff --git a/scripts/ensure-mesh-native-runtime.sh b/scripts/ensure-mesh-native-runtime.sh deleted file mode 100755 index e2b4fa0dc2..0000000000 --- a/scripts/ensure-mesh-native-runtime.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -BACKEND="${1:-}" -if [[ -z "$BACKEND" ]]; then - case "$(uname -s)" in - Darwin) BACKEND="metal" ;; - *) BACKEND="cpu" ;; - esac -fi - -CACHE_DIR="${MESH_LLM_NATIVE_RUNTIME_CACHE_DIR:-$ROOT/.cache/mesh-llm-native-runtime}" -OUT_DIR="${MESH_LLM_NATIVE_RUNTIME_OUT_DIR:-$ROOT/.cache/mesh-llm-native-runtime-artifacts}" - -metadata="$($ROOT/bin/cargo metadata --manifest-path "$ROOT/desktop/src-tauri/Cargo.toml" --features mesh-llm --format-version 1)" -SDK_MANIFEST="$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(next(p["manifest_path"] for p in data["packages"] if p["name"]=="mesh-llm-sdk"))' <<<"$metadata")" -MESH_ROOT="$(cd "$(dirname "$SDK_MANIFEST")/../.." && pwd)" -MESH_VERSION="$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(next(p["version"] for p in data["packages"] if p["name"]=="mesh-llm-sdk"))' <<<"$metadata")" - -case "$(uname -s)/$(uname -m)/$BACKEND" in - Darwin/arm64/metal) RUNTIME_ID="meshllm-native-runtime-darwin-aarch64-metal" ;; - Darwin/x86_64/metal) RUNTIME_ID="meshllm-native-runtime-darwin-x86_64-metal" ;; - */*/cpu) - case "$(uname -s)/$(uname -m)" in - Darwin/arm64) RUNTIME_ID="meshllm-native-runtime-darwin-aarch64-cpu" ;; - Darwin/x86_64) RUNTIME_ID="meshllm-native-runtime-darwin-x86_64-cpu" ;; - Linux/x86_64) RUNTIME_ID="meshllm-native-runtime-linux-x86_64-cpu" ;; - Linux/aarch64) RUNTIME_ID="meshllm-native-runtime-linux-aarch64-cpu" ;; - *) RUNTIME_ID="" ;; - esac - ;; - *) RUNTIME_ID="" ;; -esac - -if [[ -n "$RUNTIME_ID" && -f "$CACHE_DIR/$MESH_VERSION/$RUNTIME_ID/manifest.json" ]]; then - printf '%s\n' "$CACHE_DIR" - exit 0 -fi - -echo "Preparing MeshLLM native runtime ($BACKEND) for MeshLLM $MESH_VERSION..." >&2 -runtime_dir="$(cd "$MESH_ROOT" && scripts/ci-prepare-native-runtime.sh "$OUT_DIR" "$BACKEND")" -version="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["runtime"].get("mesh_version") or "unknown")' "$runtime_dir/manifest.json")" -id="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["runtime"]["id"])' "$runtime_dir/manifest.json")" -mkdir -p "$CACHE_DIR/$version" -rm -rf "$CACHE_DIR/$version/$id" -cp -a "$runtime_dir" "$CACHE_DIR/$version/$id" -printf '%s\n' "$CACHE_DIR" From 3fa1551b3e5a1941e443bcdb01b65dd00d487cf9 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Sat, 8 Aug 2026 16:10:33 +1000 Subject: [PATCH 02/12] docs(mesh): drop the deleted pre-seed script from the e2e runbook The runbook told readers to run scripts/ensure-mesh-native-runtime.sh, which this branch removes. The desktop installs the signed runtime itself on first init, so the only manual step left is clearing stale pre-0.75 cache entries. Signed-off-by: Michael Neale --- crates/buzz-test-client/tests/e2e_mesh_llm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/buzz-test-client/tests/e2e_mesh_llm.rs b/crates/buzz-test-client/tests/e2e_mesh_llm.rs index 4b1bfb3d21..9cd5175e21 100644 --- a/crates/buzz-test-client/tests/e2e_mesh_llm.rs +++ b/crates/buzz-test-client/tests/e2e_mesh_llm.rs @@ -11,7 +11,9 @@ //! # Running (manual / runbook) //! //! ```text -//! # 1. prepare the matching native runtime with `scripts/ensure-mesh-native-runtime.sh` +//! # 1. the mesh-enabled desktop installs the signed native runtime itself on +//! # first init; on a machine with a pre-0.75 runtime cache, clear stale +//! # entries under ~/.cache/mesh-llm/native-runtimes first //! # 2. start the normal membership-gated relay and a mesh-enabled desktop //! # 3. have that desktop publish status, then run the trust assertions: //! RELAY_URL=ws://localhost:3000 \ From d55d605e37bb679232ab8d7d84ec1a80d335c125 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Sat, 8 Aug 2026 17:12:36 +1000 Subject: [PATCH 03/12] refactor(agent): let MeshLLM decide when shared compute forms a committee Shared-compute `auto` now maps straight onto MeshLLM's virtual `mesh` model and sends it unconditionally. MeshLLM >= 0.75.0 resolves that per request: a Mixture-of-Agents committee when two or more workers are reachable, and a single served model when they are not (`moa_gateway::degrade_to_single_model`). Before 0.75.0 a `model=mesh` request could 503 on a lone node, so the agent kept its own hysteretic view of whether a committee was currently possible and only then dared send `mesh`. That client-side machinery is now redundant, and removing it deletes the polling and the debounce it needed to avoid flapping: - `resolve_openai_model`, `observe_mesh_virtual_model`, `cool_down_collective`, `mesh_catalog_supports_collective` - `MeshAutoState`, `MeshCatalogObservation`, and the `Llm` field holding it - the `/models` probe: 5s catalog TTL, 2s probe timeout, 30s cooldown, and the two-observation confirmation count - `PostError::MeshFallback` with `is_mesh_moa_unavailable_body` / `is_mesh_moa_failure_body` and the `detect_mesh_fallback` parameter - `looks_like_unstructured_tool_call`, whose only call site was gated on the same adaptive-mesh flag `looks_like_unstructured_tool_call` arrived with this feature (#2825) rather than as a general guard, so it leaves with it. It retried once through `auto` when a committee answered tool-call markup as prose; if that turns out to matter in its own right it should come back provider-agnostic, not gated on shared compute. Behaviour that is deliberately unchanged: an explicit model is still sent verbatim and never rewritten, and plain OpenAI `auto` (a real provider model name) is still left alone. What does change is that a 503 from an explicit `mesh` request now takes the ordinary transport retry under the same model instead of failing fast to a second model -- there is no second model to fall back to once MeshLLM resolves `mesh` itself. Three test helpers (`moa_failure`, `model_catalog`, `complete_model_with_tool`) went unused once the fourteen catalog/debounce tests were removed. New tests pin the surviving contract: shared-compute `auto` sends `mesh` and never requests `/v1/models`, plain `auto` is untouched, and an explicit model keeps its name across retries. Signed-off-by: Michael Neale --- crates/buzz-agent/src/llm.rs | 854 ++++------------------------------- 1 file changed, 100 insertions(+), 754 deletions(-) diff --git a/crates/buzz-agent/src/llm.rs b/crates/buzz-agent/src/llm.rs index 289adbd1ad..ac6033ccbe 100644 --- a/crates/buzz-agent/src/llm.rs +++ b/crates/buzz-agent/src/llm.rs @@ -4,8 +4,6 @@ use std::sync::Arc; use reqwest::Client; use serde_json::{json, Map, Value}; -use tokio::sync::Mutex; -use tokio::time::Instant; use crate::auth::{PkceOAuthConfig, PkceOAuthTokenSource, StaticTokenSource, TokenSource}; use crate::config::{ @@ -25,59 +23,19 @@ const DATABRICKS_OAUTH_SCOPES: &[&str] = &["all-apis", "offline_access"]; const MAX_LLM_RESPONSE_BYTES: usize = 16 * 1024 * 1024; const MAX_LLM_ERROR_BODY_BYTES: usize = 4 * 1024; const STALL_NOTICE_THRESHOLD: std::time::Duration = std::time::Duration::from_secs(300); +/// Buzz shared compute exposes one virtual model. MeshLLM answers a +/// `model=mesh` request with a Mixture-of-Agents committee when two or more +/// workers are reachable, and degrades to a single served model when they are +/// not (`moa_gateway::degrade_to_single_model`, MeshLLM >= 0.75.0). The agent +/// can therefore send `mesh` unconditionally instead of probing `/models` and +/// maintaining a hysteretic view of whether a committee is currently possible. const MESH_VIRTUAL_MODEL_ID: &str = "mesh"; const MESH_AUTO_MODEL_ID: &str = "auto"; -const MESH_AUTO_CATALOG_TTL: std::time::Duration = std::time::Duration::from_secs(5); -const MESH_AUTO_CATALOG_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(2); -const MESH_AUTO_COOLDOWN: std::time::Duration = std::time::Duration::from_secs(30); -const MESH_AUTO_ENABLE_OBSERVATIONS: u8 = 2; -const MESH_MOA_UNAVAILABLE_MESSAGE: &str = "MoA requires ≥2 models available in the mesh"; /// Parser for an OpenAI-family JSON response. Per-endpoint pair lives /// alongside its `_body` serializer. type OpenAiParse = fn(Value) -> Result; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum MeshCatalogObservation { - Available, - Unavailable, - Unknown, -} - -fn mesh_catalog_supports_collective(catalog: &Value) -> Option { - let models = catalog.get("data").and_then(Value::as_array)?; - let mut has_virtual_mesh = false; - let mut physical_models = BTreeSet::new(); - for id in models - .iter() - .filter_map(|model| model.get("id").and_then(Value::as_str)) - .map(str::trim) - .filter(|id| !id.is_empty()) - { - if id == MESH_VIRTUAL_MODEL_ID { - has_virtual_mesh = true; - } else if id != MESH_AUTO_MODEL_ID { - physical_models.insert(id.replace("@main", "")); - } - } - Some(has_virtual_mesh && physical_models.len() >= 2) -} - -fn looks_like_unstructured_tool_call(text: &str) -> bool { - let text = text.trim_start().to_ascii_lowercase(); - text.starts_with("<|tool_call") - || text.starts_with(", - consecutive_available: u8, - collective_enabled: bool, - cooldown_until: Option, -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum DatabricksV2Route { OpenAiResponses, @@ -92,10 +50,6 @@ pub struct Llm { /// == Auto`. Subsequent OpenAI calls then go straight to Responses /// for the lifetime of the process. auto_upgraded: AtomicBool, - /// Hysteretic view of whether mesh-llm currently advertises its virtual - /// Mixture-of-Agents model. A TTL, confirmation count, and failure cooldown - /// let long-running agents adapt without bouncing as peers briefly flap. - mesh_auto_state: Mutex, /// Bearer-token source for OpenAI-family requests. Static for OpenAI /// (the `OPENAI_COMPAT_API_KEY` env var) and Databricks-with-token /// (the `DATABRICKS_TOKEN` env var); a refreshable PKCE engine for @@ -125,7 +79,6 @@ impl Llm { Ok(Self { http, auto_upgraded: AtomicBool::new(false), - mesh_auto_state: Mutex::new(MeshAutoState::default()), auth, }) } @@ -162,37 +115,24 @@ impl Llm { .and_then(parse_openai_with_reasoning_details) } Provider::OpenAi | Provider::Databricks => { - self.openai_request( - cfg, - effective_model, - !tools.is_empty(), - |use_responses, request_model| { - // Normalize effort for model-specific availability. Startup no longer rejects - // `max` for pure OpenAI/Databricks; this per-model table is the single authority - // — it keeps `max` for gpt-5.6, clamps `max`→`xhigh` for other OpenAI-shaped - // models, and still applies corrections like none→minimal on the gpt-5 base. - let e = - effort.map(|ef| normalize_effort_for_openai_route(ef, request_model)); - if use_responses { - ( - responses_body( - cfg, - system_prompt, - history, - tools, - request_model, - e, - ), - parse_responses as OpenAiParse, - ) - } else { - ( - openai_body(cfg, system_prompt, history, tools, request_model, e), - parse_openai as OpenAiParse, - ) - } - }, - ) + self.openai_request(cfg, effective_model, |use_responses, request_model| { + // Normalize effort for model-specific availability. Startup no longer rejects + // `max` for pure OpenAI/Databricks; this per-model table is the single authority + // — it keeps `max` for gpt-5.6, clamps `max`→`xhigh` for other OpenAI-shaped + // models, and still applies corrections like none→minimal on the gpt-5 base. + let e = effort.map(|ef| normalize_effort_for_openai_route(ef, request_model)); + if use_responses { + ( + responses_body(cfg, system_prompt, history, tools, request_model, e), + parse_responses as OpenAiParse, + ) + } else { + ( + openai_body(cfg, system_prompt, history, tools, request_model, e), + parse_openai as OpenAiParse, + ) + } + }) .await } Provider::DatabricksV2 => { @@ -305,37 +245,32 @@ impl Llm { } Provider::OpenAi | Provider::Databricks => { let r = self - .openai_request( - cfg, - effective_model, - false, - |use_responses, request_model| { - if use_responses { - ( - json!({ - "model": request_model, - "max_output_tokens": max_output_tokens, - "instructions": system_prompt, - "input": user_prompt, - }), - parse_responses as OpenAiParse, - ) - } else { - ( - json!({ - "model": request_model, - "stream": false, - "max_completion_tokens": max_output_tokens, - "messages": [ - { "role": "system", "content": system_prompt }, - { "role": "user", "content": user_prompt }, - ], - }), - parse_openai as OpenAiParse, - ) - } - }, - ) + .openai_request(cfg, effective_model, |use_responses, request_model| { + if use_responses { + ( + json!({ + "model": request_model, + "max_output_tokens": max_output_tokens, + "instructions": system_prompt, + "input": user_prompt, + }), + parse_responses as OpenAiParse, + ) + } else { + ( + json!({ + "model": request_model, + "stream": false, + "max_completion_tokens": max_output_tokens, + "messages": [ + { "role": "system", "content": system_prompt }, + { "role": "user", "content": user_prompt }, + ], + }), + parse_openai as OpenAiParse, + ) + } + }) .await?; Ok(r.text) } @@ -396,7 +331,7 @@ impl Llm { async fn post_anthropic(&self, cfg: &Config, body: &Value) -> Result { let url = format!("{}/v1/messages", cfg.base_url.trim_end_matches('/')); - post(&self.http, &url, body, false, cfg.llm_timeout, |r| { + post(&self.http, &url, body, cfg.llm_timeout, |r| { r.header("x-api-key", &cfg.api_key) .header("anthropic-version", &cfg.anthropic_api_version) }) @@ -404,196 +339,31 @@ impl Llm { .map_err(PostError::into_agent) } - /// OpenAI dispatch with Buzz's relay-mesh `auto` policy layered over the - /// normal endpoint selection. When enabled, a live virtual `mesh` model is - /// preferred; if the mesh contracts between discovery and inference, retry - /// the same request once through the router's ordinary `auto` model. + /// OpenAI dispatch. Buzz shared compute maps `auto` onto MeshLLM's virtual + /// `mesh` model, which MeshLLM itself resolves per request: a + /// Mixture-of-Agents committee when two or more workers are reachable, a + /// single served model when they are not. No client-side catalog probe or + /// hysteresis is needed for that decision. async fn openai_request( &self, cfg: &Config, effective_model: &str, - tools_supplied: bool, mut build: F, ) -> Result where F: FnMut(bool, &str) -> (Value, OpenAiParse) + Send, { - let request_model = self.resolve_openai_model(cfg, effective_model).await; - let adaptive_mesh = - effective_model == MESH_AUTO_MODEL_ID && request_model == MESH_VIRTUAL_MODEL_ID; - - let first = self - .openai_request_for_model(cfg, &request_model, &mut build) - .await; - match first { - Err(PostError::MeshFallback(detail)) if adaptive_mesh => { - self.cool_down_collective().await; - tracing::warn!( - configured_model = effective_model, - attempted_model = MESH_VIRTUAL_MODEL_ID, - fallback_model = MESH_AUTO_MODEL_ID, - provider_message = detail, - "relay-mesh auto: collective request failed; retrying once with auto" - ); - self.openai_request_for_model(cfg, MESH_AUTO_MODEL_ID, &mut build) - .await - .map_err(PostError::into_agent) - } - Ok(response) - if adaptive_mesh - && tools_supplied - && response.tool_calls.is_empty() - && looks_like_unstructured_tool_call(&response.text) => - { - self.cool_down_collective().await; - tracing::warn!( - configured_model = effective_model, - attempted_model = MESH_VIRTUAL_MODEL_ID, - fallback_model = MESH_AUTO_MODEL_ID, - "relay-mesh auto: collective response emitted unstructured tool markup; retrying once with auto" - ); - self.openai_request_for_model(cfg, MESH_AUTO_MODEL_ID, &mut build) - .await - .map_err(PostError::into_agent) - } - Ok(response) => Ok(response), - Err(error) => Err(error.into_agent()), - } - } - - async fn cool_down_collective(&self) { - let now = Instant::now(); - let mut state = self.mesh_auto_state.lock().await; - state.last_checked = Some(now); - state.consecutive_available = 0; - state.collective_enabled = false; - state.cooldown_until = Some(now + MESH_AUTO_COOLDOWN); - } - - /// Resolve the model for one OpenAI-family request. Explicit model choices - /// are never changed. Relay-mesh `auto` dynamically follows the short-lived - /// `/models` catalog so long-running agents can adopt or leave MoA without - /// being restarted. - async fn resolve_openai_model(&self, cfg: &Config, effective_model: &str) -> String { - if cfg.provider != Provider::OpenAi - || !cfg.prefer_mesh_for_auto - || effective_model != MESH_AUTO_MODEL_ID + let request_model = if cfg.provider == Provider::OpenAi + && cfg.prefer_mesh_for_auto + && effective_model == MESH_AUTO_MODEL_ID { - return effective_model.to_string(); - } - - let mut state = self.mesh_auto_state.lock().await; - let now = Instant::now(); - if let Some(cooldown_until) = state.cooldown_until { - if now < cooldown_until { - return MESH_AUTO_MODEL_ID.to_string(); - } - state.cooldown_until = None; - } - if state - .last_checked - .is_some_and(|checked_at| checked_at.elapsed() < MESH_AUTO_CATALOG_TTL) - { - return if state.collective_enabled { - MESH_VIRTUAL_MODEL_ID.to_string() - } else { - MESH_AUTO_MODEL_ID.to_string() - }; - } - - let observation = self.observe_mesh_virtual_model(cfg).await; - let checked_at = Instant::now(); - state.last_checked = Some(checked_at); - match observation { - MeshCatalogObservation::Available => { - state.consecutive_available = state.consecutive_available.saturating_add(1); - if state.consecutive_available >= MESH_AUTO_ENABLE_OBSERVATIONS { - state.collective_enabled = true; - } - } - MeshCatalogObservation::Unavailable => { - if state.collective_enabled { - state.cooldown_until = Some(checked_at + MESH_AUTO_COOLDOWN); - } - state.consecutive_available = 0; - state.collective_enabled = false; - } - // A failed catalog check is not evidence that a previously stable - // mesh disappeared. Preserve the last confirmed state; inference - // itself remains authoritative and has the narrow 503 fallback. - MeshCatalogObservation::Unknown => {} - } - let request_model = if state.collective_enabled { MESH_VIRTUAL_MODEL_ID } else { - MESH_AUTO_MODEL_ID - }; - tracing::debug!( - configured_model = effective_model, - request_model, - "relay-mesh auto: resolved request model from live catalog" - ); - request_model.to_string() - } - - async fn observe_mesh_virtual_model(&self, cfg: &Config) -> MeshCatalogObservation { - let url = format!("{}/models", cfg.base_url.trim_end_matches('/')); - let bearer = match self.auth.bearer().await { - Ok(bearer) => bearer, - Err(error) => { - tracing::debug!( - %error, - "relay-mesh auto: catalog auth unavailable; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - } + effective_model }; - let response = match self - .http - .get(&url) - .bearer_auth(bearer) - .timeout(MESH_AUTO_CATALOG_TIMEOUT) - .send() + self.openai_request_for_model(cfg, request_model, &mut build) .await - { - Ok(response) => response, - Err(error) => { - tracing::debug!( - %error, - "relay-mesh auto: catalog probe failed; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - } - }; - if !response.status().is_success() { - tracing::debug!( - status = %response.status(), - "relay-mesh auto: catalog probe was not successful; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - } - match response.json::().await { - Ok(catalog) => { - let Some(available) = mesh_catalog_supports_collective(&catalog) else { - tracing::debug!( - "relay-mesh auto: catalog response has no data array; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - }; - if available { - MeshCatalogObservation::Available - } else { - MeshCatalogObservation::Unavailable - } - } - Err(error) => { - tracing::debug!( - %error, - "relay-mesh auto: invalid catalog response; preserving last confirmed route" - ); - MeshCatalogObservation::Unknown - } - } + .map_err(PostError::into_agent) } /// Dispatch one OpenAI request for an already-resolved model. Endpoint @@ -697,14 +467,9 @@ impl Llm { let mut bearer = self.auth.bearer().await.map_err(PostError::from)?; let mut refreshed = false; loop { - match post( - &self.http, - &url, - body_ref, - effective_model == MESH_VIRTUAL_MODEL_ID, - cfg.llm_timeout, - |r| r.bearer_auth(&bearer), - ) + match post(&self.http, &url, body_ref, cfg.llm_timeout, |r| { + r.bearer_auth(&bearer) + }) .await { Err(PostError::Agent(AgentError::LlmAuth(_))) if !refreshed => { @@ -1959,23 +1724,16 @@ fn terminal_llm_error(elapsed: std::time::Duration, attempts: u32, detail: &str) )) } -/// Internal HTTP failure that preserves a mesh-specific MoA failure as a -/// typed signal. This covers both pre-inference eligibility rejection and a -/// structured `moa_failure` from workers/reducers. It is consumed inside -/// `openai_request`: an adaptive `auto` call falls back once, while an explicit -/// `mesh` call is surfaced as the ordinary LLM error callers already -/// understand. +/// Internal HTTP failure wrapper for the OpenAI-family `post` helper. #[derive(Debug)] enum PostError { Agent(AgentError), - MeshFallback(String), } impl PostError { fn into_agent(self) -> AgentError { match self { Self::Agent(error) => error, - Self::MeshFallback(detail) => AgentError::Llm(detail), } } } @@ -1986,37 +1744,10 @@ impl From for PostError { } } -fn is_mesh_moa_unavailable_body(body: &str) -> bool { - serde_json::from_str::(body) - .ok() - .and_then(|value| { - value - .pointer("/error/message") - .and_then(Value::as_str) - .map(str::to_owned) - }) - .as_deref() - == Some(MESH_MOA_UNAVAILABLE_MESSAGE) -} - -fn is_mesh_moa_failure_body(body: &str) -> bool { - serde_json::from_str::(body) - .ok() - .and_then(|value| { - value - .pointer("/error/type") - .and_then(Value::as_str) - .map(str::to_owned) - }) - .as_deref() - == Some("moa_failure") -} - async fn post( http: &Client, url: &str, body: &Value, - detect_mesh_fallback: bool, base_timeout: std::time::Duration, apply: F, ) -> Result @@ -2080,13 +1811,6 @@ where } if status.is_server_error() || status == 429 || status.as_u16() == 499 { let body = read_error_body(resp).await; - let mesh_fallback = detect_mesh_fallback - && (status == reqwest::StatusCode::SERVICE_UNAVAILABLE - && is_mesh_moa_unavailable_body(&body) - || status.is_server_error() && is_mesh_moa_failure_body(&body)); - if mesh_fallback { - return Err(PostError::MeshFallback(format!("{status}: {body}"))); - } if attempt + 1 < MAX_RETRIES { tracing::warn!( attempt = attempt + 1, @@ -2771,24 +2495,6 @@ mod tests { }), } } - - fn moa_failure(status: u16, code: &str, message: &str) -> Self { - Self { - status, - body: json!({ - "choices": [{ - "finish_reason": "error", - "message": { "content": message, "role": "assistant" }, - }], - "error": { - "message": message, - "type": "moa_failure", - "code": code, - }, - "model": "mesh", - }), - } - } } async fn spawn_sequence_stub( @@ -2877,16 +2583,6 @@ mod tests { (base_url, captured) } - fn model_catalog(ids: &[&str]) -> Value { - json!({ - "object": "list", - "data": ids - .iter() - .map(|id| json!({ "id": id, "object": "model" })) - .collect::>(), - }) - } - fn chat_response(text: &str) -> Value { json!({ "choices": [{ @@ -2911,43 +2607,6 @@ mod tests { .await } - async fn complete_model_with_tool( - llm: &Llm, - cfg: &Config, - model: &str, - ) -> Result { - llm.complete( - cfg, - "system", - &[HistoryItem::User("add two numbers".into())], - &[ToolDef { - name: "add_numbers".into(), - description: "Add two numbers".into(), - input_schema: json!({ - "type": "object", - "properties": { - "a": {"type": "number"}, - "b": {"type": "number"} - }, - "required": ["a", "b"] - }), - }], - model, - ) - .await - } - - async fn expire_mesh_catalog_check(llm: &Llm) { - llm.mesh_auto_state.lock().await.last_checked = - Some(Instant::now() - MESH_AUTO_CATALOG_TTL); - } - - async fn expire_mesh_auto_cooldown(llm: &Llm) { - let mut state = llm.mesh_auto_state.lock().await; - state.last_checked = Some(Instant::now() - MESH_AUTO_CATALOG_TTL); - state.cooldown_until = Some(Instant::now() - std::time::Duration::from_secs(1)); - } - fn posted_models(requests: &[CapturedHttpRequest]) -> Vec<&str> { requests .iter() @@ -2956,383 +2615,75 @@ mod tests { .collect() } + /// An explicit model is sent verbatim and never rewritten to something + /// else. A server error is retried under the *same* model (the ordinary + /// transport retry) and then surfaced -- there is no second model to fall + /// back to now that MeshLLM resolves `mesh` server-side. #[tokio::test] - async fn mesh_auto_requires_two_stable_catalog_observations() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("direct")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("collective")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "direct" - ); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "collective" - ); - - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["auto", "mesh"]); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2 - ); - } - - #[tokio::test] - async fn mesh_auto_does_not_enable_while_second_model_flaps() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("one")), - StubHttpResponse::ok(model_catalog(&["model-a"])), - StubHttpResponse::ok(chat_response("two")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("three")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("four")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - for _ in 0..4 { - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - } - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "auto", "auto", "mesh"] - ); - } - - #[test] - fn collective_catalog_requires_two_distinct_physical_models() { - assert_eq!(mesh_catalog_supports_collective(&json!({})), None); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&[])), - Some(false) - ); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&["model-a", "mesh"])), - Some(false) - ); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&[ - "org/model@main:Q4", - "org/model:Q4", - "mesh" - ])), - Some(false), - "two spellings of one model are not collective capacity" - ); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&["model-a", "model-b", "mesh"])), - Some(true) - ); - } - - #[tokio::test] - async fn mesh_auto_tracks_models_appearing_disappearing_and_reappearing() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&[])), - StubHttpResponse::ok(chat_response("zero")), - StubHttpResponse::ok(model_catalog(&["model-a", "mesh"])), - StubHttpResponse::ok(chat_response("one")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("two-first-observation")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("two-stable")), - StubHttpResponse::ok(model_catalog(&["model-a", "mesh"])), - StubHttpResponse::ok(chat_response("contracted")), - StubHttpResponse::ok(model_catalog(&[])), - StubHttpResponse::ok(chat_response("empty-again")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("rejoin-first-observation")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("rejoined-stable")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - for call in 0..8 { - if call == 5 { - expire_mesh_auto_cooldown(&llm).await; - } else if call > 0 { - expire_mesh_catalog_check(&llm).await; - } - complete_model(&llm, &config, "auto").await.unwrap(); - } - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "auto", "auto", "mesh", "auto", "auto", "auto", "mesh"] - ); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 8 - ); - } - - #[tokio::test] - async fn mesh_auto_falls_back_once_and_cools_down_when_mesh_contracts() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("warmup")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::error(503, MESH_MOA_UNAVAILABLE_MESSAGE), - StubHttpResponse::ok(chat_response("fallback")), - StubHttpResponse::ok(chat_response("cooldown")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "fallback" - ); - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "cooldown" - ); - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "mesh", "auto", "auto"] - ); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2, - "cooldown request must not re-probe the catalog" - ); - } - - #[tokio::test] - async fn mesh_auto_falls_back_once_when_moa_reducer_fails() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("warmup")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::moa_failure( - 502, - "all_reducers_failed", - "Reducer failed: unsupported chat template", - ), - StubHttpResponse::ok(chat_response("fallback")), - StubHttpResponse::ok(chat_response("cooldown")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "fallback" - ); - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "cooldown" - ); - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "mesh", "auto", "auto"] - ); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2, - "mesh-specific failure must enter cooldown without re-probing" - ); - } - - #[tokio::test] - async fn mesh_auto_retries_unstructured_tool_markup_through_auto() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("warmup")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response( - "<|tool_call>call:add_numbers{a:17,b:25}", - )), - StubHttpResponse::ok(chat_response("safe fallback")), - StubHttpResponse::ok(chat_response("cooldown")), - ]) + async fn explicit_models_are_never_rewritten_or_fallback_retried() { + let (base_url, captured) = spawn_sequence_stub( + (0..MAX_RETRIES) + .map(|_| StubHttpResponse::error(503, "upstream unavailable")) + .collect(), + ) .await; let mut config = cfg(Provider::OpenAi); config.base_url = base_url; config.prefer_mesh_for_auto = true; let llm = Llm::new(&config).unwrap(); - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model_with_tool(&llm, &config, "auto") - .await - .unwrap() - .text, - "safe fallback" - ); - assert_eq!( - complete_model_with_tool(&llm, &config, "auto") - .await - .unwrap() - .text, - "cooldown" - ); - + let error = complete_model(&llm, &config, "mesh").await.unwrap_err(); + assert!(error.to_string().contains("upstream unavailable")); let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "mesh", "auto", "auto"] - ); - assert_eq!( + assert!( requests .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2, - "pseudo tool markup must enter cooldown without another catalog probe" + .filter(|request| request.method == "POST") + .all(|request| request.body.as_ref().and_then(|b| b.get("model")) + == Some(&json!("mesh"))), + "every attempt must keep the explicit model: {:?}", + posted_models(&requests) ); - } - - #[tokio::test] - async fn mesh_auto_catalog_failure_fails_open_to_auto() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::error(500, "catalog unavailable"), - StubHttpResponse::ok(chat_response("direct")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["auto"]); - } - - #[tokio::test] - async fn generic_openai_auto_does_not_probe_or_select_mesh() { - let (base_url, captured) = - spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("provider auto"))]).await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = false; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["auto"]); assert!(requests.iter().all(|request| request.path != "/v1/models")); } + /// Shared-compute `auto` maps straight onto the virtual `mesh` model with + /// no catalog probe: MeshLLM decides per request whether that becomes a + /// committee or a single served model. #[tokio::test] - async fn explicit_models_are_never_rewritten_or_fallback_retried() { - let (base_url, captured) = spawn_sequence_stub(vec![StubHttpResponse::error( - 503, - MESH_MOA_UNAVAILABLE_MESSAGE, - )]) - .await; + async fn relay_mesh_auto_sends_mesh_without_probing_the_catalog() { + let (base_url, captured) = + spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("ok"))]).await; let mut config = cfg(Provider::OpenAi); config.base_url = base_url; config.prefer_mesh_for_auto = true; let llm = Llm::new(&config).unwrap(); - let error = complete_model(&llm, &config, "mesh").await.unwrap_err(); - assert!(error.to_string().contains(MESH_MOA_UNAVAILABLE_MESSAGE)); + let response = complete_model(&llm, &config, "auto").await.unwrap(); + assert_eq!(response.text, "ok"); let requests = captured.lock().await; assert_eq!(posted_models(&requests), vec!["mesh"]); assert!(requests.iter().all(|request| request.path != "/v1/models")); } + /// Without shared compute, `auto` is a real provider model name and must + /// never be rewritten to the mesh virtual model. #[tokio::test] - async fn explicit_real_model_bypasses_mesh_auto_policy() { + async fn plain_openai_auto_is_left_alone() { let (base_url, captured) = - spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("explicit"))]).await; + spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("ok"))]).await; let mut config = cfg(Provider::OpenAi); config.base_url = base_url; - config.prefer_mesh_for_auto = true; + config.prefer_mesh_for_auto = false; let llm = Llm::new(&config).unwrap(); - let response = complete_model(&llm, &config, "model-a").await.unwrap(); - assert_eq!(response.text, "explicit"); + let response = complete_model(&llm, &config, "auto").await.unwrap(); + assert_eq!(response.text, "ok"); let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["model-a"]); + assert_eq!(posted_models(&requests), vec!["auto"]); assert!(requests.iter().all(|request| request.path != "/v1/models")); } - #[test] - fn mesh_unavailable_classifier_requires_exact_openai_error_message() { - assert!(is_mesh_moa_unavailable_body( - &StubHttpResponse::error(503, MESH_MOA_UNAVAILABLE_MESSAGE) - .body - .to_string() - )); - assert!(!is_mesh_moa_unavailable_body( - &StubHttpResponse::error(503, "some other outage") - .body - .to_string() - )); - } - - #[test] - fn mesh_failure_classifier_requires_structured_moa_failure_type() { - assert!(is_mesh_moa_failure_body( - &StubHttpResponse::moa_failure( - 502, - "all_reducers_failed", - "Reducer failed: unsupported chat template", - ) - .body - .to_string() - )); - assert!(!is_mesh_moa_failure_body( - &StubHttpResponse::error(502, "some other bad gateway") - .body - .to_string() - )); - } - fn image_history() -> Vec { vec![ HistoryItem::User("describe the image".into()), @@ -4608,7 +3959,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4679,7 +4029,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4737,7 +4086,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4837,7 +4185,6 @@ mod tests { &client, &url, &serde_json::json!({"model": "x"}), - false, // Small base timeout so the body stall triggers quickly. Duration::from_secs(1), |b| b, @@ -5902,7 +5249,6 @@ mod tests { .build() .unwrap(), auto_upgraded: std::sync::atomic::AtomicBool::new(false), - mesh_auto_state: Mutex::new(MeshAutoState::default()), auth, } } From 2fe1786789cb3bc98777047a54b78db8aff35065 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 10 Aug 2026 15:28:44 +1000 Subject: [PATCH 04/12] chore(mesh): move to MeshLLM v0.75.1, clearing the stale-cache blocker v0.75.1 carries the upstream fix (Mesh-LLM/mesh-llm#1196): startup now scans the native-runtime cache leniently (`installed_lenient`), skipping unusable entries instead of aborting the whole scan on the first one. That removes the blocker this branch has carried since the v0.75.0 bump. On a machine with a pre-0.75 cache, v0.75.0 failed with: native runtime artifact meshllm-native-runtime-darwin-aarch64-metal does not declare file checksums because caches written by older loaders have no per-file checksums, and the strict enumeration covered every version under the cache root. Verified on a machine that still has the offending entries in place (0.73.1, 0.74.0, 0.74.0-rc8 all with `runtime.files = 0`), with no bundle directory and no MESH_LLM_BUILD_VERSION override: - no checksum abort - the v0.75.1 runtime installed itself alongside the stale entries - crates/buzz-relay/examples/mesh_agent_e2e passed 4/4 (explicit-model chat, auto-model chat, oversized-budget rejection, agentic tool use) Pins move in both manifests (six mesh-llm crates in the desktop Tauri crate, two in buzz-relay) with both lockfiles refreshed. The v0.75.1 release publishes native-runtimes.json with 13 artifacts including darwin-aarch64-metal at Skippy ABI 0.1.35, which matches what the crates compile against. Signed-off-by: Michael Neale --- Cargo.lock | 437 ++++++++++++++++++++++------------- crates/buzz-relay/Cargo.toml | 4 +- desktop/src-tauri/Cargo.lock | 394 +++++++++++++++++++------------ desktop/src-tauri/Cargo.toml | 12 +- 4 files changed, 536 insertions(+), 311 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f87591c54..3ba06f374b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,20 +352,21 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069" +checksum = "2c713e1f14c7b82e32ea159af1c6e2f070cfadbdf23fb2512acce9af0a26f1a2" dependencies = [ - "async-utility", "futures", "futures-util", "js-sys", "tokio", + "tokio-happy-eyeballs", "tokio-rustls", "tokio-socks", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.28.0", "url", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -398,12 +399,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -597,6 +592,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "beef" version = "0.5.2" @@ -609,7 +610,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "serde", "unicode-normalization", ] @@ -644,6 +645,21 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +dependencies = [ + "bitcoin-internals", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" version = "0.1.4" @@ -657,7 +673,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals", + "hex-conservative 1.2.0", "serde", ] @@ -812,7 +840,7 @@ dependencies = [ "hex", "httparse", "nix 0.31.3", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "rustls", "serde", @@ -845,7 +873,7 @@ dependencies = [ "clap", "deadpool-redis", "hex", - "nostr", + "nostr 0.44.7", "rustls", "serde_json", "sqlx", @@ -904,7 +932,7 @@ version = "0.1.0" dependencies = [ "buzz-core", "hex", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "serde", "serde_json", @@ -926,7 +954,7 @@ dependencies = [ "http-body-util", "k8s-openapi", "kube", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "rustls", "serde", @@ -953,7 +981,7 @@ dependencies = [ "dirs", "hex", "infer", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "reqwest 0.13.4", "rustls", @@ -986,7 +1014,7 @@ dependencies = [ "chrono", "hex", "hmac 0.13.0", - "nostr", + "nostr 0.44.7", "percent-encoding", "rand 0.10.1", "serde", @@ -1008,7 +1036,7 @@ dependencies = [ "hex", "metrics", "metrics-util", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "serde", "serde_json", @@ -1032,7 +1060,7 @@ dependencies = [ "ignore", "image", "nix 0.31.3", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "rmcp", "rustls", @@ -1065,7 +1093,7 @@ dependencies = [ "imagesize", "infer", "mp4", - "nostr", + "nostr 0.44.7", "rust-s3", "serde", "serde_json", @@ -1104,7 +1132,7 @@ dependencies = [ "clap", "futures-util", "hex", - "nostr", + "nostr 0.44.7", "serde_json", "thiserror 2.0.18", "tokio", @@ -1133,7 +1161,7 @@ dependencies = [ "chrono", "deadpool-redis", "futures-util", - "nostr", + "nostr 0.44.7", "redis", "serde", "serde_json", @@ -1159,7 +1187,7 @@ dependencies = [ "metrics", "metrics-exporter-prometheus", "minicbor", - "nostr", + "nostr 0.44.7", "p256", "proptest", "rand 0.10.1", @@ -1216,7 +1244,7 @@ dependencies = [ "metrics-exporter-prometheus", "metrics-util", "moka", - "nostr", + "nostr 0.44.7", "opentelemetry 0.32.0", "opentelemetry-otlp 0.32.0", "opentelemetry_sdk 0.32.1", @@ -1257,7 +1285,7 @@ dependencies = [ "hex", "hmac 0.13.0", "iroh", - "nostr", + "nostr 0.44.7", "postcard", "proptest", "redis", @@ -1275,7 +1303,7 @@ name = "buzz-sdk" version = "0.1.0" dependencies = [ "buzz-core", - "nostr", + "nostr 0.44.7", "serde", "serde_json", "thiserror 2.0.18", @@ -1306,7 +1334,7 @@ dependencies = [ "chrono", "futures-util", "hex", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "reqwest 0.13.4", "rust-s3", @@ -1355,7 +1383,7 @@ dependencies = [ "evalexpr", "hex", "moka", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "serde", "serde_json", @@ -1371,7 +1399,7 @@ name = "buzz-ws-client" version = "0.1.0" dependencies = [ "futures-util", - "nostr", + "nostr 0.44.7", "serde_json", "thiserror 2.0.18", "tokio", @@ -1819,7 +1847,7 @@ dependencies = [ "anyhow", "buzz-sdk", "futures-util", - "nostr", + "nostr 0.44.7", "serde_json", "tokio", "tokio-tungstenite 0.29.0", @@ -2791,6 +2819,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -3177,7 +3215,7 @@ name = "git-credential-nostr" version = "0.1.0" dependencies = [ "base64 0.22.1", - "nostr", + "nostr 0.44.7", "serde_json", "zeroize", ] @@ -3190,7 +3228,7 @@ dependencies = [ "chrono", "hex", "libc", - "nostr", + "nostr 0.44.7", "serde_json", "zeroize", ] @@ -3270,6 +3308,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbag" version = "0.1.13" @@ -3328,6 +3375,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0049b265b7f201ca9ab25475b22b47fe444060126a51abe00f77d986fc5cc52e" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -3355,6 +3412,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + [[package]] name = "hf-xet" version = "1.6.0" @@ -4109,7 +4175,7 @@ dependencies = [ "iroh-base", "iroh-dns", "iroh-metrics", - "lru 0.18.0", + "lru", "n0-error", "n0-future", "noq", @@ -4576,12 +4642,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" - [[package]] name = "lru" version = "0.18.0" @@ -4728,8 +4788,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "mesh-llm-client", @@ -4738,8 +4798,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -4749,13 +4809,13 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-llm-client" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -4786,8 +4846,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -4802,8 +4862,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -4812,8 +4872,8 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "clap", @@ -4824,8 +4884,8 @@ dependencies = [ [[package]] name = "mesh-llm-gpu-bench" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -4834,8 +4894,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -4843,8 +4903,8 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "mesh-llm-native-runtime", ] @@ -4880,8 +4940,8 @@ dependencies = [ [[package]] name = "mesh-llm-host-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "argon2", @@ -4974,8 +5034,8 @@ dependencies = [ [[package]] name = "mesh-llm-identity" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "argon2", "base64 0.22.1", @@ -4996,8 +5056,8 @@ dependencies = [ [[package]] name = "mesh-llm-native-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "serde", @@ -5007,8 +5067,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-types", @@ -5021,8 +5081,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5038,8 +5098,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5057,8 +5117,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "hex", @@ -5070,8 +5130,8 @@ dependencies = [ [[package]] name = "mesh-llm-release-footer" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "sha2 0.10.9", @@ -5079,16 +5139,16 @@ dependencies = [ [[package]] name = "mesh-llm-routing" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "iroh", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5110,8 +5170,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5125,8 +5185,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5136,8 +5196,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "chrono", @@ -5163,8 +5223,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "serde", @@ -5174,13 +5234,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-mixture-of-agents" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5193,13 +5253,13 @@ dependencies = [ [[package]] name = "mesh-native-serving-plugin-api" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-native-serving-plugin-host" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "libloading", @@ -5344,8 +5404,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5355,8 +5415,8 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5373,8 +5433,8 @@ dependencies = [ [[package]] name = "model-package" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "bytes", @@ -5393,16 +5453,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "model-artifact", @@ -5821,9 +5881,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" dependencies = [ "base64 0.22.1", - "bech32", + "bech32 0.11.1", "bip39", - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "cbc", "chacha20 0.9.1", "chacha20poly1305", @@ -5839,56 +5899,71 @@ dependencies = [ ] [[package]] -name = "nostr-database" -version = "0.44.0" +name = "nostr" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" +checksum = "5dde8c76076d334409d86c2e1db3e97abe5deb8cb92744f939cbc1fa45bd69e7" dependencies = [ - "lru 0.16.4", - "nostr", - "tokio", + "base64 0.22.1", + "bech32 0.12.0", + "bip39", + "bitcoin_hashes 1.2.0", + "cbc", + "chacha20 0.9.1", + "chacha20poly1305", + "faster-hex", + "opaquerr", + "rand 0.10.1", + "secp256k1 0.30.0", + "serde", + "serde_json", + "unicode-normalization", + "universal-time", + "url", + "zeroize", ] [[package]] -name = "nostr-gossip" -version = "0.44.0" +name = "nostr-database" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6" +checksum = "4b1fdb9fcba732e32719662afad1b267e50322dbe89e506017ec13f24361bddf" dependencies = [ - "nostr", + "nostr 0.45.1", + "opaquerr", ] [[package]] -name = "nostr-relay-pool" -version = "0.44.3" +name = "nostr-gossip" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "fa07539e52a71cb91fe0d693facaa298f03fcf9edcd66a521094e18e286e2336" dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "hex", - "lru 0.16.4", - "negentropy", - "nostr", - "nostr-database", - "tokio", - "tracing", + "nostr 0.45.1", + "opaquerr", ] [[package]] name = "nostr-sdk" -version = "0.44.1" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393" +checksum = "26c86342f367bd9b173ec4a697e936e3a82d6dad5b4aa06c0d35d9b4f88a8e72" dependencies = [ "async-utility", - "nostr", + "async-wsocket", + "faster-hex", + "futures", + "lru", + "negentropy", + "nostr 0.45.1", "nostr-database", "nostr-gossip", - "nostr-relay-pool", + "opaquerr", + "rand 0.10.1", "tokio", + "tokio-stream", "tracing", + "universal-time", ] [[package]] @@ -6169,10 +6244,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opaquerr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" + [[package]] name = "openai-frontend" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "axum", @@ -7536,7 +7617,7 @@ dependencies = [ "hashbrown 0.17.1", "itertools", "kasuari", - "lru 0.18.0", + "lru", "palette", "serde", "strum", @@ -8277,13 +8358,24 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.1", + "rand 0.8.6", + "secp256k1-sys 0.10.1", +] + [[package]] name = "secp256k1" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "rand 0.9.4", "secp256k1-sys 0.11.0", ] @@ -8737,8 +8829,8 @@ checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "skippy-cache" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "blake3", @@ -8747,40 +8839,41 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "libloading", ] [[package]] name = "skippy-metrics" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "skippy-protocol" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "prost 0.14.3", "prost-build 0.14.3", "protoc-bin-vendored", "serde", + "skippy-tokenizer", ] [[package]] name = "skippy-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "libc", @@ -8793,8 +8886,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "ahash", "anyhow", @@ -8816,16 +8909,25 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-tokenizer", "socket2", "tokio", "tokio-stream", "tonic", ] +[[package]] +name = "skippy-tokenizer" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "serde", +] + [[package]] name = "skippy-topology" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -9727,6 +9829,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-happy-eyeballs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8564c32dfb6f4257f8bc6edfc178a34af97520e0b7b9815500c55eb3d092f29f" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.7.0" @@ -9795,9 +9906,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -9805,7 +9916,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite 0.28.0", "webpki-roots 0.26.11", ] @@ -10188,9 +10299,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -10367,6 +10478,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-time" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a939edecc3c5a7b83c02e5f6b3c31d2bc69eabcc9a87ab12c6d37ee6dbc856" + [[package]] name = "unsafe-libyaml" version = "0.2.11" diff --git a/crates/buzz-relay/Cargo.toml b/crates/buzz-relay/Cargo.toml index 3644b3c515..e37c332910 100644 --- a/crates/buzz-relay/Cargo.toml +++ b/crates/buzz-relay/Cargo.toml @@ -84,8 +84,8 @@ async-compression = { version = "0.4.42", features = ["tokio", "gzip"] } dev = ["buzz-auth/dev"] [dev-dependencies] -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } # Relay-driven mesh lifecycle smoke (examples/mesh_relay_lifecycle_smoke.rs): # the relay client for discovery notes and the exact ed25519 the mesh owner # keys use for binding verification. diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index fd1b932e48..feb93fd423 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -447,20 +447,21 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069" +checksum = "2c713e1f14c7b82e32ea159af1c6e2f070cfadbdf23fb2512acce9af0a26f1a2" dependencies = [ - "async-utility", "futures", "futures-util", "js-sys", "tokio", + "tokio-happy-eyeballs", "tokio-rustls", "tokio-socks", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.28.0", "url", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -507,12 +508,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -762,6 +757,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "beef" version = "0.5.2" @@ -774,7 +775,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "serde", "unicode-normalization", ] @@ -815,7 +816,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2d6094e2a1ba3c93b5a596fe5a10d1a10c3c6e06785cde89f693a044c01aa40" dependencies = [ - "bitcoin-internals", + "bitcoin-internals 0.5.0", ] [[package]] @@ -827,6 +828,12 @@ dependencies = [ "hex-conservative 0.3.2", ] +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" version = "0.1.101" @@ -847,6 +854,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals 0.6.0", + "hex-conservative 1.2.0", + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1046,7 +1065,7 @@ dependencies = [ "chrono", "hex", "hmac 0.13.0", - "nostr", + "nostr 0.44.7", "percent-encoding", "rand 0.10.2", "serde", @@ -1100,7 +1119,7 @@ dependencies = [ "mesh-llm-sdk", "mesh-llm-system", "neteq", - "nostr", + "nostr 0.44.7", "notify-rust", "objc2", "objc2-app-kit", @@ -1168,7 +1187,7 @@ dependencies = [ "imagesize", "infer", "mp4", - "nostr", + "nostr 0.44.7", "rust-s3", "serde", "serde_json", @@ -1197,7 +1216,7 @@ name = "buzz-sdk" version = "0.1.0" dependencies = [ "buzz-core", - "nostr", + "nostr 0.44.7", "serde", "serde_json", "thiserror 2.0.18", @@ -2819,6 +2838,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -3562,6 +3591,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -3620,6 +3658,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0049b265b7f201ca9ab25475b22b47fe444060126a51abe00f77d986fc5cc52e" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -3662,6 +3710,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + [[package]] name = "hf-xet" version = "1.5.3" @@ -4422,7 +4479,7 @@ dependencies = [ "iroh-base", "iroh-dns", "iroh-metrics", - "lru 0.18.1", + "lru", "n0-error", "n0-future", "noq", @@ -4904,12 +4961,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" - [[package]] name = "lru" version = "0.18.1" @@ -5107,8 +5158,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "mesh-llm-client", @@ -5117,8 +5168,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5128,13 +5179,13 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-llm-client" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5165,8 +5216,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5181,8 +5232,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -5191,8 +5242,8 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "clap", @@ -5203,8 +5254,8 @@ dependencies = [ [[package]] name = "mesh-llm-gpu-bench" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -5213,8 +5264,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -5222,8 +5273,8 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "mesh-llm-native-runtime", ] @@ -5259,8 +5310,8 @@ dependencies = [ [[package]] name = "mesh-llm-host-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "argon2", @@ -5353,8 +5404,8 @@ dependencies = [ [[package]] name = "mesh-llm-identity" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "argon2", "base64 0.22.1", @@ -5375,8 +5426,8 @@ dependencies = [ [[package]] name = "mesh-llm-native-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "serde", @@ -5386,8 +5437,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-types", @@ -5400,8 +5451,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5417,8 +5468,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5436,8 +5487,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "hex", @@ -5449,8 +5500,8 @@ dependencies = [ [[package]] name = "mesh-llm-release-footer" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "sha2 0.10.9", @@ -5458,16 +5509,16 @@ dependencies = [ [[package]] name = "mesh-llm-routing" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "iroh", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5489,8 +5540,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5504,8 +5555,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5515,8 +5566,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "chrono", @@ -5542,8 +5593,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "serde", @@ -5553,13 +5604,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-mixture-of-agents" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5572,13 +5623,13 @@ dependencies = [ [[package]] name = "mesh-native-serving-plugin-api" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-native-serving-plugin-host" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "libloading 0.8.9", @@ -5678,8 +5729,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5689,8 +5740,8 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5707,8 +5758,8 @@ dependencies = [ [[package]] name = "model-package" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "bytes", @@ -5727,16 +5778,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "model-artifact", @@ -6231,9 +6282,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" dependencies = [ "base64 0.22.1", - "bech32", + "bech32 0.11.1", "bip39", - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "cbc", "chacha20 0.9.1", "chacha20poly1305", @@ -6241,7 +6292,7 @@ dependencies = [ "hex", "instant", "scrypt", - "secp256k1", + "secp256k1 0.29.1", "serde", "serde_json", "unicode-normalization", @@ -6249,56 +6300,71 @@ dependencies = [ ] [[package]] -name = "nostr-database" -version = "0.44.0" +name = "nostr" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" +checksum = "5dde8c76076d334409d86c2e1db3e97abe5deb8cb92744f939cbc1fa45bd69e7" dependencies = [ - "lru 0.16.4", - "nostr", - "tokio", + "base64 0.22.1", + "bech32 0.12.0", + "bip39", + "bitcoin_hashes 1.2.0", + "cbc", + "chacha20 0.9.1", + "chacha20poly1305", + "faster-hex", + "opaquerr", + "rand 0.10.2", + "secp256k1 0.30.0", + "serde", + "serde_json", + "unicode-normalization", + "universal-time", + "url", + "zeroize", ] [[package]] -name = "nostr-gossip" -version = "0.44.0" +name = "nostr-database" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6" +checksum = "4b1fdb9fcba732e32719662afad1b267e50322dbe89e506017ec13f24361bddf" dependencies = [ - "nostr", + "nostr 0.45.1", + "opaquerr", ] [[package]] -name = "nostr-relay-pool" -version = "0.44.3" +name = "nostr-gossip" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "fa07539e52a71cb91fe0d693facaa298f03fcf9edcd66a521094e18e286e2336" dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "hex", - "lru 0.16.4", - "negentropy", - "nostr", - "nostr-database", - "tokio", - "tracing", + "nostr 0.45.1", + "opaquerr", ] [[package]] name = "nostr-sdk" -version = "0.44.1" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393" +checksum = "26c86342f367bd9b173ec4a697e936e3a82d6dad5b4aa06c0d35d9b4f88a8e72" dependencies = [ "async-utility", - "nostr", + "async-wsocket", + "faster-hex", + "futures", + "lru", + "negentropy", + "nostr 0.45.1", "nostr-database", "nostr-gossip", - "nostr-relay-pool", + "opaquerr", + "rand 0.10.2", "tokio", + "tokio-stream", "tracing", + "universal-time", ] [[package]] @@ -6803,6 +6869,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opaquerr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" + [[package]] name = "open" version = "5.3.6" @@ -6816,8 +6888,8 @@ dependencies = [ [[package]] name = "openai-frontend" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "axum", @@ -8210,7 +8282,7 @@ dependencies = [ "hashbrown 0.17.1", "itertools", "kasuari", - "lru 0.18.1", + "lru", "palette", "serde", "strum", @@ -9040,6 +9112,17 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.101", + "rand 0.8.6", + "secp256k1-sys", +] + [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -9596,8 +9679,8 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skippy-cache" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "blake3", @@ -9606,40 +9689,41 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "libloading 0.8.9", ] [[package]] name = "skippy-metrics" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "skippy-protocol" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "prost 0.14.4", "prost-build 0.14.4", "protoc-bin-vendored", "serde", + "skippy-tokenizer", ] [[package]] name = "skippy-runtime" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "libc", @@ -9652,8 +9736,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "ahash", "anyhow", @@ -9675,16 +9759,25 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-tokenizer", "socket2", "tokio", "tokio-stream", "tonic", ] +[[package]] +name = "skippy-tokenizer" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "serde", +] + [[package]] name = "skippy-topology" -version = "0.75.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.0#0ddaa87d4a24e5ec7c891afccf515748298b7027" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -11005,6 +11098,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-happy-eyeballs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8564c32dfb6f4257f8bc6edfc178a34af97520e0b7b9815500c55eb3d092f29f" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.7.0" @@ -11073,9 +11175,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -11083,7 +11185,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite 0.28.0", "webpki-roots 0.26.11", ] @@ -11514,9 +11616,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -11734,6 +11836,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-time" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a939edecc3c5a7b83c02e5f6b3c31d2bc69eabcc9a87ab12c6d37ee6dbc856" + [[package]] name = "unsafe-libyaml" version = "0.2.11" diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 477a189cc8..2948befb69 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -109,14 +109,14 @@ buzz_voice_pkg = { package = "buzz-voice", path = "../../crates/buzz-voice" } buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" } portable-pty = "0.9" iroh = { version = "1.0.2", optional = true } -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } # Model catalog + hardware survey for the Share-compute model picker (same # diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client. -mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-client", optional = true } -mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-node", optional = true } -mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-system", optional = true } -mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.0", package = "mesh-llm-events", optional = true } +mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-client", optional = true } +mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-node", optional = true } +mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-system", optional = true } +mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-events", optional = true } base64 = "0.22" sha2 = "0.11" tar = "0.4" From 732b201872572afa8db2264d72ef8d8bf9e58b7f Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 10 Aug 2026 16:01:56 +1000 Subject: [PATCH 05/12] refactor(mesh): resolve the shared-compute model in one place Shared compute either uses MeshLLM's virtual `mesh` model or a model the user named. Nothing else. That decision now happens once, in the desktop translation layer that already turns the native provider into an OpenAI-compatible transport: stored "auto" (or blank legacy) -> BUZZ_AGENT_MODEL=mesh stored "" -> BUZZ_AGENT_MODEL= MeshLLM resolves `mesh` per request -- a Mixture-of-Agents committee when two or more workers are reachable, a single served model when they are not (`moa_gateway::degrade_to_single_model`) -- so the caller never has to know which of those will happen. buzz-agent therefore needs no mesh concepts at all. Removed: - the `auto` -> `mesh` mapping in `openai_request` - `MESH_VIRTUAL_MODEL_ID` / `MESH_AUTO_MODEL_ID` - `Config::prefer_mesh_for_auto` and the `BUZZ_AGENT_PREFER_MESH_FOR_AUTO` env var that fed it `openai_request` now sends the configured model verbatim, which is what it does for every other provider. `auto` stays a real provider model name for plain OpenAI hosts, unchanged, because nothing rewrites it any more. The previous commit moved this decision from a `/models` probe into a compile-time mapping; this moves it out of the agent entirely. The stored value and the picker label both stay "auto" -- that is the user's word for "let the mesh decide", and it is still what gets persisted. Tests: `relay_mesh.rs` gains three that pin the translation (stored `auto` and blank both become `mesh`; a named model is sent verbatim). In `llm.rs`, the two mapping tests are replaced by one asserting the configured model reaches the wire unchanged for `auto`, `mesh`, and a named model, with no catalog request on any of them. Signed-off-by: Michael Neale --- crates/buzz-agent/src/config.rs | 8 -- crates/buzz-agent/src/llm.rs | 83 ++++++------------ .../src/managed_agents/relay_mesh.rs | 84 +++++++++++++++---- 3 files changed, 94 insertions(+), 81 deletions(-) diff --git a/crates/buzz-agent/src/config.rs b/crates/buzz-agent/src/config.rs index ecd2e88668..a6e1ac7542 100644 --- a/crates/buzz-agent/src/config.rs +++ b/crates/buzz-agent/src/config.rs @@ -833,12 +833,6 @@ pub struct Config { pub anthropic_api_version: String, /// OpenAI endpoint selection. See [`OpenAiApi`]. pub openai_api: OpenAiApi, - /// Prefer mesh-llm's virtual `mesh` model when the configured/effective - /// OpenAI model is `auto` and the live model catalog advertises it. - /// Set by Buzz's relay-mesh provider via - /// `BUZZ_AGENT_PREFER_MESH_FOR_AUTO=1`; other providers keep their - /// existing `auto` semantics. - pub prefer_mesh_for_auto: bool, pub hints_enabled: bool, /// Thinking/reasoning effort level. `None` = use provider default (no /// thinking config sent). Set via `BUZZ_AGENT_THINKING_EFFORT`. @@ -936,7 +930,6 @@ impl Config { base_url, anthropic_api_version: env_or("ANTHROPIC_API_VERSION", "2023-06-01"), openai_api, - prefer_mesh_for_auto: parse_env("BUZZ_AGENT_PREFER_MESH_FOR_AUTO", 0u8)? != 0, max_rounds: parse_env("BUZZ_AGENT_MAX_ROUNDS", 0)?, max_output_tokens: parse_env("BUZZ_AGENT_MAX_OUTPUT_TOKENS", 32_768)?, llm_timeout: Duration::from_secs(parse_env("BUZZ_AGENT_LLM_TIMEOUT_SECS", 240)?), @@ -988,7 +981,6 @@ impl Config { system_prompt: String::new(), anthropic_api_version: "2023-06-01".into(), openai_api: OpenAiApi::Chat, - prefer_mesh_for_auto: false, max_rounds: 0, max_output_tokens: 1, llm_timeout: Duration::from_secs(30), diff --git a/crates/buzz-agent/src/llm.rs b/crates/buzz-agent/src/llm.rs index ac6033ccbe..480ffd0ec4 100644 --- a/crates/buzz-agent/src/llm.rs +++ b/crates/buzz-agent/src/llm.rs @@ -23,14 +23,6 @@ const DATABRICKS_OAUTH_SCOPES: &[&str] = &["all-apis", "offline_access"]; const MAX_LLM_RESPONSE_BYTES: usize = 16 * 1024 * 1024; const MAX_LLM_ERROR_BODY_BYTES: usize = 4 * 1024; const STALL_NOTICE_THRESHOLD: std::time::Duration = std::time::Duration::from_secs(300); -/// Buzz shared compute exposes one virtual model. MeshLLM answers a -/// `model=mesh` request with a Mixture-of-Agents committee when two or more -/// workers are reachable, and degrades to a single served model when they are -/// not (`moa_gateway::degrade_to_single_model`, MeshLLM >= 0.75.0). The agent -/// can therefore send `mesh` unconditionally instead of probing `/models` and -/// maintaining a hysteretic view of whether a committee is currently possible. -const MESH_VIRTUAL_MODEL_ID: &str = "mesh"; -const MESH_AUTO_MODEL_ID: &str = "auto"; /// Parser for an OpenAI-family JSON response. Per-endpoint pair lives /// alongside its `_body` serializer. @@ -339,11 +331,9 @@ impl Llm { .map_err(PostError::into_agent) } - /// OpenAI dispatch. Buzz shared compute maps `auto` onto MeshLLM's virtual - /// `mesh` model, which MeshLLM itself resolves per request: a - /// Mixture-of-Agents committee when two or more workers are reachable, a - /// single served model when they are not. No client-side catalog probe or - /// hysteresis is needed for that decision. + /// OpenAI dispatch. The configured model is sent as given: callers that + /// route through a mesh (Buzz shared compute) resolve their own model name + /// before spawning the agent, so nothing here needs to know about meshes. async fn openai_request( &self, cfg: &Config, @@ -353,15 +343,7 @@ impl Llm { where F: FnMut(bool, &str) -> (Value, OpenAiParse) + Send, { - let request_model = if cfg.provider == Provider::OpenAi - && cfg.prefer_mesh_for_auto - && effective_model == MESH_AUTO_MODEL_ID - { - MESH_VIRTUAL_MODEL_ID - } else { - effective_model - }; - self.openai_request_for_model(cfg, request_model, &mut build) + self.openai_request_for_model(cfg, effective_model, &mut build) .await .map_err(PostError::into_agent) } @@ -2457,7 +2439,6 @@ mod tests { base_url: "http://example.invalid".into(), anthropic_api_version: "2023-06-01".into(), openai_api: OpenAiApi::Chat, - prefer_mesh_for_auto: false, hints_enabled: true, thinking_effort: None, thinking_summary: ThinkingSummary::Auto, @@ -2629,7 +2610,6 @@ mod tests { .await; let mut config = cfg(Provider::OpenAi); config.base_url = base_url; - config.prefer_mesh_for_auto = true; let llm = Llm::new(&config).unwrap(); let error = complete_model(&llm, &config, "mesh").await.unwrap_err(); @@ -2647,41 +2627,28 @@ mod tests { assert!(requests.iter().all(|request| request.path != "/v1/models")); } - /// Shared-compute `auto` maps straight onto the virtual `mesh` model with - /// no catalog probe: MeshLLM decides per request whether that becomes a - /// committee or a single served model. + /// Whatever model the config names is what goes on the wire -- including + /// `auto`, which is a real provider model name for plain OpenAI hosts. A + /// caller routing through a mesh resolves its own name before spawning the + /// agent, so there is nothing to rewrite here and no catalog to probe. #[tokio::test] - async fn relay_mesh_auto_sends_mesh_without_probing_the_catalog() { - let (base_url, captured) = - spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("ok"))]).await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - let response = complete_model(&llm, &config, "auto").await.unwrap(); - assert_eq!(response.text, "ok"); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["mesh"]); - assert!(requests.iter().all(|request| request.path != "/v1/models")); - } - - /// Without shared compute, `auto` is a real provider model name and must - /// never be rewritten to the mesh virtual model. - #[tokio::test] - async fn plain_openai_auto_is_left_alone() { - let (base_url, captured) = - spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("ok"))]).await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = false; - let llm = Llm::new(&config).unwrap(); - - let response = complete_model(&llm, &config, "auto").await.unwrap(); - assert_eq!(response.text, "ok"); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["auto"]); - assert!(requests.iter().all(|request| request.path != "/v1/models")); + async fn the_configured_model_is_sent_verbatim() { + for model in ["auto", "mesh", "unsloth/Qwen3-8B-GGUF:Q4_K_M"] { + let (base_url, captured) = + spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("ok"))]).await; + let mut config = cfg(Provider::OpenAi); + config.base_url = base_url; + let llm = Llm::new(&config).unwrap(); + + let response = complete_model(&llm, &config, model).await.unwrap(); + assert_eq!(response.text, "ok"); + let requests = captured.lock().await; + assert_eq!(posted_models(&requests), vec![model]); + assert!( + requests.iter().all(|request| request.path != "/v1/models"), + "no catalog probe belongs on this path" + ); + } } fn image_history() -> Vec { diff --git a/desktop/src-tauri/src/managed_agents/relay_mesh.rs b/desktop/src-tauri/src/managed_agents/relay_mesh.rs index 5c246feedc..2b0909a897 100644 --- a/desktop/src-tauri/src/managed_agents/relay_mesh.rs +++ b/desktop/src-tauri/src/managed_agents/relay_mesh.rs @@ -1,9 +1,15 @@ pub const RELAY_MESH_API_BASE_URL: &str = "http://127.0.0.1:9337/v1"; pub const RELAY_MESH_API_KEY_PLACEHOLDER: &str = "buzz-mesh-local"; pub const RELAY_MESH_PROVIDER_ID: &str = "relay-mesh"; +/// Stored value for "let the mesh decide", kept as the user-facing word. pub const RELAY_MESH_AUTO_MODEL_ID: &str = "auto"; +/// MeshLLM's virtual model. It resolves per request: a Mixture-of-Agents +/// committee when two or more workers are reachable, a single served model when +/// they are not (`moa_gateway::degrade_to_single_model`). Sending it is always +/// safe, so Buzz translates the stored `auto` into it here rather than teaching +/// buzz-agent anything about the mesh. #[cfg(feature = "mesh-llm")] -pub const RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV: &str = "BUZZ_AGENT_PREFER_MESH_FOR_AUTO"; +pub const RELAY_MESH_VIRTUAL_MODEL_ID: &str = "mesh"; /// Translate the native Buzz shared compute provider into the OpenAI-compatible /// transport understood by buzz-agent. These are derived runtime details, not @@ -17,11 +23,17 @@ pub fn apply_relay_mesh_env( if provider.map(str::trim) != Some(RELAY_MESH_PROVIDER_ID) { return; } - let model = model + // The stored model is either a named model or `auto`; `auto` (and a blank + // legacy value) becomes MeshLLM's virtual `mesh` model on the wire. + let stored = model .map(str::trim) .filter(|value| !value.is_empty()) - .unwrap_or(RELAY_MESH_AUTO_MODEL_ID) - .to_string(); + .unwrap_or(RELAY_MESH_AUTO_MODEL_ID); + let model = if stored == RELAY_MESH_AUTO_MODEL_ID { + RELAY_MESH_VIRTUAL_MODEL_ID.to_string() + } else { + stored.to_string() + }; env.insert("BUZZ_AGENT_PROVIDER".to_string(), "openai".to_string()); env.insert("BUZZ_AGENT_MODEL".to_string(), model.clone()); env.insert( @@ -34,14 +46,6 @@ pub fn apply_relay_mesh_env( RELAY_MESH_API_KEY_PLACEHOLDER.to_string(), ); env.insert("OPENAI_COMPAT_API".to_string(), "chat".to_string()); - // Buzz owns the meaning of relay-mesh `auto`: buzz-agent dynamically uses - // mesh-llm's virtual Mixture-of-Agents model whenever the live catalog says - // at least two distinct models are available, and otherwise keeps the - // router's normal single-model `auto` behavior. - env.insert( - RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV.to_string(), - "1".to_string(), - ); // Keep the requested response inside smaller local-model context windows. // These are defaults, not policy: the effective agent/persona/global env // may deliberately choose a smaller cap or a different effort. This function @@ -128,10 +132,60 @@ mod tests { // stops gemma tool-calling; enabling thinking makes Qwen3 burn ~4x the // output budget). assert_eq!(env.get("BUZZ_AGENT_THINKING_EFFORT"), None); + } + + /// Stored `auto` is translated here, so buzz-agent receives a plain model + /// name and needs no knowledge of the mesh. MeshLLM decides per request + /// whether `mesh` becomes a committee or a single served model. + #[test] + fn stored_auto_becomes_the_virtual_mesh_model_on_the_wire() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some(RELAY_MESH_AUTO_MODEL_ID), + ); + assert_eq!( - env.get(RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV) - .map(String::as_str), - Some("1") + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + assert_eq!( + env.get("OPENAI_COMPAT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + } + + /// A blank stored model is the legacy encoding of the same intent. + #[test] + fn blank_stored_model_becomes_the_virtual_mesh_model() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env(&mut env, Some(RELAY_MESH_PROVIDER_ID), Some(" ")); + + assert_eq!( + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + } + + /// A named model is sent verbatim: picking one is an explicit choice to + /// bypass mesh routing, and must not be rewritten. + #[test] + fn a_named_model_is_sent_verbatim() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M"), + ); + + assert_eq!( + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M") + ); + assert_eq!( + env.get("OPENAI_COMPAT_MODEL").map(String::as_str), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M") ); } From 276abb76514e576232988a7bd9311e034bcc49e1 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 10 Aug 2026 17:13:25 +1000 Subject: [PATCH 06/12] fix(mesh): name the mesh model the same way everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two env vars told the agent process which model to use, and only one of them was translated. `BUZZ_AGENT_MODEL` said `mesh`; `BUZZ_ACP_MODEL` was written straight from the stored value and still said `auto` (`runtime.rs`, before the mesh override runs). `auto` is not a model the mesh advertises, so buzz-acp rejected it as unavailable and fell back to its default with a warning on every new session — recoverable, but the two consumers were being told different things. `relay_mesh_wire_model` is now the one place a stored value becomes a wire name: `auto` (and a blank legacy value) becomes MeshLLM's virtual `mesh` model, anything else is a model the user named and passes through unchanged. Both the LLM transport env and the ACP harness go through it, so they cannot disagree. Also narrows a comment I had overstated. Sending `mesh` is not "always safe": `degrade_to_single_model` is a pre-flight capacity decision, so a committee that forms and then loses a worker still surfaces as a failed turn. MoA repairs partial results internally (`repair_tool_result_answer`, `fallback_arguments`) before it reaches that point, which is why no client-side reroll is being added back. The headless harness now exercises the shipped path. P2, P3, and P4 send `mesh` where they previously sent `auto` (MeshLLM's own router) or the physical model id (bypassing mesh routing entirely) — so no leg was covering what Buzz actually puts on the wire. With one served model there is no committee, which makes these a direct test of the degradation path. Verified on this machine at this commit, three models, 4/4 each: gemma-4-E4B-it-Q4_K_M, gemma-4-26B-A4B-it-UD-Q4_K_M, Qwen3-8B-Q4_K_M. P4 is the load-bearing one: a real ACP tool call through `mesh` into buzz-dev-mcp, asserted by reading `BUZZ_OK` back off disk rather than trusting the agent's own account of itself. P3 confirms the wire name in its error text (`llm: (mesh) exhausted retries: 503`). The harness comment claiming it supplies "exactly" what `apply_relay_mesh_env` does was wrong — it omits `BUZZ_AGENT_REQUIRE_REPLY`, which needs Buzz's publish tools to mean anything. It now says transport subset. Signed-off-by: Michael Neale --- crates/buzz-relay/examples/mesh_agent_e2e.rs | 22 +++++--- .../src/managed_agents/relay_mesh.rs | 54 +++++++++++++------ .../src-tauri/src/managed_agents/runtime.rs | 14 ++++- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/crates/buzz-relay/examples/mesh_agent_e2e.rs b/crates/buzz-relay/examples/mesh_agent_e2e.rs index 345ca4c746..db0fc6090b 100644 --- a/crates/buzz-relay/examples/mesh_agent_e2e.rs +++ b/crates/buzz-relay/examples/mesh_agent_e2e.rs @@ -112,10 +112,12 @@ async fn run() -> anyhow::Result<()> { Err(e) => record("P1 explicit-model chat", false, e.to_string()), } - // P2: auto — router picks the model. + // P2: the virtual `mesh` model — exactly what apply_relay_mesh_env now puts + // on the wire for shared compute. With one served model there is no + // committee, so this proves MeshLLM's degrade_to_single_model path. let r = agent_chat( &base, - "auto", + "mesh", None, "Reply with exactly one word: PONG", &[], @@ -123,11 +125,15 @@ async fn run() -> anyhow::Result<()> { .await; match r { Ok(text) => record( - "P2 auto-model chat", + "P2 virtual-mesh chat (degrades to single model)", text.to_uppercase().contains("PONG"), text, ), - Err(e) => record("P2 auto-model chat", false, e.to_string()), + Err(e) => record( + "P2 virtual-mesh chat (degrades to single model)", + false, + e.to_string(), + ), } // P3: regression — an output budget no served model's context can hold @@ -139,7 +145,7 @@ async fn run() -> anyhow::Result<()> { // GLM-4.7-Flash. let r = agent_chat( &base, - &served_id, + "mesh", Some("150000"), "Reply with exactly one word: PONG", &[], @@ -169,7 +175,7 @@ async fn run() -> anyhow::Result<()> { ); let mcp = vec![("dev".to_string(), repo_bin("buzz-dev-mcp")?)]; let (r, marker) = - agent_chat_with_marker(&base, &served_id, None, &prompt, &mcp, &marker_name).await; + agent_chat_with_marker(&base, "mesh", None, &prompt, &mcp, &marker_name).await; let file_ok = std::fs::read_to_string(&marker) .map(|c| c.contains("BUZZ_OK")) .unwrap_or(false); @@ -270,7 +276,9 @@ async fn agent_chat_in_isolated_home( .env_clear() .env("PATH", std::env::var("PATH").unwrap_or_default()) .env("HOME", &home) - // Exactly the environment apply_relay_mesh_env() supplies. + // The transport subset of apply_relay_mesh_env(): provider, base URL, + // model, key, and chat API. Not BUZZ_AGENT_REQUIRE_REPLY, which needs + // Buzz's publish tools to mean anything. .env("BUZZ_AGENT_PROVIDER", "openai") .env("BUZZ_AGENT_MODEL", model) .env("OPENAI_COMPAT_BASE_URL", base) diff --git a/desktop/src-tauri/src/managed_agents/relay_mesh.rs b/desktop/src-tauri/src/managed_agents/relay_mesh.rs index 2b0909a897..3858212bbb 100644 --- a/desktop/src-tauri/src/managed_agents/relay_mesh.rs +++ b/desktop/src-tauri/src/managed_agents/relay_mesh.rs @@ -4,13 +4,31 @@ pub const RELAY_MESH_PROVIDER_ID: &str = "relay-mesh"; /// Stored value for "let the mesh decide", kept as the user-facing word. pub const RELAY_MESH_AUTO_MODEL_ID: &str = "auto"; /// MeshLLM's virtual model. It resolves per request: a Mixture-of-Agents -/// committee when two or more workers are reachable, a single served model when -/// they are not (`moa_gateway::degrade_to_single_model`). Sending it is always -/// safe, so Buzz translates the stored `auto` into it here rather than teaching -/// buzz-agent anything about the mesh. +/// committee when two or more workers are reachable, and otherwise degrades to +/// a single served model rather than erroring +/// (`moa_gateway::degrade_to_single_model`). That degradation is a pre-flight +/// capacity decision, so a committee that forms and *then* loses a worker still +/// surfaces as a failed turn — MoA repairs partial results internally +/// (`repair_tool_result_answer`) before it gets that far. Buzz translates the +/// stored `auto` here rather than teaching buzz-agent anything about meshes. #[cfg(feature = "mesh-llm")] pub const RELAY_MESH_VIRTUAL_MODEL_ID: &str = "mesh"; +/// The wire name for a stored shared-compute model: `auto` (and a blank legacy +/// value) means "let the mesh decide" and becomes MeshLLM's virtual `mesh` +/// model; anything else is a model the user named and is passed through. +/// +/// The single place this mapping happens. Every consumer that has to name a +/// model to the mesh — the LLM transport env and the ACP harness — goes through +/// here, so they cannot disagree. +#[cfg(feature = "mesh-llm")] +pub fn relay_mesh_wire_model(stored: &str) -> &str { + match stored.trim() { + "" | RELAY_MESH_AUTO_MODEL_ID => RELAY_MESH_VIRTUAL_MODEL_ID, + named => named, + } +} + /// Translate the native Buzz shared compute provider into the OpenAI-compatible /// transport understood by buzz-agent. These are derived runtime details, not /// user-owned agent configuration. @@ -23,17 +41,7 @@ pub fn apply_relay_mesh_env( if provider.map(str::trim) != Some(RELAY_MESH_PROVIDER_ID) { return; } - // The stored model is either a named model or `auto`; `auto` (and a blank - // legacy value) becomes MeshLLM's virtual `mesh` model on the wire. - let stored = model - .map(str::trim) - .filter(|value| !value.is_empty()) - .unwrap_or(RELAY_MESH_AUTO_MODEL_ID); - let model = if stored == RELAY_MESH_AUTO_MODEL_ID { - RELAY_MESH_VIRTUAL_MODEL_ID.to_string() - } else { - stored.to_string() - }; + let model = relay_mesh_wire_model(model.unwrap_or(RELAY_MESH_AUTO_MODEL_ID)).to_string(); env.insert("BUZZ_AGENT_PROVIDER".to_string(), "openai".to_string()); env.insert("BUZZ_AGENT_MODEL".to_string(), model.clone()); env.insert( @@ -168,6 +176,22 @@ mod tests { ); } + /// Every consumer that names a model to the mesh goes through one helper, + /// so the LLM transport and the ACP harness cannot be told different things. + #[test] + fn wire_model_maps_auto_and_blank_but_passes_named_through() { + assert_eq!( + relay_mesh_wire_model(RELAY_MESH_AUTO_MODEL_ID), + RELAY_MESH_VIRTUAL_MODEL_ID + ); + assert_eq!(relay_mesh_wire_model(""), RELAY_MESH_VIRTUAL_MODEL_ID); + assert_eq!(relay_mesh_wire_model(" "), RELAY_MESH_VIRTUAL_MODEL_ID); + assert_eq!( + relay_mesh_wire_model("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M"), + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + ); + } + /// A named model is sent verbatim: picking one is an explicit choice to /// bypass mesh routing, and must not be rewritten. #[test] diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index ec804869c4..d95c595417 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -713,7 +713,19 @@ pub fn spawn_agent_child( } else { command.env_remove("BUZZ_ACP_SYSTEM_PROMPT"); } - if let Some(model) = effective_model.as_deref() { + // Shared compute stores `auto`, but the wire name is MeshLLM's virtual + // `mesh` model. Translate here too, so the harness and the LLM client are + // told the same thing: `BUZZ_ACP_MODEL=auto` would name a model the mesh + // never advertises, leaving buzz-acp to warn and fall back on every new + // session while `BUZZ_AGENT_MODEL` said `mesh`. + #[cfg(feature = "mesh-llm")] + let acp_model = match (&mesh_model_id, effective_model.as_deref()) { + (Some(mesh_model_id), _) => Some(super::relay_mesh_wire_model(mesh_model_id).to_string()), + (None, model) => model.map(str::to_owned), + }; + #[cfg(not(feature = "mesh-llm"))] + let acp_model = effective_model.as_deref().map(str::to_owned); + if let Some(model) = acp_model.as_deref() { command.env("BUZZ_ACP_MODEL", model); } else { command.env_remove("BUZZ_ACP_MODEL"); From 812d01b5fa5225fa04d90778d447c0e2217e7a36 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 10 Aug 2026 17:43:24 +1000 Subject: [PATCH 07/12] fix(agent): drop the removed post() argument from merged-in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit origin/main added `post()` call sites carrying `detect_mesh_fallback`, which this branch removed along with `PostError::MeshFallback`. Neither side touched the same lines, so the merge applied cleanly and did not compile — the failure only appears in `--all-targets`, which is why per-crate `cargo check` missed it and CI (testing the merge result) did not. Signed-off-by: Michael Neale --- crates/buzz-agent/src/llm.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/buzz-agent/src/llm.rs b/crates/buzz-agent/src/llm.rs index c6aef3982c..762d70eed9 100644 --- a/crates/buzz-agent/src/llm.rs +++ b/crates/buzz-agent/src/llm.rs @@ -4227,7 +4227,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4291,7 +4290,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) From 00a4ddf5920fd928ef35078b4d99f3b21bbbf6f7 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 10 Aug 2026 18:03:15 +1000 Subject: [PATCH 08/12] fix(mesh): probe the model the agent will actually request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `wait_for_mesh_inference` posted the caller's stored model id. For shared-compute `auto` that is not a name the mesh advertises, so readiness validated a route no agent uses — and could fail startup while the real route worked. It now translates through `relay_mesh_wire_model`, the same mapping the spawn env and the ACP harness use. Named models pass through unchanged, so the serve-side callers that already pass a real model are unaffected. Translating inside the probe rather than at its seven call sites keeps the mapping in one place, which was the point of the previous commit. Also corrects the e2e runbook: it still told readers to clear stale pre-0.75 runtime cache entries by hand, which is exactly what pinning v0.75.1 removed the need for. Signed-off-by: Michael Neale --- crates/buzz-test-client/tests/e2e_mesh_llm.rs | 4 ++-- desktop/src-tauri/src/commands/mesh_llm.rs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/buzz-test-client/tests/e2e_mesh_llm.rs b/crates/buzz-test-client/tests/e2e_mesh_llm.rs index 9cd5175e21..21a8ec29d7 100644 --- a/crates/buzz-test-client/tests/e2e_mesh_llm.rs +++ b/crates/buzz-test-client/tests/e2e_mesh_llm.rs @@ -12,8 +12,8 @@ //! //! ```text //! # 1. the mesh-enabled desktop installs the signed native runtime itself on -//! # first init; on a machine with a pre-0.75 runtime cache, clear stale -//! # entries under ~/.cache/mesh-llm/native-runtimes first +//! # first init; stale pre-0.75 cache entries are skipped rather than +//! # fatal (MeshLLM >= 0.75.1), so no manual cleanup is needed //! # 2. start the normal membership-gated relay and a mesh-enabled desktop //! # 3. have that desktop publish status, then run the trust assertions: //! RELAY_URL=ws://localhost:3000 \ diff --git a/desktop/src-tauri/src/commands/mesh_llm.rs b/desktop/src-tauri/src/commands/mesh_llm.rs index 528ca38767..b3bfcbe3a8 100644 --- a/desktop/src-tauri/src/commands/mesh_llm.rs +++ b/desktop/src-tauri/src/commands/mesh_llm.rs @@ -595,6 +595,12 @@ fn mesh_readiness_failure_message( /// `HTTP 429`, so the UI can tell "still warming up" apart from "can't reach /// the host". async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { + // Probe the name that will actually be requested. Callers pass a stored + // value, which for shared-compute `auto` is not a model the mesh + // advertises: probing it would validate a route no agent uses, and could + // fail readiness while the real route works. Named models pass through + // unchanged, so this is safe for the serve-side callers too. + let model_id = crate::managed_agents::relay_mesh_wire_model(model_id); let client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(30)) .build() From 8e3b15019d09a17de0cd21144ac62b366f7ac5d7 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 10 Aug 2026 20:17:23 +1000 Subject: [PATCH 09/12] chore(security): drop the nostr-relay-pool exception its own condition retired #5404 allowed RUSTSEC-2026-0243 with the removal condition written into the comment: "Remove after mesh-llm migrates to nostr-sdk >= 0.45, which absorbed the standalone relay pool." MeshLLM v0.75.1 does exactly that, so nostr-relay-pool is no longer in either lockfile and the advisory has nothing to fire on. That PR measured 13 MeshLLM API compilation errors from bumping nostr-sdk to 0.45 against v0.74; upstream did the migration in v0.75, which is why this needs no source change here. Verified with the same commands #5404 used: bin/cargo-deny --locked check --config deny.toml advisories, and bin/cargo-deny --locked check -- advisories ok, bans ok, licenses ok, sources ok. Leaving a satisfied exception in place would suppress a real future advisory for this crate, so it goes with the bump that retired it rather than waiting for someone to notice. Signed-off-by: Michael Neale --- deny.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/deny.toml b/deny.toml index c432a20ea4..d3c5fcd4bc 100644 --- a/deny.toml +++ b/deny.toml @@ -15,10 +15,6 @@ ignore = [ # remove these when upstream catches up. { id = "RUSTSEC-2026-0194", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" }, { id = "RUSTSEC-2026-0195", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" }, - # nostr-relay-pool 0.44.3 — informational/unmaintained, not a vulnerability. - # Transitive via mesh-llm 0.74 → nostr-sdk 0.44.1. Remove after mesh-llm - # migrates to nostr-sdk >= 0.45, which absorbed the standalone relay pool. - { id = "RUSTSEC-2026-0243", reason = "transitive via mesh-llm; upstream nostr-sdk 0.45 migration requires API changes" }, ] [licenses] From c3bcdea78ae4b372901b7356ca3489ab69b3d2df Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Tue, 11 Aug 2026 09:14:00 +1000 Subject: [PATCH 10/12] fix(mesh): keep readiness diagnosis honest on a single-worker mesh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Translating the probe to the wire name left the catalog-visibility check below it comparing against `AUTO_MODEL_ID`. Post-translation that value can only be `mesh` or a named model, so the delegated-choice branch was unreachable. MeshLLM only advertises the virtual `mesh` model once two non-virtual models are reachable (`should_advertise_virtual_mesh`), so on a single-worker mesh the name is never listed and `model_ever_visible` stayed false for the whole wait. A readiness timeout — a lone host still loading weights, say — was then reported as `CatalogNeverSynced`, "a network path problem between this machine and the host", when `RoutingNeverCompleted`, "try again shortly", is the truth. Confirmed against a live node here: with one worker, `/v1/models` lists only the served model. Only the wrong-diagnosis-on-timeout is affected; the happy path always worked, because the chat probe sends `mesh` and the server degrades it. The comparison now keys on the translated name, preserving the original "delegated choice — any advertised model counts" semantics. The predicate is extracted as `mesh_catalog_shows_model` so that rule is testable rather than buried in the poll loop, and the failure copy now names the model the caller asked for, so someone who picked "Auto" is not shown "mesh". Four tests cover it. Verified they fail with the old comparison restored, so they pin the behaviour rather than merely passing alongside it. Reported by @wpfleger96 in review; the diagnosis and the suggested fix are theirs. Signed-off-by: Michael Neale --- desktop/src-tauri/src/commands/mesh_llm.rs | 49 ++++++++++++++----- .../src-tauri/src/commands/mesh_llm_tests.rs | 40 +++++++++++++++ 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/desktop/src-tauri/src/commands/mesh_llm.rs b/desktop/src-tauri/src/commands/mesh_llm.rs index b3bfcbe3a8..e601f71401 100644 --- a/desktop/src-tauri/src/commands/mesh_llm.rs +++ b/desktop/src-tauri/src/commands/mesh_llm.rs @@ -558,6 +558,28 @@ enum MeshReadinessFailure { /// Pure classifier: given whether the served model was ever observed in the /// local `/v1/models` catalog during the wait, decide which stage failed. /// Split out so the diagnosis is unit-testable without a live mesh. +/// Whether the catalog has synced enough to count, for the model actually being +/// requested (a wire name — see `relay_mesh_wire_model`). +/// +/// The virtual `mesh` model delegates the choice to the router, so any +/// advertised model proves the catalog synced. It has to work that way: MeshLLM +/// only advertises `mesh` itself once two non-virtual models are reachable +/// (`should_advertise_virtual_mesh`), so requiring it by name would leave a +/// single-worker mesh looking permanently unsynced and misreport a slow model +/// load as a network path problem. +fn mesh_catalog_shows_model(advertised: &[String], wire_model: &str) -> bool { + if advertised.is_empty() { + return false; + } + if wire_model == crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID { + return true; + } + let wanted = wire_model.trim().replace("@main", ""); + advertised + .iter() + .any(|id| id.replace("@main", "") == wanted) +} + fn classify_mesh_readiness_failure(model_ever_visible: bool) -> MeshReadinessFailure { if model_ever_visible { MeshReadinessFailure::RoutingNeverCompleted @@ -600,6 +622,7 @@ async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { // advertises: probing it would validate a route no agent uses, and could // fail readiness while the real route works. Named models pass through // unchanged, so this is safe for the serve-side callers too. + let requested_model = model_id; let model_id = crate::managed_agents::relay_mesh_wire_model(model_id); let client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(30)) @@ -617,8 +640,12 @@ async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { let mut model_ever_visible = false; while tokio::time::Instant::now() < deadline { - // Refresh catalog visibility. "auto" delegates model choice to the - // router, so any advertised model counts as the catalog having synced. + // Refresh catalog visibility. The virtual `mesh` model delegates the + // choice to the router, so any advertised model counts as the catalog + // having synced — and it must, because MeshLLM only advertises `mesh` + // itself once two non-virtual models are reachable + // (`should_advertise_virtual_mesh`). Requiring it by name would leave a + // single-worker mesh looking permanently unsynced. if let Ok(response) = client .get(&models_url) .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) @@ -627,16 +654,12 @@ async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { { if let Ok(body) = response.json::().await { if let Some(data) = body.get("data").and_then(|d| d.as_array()) { - let wanted = model_id.trim().replace("@main", ""); - let visible = !data.is_empty() - && (model_id == crate::mesh_llm::AUTO_MODEL_ID - || data.iter().any(|m| { - m.get("id") - .and_then(|id| id.as_str()) - .map(|id| id.replace("@main", "") == wanted) - .unwrap_or(false) - })); - model_ever_visible |= visible; + let advertised: Vec = data + .iter() + .filter_map(|m| m.get("id").and_then(|id| id.as_str())) + .map(str::to_owned) + .collect(); + model_ever_visible |= mesh_catalog_shows_model(&advertised, model_id); } } } @@ -667,7 +690,7 @@ async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { let failure = classify_mesh_readiness_failure(model_ever_visible); Err(mesh_readiness_failure_message( failure, - model_id, + requested_model, &last_error, )) } diff --git a/desktop/src-tauri/src/commands/mesh_llm_tests.rs b/desktop/src-tauri/src/commands/mesh_llm_tests.rs index 26eb1f5fba..ced6a56ff2 100644 --- a/desktop/src-tauri/src/commands/mesh_llm_tests.rs +++ b/desktop/src-tauri/src/commands/mesh_llm_tests.rs @@ -178,6 +178,46 @@ fn role_switch_checkpoint_starts_exactly_once_after_restart() { assert_eq!(consumed.relay_url, config.relay_url); } +/// The regression this guards: MeshLLM only advertises the virtual `mesh` model +/// once two non-virtual models are reachable, so a single-worker mesh never +/// lists it by name. Keying visibility on the literal name would report a lone +/// host that is still loading weights as a network path problem. +#[test] +fn virtual_mesh_counts_any_advertised_model_as_a_synced_catalog() { + let one_worker = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; + assert!(mesh_catalog_shows_model( + &one_worker, + crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID + )); +} + +#[test] +fn an_empty_catalog_is_never_synced_even_for_the_virtual_model() { + assert!(!mesh_catalog_shows_model( + &[], + crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID + )); +} + +#[test] +fn a_named_model_must_actually_be_advertised() { + let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; + assert!(mesh_catalog_shows_model( + &advertised, + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + )); + assert!(!mesh_catalog_shows_model(&advertised, "some/other-model")); +} + +#[test] +fn a_named_model_ignores_the_main_revision_suffix() { + let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M@main".to_string()]; + assert!(mesh_catalog_shows_model( + &advertised, + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + )); +} + #[test] fn readiness_failure_is_catalog_sync_when_model_never_visible() { assert_eq!( From d3cdb0efb0bebf4f646425ad065bb4c6f04b2c7b Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Tue, 11 Aug 2026 09:29:23 +1000 Subject: [PATCH 11/12] refactor(mesh): give startup readiness its own module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The review fix pushed `commands/mesh_llm.rs` from 994 to 1023 lines, over the 1000-line ceiling the desktop ratchet enforces — a file already over the limit may not grow, and this one was three lines under it. Readiness is a separable responsibility: probe the chat path agents actually use, and classify a timeout into copy that names the stage. It moves whole, with its tests, into `commands/mesh_readiness.rs`. No behaviour change. mesh_llm.rs 1023 -> 858 mesh_readiness.rs 251 (with its 7 tests) The tests move with the code they cover rather than staying behind in `mesh_llm_tests.rs`, so the module owns its own contract. Signed-off-by: Michael Neale --- desktop/src-tauri/src/commands/mesh_llm.rs | 166 +----------- .../src-tauri/src/commands/mesh_llm_tests.rs | 76 ------ .../src-tauri/src/commands/mesh_readiness.rs | 251 ++++++++++++++++++ desktop/src-tauri/src/commands/mod.rs | 2 + 4 files changed, 254 insertions(+), 241 deletions(-) create mode 100644 desktop/src-tauri/src/commands/mesh_readiness.rs diff --git a/desktop/src-tauri/src/commands/mesh_llm.rs b/desktop/src-tauri/src/commands/mesh_llm.rs index e601f71401..7356cd7fc0 100644 --- a/desktop/src-tauri/src/commands/mesh_llm.rs +++ b/desktop/src-tauri/src/commands/mesh_llm.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use sha2::{Digest, Sha256}; use tauri::{AppHandle, Manager, State}; +use super::mesh_readiness::wait_for_mesh_inference; use crate::{app_state::AppState, mesh_llm, relay}; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -530,171 +531,6 @@ pub async fn mesh_start_node( Ok(status) } -/// Mesh can bind its HTTP ingress and advertise a model shortly before the -/// router has installed a usable target. Probe the exact chat path agents use -/// so startup cannot race that gap (`single target None unavailable`). -/// Which startup stage a mesh client is stuck at when it never becomes -/// inference-ready. The two live-observed failure modes are physically -/// distinct and want different user copy: -/// -/// * `CatalogNeverSynced` — the local client node came up and connected to -/// the host at the control level (ping/RTT fine), but the served model -/// never appeared in the local `/v1/models` catalog. That catalog is -/// populated by the peer gossip exchange; when the gossip bi-stream can't -/// establish across the network (observed as iroh -/// `MultipathNotNegotiated` / unreachable direct path), the catalog stays -/// empty forever and every request is rejected "model not available". -/// Root cause is the network path between this machine and the host. -/// * `RoutingNeverCompleted` — the model *did* sync into the catalog, but -/// inference requests never completed (routing/transport to the host -/// failing per-request). The host is discoverable and advertised but not -/// actually serving us. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum MeshReadinessFailure { - CatalogNeverSynced, - RoutingNeverCompleted, -} - -/// Pure classifier: given whether the served model was ever observed in the -/// local `/v1/models` catalog during the wait, decide which stage failed. -/// Split out so the diagnosis is unit-testable without a live mesh. -/// Whether the catalog has synced enough to count, for the model actually being -/// requested (a wire name — see `relay_mesh_wire_model`). -/// -/// The virtual `mesh` model delegates the choice to the router, so any -/// advertised model proves the catalog synced. It has to work that way: MeshLLM -/// only advertises `mesh` itself once two non-virtual models are reachable -/// (`should_advertise_virtual_mesh`), so requiring it by name would leave a -/// single-worker mesh looking permanently unsynced and misreport a slow model -/// load as a network path problem. -fn mesh_catalog_shows_model(advertised: &[String], wire_model: &str) -> bool { - if advertised.is_empty() { - return false; - } - if wire_model == crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID { - return true; - } - let wanted = wire_model.trim().replace("@main", ""); - advertised - .iter() - .any(|id| id.replace("@main", "") == wanted) -} - -fn classify_mesh_readiness_failure(model_ever_visible: bool) -> MeshReadinessFailure { - if model_ever_visible { - MeshReadinessFailure::RoutingNeverCompleted - } else { - MeshReadinessFailure::CatalogNeverSynced - } -} - -/// Actionable, non-technical copy for a readiness failure. `last_detail` is the -/// last raw transport/HTTP error, appended for support triage. -fn mesh_readiness_failure_message( - failure: MeshReadinessFailure, - model_id: &str, - last_detail: &str, -) -> String { - match failure { - MeshReadinessFailure::CatalogNeverSynced => format!( - "Buzz shared compute connected to the serving member but could not sync \ - the model list for \"{model_id}\" — this is a network path problem \ - between this machine and the host (the compute node is reachable for \ - pings but the model-sync stream did not establish). Try again, or have \ - the host and this machine on a more direct network. (last: {last_detail})" - ), - MeshReadinessFailure::RoutingNeverCompleted => format!( - "Buzz shared compute found \"{model_id}\" on a serving member but inference \ - requests did not complete — the host is discoverable but not currently \ - reachable for requests. Try again shortly. (last: {last_detail})" - ), - } -} - -/// Poll the local mesh OpenAI ingress until a real inference for `model_id` -/// succeeds, or a deadline elapses. On failure, returns a stage-specific, -/// actionable message (see [`MeshReadinessFailure`]) rather than a raw -/// `HTTP 429`, so the UI can tell "still warming up" apart from "can't reach -/// the host". -async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { - // Probe the name that will actually be requested. Callers pass a stored - // value, which for shared-compute `auto` is not a model the mesh - // advertises: probing it would validate a route no agent uses, and could - // fail readiness while the real route works. Named models pass through - // unchanged, so this is safe for the serve-side callers too. - let requested_model = model_id; - let model_id = crate::managed_agents::relay_mesh_wire_model(model_id); - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(30)) - .build() - .map_err(|error| format!("failed to build mesh readiness client: {error}"))?; - let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(120); - let models_url = format!("{}/models", crate::managed_agents::RELAY_MESH_API_BASE_URL); - let chat_url = format!( - "{}/chat/completions", - crate::managed_agents::RELAY_MESH_API_BASE_URL - ); - let mut last_error = "mesh inference is not ready".to_string(); - // Track whether the served model ever reached the local catalog — the - // signal that splits "catalog never synced" from "routing never completed". - let mut model_ever_visible = false; - - while tokio::time::Instant::now() < deadline { - // Refresh catalog visibility. The virtual `mesh` model delegates the - // choice to the router, so any advertised model counts as the catalog - // having synced — and it must, because MeshLLM only advertises `mesh` - // itself once two non-virtual models are reachable - // (`should_advertise_virtual_mesh`). Requiring it by name would leave a - // single-worker mesh looking permanently unsynced. - if let Ok(response) = client - .get(&models_url) - .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) - .send() - .await - { - if let Ok(body) = response.json::().await { - if let Some(data) = body.get("data").and_then(|d| d.as_array()) { - let advertised: Vec = data - .iter() - .filter_map(|m| m.get("id").and_then(|id| id.as_str())) - .map(str::to_owned) - .collect(); - model_ever_visible |= mesh_catalog_shows_model(&advertised, model_id); - } - } - } - - match client - .post(&chat_url) - .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) - .json(&serde_json::json!({ - "model": model_id, - "messages": [{"role": "user", "content": "Reply OK"}], - "max_tokens": 1, - "stream": false - })) - .send() - .await - { - Ok(response) if response.status().is_success() => return Ok(()), - Ok(response) => { - let status = response.status(); - let body = response.text().await.unwrap_or_default(); - last_error = format!("HTTP {status}: {body}"); - } - Err(error) => last_error = error.to_string(), - } - tokio::time::sleep(std::time::Duration::from_secs(2)).await; - } - - let failure = classify_mesh_readiness_failure(model_ever_visible); - Err(mesh_readiness_failure_message( - failure, - requested_model, - &last_error, - )) -} - pub(crate) async fn ensure_client_node_for_model( state: &AppState, model_id: impl AsRef, diff --git a/desktop/src-tauri/src/commands/mesh_llm_tests.rs b/desktop/src-tauri/src/commands/mesh_llm_tests.rs index ced6a56ff2..c4e1ae2e42 100644 --- a/desktop/src-tauri/src/commands/mesh_llm_tests.rs +++ b/desktop/src-tauri/src/commands/mesh_llm_tests.rs @@ -178,82 +178,6 @@ fn role_switch_checkpoint_starts_exactly_once_after_restart() { assert_eq!(consumed.relay_url, config.relay_url); } -/// The regression this guards: MeshLLM only advertises the virtual `mesh` model -/// once two non-virtual models are reachable, so a single-worker mesh never -/// lists it by name. Keying visibility on the literal name would report a lone -/// host that is still loading weights as a network path problem. -#[test] -fn virtual_mesh_counts_any_advertised_model_as_a_synced_catalog() { - let one_worker = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; - assert!(mesh_catalog_shows_model( - &one_worker, - crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID - )); -} - -#[test] -fn an_empty_catalog_is_never_synced_even_for_the_virtual_model() { - assert!(!mesh_catalog_shows_model( - &[], - crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID - )); -} - -#[test] -fn a_named_model_must_actually_be_advertised() { - let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; - assert!(mesh_catalog_shows_model( - &advertised, - "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" - )); - assert!(!mesh_catalog_shows_model(&advertised, "some/other-model")); -} - -#[test] -fn a_named_model_ignores_the_main_revision_suffix() { - let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M@main".to_string()]; - assert!(mesh_catalog_shows_model( - &advertised, - "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" - )); -} - -#[test] -fn readiness_failure_is_catalog_sync_when_model_never_visible() { - assert_eq!( - classify_mesh_readiness_failure(false), - MeshReadinessFailure::CatalogNeverSynced - ); -} - -#[test] -fn readiness_failure_is_routing_when_model_was_visible() { - assert_eq!( - classify_mesh_readiness_failure(true), - MeshReadinessFailure::RoutingNeverCompleted - ); -} - -#[test] -fn readiness_messages_are_distinct_and_actionable() { - let catalog = mesh_readiness_failure_message( - MeshReadinessFailure::CatalogNeverSynced, - "auto", - "HTTP 429", - ); - let routing = mesh_readiness_failure_message( - MeshReadinessFailure::RoutingNeverCompleted, - "auto", - "HTTP 503", - ); - // Distinct diagnoses, each names the model and carries the raw detail. - assert_ne!(catalog, routing); - assert!(catalog.contains("network path")); - assert!(catalog.contains("HTTP 429")); - assert!(routing.contains("did not complete")); - assert!(routing.contains("HTTP 503")); -} - #[test] fn mesh_status_cursor_uses_relay_composite_tiebreak() { let event = nostr::EventBuilder::new(nostr::Kind::TextNote, "status") diff --git a/desktop/src-tauri/src/commands/mesh_readiness.rs b/desktop/src-tauri/src/commands/mesh_readiness.rs new file mode 100644 index 0000000000..023695a6f9 --- /dev/null +++ b/desktop/src-tauri/src/commands/mesh_readiness.rs @@ -0,0 +1,251 @@ +//! Startup readiness for Buzz shared compute. +//! +//! Mesh can bind its HTTP ingress and advertise a model shortly before the +//! router has installed a usable target. These helpers probe the exact chat +//! path agents use, so startup cannot race that gap +//! (`single target None unavailable`), and classify a timeout into copy that +//! names the actual stage rather than a raw `HTTP 429`. + +use super::CmdResult; + +/// Which startup stage a mesh client is stuck at when it never becomes +/// inference-ready. The two live-observed failure modes are physically +/// distinct and want different user copy: +/// +/// * `CatalogNeverSynced` — the local client node came up and connected to +/// the host at the control level (ping/RTT fine), but the served model +/// never appeared in the local `/v1/models` catalog. That catalog is +/// populated by the peer gossip exchange; when the gossip bi-stream can't +/// establish across the network (observed as iroh +/// `MultipathNotNegotiated` / unreachable direct path), the catalog stays +/// empty forever and every request is rejected "model not available". +/// Root cause is the network path between this machine and the host. +/// * `RoutingNeverCompleted` — the model *did* sync into the catalog, but +/// inference requests never completed (routing/transport to the host +/// failing per-request). The host is discoverable and advertised but not +/// actually serving us. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum MeshReadinessFailure { + CatalogNeverSynced, + RoutingNeverCompleted, +} + +/// Pure classifier: given whether the served model was ever observed in the +/// local `/v1/models` catalog during the wait, decide which stage failed. +/// Split out so the diagnosis is unit-testable without a live mesh. +/// Whether the catalog has synced enough to count, for the model actually being +/// requested (a wire name — see `relay_mesh_wire_model`). +/// +/// The virtual `mesh` model delegates the choice to the router, so any +/// advertised model proves the catalog synced. It has to work that way: MeshLLM +/// only advertises `mesh` itself once two non-virtual models are reachable +/// (`should_advertise_virtual_mesh`), so requiring it by name would leave a +/// single-worker mesh looking permanently unsynced and misreport a slow model +/// load as a network path problem. +fn mesh_catalog_shows_model(advertised: &[String], wire_model: &str) -> bool { + if advertised.is_empty() { + return false; + } + if wire_model == crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID { + return true; + } + let wanted = wire_model.trim().replace("@main", ""); + advertised + .iter() + .any(|id| id.replace("@main", "") == wanted) +} + +fn classify_mesh_readiness_failure(model_ever_visible: bool) -> MeshReadinessFailure { + if model_ever_visible { + MeshReadinessFailure::RoutingNeverCompleted + } else { + MeshReadinessFailure::CatalogNeverSynced + } +} + +/// Actionable, non-technical copy for a readiness failure. `last_detail` is the +/// last raw transport/HTTP error, appended for support triage. +fn mesh_readiness_failure_message( + failure: MeshReadinessFailure, + model_id: &str, + last_detail: &str, +) -> String { + match failure { + MeshReadinessFailure::CatalogNeverSynced => format!( + "Buzz shared compute connected to the serving member but could not sync \ + the model list for \"{model_id}\" — this is a network path problem \ + between this machine and the host (the compute node is reachable for \ + pings but the model-sync stream did not establish). Try again, or have \ + the host and this machine on a more direct network. (last: {last_detail})" + ), + MeshReadinessFailure::RoutingNeverCompleted => format!( + "Buzz shared compute found \"{model_id}\" on a serving member but inference \ + requests did not complete — the host is discoverable but not currently \ + reachable for requests. Try again shortly. (last: {last_detail})" + ), + } +} + +/// Poll the local mesh OpenAI ingress until a real inference for `model_id` +/// succeeds, or a deadline elapses. On failure, returns a stage-specific, +/// actionable message (see [`MeshReadinessFailure`]) rather than a raw +/// `HTTP 429`, so the UI can tell "still warming up" apart from "can't reach +/// the host". +pub(crate) async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { + // Probe the name that will actually be requested. Callers pass a stored + // value, which for shared-compute `auto` is not a model the mesh + // advertises: probing it would validate a route no agent uses, and could + // fail readiness while the real route works. Named models pass through + // unchanged, so this is safe for the serve-side callers too. + let requested_model = model_id; + let model_id = crate::managed_agents::relay_mesh_wire_model(model_id); + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .build() + .map_err(|error| format!("failed to build mesh readiness client: {error}"))?; + let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(120); + let models_url = format!("{}/models", crate::managed_agents::RELAY_MESH_API_BASE_URL); + let chat_url = format!( + "{}/chat/completions", + crate::managed_agents::RELAY_MESH_API_BASE_URL + ); + let mut last_error = "mesh inference is not ready".to_string(); + // Track whether the served model ever reached the local catalog — the + // signal that splits "catalog never synced" from "routing never completed". + let mut model_ever_visible = false; + + while tokio::time::Instant::now() < deadline { + // Refresh catalog visibility. The virtual `mesh` model delegates the + // choice to the router, so any advertised model counts as the catalog + // having synced — and it must, because MeshLLM only advertises `mesh` + // itself once two non-virtual models are reachable + // (`should_advertise_virtual_mesh`). Requiring it by name would leave a + // single-worker mesh looking permanently unsynced. + if let Ok(response) = client + .get(&models_url) + .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) + .send() + .await + { + if let Ok(body) = response.json::().await { + if let Some(data) = body.get("data").and_then(|d| d.as_array()) { + let advertised: Vec = data + .iter() + .filter_map(|m| m.get("id").and_then(|id| id.as_str())) + .map(str::to_owned) + .collect(); + model_ever_visible |= mesh_catalog_shows_model(&advertised, model_id); + } + } + } + + match client + .post(&chat_url) + .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) + .json(&serde_json::json!({ + "model": model_id, + "messages": [{"role": "user", "content": "Reply OK"}], + "max_tokens": 1, + "stream": false + })) + .send() + .await + { + Ok(response) if response.status().is_success() => return Ok(()), + Ok(response) => { + let status = response.status(); + let body = response.text().await.unwrap_or_default(); + last_error = format!("HTTP {status}: {body}"); + } + Err(error) => last_error = error.to_string(), + } + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + } + + let failure = classify_mesh_readiness_failure(model_ever_visible); + Err(mesh_readiness_failure_message( + failure, + requested_model, + &last_error, + )) +} +#[cfg(test)] +mod tests { + use super::*; + + /// The regression this guards: MeshLLM only advertises the virtual `mesh` model + /// once two non-virtual models are reachable, so a single-worker mesh never + /// lists it by name. Keying visibility on the literal name would report a lone + /// host that is still loading weights as a network path problem. + #[test] + fn virtual_mesh_counts_any_advertised_model_as_a_synced_catalog() { + let one_worker = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; + assert!(mesh_catalog_shows_model( + &one_worker, + crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID + )); + } + + #[test] + fn an_empty_catalog_is_never_synced_even_for_the_virtual_model() { + assert!(!mesh_catalog_shows_model( + &[], + crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID + )); + } + + #[test] + fn a_named_model_must_actually_be_advertised() { + let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; + assert!(mesh_catalog_shows_model( + &advertised, + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + )); + assert!(!mesh_catalog_shows_model(&advertised, "some/other-model")); + } + + #[test] + fn a_named_model_ignores_the_main_revision_suffix() { + let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M@main".to_string()]; + assert!(mesh_catalog_shows_model( + &advertised, + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + )); + } + + #[test] + fn readiness_failure_is_catalog_sync_when_model_never_visible() { + assert_eq!( + classify_mesh_readiness_failure(false), + MeshReadinessFailure::CatalogNeverSynced + ); + } + + #[test] + fn readiness_failure_is_routing_when_model_was_visible() { + assert_eq!( + classify_mesh_readiness_failure(true), + MeshReadinessFailure::RoutingNeverCompleted + ); + } + + #[test] + fn readiness_messages_are_distinct_and_actionable() { + let catalog = mesh_readiness_failure_message( + MeshReadinessFailure::CatalogNeverSynced, + "auto", + "HTTP 429", + ); + let routing = mesh_readiness_failure_message( + MeshReadinessFailure::RoutingNeverCompleted, + "auto", + "HTTP 503", + ); + // Distinct diagnoses, each names the model and carries the raw detail. + assert_ne!(catalog, routing); + assert!(catalog.contains("network path")); + assert!(catalog.contains("HTTP 429")); + assert!(routing.contains("did not complete")); + assert!(routing.contains("HTTP 503")); + } +} diff --git a/desktop/src-tauri/src/commands/mod.rs b/desktop/src-tauri/src/commands/mod.rs index 322834630a..1ab3bb70d7 100644 --- a/desktop/src-tauri/src/commands/mod.rs +++ b/desktop/src-tauri/src/commands/mod.rs @@ -35,6 +35,8 @@ mod media_transcode; mod media_upload_progress; #[cfg(feature = "mesh-llm")] pub(crate) mod mesh_llm; +#[cfg(feature = "mesh-llm")] +pub(crate) mod mesh_readiness; mod messages; mod notifications; mod observer_archive; From fbba6de04910524ff1b5a872ec18fc22bccfb0c4 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Tue, 11 Aug 2026 12:04:52 +1000 Subject: [PATCH 12/12] fix(mesh): let the hardware smokes install the runtime they need MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `just mesh-e2e-hardware` and `just mesh-e2e-admission` failed on a clean checkout, each telling the reader to run the recipe that was already running: MeshLLM native runtime for MeshLLM 0.75.1 is not installed; run `just mesh-e2e-hardware` to prepare it That instruction was true while the recipes pre-seeded the cache with the deleted `ensure-mesh-native-runtime.sh`. Without it the guard is circular, and `mesh-e2e-confidence` never reaches its third example. The guard was also unnecessary: in both files the very next statement is `initialize_host_runtime()`, which installs the signed release runtime when the cache has none. It refused immediately before the code that would have fixed the condition it was complaining about. `mesh_agent_e2e` never had the guard, which is why it kept working. Downloading the signed artifact is also the behaviour we want here. The old script built llama.cpp from source into the cache, so a cold run compiled a toolchain to run a 100MB smoke test. Verified on a genuinely cold cache — every entry moved aside first, so the only runtime present afterwards was the one this installed: just mesh-e2e-hardware cache: (empty) -> 0.75.1, downloaded not built [smoke] completion status=200 OK content="PONG" [smoke] PASS: serve→client→inference proven over mesh just mesh-e2e-admission [admission] PASS 1/3: trusted client admitted, sees routed model [admission] PASS 2/3: trusted client inference routed over mesh: "PONG" [admission] PASS 3/3: non-member saw gossip but inference was rejected: 503 [admission] PASS: owner allowlist gates mesh membership and inference Reported by @wesbillman in review. CI never runs these recipes and a warm developer cache hides the failure, so neither CI nor local iteration would have caught it. Signed-off-by: Michael Neale --- crates/buzz-relay/examples/mesh_admission_smoke.rs | 13 ++++--------- .../buzz-relay/examples/mesh_serve_client_smoke.rs | 14 +++++--------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/crates/buzz-relay/examples/mesh_admission_smoke.rs b/crates/buzz-relay/examples/mesh_admission_smoke.rs index a541f35bbc..94c86c2a68 100644 --- a/crates/buzz-relay/examples/mesh_admission_smoke.rs +++ b/crates/buzz-relay/examples/mesh_admission_smoke.rs @@ -54,16 +54,11 @@ fn env(name: &str) -> anyhow::Result { std::env::var(name).map_err(|_| anyhow::anyhow!("{name} is required for this role")) } +/// Installs the signed release runtime when none is cached — the same path the +/// desktop takes. Deliberately no "is it installed?" precondition: this runs in +/// three separate processes, and a guard here would refuse before reaching the +/// call that does the installing. async fn init_native_runtime() -> anyhow::Result<()> { - let cache = mesh_llm_sdk::native_runtime::native_runtime_cache(None)?; - let current = mesh_llm_sdk::native_runtime::CURRENT_MESH_VERSION; - if !cache - .installed()? - .iter() - .any(|runtime| runtime.mesh_version == current) - { - anyhow::bail!("MeshLLM native runtime for MeshLLM {current} is not installed; run `just mesh-e2e-hardware` once to prepare it"); - } mesh_llm_host_runtime::initialize_host_runtime() .await .map_err(|error| anyhow::anyhow!("MeshLLM host runtime init failed: {error}")) diff --git a/crates/buzz-relay/examples/mesh_serve_client_smoke.rs b/crates/buzz-relay/examples/mesh_serve_client_smoke.rs index 903d1a73c0..19e6c775d7 100644 --- a/crates/buzz-relay/examples/mesh_serve_client_smoke.rs +++ b/crates/buzz-relay/examples/mesh_serve_client_smoke.rs @@ -41,15 +41,11 @@ const CLIENT_CONSOLE_PORT: u16 = 13132; async fn main() -> anyhow::Result<()> { let model = std::env::var("MESH_SMOKE_MODEL").unwrap_or_else(|_| DEFAULT_MODEL.to_string()); eprintln!("[smoke] model: {model}"); - let cache = mesh_llm_sdk::native_runtime::native_runtime_cache(None)?; - let current = mesh_llm_sdk::native_runtime::CURRENT_MESH_VERSION; - if !cache - .installed()? - .iter() - .any(|runtime| runtime.mesh_version == current) - { - anyhow::bail!("MeshLLM native runtime for MeshLLM {current} is not installed; run `just mesh-e2e-hardware` to prepare it"); - } + // No cache precondition: `initialize_host_runtime` installs the signed + // release runtime itself when none is present, which is how the desktop + // gets one too. The guard that used to stand here refused before reaching + // this line and told the reader to run the very recipe that runs this + // example. mesh_llm_host_runtime::initialize_host_runtime() .await .map_err(|error| anyhow::anyhow!("MeshLLM host runtime init failed: {error}"))?;