mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
## Summary This is **part 1 of 2** split out from #3467 (per Tyler's request), carrying only the mesh-scoped changes. The agent/ACP response-behavior changes and the new `send_message` tool stay in #3467 as part 2. All commits are @michaelneale's work, cherry-picked with authorship preserved. - Upgrade embedded Mesh to v0.74.0 (tag-pinned instead of commit rev) and use canonical Gemma model IDs. - Keep shared compute serving through member joins, roster changes, app recovery, and community switching. - Wait for actual model readiness and avoid resuming incomplete downloads after quit. - Leave `BUZZ_AGENT_THINKING_EFFORT` unset by default so each model's chat template picks its own thinking default (`none` suppressed Gemma tool-calling entirely; pinning `low` made Qwen3 burn ~4x output budget). Explicit agent/persona/global values still win. ## Relationship to #3467 Contains the mesh commits from #3467 (`2cd640b23`, `0ad81c341`, `ad13ed841`) rebased onto current main, with one deliberate exclusion: the `crates/buzz-agent/src/llm.rs` reasoning→text parser change from `2cd640b23` is **not** here. That change unconditionally affects every OpenAI-compat/Responses provider, so it belongs with the reply-behavior work in part 2, where it can be reviewed as what it is. Not included (remaining in #3467 / part 2): - typed `send_message` tool in dev-mcp + `BUZZ_ACP_SEND_MESSAGE_TOOL` gating - plain-reply delivery fallback in buzz-acp (`BUZZ_ACP_DELIVER_PLAIN_REPLIES`) - the mesh_agent_e2e P5/P6 rewrite (exists to prove the reply path) - the two `env.insert` preset opt-ins in `relay_mesh.rs` for the flags above - the llm.rs parser change This PR is independently mergeable; part 2's flags are all off by default so it can land before or after. ## Testing - `cargo test -p buzz-relay --locked` — 780 passed (one telemetry test is order-sensitive under parallel default settings; passes in the pre-push suite and standalone, unrelated to this diff — files untouched here). - `just desktop-tauri-test` (default features) — 1877 passed. - `cargo test --locked --features mesh-llm` in `desktop/src-tauri` — 1961 passed, including the new relay-mesh preset and coordinator/recovery tests. - Both `Cargo.lock`s resolve with `--locked` against the v0.74.0 tag. - Full pre-push hook suite green (rust-tests, desktop-check/test, tauri checks). Live validation of the mesh v0.74 upgrade itself is documented on #3467 (two-Mac cross-version test). --------- Signed-off-by: Michael Neale <michael.neale@gmail.com> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
193
Cargo.lock
generated
193
Cargo.lock
generated
@@ -43,6 +43,19 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.4",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
@@ -2177,7 +2190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3009,8 +3022,8 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
"windows-link 0.1.3",
|
||||
"windows-result 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3609,7 +3622,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.62.2",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4501,8 +4514,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-api-client"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"mesh-llm-client",
|
||||
@@ -4511,8 +4524,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-api-server"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-api-client",
|
||||
@@ -4522,13 +4535,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-build-info"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-client"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4559,8 +4572,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-config"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -4575,8 +4588,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-embedded-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-host-runtime",
|
||||
@@ -4585,8 +4598,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-events"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -4597,8 +4610,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-gpu-bench"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
@@ -4610,8 +4623,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-guardrails"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4619,16 +4632,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-hardware-profile"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"mesh-llm-native-runtime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-host-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -4650,7 +4663,6 @@ dependencies = [
|
||||
"http",
|
||||
"http-body-util",
|
||||
"httparse",
|
||||
"if-addrs",
|
||||
"iroh",
|
||||
"json5",
|
||||
"keyring",
|
||||
@@ -4698,6 +4710,7 @@ dependencies = [
|
||||
"serde_yaml",
|
||||
"sha2 0.10.9",
|
||||
"skippy-coordinator",
|
||||
"skippy-ffi",
|
||||
"skippy-protocol",
|
||||
"skippy-runtime",
|
||||
"skippy-server",
|
||||
@@ -4720,8 +4733,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-identity"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"base64",
|
||||
@@ -4742,8 +4755,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-native-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -4753,8 +4766,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-node"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-types",
|
||||
@@ -4767,8 +4780,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-plugin"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4784,8 +4797,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-plugin-manager"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -4795,6 +4808,7 @@ dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"zip",
|
||||
@@ -4802,8 +4816,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-protocol"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hex",
|
||||
@@ -4815,16 +4829,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-routing"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"iroh",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-runtime-install"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -4846,8 +4860,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-sdk"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-api-client",
|
||||
@@ -4861,8 +4875,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-skills"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -4872,8 +4886,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-system"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -4895,8 +4909,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-types"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"serde",
|
||||
@@ -4906,13 +4920,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-ui"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
|
||||
[[package]]
|
||||
name = "mesh-mixture-of-agents"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"mesh-llm-guardrails",
|
||||
@@ -5038,8 +5052,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-artifact"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5049,8 +5063,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-hf"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5067,8 +5081,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-package"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -5087,16 +5101,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-ref"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "model-resolver"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"model-artifact",
|
||||
@@ -5815,8 +5829,8 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "openai-frontend"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -7368,9 +7382,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rmcp"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e"
|
||||
checksum = "1d1f571c72940a19d9532fe52dbea8bc9912bf1d766c2970bb824056b86f3f59"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
@@ -7401,9 +7415,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rmcp-macros"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aefac48c364756e97f04c0401ba3231e8607882c7c1d92da0437dc16307904d"
|
||||
checksum = "1aad0035b69380782d78ea95b508327e6deaa2235909053e596eea8f27b5e1d5"
|
||||
dependencies = [
|
||||
"darling 0.23.0",
|
||||
"proc-macro2",
|
||||
@@ -8153,8 +8167,8 @@ checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b"
|
||||
|
||||
[[package]]
|
||||
name = "skippy-cache"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"blake3",
|
||||
@@ -8163,29 +8177,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-coordinator"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "skippy-ffi"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "skippy-metrics"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
|
||||
[[package]]
|
||||
name = "skippy-protocol"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"prost",
|
||||
"prost-build",
|
||||
@@ -8195,8 +8209,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"libc",
|
||||
@@ -8209,9 +8223,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-server"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -8237,8 +8252,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-topology"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.73.1#43103c5c40292be688ac0261129bcbab0e7b9132"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -10465,8 +10480,8 @@ dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
"windows 0.62.2",
|
||||
"windows-core 0.62.2",
|
||||
"windows 0.61.3",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -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.73.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.73.1", 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.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"] }
|
||||
buzz-core = { workspace = true, features = ["test-utils"] }
|
||||
buzz-auth = { workspace = true, features = ["dev"] }
|
||||
reqwest = { workspace = true }
|
||||
|
||||
@@ -278,7 +278,9 @@ async fn agent_chat_in_isolated_home(
|
||||
.env("OPENAI_COMPAT_API_KEY", "buzz-mesh-local")
|
||||
.env("OPENAI_COMPAT_API", "chat")
|
||||
.env("BUZZ_AGENT_MAX_OUTPUT_TOKENS", "4096")
|
||||
.env("BUZZ_AGENT_THINKING_EFFORT", "none")
|
||||
// No BUZZ_AGENT_THINKING_EFFORT: apply_relay_mesh_env() deliberately
|
||||
// leaves it unset so each model's chat template picks its own default.
|
||||
// Pinning a value here would test a config the product does not ship.
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null());
|
||||
|
||||
201
desktop/src-tauri/Cargo.lock
generated
201
desktop/src-tauri/Cargo.lock
generated
@@ -75,6 +75,19 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.4",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
@@ -1511,7 +1524,7 @@ version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1748,7 +1761,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows 0.61.3",
|
||||
"windows 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2152,7 +2165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3102,8 +3115,8 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows-link 0.1.3",
|
||||
"windows-result 0.3.4",
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3848,7 +3861,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"windows-registry 0.5.3",
|
||||
"windows-registry 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3863,7 +3876,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.61.2",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4939,8 +4952,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-api-client"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"mesh-llm-client",
|
||||
@@ -4949,8 +4962,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-api-server"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-api-client",
|
||||
@@ -4960,13 +4973,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-build-info"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-client"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4997,8 +5010,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-config"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -5013,8 +5026,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-embedded-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-host-runtime",
|
||||
@@ -5023,8 +5036,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-events"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -5035,8 +5048,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-gpu-bench"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
@@ -5048,8 +5061,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-guardrails"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5057,16 +5070,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-hardware-profile"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"mesh-llm-native-runtime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-host-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -5088,7 +5101,6 @@ dependencies = [
|
||||
"http",
|
||||
"http-body-util",
|
||||
"httparse",
|
||||
"if-addrs",
|
||||
"iroh",
|
||||
"json5",
|
||||
"keyring",
|
||||
@@ -5136,6 +5148,7 @@ dependencies = [
|
||||
"serde_yaml",
|
||||
"sha2 0.10.9",
|
||||
"skippy-coordinator",
|
||||
"skippy-ffi",
|
||||
"skippy-protocol",
|
||||
"skippy-runtime",
|
||||
"skippy-server",
|
||||
@@ -5158,8 +5171,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-identity"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"base64 0.22.1",
|
||||
@@ -5180,8 +5193,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-native-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -5191,8 +5204,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-node"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-types",
|
||||
@@ -5205,8 +5218,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-plugin"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5222,8 +5235,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-plugin-manager"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -5233,6 +5246,7 @@ dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"zip 2.4.2",
|
||||
@@ -5240,8 +5254,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-protocol"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hex",
|
||||
@@ -5253,16 +5267,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-routing"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"iroh",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-runtime-install"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -5284,8 +5298,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-sdk"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mesh-llm-api-client",
|
||||
@@ -5299,8 +5313,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-skills"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs",
|
||||
@@ -5310,8 +5324,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-system"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -5333,8 +5347,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-types"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"serde",
|
||||
@@ -5344,13 +5358,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mesh-llm-ui"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
|
||||
[[package]]
|
||||
name = "mesh-mixture-of-agents"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"mesh-llm-guardrails",
|
||||
@@ -5422,8 +5436,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-artifact"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5433,8 +5447,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-hf"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5451,8 +5465,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-package"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -5471,16 +5485,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "model-ref"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "model-resolver"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"model-artifact",
|
||||
@@ -6134,7 +6148,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
|
||||
dependencies = [
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro-crate 2.0.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.118",
|
||||
@@ -6508,8 +6522,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openai-frontend"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -6702,7 +6716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7412,7 +7426,7 @@ version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"heck 0.5.0",
|
||||
"itertools",
|
||||
"log",
|
||||
"multimap",
|
||||
@@ -9041,8 +9055,8 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
|
||||
|
||||
[[package]]
|
||||
name = "skippy-cache"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"blake3",
|
||||
@@ -9051,29 +9065,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-coordinator"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "skippy-ffi"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"libloading 0.8.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "skippy-metrics"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
|
||||
[[package]]
|
||||
name = "skippy-protocol"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"prost",
|
||||
"prost-build",
|
||||
@@ -9083,8 +9097,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-runtime"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"libc",
|
||||
@@ -9097,9 +9111,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-server"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -9125,8 +9140,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "skippy-topology"
|
||||
version = "0.73.1"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?rev=f455d493a2ae82baf2a326e2d0fda351433b4b30#f455d493a2ae82baf2a326e2d0fda351433b4b30"
|
||||
version = "0.74.0"
|
||||
source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -10153,7 +10168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.4",
|
||||
"getrandom 0.4.3",
|
||||
"once_cell",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.61.2",
|
||||
@@ -11789,7 +11804,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12418,8 +12433,8 @@ dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
"windows 0.61.3",
|
||||
"windows-core 0.61.2",
|
||||
"windows 0.62.2",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -96,14 +96,14 @@ buzz_persona_pkg = { package = "buzz-persona", path = "../../crates/buzz-persona
|
||||
buzz_sdk_pkg = { package = "buzz-sdk", path = "../../crates/buzz-sdk" }
|
||||
buzz_agent_pkg = { package = "buzz-agent", path = "../../crates/buzz-agent" }
|
||||
iroh = { version = "1.0.2", optional = true }
|
||||
mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", rev = "f455d493a2ae82baf2a326e2d0fda351433b4b30", 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", rev = "f455d493a2ae82baf2a326e2d0fda351433b4b30", 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.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 }
|
||||
# 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", rev = "f455d493a2ae82baf2a326e2d0fda351433b4b30", package = "mesh-llm-client", optional = true }
|
||||
mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", rev = "f455d493a2ae82baf2a326e2d0fda351433b4b30", package = "mesh-llm-node", optional = true }
|
||||
mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", rev = "f455d493a2ae82baf2a326e2d0fda351433b4b30", package = "mesh-llm-system", optional = true }
|
||||
mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", rev = "f455d493a2ae82baf2a326e2d0fda351433b4b30", package = "mesh-llm-events", optional = true }
|
||||
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 }
|
||||
base64 = "0.22"
|
||||
sha2 = "0.11"
|
||||
tar = "0.4"
|
||||
|
||||
@@ -5,12 +5,35 @@ use tauri::{AppHandle, Manager, State};
|
||||
|
||||
use crate::{app_state::AppState, mesh_llm, relay};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct MeshSharingConfig {
|
||||
enabled: bool,
|
||||
/// A fresh Share Compute request that must cross a process boundary before
|
||||
/// it can start. Consumed before startup so an interrupted download is not
|
||||
/// resumed on a later launch.
|
||||
#[serde(default)]
|
||||
start_on_next_launch: bool,
|
||||
model_id: String,
|
||||
max_vram_gb: Option<u64>,
|
||||
/// Community relay where Share Compute was explicitly enabled. Older
|
||||
/// configs predate community binding and restore against the active relay.
|
||||
#[serde(default)]
|
||||
relay_url: Option<String>,
|
||||
}
|
||||
|
||||
fn pending_new_start_checkpoint(config: &MeshSharingConfig) -> MeshSharingConfig {
|
||||
let mut checkpoint = config.clone();
|
||||
checkpoint.enabled = false;
|
||||
checkpoint.start_on_next_launch = false;
|
||||
checkpoint
|
||||
}
|
||||
|
||||
fn one_shot_restart_checkpoint(config: &MeshSharingConfig) -> MeshSharingConfig {
|
||||
let mut checkpoint = config.clone();
|
||||
checkpoint.enabled = false;
|
||||
checkpoint.start_on_next_launch = true;
|
||||
checkpoint
|
||||
}
|
||||
|
||||
fn mesh_sharing_config_path(app: &AppHandle) -> Result<PathBuf, String> {
|
||||
@@ -89,8 +112,10 @@ fn sharing_config_from_request(
|
||||
.ok_or_else(|| "modelId is required for serve mode".to_string())?;
|
||||
Ok(MeshSharingConfig {
|
||||
enabled: true,
|
||||
start_on_next_launch: false,
|
||||
model_id: model_id.to_string(),
|
||||
max_vram_gb: request.max_vram_gb,
|
||||
relay_url: request.relay_url.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,7 +142,7 @@ fn restart_to_share(
|
||||
app: &AppHandle,
|
||||
config: &MeshSharingConfig,
|
||||
) -> CmdResult<mesh_llm::MeshNodeStatus> {
|
||||
save_mesh_sharing_config(app, config)?;
|
||||
save_mesh_sharing_config(app, &one_shot_restart_checkpoint(config))?;
|
||||
let status = restarting_share_status(config);
|
||||
app.request_restart();
|
||||
Ok(status)
|
||||
@@ -133,7 +158,7 @@ fn buzz_mesh_name_for_relay(relay_url: &str) -> String {
|
||||
format!("buzz-community-{}", &digest[..32])
|
||||
}
|
||||
|
||||
fn buzz_mesh_name(state: &AppState) -> String {
|
||||
pub(super) fn buzz_mesh_name(state: &AppState) -> String {
|
||||
buzz_mesh_name_for_relay(&relay::relay_ws_url_with_override(state))
|
||||
}
|
||||
|
||||
@@ -150,8 +175,13 @@ fn advance_mesh_status_cursor(
|
||||
Ok(cursor)
|
||||
}
|
||||
|
||||
async fn query_mesh_discovery_events(state: &AppState) -> Result<Vec<nostr::Event>, String> {
|
||||
let mut events = relay::query_relay(state, &[mesh_llm::relay_membership_filter()]).await?;
|
||||
async fn query_mesh_discovery_events_at(
|
||||
state: &AppState,
|
||||
relay_url: &str,
|
||||
) -> Result<Vec<nostr::Event>, String> {
|
||||
let api_base_url = relay::relay_http_base_url(relay_url);
|
||||
let mut events =
|
||||
relay::query_relay_at(state, &api_base_url, &[mesh_llm::relay_membership_filter()]).await?;
|
||||
let member_pubkeys = mesh_llm::current_member_pubkeys(&events);
|
||||
if member_pubkeys.is_empty() {
|
||||
// Distinguish "relay returned a membership snapshot listing zero
|
||||
@@ -172,7 +202,7 @@ async fn query_mesh_discovery_events(state: &AppState) -> Result<Vec<nostr::Even
|
||||
let mut previous_cursor: Option<(u64, String)> = None;
|
||||
|
||||
loop {
|
||||
let page = relay::query_relay(state, &[status_filter.clone()]).await?;
|
||||
let page = relay::query_relay_at(state, &api_base_url, &[status_filter.clone()]).await?;
|
||||
let done = page.len() < mesh_llm::MESH_STATUS_PAGE_SIZE;
|
||||
if !done {
|
||||
let cursor = advance_mesh_status_cursor(&mut status_filter, &page)?;
|
||||
@@ -188,6 +218,10 @@ async fn query_mesh_discovery_events(state: &AppState) -> Result<Vec<nostr::Even
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_mesh_discovery_events(state: &AppState) -> Result<Vec<nostr::Event>, String> {
|
||||
query_mesh_discovery_events_at(state, &relay::relay_ws_url_with_override(state)).await
|
||||
}
|
||||
|
||||
/// Resolve the admission roster by intersecting member-signed mesh status
|
||||
/// reporters with the current NIP-43 direct-member list.
|
||||
///
|
||||
@@ -201,6 +235,14 @@ pub(crate) async fn resolve_trusted_owner_ids(state: &AppState) -> Result<Vec<St
|
||||
Ok(mesh_llm::owner_ids_from_events(&events))
|
||||
}
|
||||
|
||||
pub(crate) async fn resolve_trusted_owner_ids_at(
|
||||
state: &AppState,
|
||||
relay_url: &str,
|
||||
) -> Result<Vec<String>, String> {
|
||||
let events = query_mesh_discovery_events_at(state, relay_url).await?;
|
||||
Ok(mesh_llm::owner_ids_from_events(&events))
|
||||
}
|
||||
|
||||
/// Resolve the roster for an initial node *start*, failing closed to self-only
|
||||
/// (an empty roster) when the relay query fails. This is safe only at start:
|
||||
/// there is no established allowlist to preserve yet. The periodic
|
||||
@@ -244,10 +286,11 @@ fn buzz_mesh_join_targets(
|
||||
/// Resolve the validated member endpoint this runtime should join to enter the
|
||||
/// existing Buzz community mesh. `Ok(None)` means this machine is the first
|
||||
/// live serving member (or is itself the shared bootstrap contact).
|
||||
pub(crate) async fn resolve_buzz_mesh_join_targets(
|
||||
pub(crate) async fn resolve_buzz_mesh_join_targets_at(
|
||||
state: &AppState,
|
||||
relay_url: &str,
|
||||
) -> Result<Vec<mesh_llm::MeshServeTarget>, String> {
|
||||
let events = query_mesh_discovery_events(state).await?;
|
||||
let events = query_mesh_discovery_events_at(state, relay_url).await?;
|
||||
let self_owner_id = mesh_llm::ensure_owner_identity()
|
||||
.map_err(|error| format!("failed to load mesh owner identity: {error}"))?
|
||||
.owner_id;
|
||||
@@ -261,8 +304,11 @@ pub(crate) async fn resolve_buzz_mesh_join_targets(
|
||||
/// snapshot. A node start used to repeat the full membership + status query
|
||||
/// for each value, making Share Compute startup both slower and more exposed
|
||||
/// to inconsistent snapshots.
|
||||
async fn resolve_buzz_mesh_startup(state: &AppState) -> (Vec<String>, Option<String>) {
|
||||
match query_mesh_discovery_events(state).await {
|
||||
async fn resolve_buzz_mesh_startup_at(
|
||||
state: &AppState,
|
||||
relay_url: &str,
|
||||
) -> (Vec<String>, Option<String>) {
|
||||
match query_mesh_discovery_events_at(state, relay_url).await {
|
||||
Ok(events) => {
|
||||
let trusted_owner_ids = mesh_llm::owner_ids_from_events(&events);
|
||||
let join_token = mesh_llm::ensure_owner_identity()
|
||||
@@ -291,32 +337,60 @@ async fn resolve_buzz_mesh_startup(state: &AppState) -> (Vec<String>, Option<Str
|
||||
}
|
||||
|
||||
pub(crate) async fn restore_mesh_sharing(app: &AppHandle, state: &AppState) -> CmdResult<()> {
|
||||
let Some(config) = load_mesh_sharing_config(app)? else {
|
||||
let Some(mut config) = load_mesh_sharing_config(app)? else {
|
||||
return Ok(());
|
||||
};
|
||||
if !config.enabled || config.model_id.trim().is_empty() {
|
||||
if (!config.enabled && !config.start_on_next_launch) || config.model_id.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
config.model_id = mesh_llm::canonical_curated_model_id(&config.model_id).to_string();
|
||||
if state.mesh_llm_runtime.lock().await.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
let (trusted_owner_ids, join_token) = resolve_buzz_mesh_startup(state).await;
|
||||
let relay_url = config
|
||||
.relay_url
|
||||
.clone()
|
||||
.unwrap_or_else(|| relay::relay_ws_url_with_override(state));
|
||||
let (trusted_owner_ids, join_token) = resolve_buzz_mesh_startup_at(state, &relay_url).await;
|
||||
let mut runtime = state.mesh_llm_runtime.lock().await;
|
||||
if runtime.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
if config.start_on_next_launch {
|
||||
// Consume a role-switch request before doing any potentially long model
|
||||
// work. If Buzz exits during that work, the next launch stays stopped.
|
||||
config = pending_new_start_checkpoint(&config);
|
||||
save_mesh_sharing_config(app, &config)?;
|
||||
}
|
||||
// This is restoration of a previously inference-ready serving node. Keep
|
||||
// the enabled checkpoint armed while restoring so a transient startup
|
||||
// failure does not silently turn Share Compute off. New starts remain
|
||||
// disarmed in `mesh_start_node` until their first inference probe passes.
|
||||
let request = mesh_llm::StartMeshNodeRequest {
|
||||
mode: mesh_llm::MeshNodeMode::Serve,
|
||||
model_id: Some(config.model_id),
|
||||
model_id: Some(config.model_id.clone()),
|
||||
max_vram_gb: config.max_vram_gb,
|
||||
join_token,
|
||||
mesh_name: Some(buzz_mesh_name(state)),
|
||||
mesh_name: Some(buzz_mesh_name_for_relay(&relay_url)),
|
||||
relay_url: Some(relay_url),
|
||||
trusted_owner_ids: Some(trusted_owner_ids),
|
||||
};
|
||||
let started = mesh_llm::DesktopMeshRuntime::start(request)
|
||||
.await
|
||||
.map_err(|error| format!("failed to restore Share Compute: {error:#}"))?;
|
||||
if let Err(error) = wait_for_mesh_inference(&config.model_id).await {
|
||||
let cleanup = started.stop().await;
|
||||
if let Err(cleanup_error) = cleanup {
|
||||
eprintln!(
|
||||
"buzz-mesh: restored node failed inference readiness and cleanup was incomplete: {cleanup_error:#}"
|
||||
);
|
||||
}
|
||||
return Err(format!("failed to restore Share Compute: {error}"));
|
||||
}
|
||||
*runtime = Some(started);
|
||||
config.enabled = true;
|
||||
config.start_on_next_launch = false;
|
||||
save_mesh_sharing_config(app, &config)?;
|
||||
drop(runtime);
|
||||
mesh_llm::publish_current_status_once(app, "restore").await;
|
||||
Ok(())
|
||||
@@ -328,6 +402,11 @@ pub async fn mesh_start_node(
|
||||
state: State<'_, AppState>,
|
||||
mut request: mesh_llm::StartMeshNodeRequest,
|
||||
) -> CmdResult<mesh_llm::MeshNodeStatus> {
|
||||
let relay_url = relay::relay_ws_url_with_override(&state);
|
||||
request.relay_url = Some(relay_url.clone());
|
||||
if let Some(model_id) = request.model_id.as_mut() {
|
||||
*model_id = mesh_llm::canonical_curated_model_id(model_id).to_string();
|
||||
}
|
||||
let sharing_config = if request.mode == mesh_llm::MeshNodeMode::Serve {
|
||||
Some(sharing_config_from_request(&request)?)
|
||||
} else {
|
||||
@@ -362,13 +441,14 @@ pub async fn mesh_start_node(
|
||||
// Frontend requests never carry a roster. Resolve it and the bootstrap
|
||||
// endpoint from one snapshot so UI startup does not repeat relay probes.
|
||||
if request.trusted_owner_ids.is_none() || request.join_token.is_none() {
|
||||
let (trusted_owner_ids, join_token) = resolve_buzz_mesh_startup(&state).await;
|
||||
let (trusted_owner_ids, join_token) =
|
||||
resolve_buzz_mesh_startup_at(&state, &relay_url).await;
|
||||
request.trusted_owner_ids.get_or_insert(trusted_owner_ids);
|
||||
if request.join_token.is_none() {
|
||||
request.join_token = join_token;
|
||||
}
|
||||
}
|
||||
request.mesh_name = Some(buzz_mesh_name(&state));
|
||||
request.mesh_name = Some(buzz_mesh_name_for_relay(&relay_url));
|
||||
let mut runtime = state.mesh_llm_runtime.lock().await;
|
||||
|
||||
let plan = match runtime.as_ref() {
|
||||
@@ -386,6 +466,13 @@ pub async fn mesh_start_node(
|
||||
return Err("mesh node is already running".to_string());
|
||||
}
|
||||
|
||||
if let Some(config) = sharing_config.as_ref() {
|
||||
// Do not arm launch restoration until the exact inference path used by
|
||||
// agents succeeds. Mesh may bind its ports after primary weights load
|
||||
// while package layers are still downloading.
|
||||
save_mesh_sharing_config(&app, &pending_new_start_checkpoint(config))?;
|
||||
}
|
||||
|
||||
let started = mesh_llm::DesktopMeshRuntime::start(request)
|
||||
.await
|
||||
.map_err(|error| format!("{error:#}"))?;
|
||||
@@ -409,6 +496,21 @@ pub async fn mesh_start_node(
|
||||
));
|
||||
}
|
||||
};
|
||||
if let Some(config) = sharing_config.as_ref() {
|
||||
if let Err(error) = wait_for_mesh_inference(&config.model_id).await {
|
||||
let cleanup = started.stop().await;
|
||||
if let Err(cleanup_error) = &cleanup {
|
||||
eprintln!(
|
||||
"buzz-mesh: started node failed inference readiness and cleanup was incomplete: {cleanup_error:#}"
|
||||
);
|
||||
}
|
||||
drop(runtime);
|
||||
app.request_restart();
|
||||
return Err(format!(
|
||||
"mesh node started but inference never became ready: {error}; Buzz is restarting to guarantee cleanup"
|
||||
));
|
||||
}
|
||||
}
|
||||
*runtime = Some(started);
|
||||
drop(runtime);
|
||||
if let Some(config) = sharing_config.as_ref() {
|
||||
@@ -612,6 +714,7 @@ pub(crate) async fn ensure_client_node_for_model(
|
||||
max_vram_gb: None,
|
||||
join_token: Some(join_token.clone()),
|
||||
mesh_name: Some(buzz_mesh_name(state)),
|
||||
relay_url: Some(relay::relay_ws_url_with_override(state)),
|
||||
trusted_owner_ids: Some(resolve_trusted_owner_ids_or_self_only(state).await),
|
||||
};
|
||||
let mut runtime = state.mesh_llm_runtime.lock().await;
|
||||
@@ -753,6 +856,18 @@ pub(crate) async fn ensure_relay_mesh_for_record(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A persisted Share Compute configuration is authoritative about this
|
||||
// machine's role. If no runtime is currently tracked (for example after a
|
||||
// clean process restart), restore the serving node instead of treating an
|
||||
// agent request as permission to replace it with a client node.
|
||||
if load_mesh_sharing_config(app)?
|
||||
.is_some_and(|config| config.enabled && !config.model_id.trim().is_empty())
|
||||
{
|
||||
restore_mesh_sharing(app, &state).await?;
|
||||
return wait_for_mesh_inference(model_id).await;
|
||||
}
|
||||
|
||||
let target = match resolve_mesh_bootstrap_target(&state, model_id).await {
|
||||
Ok(Some(target)) => target,
|
||||
Ok(None) => {
|
||||
@@ -768,15 +883,9 @@ pub(crate) async fn ensure_relay_mesh_for_record(
|
||||
}
|
||||
};
|
||||
|
||||
// Serve→Client re-arm transition (micspiral review #3, intentional-by-design):
|
||||
// if the dead ingress belonged to a *serve* node with running consumer
|
||||
// agents, this re-arms it as a Client (`MeshNodeMode::Client`). That is the
|
||||
// correct/safe recovery here — config-backed serve restoration is
|
||||
// `restore_mesh_sharing`'s job (`MeshNodeMode::Serve`), and
|
||||
// `ensure_client_node_for_model` reuses any live runtime of *either* mode
|
||||
// (the router resolves per-request), so it only cold-starts a Client when
|
||||
// there is genuinely no runtime. Falling back to Client if a serve node
|
||||
// crashed under local pressure is a desirable fail-safe, not a regression.
|
||||
// No serving configuration exists, so this is a genuine consumer-only
|
||||
// start. A configured serving machine is restored above and never reaches
|
||||
// this client fallback.
|
||||
ensure_client_node_for_model(&state, model_id, Some(target.endpoint_addr)).await?;
|
||||
wait_for_mesh_inference(model_id).await
|
||||
}
|
||||
@@ -792,14 +901,17 @@ pub async fn mesh_stop_node(
|
||||
// role under the lock and, when it's a consume session, leave it running
|
||||
// and return its live status unchanged. The frontend also guards this, but
|
||||
// status can be stale between polls, so the backend is authoritative.
|
||||
let taken = {
|
||||
let (taken, bound_relay_url) = {
|
||||
let mut guard = state.mesh_llm_runtime.lock().await;
|
||||
if let Some(runtime) = guard.as_ref() {
|
||||
if !share_stop_should_teardown(runtime.mode()) {
|
||||
return runtime.status().await.map_err(|error| error.to_string());
|
||||
}
|
||||
}
|
||||
guard.take()
|
||||
let bound_relay_url = guard
|
||||
.as_ref()
|
||||
.and_then(|runtime| runtime.start_request().relay_url.clone());
|
||||
(guard.take(), bound_relay_url)
|
||||
};
|
||||
if let Some(runtime) = taken {
|
||||
runtime.stop().await.map_err(|error| error.to_string())?;
|
||||
@@ -808,11 +920,13 @@ pub async fn mesh_stop_node(
|
||||
&app,
|
||||
&MeshSharingConfig {
|
||||
enabled: false,
|
||||
start_on_next_launch: false,
|
||||
model_id: String::new(),
|
||||
max_vram_gb: None,
|
||||
relay_url: None,
|
||||
},
|
||||
)?;
|
||||
mesh_llm::publish_stopped_status_once(&app, "stop").await;
|
||||
mesh_llm::publish_stopped_status_once_at(&app, bound_relay_url.as_deref(), "stop").await;
|
||||
Ok(mesh_llm::stopped_status())
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,74 @@ fn buzz_mesh_name_is_stable_and_does_not_expose_the_relay() {
|
||||
assert!(!first.contains("example"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sharing_config_keeps_the_community_where_sharing_was_enabled() {
|
||||
let request = mesh_llm::StartMeshNodeRequest {
|
||||
mode: mesh_llm::MeshNodeMode::Serve,
|
||||
model_id: Some("test-model".to_string()),
|
||||
max_vram_gb: Some(24),
|
||||
join_token: None,
|
||||
mesh_name: Some("buzz-community-test".to_string()),
|
||||
relay_url: Some("wss://community.example".to_string()),
|
||||
trusted_owner_ids: Some(Vec::new()),
|
||||
};
|
||||
|
||||
let config = sharing_config_from_request(&request).expect("valid sharing config");
|
||||
assert_eq!(config.relay_url.as_deref(), Some("wss://community.example"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_sharing_config_without_community_binding_still_loads() {
|
||||
let config: MeshSharingConfig = serde_json::from_value(serde_json::json!({
|
||||
"enabled": true,
|
||||
"modelId": "test-model",
|
||||
"maxVramGb": null
|
||||
}))
|
||||
.expect("legacy sharing config");
|
||||
|
||||
assert_eq!(config.relay_url, None);
|
||||
assert!(!config.start_on_next_launch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_start_checkpoint_prevents_incomplete_download_restore() {
|
||||
let config = MeshSharingConfig {
|
||||
enabled: true,
|
||||
start_on_next_launch: false,
|
||||
model_id: "test-model".to_string(),
|
||||
max_vram_gb: Some(24),
|
||||
relay_url: Some("wss://community.example".to_string()),
|
||||
};
|
||||
|
||||
let checkpoint = pending_new_start_checkpoint(&config);
|
||||
assert!(!checkpoint.enabled);
|
||||
assert!(!checkpoint.start_on_next_launch);
|
||||
assert_eq!(checkpoint.model_id, config.model_id);
|
||||
assert_eq!(checkpoint.max_vram_gb, config.max_vram_gb);
|
||||
assert_eq!(checkpoint.relay_url, config.relay_url);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn role_switch_checkpoint_starts_exactly_once_after_restart() {
|
||||
let config = MeshSharingConfig {
|
||||
enabled: true,
|
||||
start_on_next_launch: false,
|
||||
model_id: "test-model".to_string(),
|
||||
max_vram_gb: Some(24),
|
||||
relay_url: Some("wss://community.example".to_string()),
|
||||
};
|
||||
|
||||
let restart = one_shot_restart_checkpoint(&config);
|
||||
assert!(!restart.enabled);
|
||||
assert!(restart.start_on_next_launch);
|
||||
|
||||
let consumed = pending_new_start_checkpoint(&restart);
|
||||
assert!(!consumed.enabled);
|
||||
assert!(!consumed.start_on_next_launch);
|
||||
assert_eq!(consumed.model_id, config.model_id);
|
||||
assert_eq!(consumed.relay_url, config.relay_url);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn readiness_failure_is_catalog_sync_when_model_never_visible() {
|
||||
assert_eq!(
|
||||
@@ -345,6 +413,7 @@ fn ensure_serve_runtime_serves_other_model() {
|
||||
max_vram_gb: None,
|
||||
join_token: None,
|
||||
mesh_name: None,
|
||||
relay_url: None,
|
||||
trusted_owner_ids: None,
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -42,15 +42,51 @@ pub fn apply_relay_mesh_env(
|
||||
RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV.to_string(),
|
||||
"1".to_string(),
|
||||
);
|
||||
// Keep the requested response inside smaller local-model context windows,
|
||||
// and spend that budget on an answer/tool call instead of hidden reasoning.
|
||||
// Without both settings Qwen3 either fails the router's fit check at the
|
||||
// agent default (32K) or can consume a tight cap before serializing a tool.
|
||||
env.insert(
|
||||
"BUZZ_AGENT_MAX_OUTPUT_TOKENS".to_string(),
|
||||
"4096".to_string(),
|
||||
);
|
||||
env.insert("BUZZ_AGENT_THINKING_EFFORT".to_string(), "none".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
|
||||
// runs after those layers during readiness, so never clobber their values.
|
||||
insert_default_if_unset(env, "BUZZ_AGENT_MAX_OUTPUT_TOKENS", "4096");
|
||||
// Deliberately no BUZZ_AGENT_THINKING_EFFORT default: mesh translates
|
||||
// `reasoning_effort` into the chat template's `enable_thinking` flag, so any
|
||||
// value we pick overrides each model's own template default — and the right
|
||||
// value is model-specific. Measured with the real prompt and toolset:
|
||||
// gemma-4-E4B delivers 0/8 at `none` but 6/6 with the field absent, while
|
||||
// Qwen3-8B delivers 8/8 either way and burns ~4x the output tokens once
|
||||
// thinking is on (121 -> ~470), risking the 4096 cap. Omitting the field
|
||||
// lets every model use its own default; explicit agent/persona/global
|
||||
// values still apply.
|
||||
}
|
||||
|
||||
#[cfg(feature = "mesh-llm")]
|
||||
fn insert_default_if_unset(
|
||||
env: &mut std::collections::BTreeMap<String, String>,
|
||||
key: &str,
|
||||
value: &str,
|
||||
) {
|
||||
if env.get(key).is_none_or(|current| current.trim().is_empty()) {
|
||||
env.insert(key.to_string(), value.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the final Mesh-specific process overrides from the already-resolved
|
||||
/// harness environment. Only user-owned generation controls are seeded: the
|
||||
/// derived provider/base URL/model values remain authoritative, and unrelated
|
||||
/// credentials (notably `OPENAI_API_KEY`) must not be copied back after the
|
||||
/// spawn path removes them.
|
||||
#[cfg(feature = "mesh-llm")]
|
||||
pub fn relay_mesh_process_env(
|
||||
effective_env: &std::collections::BTreeMap<String, String>,
|
||||
model: &str,
|
||||
) -> std::collections::BTreeMap<String, String> {
|
||||
let mut env = std::collections::BTreeMap::new();
|
||||
for key in ["BUZZ_AGENT_MAX_OUTPUT_TOKENS", "BUZZ_AGENT_THINKING_EFFORT"] {
|
||||
if let Some(value) = effective_env.get(key) {
|
||||
env.insert(key.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
apply_relay_mesh_env(&mut env, Some(RELAY_MESH_PROVIDER_ID), Some(model));
|
||||
env
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "mesh-llm"))]
|
||||
@@ -60,7 +96,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn native_provider_uses_context_safe_non_reasoning_budget() {
|
||||
fn native_provider_uses_context_safe_tool_calling_budget() {
|
||||
let mut env = BTreeMap::new();
|
||||
apply_relay_mesh_env(
|
||||
&mut env,
|
||||
@@ -72,14 +108,63 @@ mod tests {
|
||||
env.get("BUZZ_AGENT_MAX_OUTPUT_TOKENS").map(String::as_str),
|
||||
Some("4096")
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("BUZZ_AGENT_THINKING_EFFORT").map(String::as_str),
|
||||
Some("none")
|
||||
);
|
||||
// Must stay unset: any value we pick overrides the model's own chat
|
||||
// template default, and the right value is model-specific ("none"
|
||||
// stops gemma tool-calling; enabling thinking makes Qwen3 burn ~4x the
|
||||
// output budget).
|
||||
assert_eq!(env.get("BUZZ_AGENT_THINKING_EFFORT"), None);
|
||||
assert_eq!(
|
||||
env.get(RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV)
|
||||
.map(String::as_str),
|
||||
Some("1")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_provider_preserves_explicit_generation_controls() {
|
||||
let mut env = BTreeMap::from([
|
||||
(
|
||||
"BUZZ_AGENT_MAX_OUTPUT_TOKENS".to_string(),
|
||||
"2048".to_string(),
|
||||
),
|
||||
("BUZZ_AGENT_THINKING_EFFORT".to_string(), "high".to_string()),
|
||||
]);
|
||||
apply_relay_mesh_env(
|
||||
&mut env,
|
||||
Some(RELAY_MESH_PROVIDER_ID),
|
||||
Some(RELAY_MESH_AUTO_MODEL_ID),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
env.get("BUZZ_AGENT_MAX_OUTPUT_TOKENS").map(String::as_str),
|
||||
Some("2048")
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("BUZZ_AGENT_THINKING_EFFORT").map(String::as_str),
|
||||
Some("high")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_env_seeds_controls_without_restoring_unrelated_credentials() {
|
||||
let effective_env = BTreeMap::from([
|
||||
(
|
||||
"BUZZ_AGENT_MAX_OUTPUT_TOKENS".to_string(),
|
||||
"1024".to_string(),
|
||||
),
|
||||
("OPENAI_API_KEY".to_string(), "must-not-leak".to_string()),
|
||||
]);
|
||||
|
||||
let env = relay_mesh_process_env(&effective_env, "Gemma-4");
|
||||
|
||||
assert_eq!(
|
||||
env.get("BUZZ_AGENT_MAX_OUTPUT_TOKENS").map(String::as_str),
|
||||
Some("1024")
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("OPENAI_COMPAT_MODEL").map(String::as_str),
|
||||
Some("Gemma-4")
|
||||
);
|
||||
assert!(!env.contains_key("OPENAI_API_KEY"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,12 +869,7 @@ pub fn spawn_agent_child(
|
||||
// uses the same trim semantics as the preflight callers.
|
||||
#[cfg(feature = "mesh-llm")]
|
||||
if let Some(ref mesh_model_id) = mesh_model_id {
|
||||
let mut mesh_env = std::collections::BTreeMap::new();
|
||||
super::apply_relay_mesh_env(
|
||||
&mut mesh_env,
|
||||
Some(super::RELAY_MESH_PROVIDER_ID),
|
||||
Some(mesh_model_id.as_str()),
|
||||
);
|
||||
let mesh_env = super::relay_mesh_process_env(&descriptor.env, mesh_model_id);
|
||||
command.env_remove("OPENAI_API_KEY");
|
||||
for (key, value) in mesh_env {
|
||||
command.env(key, value);
|
||||
|
||||
@@ -19,12 +19,14 @@ use mesh_llm_system::vram::{format_rated_capacity, rated_capacity_gb};
|
||||
/// The large pick is resolved through mesh-llm's remote catalog
|
||||
/// (huggingface.co/datasets/meshllm/catalog), so it does not need to exist in
|
||||
/// the compiled `MODEL_CATALOG`; the entry is synthesized below.
|
||||
const CURATED_LARGE: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M";
|
||||
const CURATED_LARGE: &str = "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M";
|
||||
const CURATED_LARGE_ALIAS: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M";
|
||||
const CURATED_LARGE_SIZE: &str = "17GB";
|
||||
const CURATED_LARGE_FILE: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf";
|
||||
const CURATED_LARGE_DESCRIPTION: &str =
|
||||
"Gemma 4 26B MoE (4B active) — Buzz default for 64GB+ machines";
|
||||
const CURATED_SMALL: &str = "Gemma-4-E4B-it-Q4_K_M";
|
||||
const CURATED_SMALL: &str = "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M";
|
||||
const CURATED_SMALL_ALIAS: &str = "Gemma-4-E4B-it-Q4_K_M";
|
||||
/// Rated-capacity boundary between the two curated tiers, in GB (marketing
|
||||
/// capacity — a "64GB" Mac rates as 64 even though usable AI memory is less).
|
||||
const CURATED_LARGE_MIN_RATED_GB: u64 = 64;
|
||||
@@ -37,6 +39,16 @@ fn buzz_recommended_model(rated_gb: Option<u64>) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Buzz's pre-0.74 curated package aliases into the canonical model
|
||||
/// ids advertised and accepted by Mesh's OpenAI ingress.
|
||||
pub(crate) fn canonical_curated_model_id(model_id: &str) -> &str {
|
||||
match model_id.trim() {
|
||||
CURATED_SMALL_ALIAS => CURATED_SMALL,
|
||||
CURATED_LARGE_ALIAS => CURATED_LARGE,
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
/// How a model sits inside this machine's usable AI memory.
|
||||
/// Mirrors mesh-llm's private `fit_code_for_size_label` thresholds.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||
@@ -146,12 +158,13 @@ fn build_catalog(
|
||||
.filter(|m| !is_draft_only(&m.name))
|
||||
.map(|m| {
|
||||
let size_gb = parse_size_gb(&m.size);
|
||||
let name = canonical_curated_model_id(&m.name).to_string();
|
||||
MeshCatalogEntry {
|
||||
fit: fit_code(size_gb, vram_gb),
|
||||
installed: is_installed(&m.file, &m.name),
|
||||
installed: is_installed(&m.file, &name) || is_installed(&m.file, &m.name),
|
||||
recommended: false,
|
||||
curated: false,
|
||||
name: m.name.clone(),
|
||||
name,
|
||||
size: m.size.clone(),
|
||||
size_gb,
|
||||
description: m.description.clone(),
|
||||
@@ -166,7 +179,8 @@ fn build_catalog(
|
||||
let size_gb = parse_size_gb(CURATED_LARGE_SIZE);
|
||||
entries.push(MeshCatalogEntry {
|
||||
fit: fit_code(size_gb, vram_gb),
|
||||
installed: is_installed(CURATED_LARGE_FILE, CURATED_LARGE),
|
||||
installed: is_installed(CURATED_LARGE_FILE, CURATED_LARGE)
|
||||
|| is_installed(CURATED_LARGE_FILE, CURATED_LARGE_ALIAS),
|
||||
recommended: false,
|
||||
curated: false,
|
||||
name: CURATED_LARGE.to_string(),
|
||||
@@ -256,6 +270,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn recommendation_follows_buzz_curated_tiers() {
|
||||
assert_eq!(CURATED_SMALL, "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M");
|
||||
assert_eq!(CURATED_LARGE, "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M");
|
||||
// 64GB+ rated machines get the large curated pick.
|
||||
let large = build_catalog(None, 64_000_000_000, 64.0, &[]);
|
||||
assert_eq!(large.recommended.as_deref(), Some(CURATED_LARGE));
|
||||
@@ -269,6 +285,22 @@ mod tests {
|
||||
assert_eq!(tiny.recommended.as_deref(), Some(CURATED_SMALL));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn curated_package_aliases_migrate_to_openai_model_ids() {
|
||||
assert_eq!(
|
||||
canonical_curated_model_id(CURATED_SMALL_ALIAS),
|
||||
CURATED_SMALL
|
||||
);
|
||||
assert_eq!(
|
||||
canonical_curated_model_id(CURATED_LARGE_ALIAS),
|
||||
CURATED_LARGE
|
||||
);
|
||||
assert_eq!(
|
||||
canonical_curated_model_id("other/model:Q4"),
|
||||
"other/model:Q4"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn curated_picks_lead_the_catalog() {
|
||||
let catalog = build_catalog(None, 96_000_000_000, 96.0, &[]);
|
||||
|
||||
@@ -132,7 +132,7 @@ pub async fn start_coordinator(app: AppHandle) {
|
||||
/// MeshLLM establishes the encrypted peer transport itself.
|
||||
async fn reconcile_buzz_mesh_join(app: &AppHandle) -> Result<(), String> {
|
||||
let state = app.state::<AppState>();
|
||||
let peer_ids = {
|
||||
let (peer_ids, relay_url) = {
|
||||
let runtime = state.mesh_llm_runtime.lock().await;
|
||||
let Some(runtime) = runtime.as_ref() else {
|
||||
return Ok(());
|
||||
@@ -141,10 +141,16 @@ async fn reconcile_buzz_mesh_join(app: &AppHandle) -> Result<(), String> {
|
||||
.status_report_payload()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
visible_peer_ids(&payload)
|
||||
let relay_url = runtime
|
||||
.start_request()
|
||||
.relay_url
|
||||
.clone()
|
||||
.unwrap_or_else(|| crate::relay::relay_ws_url_with_override(&state));
|
||||
(visible_peer_ids(&payload), relay_url)
|
||||
};
|
||||
|
||||
let targets = crate::commands::mesh_llm::resolve_buzz_mesh_join_targets(&state).await?;
|
||||
let targets =
|
||||
crate::commands::mesh_llm::resolve_buzz_mesh_join_targets_at(&state, &relay_url).await?;
|
||||
let Some(target) = targets
|
||||
.into_iter()
|
||||
.find(|target| !target_is_visible(target, &peer_ids))
|
||||
@@ -201,8 +207,13 @@ fn target_is_visible(target: &crate::mesh_llm::MeshServeTarget, peer_ids: &[Stri
|
||||
enum RosterReconcileAction {
|
||||
/// Keep the running allowlist untouched (no-op, or a failure we ride out).
|
||||
Keep,
|
||||
/// Restart the node with a freshly resolved roster.
|
||||
Restart(Vec<String>),
|
||||
/// Restart Buzz so MeshLLM is rebuilt with a freshly resolved roster.
|
||||
///
|
||||
/// MeshLLM's native listeners are process-owned in practice: stopping and
|
||||
/// starting the embedded runtime in one process can terminate Buzz or race
|
||||
/// ports 9337/3131. The process boundary is therefore part of the safety
|
||||
/// contract, not an implementation detail.
|
||||
RestartProcess,
|
||||
/// Observed a *shrink* (or empty) once. Hold the current allowlist and
|
||||
/// require the same reduced roster on the next poll before tearing down,
|
||||
/// so a single transient short-read never drops a member mid-inference.
|
||||
@@ -224,9 +235,9 @@ fn roster_shrinks(current: &[String], fresh: &[String]) -> bool {
|
||||
/// Rules:
|
||||
/// - query failed (`Err`) → `Keep` (never de-admit on a relay blip)
|
||||
/// - resolved roster == current → `Keep` (no-op)
|
||||
/// - grows (only additions) → `Restart` immediately (fast admission)
|
||||
/// - grows (only additions) → `RestartProcess` immediately (fast admission)
|
||||
/// - shrinks/empties, first observation → `AwaitConfirm` (hold, re-check next poll)
|
||||
/// - shrinks/empties, confirmed → `Restart` (same reduced roster twice)
|
||||
/// - shrinks/empties, confirmed → `RestartProcess` (same reduced roster twice)
|
||||
fn roster_reconcile_action(
|
||||
current_owners: &[String],
|
||||
pending_shrink: Option<&[String]>,
|
||||
@@ -248,13 +259,13 @@ fn roster_reconcile_action(
|
||||
|
||||
// Growth (pure additions) is safe to apply immediately.
|
||||
if !roster_shrinks(current_owners, &fresh) {
|
||||
return RosterReconcileAction::Restart(fresh);
|
||||
return RosterReconcileAction::RestartProcess;
|
||||
}
|
||||
|
||||
// A shrink (including down to empty) must be confirmed across two
|
||||
// consecutive polls with the *same* reduced roster before we tear down.
|
||||
match pending_shrink {
|
||||
Some(pending) if pending == fresh => RosterReconcileAction::Restart(fresh),
|
||||
Some(pending) if pending == fresh => RosterReconcileAction::RestartProcess,
|
||||
_ => RosterReconcileAction::AwaitConfirm(fresh),
|
||||
}
|
||||
}
|
||||
@@ -283,8 +294,13 @@ async fn reconcile_roster(
|
||||
// other member on a transient relay blip (the flapping restart loop). Keep
|
||||
// the current allowlist and try again on the next poll. A shrink is held
|
||||
// for one extra poll (hysteresis) so a single short-read never tears down.
|
||||
let query = crate::commands::mesh_llm::resolve_trusted_owner_ids(&state).await;
|
||||
let fresh = match roster_reconcile_action(current_owners, pending_shrink.as_deref(), query) {
|
||||
let relay_url = current_request
|
||||
.relay_url
|
||||
.as_deref()
|
||||
.map(str::to_owned)
|
||||
.unwrap_or_else(|| crate::relay::relay_ws_url_with_override(&state));
|
||||
let query = crate::commands::mesh_llm::resolve_trusted_owner_ids_at(&state, &relay_url).await;
|
||||
match roster_reconcile_action(current_owners, pending_shrink.as_deref(), query) {
|
||||
RosterReconcileAction::Keep => {
|
||||
*pending_shrink = None;
|
||||
return Ok(());
|
||||
@@ -294,34 +310,12 @@ async fn reconcile_roster(
|
||||
*pending_shrink = Some(reduced);
|
||||
return Ok(());
|
||||
}
|
||||
RosterReconcileAction::Restart(fresh) => {
|
||||
RosterReconcileAction::RestartProcess => {
|
||||
*pending_shrink = None;
|
||||
fresh
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut request = current_request.clone();
|
||||
request.trusted_owner_ids = Some(fresh);
|
||||
// Bootstrap endpoints are live device state, not configuration. The
|
||||
// endpoint used at the previous start may belong to the member that just
|
||||
// left or to a device whose iroh identity rotated while offline. Resolve a
|
||||
// fresh validated peer for this restart; starting isolated is safe because
|
||||
// the join watcher will converge it when a member next publishes.
|
||||
request.join_token = match crate::commands::mesh_llm::resolve_buzz_mesh_join_targets(&state)
|
||||
.await
|
||||
{
|
||||
Ok(targets) => targets
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|target| target.endpoint_addr),
|
||||
Err(error) => {
|
||||
eprintln!(
|
||||
"buzz-mesh: could not refresh bootstrap endpoint for roster restart; starting isolated: {error}"
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
let mut guard = state.mesh_llm_runtime.lock().await;
|
||||
let guard = state.mesh_llm_runtime.lock().await;
|
||||
let startup_pending = match guard.as_ref() {
|
||||
Some(runtime) => runtime.is_starting().await,
|
||||
None => false,
|
||||
@@ -342,24 +336,14 @@ async fn reconcile_roster(
|
||||
// snapshot.
|
||||
return Ok(());
|
||||
}
|
||||
let Some(running) = guard.take() else {
|
||||
if guard.is_none() {
|
||||
return Ok(());
|
||||
};
|
||||
eprintln!("buzz-mesh: membership roster changed; restarting mesh node with fresh allowlist");
|
||||
if let Err(error) = running.stop().await {
|
||||
drop(guard);
|
||||
eprintln!(
|
||||
"buzz-mesh: stopping mesh node for roster restart failed; restarting Buzz instead of racing the occupied ingress: {error}"
|
||||
);
|
||||
app.request_restart();
|
||||
return Err(format!(
|
||||
"mesh node shutdown failed during roster change: {error}"
|
||||
));
|
||||
}
|
||||
let replacement = crate::mesh_llm::DesktopMeshRuntime::start(request)
|
||||
.await
|
||||
.map_err(|error| format!("mesh node restart after roster change failed: {error:#}"))?;
|
||||
*guard = Some(replacement);
|
||||
drop(guard);
|
||||
eprintln!(
|
||||
"buzz-mesh: membership roster changed; restarting Buzz to rebuild MeshLLM with the fresh community allowlist"
|
||||
);
|
||||
app.request_restart();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -377,11 +361,15 @@ pub(crate) async fn publish_current_status_once(app: &AppHandle, reason: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn publish_stopped_status_once(app: &AppHandle, reason: &str) {
|
||||
pub(crate) async fn publish_stopped_status_once_at(
|
||||
app: &AppHandle,
|
||||
relay_url: Option<&str>,
|
||||
reason: &str,
|
||||
) {
|
||||
let state = app.state::<AppState>();
|
||||
match tokio::time::timeout(
|
||||
STATUS_PUBLISH_TIMEOUT,
|
||||
publish_stopped_status_for_state(&state),
|
||||
publish_stopped_status_for_state(&state, relay_url),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -396,26 +384,43 @@ pub(crate) async fn publish_stopped_status_once(app: &AppHandle, reason: &str) {
|
||||
async fn publish_current_status_for_state(state: &AppState) -> Result<(), String> {
|
||||
let identity = super::ensure_owner_identity()
|
||||
.map_err(|error| format!("failed to load mesh owner identity: {error}"))?;
|
||||
let mut payload = {
|
||||
let (mut payload, relay_url) = {
|
||||
let runtime = state.mesh_llm_runtime.lock().await;
|
||||
match runtime.as_ref() {
|
||||
Some(runtime) => runtime
|
||||
.status_report_payload()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?,
|
||||
None => stopped_status_payload(&identity),
|
||||
Some(runtime) => {
|
||||
let payload = runtime
|
||||
.status_report_payload()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
let relay_url = runtime
|
||||
.start_request()
|
||||
.relay_url
|
||||
.clone()
|
||||
.unwrap_or_else(|| crate::relay::relay_ws_url_with_override(state));
|
||||
(payload, relay_url)
|
||||
}
|
||||
None => (
|
||||
stopped_status_payload(&identity),
|
||||
crate::relay::relay_ws_url_with_override(state),
|
||||
),
|
||||
}
|
||||
};
|
||||
bind_payload_to_member(state, &identity, &mut payload)?;
|
||||
publish_status_report(state, payload).await
|
||||
publish_status_report_at(state, &relay_url, payload).await
|
||||
}
|
||||
|
||||
async fn publish_stopped_status_for_state(state: &AppState) -> Result<(), String> {
|
||||
async fn publish_stopped_status_for_state(
|
||||
state: &AppState,
|
||||
relay_url: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
let identity = super::ensure_owner_identity()
|
||||
.map_err(|error| format!("failed to load mesh owner identity: {error}"))?;
|
||||
let mut payload = stopped_status_payload(&identity);
|
||||
bind_payload_to_member(state, &identity, &mut payload)?;
|
||||
publish_status_report(state, payload).await
|
||||
let relay_url = relay_url
|
||||
.map(str::to_owned)
|
||||
.unwrap_or_else(|| crate::relay::relay_ws_url_with_override(state));
|
||||
publish_status_report_at(state, &relay_url, payload).await
|
||||
}
|
||||
|
||||
fn stopped_status_payload(identity: &super::identity::OwnerIdentity) -> serde_json::Value {
|
||||
@@ -469,13 +474,21 @@ pub(crate) fn build_status_report_event(
|
||||
.tags([d, k]))
|
||||
}
|
||||
|
||||
pub(crate) async fn publish_status_report(
|
||||
async fn publish_status_report_at(
|
||||
state: &AppState,
|
||||
relay_url: &str,
|
||||
payload: serde_json::Value,
|
||||
) -> Result<(), String> {
|
||||
crate::relay::submit_event(build_status_report_event(payload)?, state)
|
||||
.await
|
||||
.map(|_| ())
|
||||
let api_base_url = crate::relay::relay_http_base_url(relay_url);
|
||||
let keys = state.signing_keys()?;
|
||||
crate::relay::submit_event_at_with_keys(
|
||||
build_status_report_event(payload)?,
|
||||
state,
|
||||
&api_base_url,
|
||||
&keys,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -554,11 +567,11 @@ mod tests {
|
||||
|
||||
// Growth (pure additions) applies immediately — fast admission is fine.
|
||||
#[test]
|
||||
fn roster_growth_restarts_immediately() {
|
||||
fn roster_growth_requests_process_restart_immediately() {
|
||||
let current = vec!["owner-a".to_string()];
|
||||
let fresh = vec!["owner-a".to_string(), "owner-c".to_string()];
|
||||
let action = roster_reconcile_action(¤t, None, Ok(fresh.clone()));
|
||||
assert_eq!(action, RosterReconcileAction::Restart(fresh));
|
||||
let action = roster_reconcile_action(¤t, None, Ok(fresh));
|
||||
assert_eq!(action, RosterReconcileAction::RestartProcess);
|
||||
}
|
||||
|
||||
// A shrink is NOT applied on first observation — it must be confirmed.
|
||||
@@ -572,11 +585,11 @@ mod tests {
|
||||
|
||||
// The same reduced roster on two consecutive polls confirms the shrink.
|
||||
#[test]
|
||||
fn roster_shrink_restarts_once_confirmed() {
|
||||
fn roster_shrink_requests_process_restart_once_confirmed() {
|
||||
let current = vec!["owner-a".to_string(), "owner-b".to_string()];
|
||||
let reduced = vec!["owner-a".to_string()];
|
||||
let action = roster_reconcile_action(¤t, Some(&reduced), Ok(reduced.clone()));
|
||||
assert_eq!(action, RosterReconcileAction::Restart(reduced));
|
||||
assert_eq!(action, RosterReconcileAction::RestartProcess);
|
||||
}
|
||||
|
||||
// A shrink that changes between polls is not confirmed — it re-holds with
|
||||
@@ -600,7 +613,7 @@ mod tests {
|
||||
assert_eq!(first, RosterReconcileAction::AwaitConfirm(Vec::new()));
|
||||
let empty: Vec<String> = Vec::new();
|
||||
let confirmed = roster_reconcile_action(¤t, Some(&empty), Ok(Vec::new()));
|
||||
assert_eq!(confirmed, RosterReconcileAction::Restart(Vec::new()));
|
||||
assert_eq!(confirmed, RosterReconcileAction::RestartProcess);
|
||||
}
|
||||
|
||||
// A shrink followed by recovery to the full roster cancels the teardown.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
mod coordinator;
|
||||
pub(crate) use coordinator::{publish_current_status_once, publish_stopped_status_once};
|
||||
pub(crate) use coordinator::{publish_current_status_once, publish_stopped_status_once_at};
|
||||
pub use coordinator::{start_coordinator, MeshCoordinator, KIND_BUZZ_MESH_MEMBER_STATUS};
|
||||
|
||||
mod discovery;
|
||||
@@ -14,6 +14,7 @@ pub(crate) use discovery::{
|
||||
use discovery::{device_name_from_status, endpoint_id_from_status, enrich_status_payload_identity};
|
||||
|
||||
mod catalog;
|
||||
pub(crate) use catalog::canonical_curated_model_id;
|
||||
pub use catalog::{model_catalog, MeshModelCatalog};
|
||||
|
||||
mod identity;
|
||||
@@ -200,6 +201,11 @@ pub struct StartMeshNodeRequest {
|
||||
/// accepted from the frontend and contains no relay address.
|
||||
#[serde(default, skip_deserializing)]
|
||||
pub mesh_name: Option<String>,
|
||||
/// Relay this runtime's community membership and discovery are bound to.
|
||||
/// Injected by the backend when sharing starts and retained across UI
|
||||
/// workspace switches; moving a share requires an explicit stop/start.
|
||||
#[serde(default, skip_deserializing)]
|
||||
pub relay_url: Option<String>,
|
||||
/// Mesh owner ids admitted to this node (the member roster from
|
||||
/// member-signed discovery notes). `None` = caller did not resolve a roster
|
||||
/// (tests, direct invocations): the node runs without allowlist
|
||||
@@ -308,17 +314,20 @@ pub const MESH_WORKER_STACK_SIZE: usize = 8 * 1024 * 1024;
|
||||
/// before the node starts. Without this the download happens *inside*
|
||||
/// `serve::start()` where the UI can only show a frozen "starting…" state.
|
||||
/// Already-installed models return immediately from the cache scan.
|
||||
async fn ensure_model_downloaded(model: &str) -> anyhow::Result<()> {
|
||||
let model_owned = model.to_string();
|
||||
let installed = tokio::task::spawn_blocking(move || {
|
||||
async fn model_is_installed(model: &str) -> bool {
|
||||
let model_owned = model.replace("@main", "");
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let cache = mesh_llm_node::models::default_huggingface_cache_dir();
|
||||
mesh_llm_node::models::scan_installed_models(cache)
|
||||
.iter()
|
||||
.any(|m| m.model_ref.contains(&model_owned))
|
||||
.any(|m| m.model_ref.replace("@main", "").contains(&model_owned))
|
||||
})
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
if installed {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
async fn ensure_model_downloaded(model: &str) -> anyhow::Result<()> {
|
||||
if model_is_installed(model).await {
|
||||
return Ok(());
|
||||
}
|
||||
mesh_llm_host_runtime::models::download_model_ref_with_progress_details(model, true)
|
||||
|
||||
@@ -12,6 +12,7 @@ fn pending_client_runtime(
|
||||
max_vram_gb: None,
|
||||
join_token: Some("initial-token".to_string()),
|
||||
mesh_name: None,
|
||||
relay_url: None,
|
||||
trusted_owner_ids: None,
|
||||
};
|
||||
super::DesktopMeshRuntime {
|
||||
|
||||
@@ -153,6 +153,13 @@ fn should_evict_after_probe(
|
||||
|| consecutive >= DEAD_PROBE_EVICT_THRESHOLD
|
||||
}
|
||||
|
||||
fn requires_process_restart(
|
||||
mode: crate::mesh_llm::MeshNodeMode,
|
||||
startup_in_progress: bool,
|
||||
) -> bool {
|
||||
startup_in_progress || mode == crate::mesh_llm::MeshNodeMode::Serve
|
||||
}
|
||||
|
||||
/// Probe and, when justified, remove one stale runtime. A closed port is
|
||||
/// decisive for a foreground agent start; watchdog and ambiguous/unhealthy
|
||||
/// ports require consecutive failures to avoid restarting on a transient load
|
||||
@@ -161,22 +168,23 @@ pub(crate) async fn recover_stale_mesh_runtime(
|
||||
state: &AppState,
|
||||
urgency: MeshRecoveryUrgency,
|
||||
) -> MeshRuntimeRecovery {
|
||||
let (candidate_id, startup_in_progress) = match state.mesh_llm_runtime.lock().await.as_ref() {
|
||||
Some(runtime) => (runtime.id(), runtime.is_starting().await),
|
||||
None => {
|
||||
state.mesh_recovery.reset_probe_streak();
|
||||
// A cancelled SDK startup can outlive its Buzz-side task briefly
|
||||
// because the embedded runtime runs on its own thread. Never start
|
||||
// a replacement merely because the tracked handle is gone: first
|
||||
// prove the old ingress is either still useful or has released the
|
||||
// port. This closes the port-conflict loop in #2304.
|
||||
return match probe_mesh_ingress().await {
|
||||
MeshIngressProbe::Live => MeshRuntimeRecovery::Live,
|
||||
MeshIngressProbe::PortClosed => MeshRuntimeRecovery::Absent,
|
||||
MeshIngressProbe::Unhealthy => MeshRuntimeRecovery::ReleasePending,
|
||||
};
|
||||
}
|
||||
};
|
||||
let (candidate_id, startup_in_progress, candidate_mode) =
|
||||
match state.mesh_llm_runtime.lock().await.as_ref() {
|
||||
Some(runtime) => (runtime.id(), runtime.is_starting().await, runtime.mode()),
|
||||
None => {
|
||||
state.mesh_recovery.reset_probe_streak();
|
||||
// A cancelled SDK startup can outlive its Buzz-side task briefly
|
||||
// because the embedded runtime runs on its own thread. Never start
|
||||
// a replacement merely because the tracked handle is gone: first
|
||||
// prove the old ingress is either still useful or has released the
|
||||
// port. This closes the port-conflict loop in #2304.
|
||||
return match probe_mesh_ingress().await {
|
||||
MeshIngressProbe::Live => MeshRuntimeRecovery::Live,
|
||||
MeshIngressProbe::PortClosed => MeshRuntimeRecovery::Absent,
|
||||
MeshIngressProbe::Unhealthy => MeshRuntimeRecovery::ReleasePending,
|
||||
};
|
||||
}
|
||||
};
|
||||
let probe = probe_mesh_ingress().await;
|
||||
if probe == MeshIngressProbe::Live {
|
||||
state.mesh_recovery.reset_probe_streak();
|
||||
@@ -196,12 +204,13 @@ pub(crate) async fn recover_stale_mesh_runtime(
|
||||
return MeshRuntimeRecovery::Debouncing;
|
||||
}
|
||||
|
||||
// The pinned SDK does not yield its control handle until the management
|
||||
// API is ready. Dropping its still-pending start future would detach the
|
||||
// embedded runtime thread without sending a shutdown request, so Buzz must
|
||||
// not evict it and race a replacement onto the same ports. A controlled
|
||||
// app relaunch is the only process-owned cleanup boundary in this state.
|
||||
if startup_in_progress {
|
||||
// Never replace a serving runtime in-process. Its native listeners and
|
||||
// model host are process-owned; stopping it here and then cold-starting a
|
||||
// client silently disables Share Compute and can race ports 9337/3131.
|
||||
// Pending client startups have the same ownership problem because the SDK
|
||||
// has not yielded a shutdown handle yet. In both cases, process restart is
|
||||
// the only boundary that preserves the configured role safely.
|
||||
if requires_process_restart(candidate_mode, startup_in_progress) {
|
||||
state.mesh_recovery.reset_probe_streak();
|
||||
return MeshRuntimeRecovery::RestartRequired;
|
||||
}
|
||||
@@ -241,6 +250,12 @@ pub(crate) async fn recover_stale_mesh_runtime(
|
||||
pub(crate) async fn rearm_relay_mesh_for_running_agents(app: &AppHandle) -> Result<(), String> {
|
||||
let state = app.state::<AppState>();
|
||||
let _rearm_guard = state.mesh_recovery.rearm_lock.lock().await;
|
||||
let runtime_mode = state
|
||||
.mesh_llm_runtime
|
||||
.lock()
|
||||
.await
|
||||
.as_ref()
|
||||
.map(|runtime| runtime.mode());
|
||||
let recovery = recover_stale_mesh_runtime(&state, MeshRecoveryUrgency::Watchdog).await;
|
||||
let active_pubkeys = active_managed_agent_pubkeys(&state);
|
||||
// Mesh participation is resolved through the same definition-authoritative
|
||||
@@ -254,6 +269,13 @@ pub(crate) async fn rearm_relay_mesh_for_running_agents(app: &AppHandle) -> Resu
|
||||
| MeshRuntimeRecovery::Debouncing
|
||||
| MeshRuntimeRecovery::Replaced => return Ok(()),
|
||||
MeshRuntimeRecovery::RestartRequired => {
|
||||
if runtime_mode == Some(crate::mesh_llm::MeshNodeMode::Serve) {
|
||||
eprintln!(
|
||||
"buzz-mesh: serving ingress failed; restarting Buzz to restore Share Compute without changing roles"
|
||||
);
|
||||
app.request_restart();
|
||||
return Ok(());
|
||||
}
|
||||
let records = crate::managed_agents::load_managed_agents(app).unwrap_or_default();
|
||||
if !records.iter().any(|record| {
|
||||
running_relay_mesh_model_id(record, &active_pubkeys, &personas, &global).is_some()
|
||||
@@ -410,6 +432,7 @@ mod tests {
|
||||
name_pool: Vec::new(),
|
||||
is_builtin: false,
|
||||
is_active: true,
|
||||
shared: false,
|
||||
source_team: None,
|
||||
source_team_persona_slug: None,
|
||||
catalog_source: None,
|
||||
@@ -481,6 +504,22 @@ mod tests {
|
||||
assert!(STALE_STOP_TIMEOUT <= Duration::from_secs(15));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_serving_runtime_requires_process_restart_instead_of_client_fallback() {
|
||||
assert!(requires_process_restart(
|
||||
crate::mesh_llm::MeshNodeMode::Serve,
|
||||
false
|
||||
));
|
||||
assert!(requires_process_restart(
|
||||
crate::mesh_llm::MeshNodeMode::Client,
|
||||
true
|
||||
));
|
||||
assert!(!requires_process_restart(
|
||||
crate::mesh_llm::MeshNodeMode::Client,
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_running_relay_mesh_agents_trigger_rearm() {
|
||||
let personas: Vec<crate::managed_agents::AgentDefinition> = Vec::new();
|
||||
|
||||
@@ -107,7 +107,9 @@ export function MeshComputeSettingsCard() {
|
||||
|
||||
// One-shot hardware-aware catalog fetch. Purely additive: when it fails
|
||||
// (stub build, survey error) the card falls back to the free-text field.
|
||||
// Keep an empty draft empty so the UI can explicitly ask the member to choose.
|
||||
// When there is no saved choice, make the curated recommendation the actual
|
||||
// default so a new member can turn Share Compute on directly. An explicit
|
||||
// saved draft always wins.
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
@@ -115,6 +117,11 @@ export function MeshComputeSettingsCard() {
|
||||
const value = await meshModelCatalog();
|
||||
if (cancelled) return;
|
||||
setCatalog(value);
|
||||
setModelInput((current) => {
|
||||
if (current.trim() !== "" || !value.recommended) return current;
|
||||
writeDraft(MODEL_DRAFT_STORAGE_KEY, value.recommended);
|
||||
return value.recommended;
|
||||
});
|
||||
} catch {
|
||||
// Non-fatal — picker just doesn't render.
|
||||
}
|
||||
|
||||
@@ -2890,9 +2890,7 @@ const mockMeshState: {
|
||||
servingUsage: MockServingUsage;
|
||||
} = {
|
||||
admitted: true,
|
||||
models: [
|
||||
{ id: "hf://demo/SmolLM2-135M-Instruct-GGUF:Q4_K_M", name: "SmolLM2 135M" },
|
||||
],
|
||||
models: [{ id: "Gemma-4-E4B-it-Q4_K_M", name: "Gemma 4 E4B" }],
|
||||
denyReason: "not a relay member",
|
||||
nodeState: "off",
|
||||
nodeMode: null,
|
||||
@@ -2901,9 +2899,7 @@ const mockMeshState: {
|
||||
|
||||
function resetMockMesh() {
|
||||
mockMeshState.admitted = true;
|
||||
mockMeshState.models = [
|
||||
{ id: "hf://demo/SmolLM2-135M-Instruct-GGUF:Q4_K_M", name: "SmolLM2 135M" },
|
||||
];
|
||||
mockMeshState.models = [{ id: "Gemma-4-E4B-it-Q4_K_M", name: "Gemma 4 E4B" }];
|
||||
mockMeshState.denyReason = "not a relay member";
|
||||
mockMeshState.nodeState = "off";
|
||||
mockMeshState.nodeMode = null;
|
||||
@@ -9744,6 +9740,25 @@ export function maybeInstallE2eTauriMocks() {
|
||||
}
|
||||
case "mesh_installed_models":
|
||||
return mockMeshState.models;
|
||||
case "mesh_model_catalog":
|
||||
return {
|
||||
gpuName: "Mock Apple GPU",
|
||||
vramDisplay: "32 GB",
|
||||
vramGb: 32,
|
||||
recommended: "Gemma-4-E4B-it-Q4_K_M",
|
||||
entries: [
|
||||
{
|
||||
name: "Gemma-4-E4B-it-Q4_K_M",
|
||||
size: "3.5GB",
|
||||
sizeGb: 3.5,
|
||||
description: "Buzz-curated local agent model",
|
||||
fit: "comfortable",
|
||||
installed: true,
|
||||
recommended: true,
|
||||
curated: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
case "mesh_node_status":
|
||||
return meshNodeStatus(mockMeshState.nodeState, mockMeshState.nodeMode);
|
||||
case "mesh_serving_usage":
|
||||
|
||||
@@ -15,7 +15,7 @@ type E2eWindow = Window & {
|
||||
}) => void;
|
||||
};
|
||||
|
||||
test("Share compute has a clear empty state and starts and stops sharing", async ({
|
||||
test("Share compute selects the curated default and starts and stops sharing", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page);
|
||||
@@ -30,22 +30,32 @@ test("Share compute has a clear empty state and starts and stops sharing", async
|
||||
await expect(card).toContainText(
|
||||
"Choose a suggested model below, or enter a model reference or local file",
|
||||
);
|
||||
await expect(toggle).toBeDisabled();
|
||||
|
||||
await model.fill("hf://demo/SmolLM2-135M-Instruct-GGUF:Q4_K_M");
|
||||
await expect(model).toHaveValue("Gemma-4-E4B-it-Q4_K_M");
|
||||
await expect(toggle).toBeEnabled();
|
||||
await expect(card).toContainText(
|
||||
"Buzz downloads remote models when sharing starts",
|
||||
);
|
||||
await expect(toggle).toBeEnabled();
|
||||
|
||||
await toggle.click();
|
||||
await expect(toggle).toBeChecked();
|
||||
await expect(card).toContainText("Sharing SmolLM2 135M with relay members");
|
||||
await expect(card).toContainText("Sharing Gemma 4 E4B with relay members");
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(() => (window as E2eWindow).__BUZZ_E2E_COMMANDS__ ?? []),
|
||||
)
|
||||
.toContain("mesh_start_node");
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(
|
||||
() => (window as E2eWindow).__BUZZ_E2E_COMMAND_PAYLOADS__ ?? [],
|
||||
),
|
||||
)
|
||||
.toContainEqual({
|
||||
command: "mesh_start_node",
|
||||
payload: {
|
||||
request: { mode: "serve", modelId: "Gemma-4-E4B-it-Q4_K_M" },
|
||||
},
|
||||
});
|
||||
|
||||
await toggle.click();
|
||||
await expect(toggle).not.toBeChecked();
|
||||
|
||||
Reference in New Issue
Block a user