diff --git a/Cargo.lock b/Cargo.lock index ac4ea620bb..eae8dd9e37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,20 +352,21 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069" +checksum = "2c713e1f14c7b82e32ea159af1c6e2f070cfadbdf23fb2512acce9af0a26f1a2" dependencies = [ - "async-utility", "futures", "futures-util", "js-sys", "tokio", + "tokio-happy-eyeballs", "tokio-rustls", "tokio-socks", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.28.0", "url", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -398,12 +399,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -597,6 +592,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "beef" version = "0.5.2" @@ -609,7 +610,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "serde", "unicode-normalization", ] @@ -644,6 +645,21 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +dependencies = [ + "bitcoin-internals", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" version = "0.1.4" @@ -657,7 +673,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals", + "hex-conservative 1.2.0", "serde", ] @@ -812,7 +840,7 @@ dependencies = [ "hex", "httparse", "nix 0.31.3", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "rustls", "serde", @@ -845,7 +873,7 @@ dependencies = [ "clap", "deadpool-redis", "hex", - "nostr", + "nostr 0.44.7", "rustls", "serde_json", "sqlx", @@ -904,7 +932,7 @@ version = "0.1.0" dependencies = [ "buzz-core", "hex", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "serde", "serde_json", @@ -926,7 +954,7 @@ dependencies = [ "http-body-util", "k8s-openapi", "kube", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "rustls", "serde", @@ -953,7 +981,7 @@ dependencies = [ "dirs", "hex", "infer", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "reqwest 0.13.4", "rustls", @@ -986,7 +1014,7 @@ dependencies = [ "chrono", "hex", "hmac 0.13.0", - "nostr", + "nostr 0.44.7", "percent-encoding", "rand 0.10.1", "serde", @@ -1008,7 +1036,7 @@ dependencies = [ "hex", "metrics", "metrics-util", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "serde", "serde_json", @@ -1032,7 +1060,7 @@ dependencies = [ "ignore", "image", "nix 0.31.3", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "rmcp", "rustls", @@ -1065,7 +1093,7 @@ dependencies = [ "imagesize", "infer", "mp4", - "nostr", + "nostr 0.44.7", "rust-s3", "serde", "serde_json", @@ -1104,7 +1132,7 @@ dependencies = [ "clap", "futures-util", "hex", - "nostr", + "nostr 0.44.7", "serde_json", "thiserror 2.0.18", "tokio", @@ -1133,7 +1161,7 @@ dependencies = [ "chrono", "deadpool-redis", "futures-util", - "nostr", + "nostr 0.44.7", "redis", "serde", "serde_json", @@ -1159,7 +1187,7 @@ dependencies = [ "metrics", "metrics-exporter-prometheus", "minicbor", - "nostr", + "nostr 0.44.7", "p256", "proptest", "rand 0.10.1", @@ -1216,7 +1244,7 @@ dependencies = [ "metrics-exporter-prometheus", "metrics-util", "moka", - "nostr", + "nostr 0.44.7", "opentelemetry 0.32.0", "opentelemetry-otlp 0.32.0", "opentelemetry_sdk 0.32.1", @@ -1257,7 +1285,7 @@ dependencies = [ "hex", "hmac 0.13.0", "iroh", - "nostr", + "nostr 0.44.7", "postcard", "proptest", "redis", @@ -1275,7 +1303,7 @@ name = "buzz-sdk" version = "0.1.0" dependencies = [ "buzz-core", - "nostr", + "nostr 0.44.7", "serde", "serde_json", "thiserror 2.0.18", @@ -1306,7 +1334,7 @@ dependencies = [ "chrono", "futures-util", "hex", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "reqwest 0.13.4", "rust-s3", @@ -1355,7 +1383,7 @@ dependencies = [ "evalexpr", "hex", "moka", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "serde", "serde_json", @@ -1371,7 +1399,7 @@ name = "buzz-ws-client" version = "0.1.0" dependencies = [ "futures-util", - "nostr", + "nostr 0.44.7", "serde_json", "thiserror 2.0.18", "tokio", @@ -1819,7 +1847,7 @@ dependencies = [ "anyhow", "buzz-sdk", "futures-util", - "nostr", + "nostr 0.44.7", "serde_json", "tokio", "tokio-tungstenite 0.29.0", @@ -2070,43 +2098,16 @@ dependencies = [ "phf", ] -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - [[package]] name = "ctor" -version = "0.6.3" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e" +checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f" dependencies = [ - "ctor-proc-macro", - "dtor", + "link-section", + "linktime-proc-macro", ] -[[package]] -name = "ctor-proc-macro" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" - [[package]] name = "ctr" version = "0.9.2" @@ -2557,21 +2558,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dtor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - [[package]] name = "dunce" version = "1.0.5" @@ -2833,6 +2819,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -3219,7 +3215,7 @@ name = "git-credential-nostr" version = "0.1.0" dependencies = [ "base64 0.22.1", - "nostr", + "nostr 0.44.7", "serde_json", "zeroize", ] @@ -3232,7 +3228,7 @@ dependencies = [ "chrono", "hex", "libc", - "nostr", + "nostr 0.44.7", "serde_json", "zeroize", ] @@ -3312,6 +3308,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbag" version = "0.1.13" @@ -3370,6 +3375,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0049b265b7f201ca9ab25475b22b47fe444060126a51abe00f77d986fc5cc52e" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -3398,45 +3413,31 @@ dependencies = [ ] [[package]] -name = "hf-hub" -version = "1.0.0-rc.1" +name = "hex-conservative" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89305dc8fe34e165eaf0eb12b6e294e12381d9df9a431bcc52a5809bab4319" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" dependencies = [ - "base64 0.22.1", - "bon", - "bytes", - "futures", - "globset", - "hf-xet", - "hyper", - "pathdiff", - "reqwest 0.13.4", - "serde", - "serde_json", - "sha2 0.11.0", - "thiserror 2.0.18", - "tokio", - "tokio-retry", - "tokio-util", - "tracing", - "url", + "arrayvec", ] [[package]] name = "hf-xet" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430b33fa84f92796d4d263070b6c0d3ca219df7b9a0e1853ee431029b1612bcd" +checksum = "c237ef4fb0ce1962a5117f8bd8c74454b41629826a9df17d14a1840ca18f0754" dependencies = [ + "anyhow", "async-trait", "bytes", "http", "more-asserts", "serde", + "serde_json", "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "uuid", "xet-client", @@ -4174,7 +4175,7 @@ dependencies = [ "iroh-base", "iroh-dns", "iroh-metrics", - "lru 0.18.0", + "lru", "n0-error", "n0-future", "noq", @@ -4544,6 +4545,18 @@ dependencies = [ "bitflags 2.13.0", ] +[[package]] +name = "link-section" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee1a0d6e252afe82e7bc2db42fba60e02ddf3b1accaf8cb21d96e34ba61f3d4" + +[[package]] +name = "linktime-proc-macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348d0075b1fc163b26d72a7f75fc5141daf2fd1bdf128d873cbaf6785d495bdf" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4629,12 +4642,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" - [[package]] name = "lru" version = "0.18.0" @@ -4781,8 +4788,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "mesh-llm-client", @@ -4791,8 +4798,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -4802,13 +4809,13 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-llm-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -4839,8 +4846,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -4855,8 +4862,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -4865,8 +4872,8 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "clap", @@ -4877,12 +4884,9 @@ dependencies = [ [[package]] name = "mesh-llm-gpu-bench" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ - "anyhow", - "cc", - "libc", "serde", "serde_json", "tracing", @@ -4890,8 +4894,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -4899,16 +4903,45 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "mesh-llm-native-runtime", ] +[[package]] +name = "mesh-llm-hf-hub" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43088a838cf0c6715c65f65a5ac99045fd6d6e90949a8a4183b8104ab791e96b" +dependencies = [ + "base64 0.22.1", + "bon", + "bytes", + "futures", + "getrandom 0.2.17", + "globset", + "hf-xet", + "hyper", + "pathdiff", + "percent-encoding", + "reqwest 0.13.4", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "tokio-retry", + "tokio-util", + "tracing", + "url", + "wasm-bindgen-futures", +] + [[package]] name = "mesh-llm-host-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "argon2", @@ -4926,7 +4959,6 @@ dependencies = [ "flate2", "futures-util", "hex", - "hf-hub", "http", "http-body-util", "httparse", @@ -4941,6 +4973,7 @@ dependencies = [ "mesh-llm-config", "mesh-llm-events", "mesh-llm-guardrails", + "mesh-llm-hf-hub", "mesh-llm-identity", "mesh-llm-native-runtime", "mesh-llm-node", @@ -4953,6 +4986,7 @@ dependencies = [ "mesh-llm-types", "mesh-llm-ui", "mesh-mixture-of-agents", + "mesh-native-serving-plugin-host", "model-artifact", "model-hf", "model-package", @@ -5000,8 +5034,8 @@ dependencies = [ [[package]] name = "mesh-llm-identity" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "argon2", "base64 0.22.1", @@ -5022,8 +5056,8 @@ dependencies = [ [[package]] name = "mesh-llm-native-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "serde", @@ -5033,8 +5067,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-types", @@ -5047,8 +5081,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5064,8 +5098,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5083,8 +5117,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "hex", @@ -5094,18 +5128,27 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "mesh-llm-release-footer" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "hex", + "sha2 0.10.9", +] + [[package]] name = "mesh-llm-routing" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "iroh", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5127,8 +5170,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5142,8 +5185,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5153,8 +5196,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "chrono", @@ -5162,8 +5205,12 @@ dependencies = [ "dirs", "hex", "libc", + "libloading", "mesh-llm-build-info", "mesh-llm-gpu-bench", + "mesh-llm-native-runtime", + "mesh-llm-release-footer", + "mesh-llm-runtime-install", "reqwest 0.12.28", "semver", "serde", @@ -5176,8 +5223,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "serde", @@ -5187,13 +5234,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-mixture-of-agents" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5204,6 +5251,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "mesh-native-serving-plugin-api" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" + +[[package]] +name = "mesh-native-serving-plugin-host" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "anyhow", + "libloading", + "mesh-native-serving-plugin-api", + "skippy-server", +] + [[package]] name = "metrics" version = "0.24.6" @@ -5341,8 +5404,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5352,14 +5415,14 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", - "hf-hub", + "mesh-llm-hf-hub", "model-artifact", "model-ref", "serde", @@ -5370,14 +5433,14 @@ dependencies = [ [[package]] name = "model-package" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "bytes", "chrono", "futures", - "hf-hub", + "mesh-llm-hf-hub", "model-hf", "model-ref", "reqwest 0.12.28", @@ -5390,16 +5453,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "model-artifact", @@ -5777,6 +5840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" dependencies = [ "aes-gcm", + "aws-lc-rs", "bytes", "derive_more", "enum-assoc", @@ -5817,9 +5881,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" dependencies = [ "base64 0.22.1", - "bech32", + "bech32 0.11.1", "bip39", - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "cbc", "chacha20 0.9.1", "chacha20poly1305", @@ -5835,56 +5899,71 @@ dependencies = [ ] [[package]] -name = "nostr-database" -version = "0.44.0" +name = "nostr" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" +checksum = "5dde8c76076d334409d86c2e1db3e97abe5deb8cb92744f939cbc1fa45bd69e7" dependencies = [ - "lru 0.16.4", - "nostr", - "tokio", + "base64 0.22.1", + "bech32 0.12.0", + "bip39", + "bitcoin_hashes 1.2.0", + "cbc", + "chacha20 0.9.1", + "chacha20poly1305", + "faster-hex", + "opaquerr", + "rand 0.10.1", + "secp256k1 0.30.0", + "serde", + "serde_json", + "unicode-normalization", + "universal-time", + "url", + "zeroize", ] [[package]] -name = "nostr-gossip" -version = "0.44.0" +name = "nostr-database" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6" +checksum = "4b1fdb9fcba732e32719662afad1b267e50322dbe89e506017ec13f24361bddf" dependencies = [ - "nostr", + "nostr 0.45.1", + "opaquerr", ] [[package]] -name = "nostr-relay-pool" -version = "0.44.3" +name = "nostr-gossip" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "fa07539e52a71cb91fe0d693facaa298f03fcf9edcd66a521094e18e286e2336" dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "hex", - "lru 0.16.4", - "negentropy", - "nostr", - "nostr-database", - "tokio", - "tracing", + "nostr 0.45.1", + "opaquerr", ] [[package]] name = "nostr-sdk" -version = "0.44.1" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393" +checksum = "26c86342f367bd9b173ec4a697e936e3a82d6dad5b4aa06c0d35d9b4f88a8e72" dependencies = [ "async-utility", - "nostr", + "async-wsocket", + "faster-hex", + "futures", + "lru", + "negentropy", + "nostr 0.45.1", "nostr-database", "nostr-gossip", - "nostr-relay-pool", + "opaquerr", + "rand 0.10.1", "tokio", + "tokio-stream", "tracing", + "universal-time", ] [[package]] @@ -6165,10 +6244,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opaquerr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" + [[package]] name = "openai-frontend" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "axum", @@ -7532,7 +7617,7 @@ dependencies = [ "hashbrown 0.17.1", "itertools", "kasuari", - "lru 0.18.0", + "lru", "palette", "serde", "strum", @@ -8273,13 +8358,24 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.1", + "rand 0.8.6", + "secp256k1-sys 0.10.1", +] + [[package]] name = "secp256k1" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "rand 0.9.4", "secp256k1-sys 0.11.0", ] @@ -8577,7 +8673,6 @@ dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.17", "digest 0.10.7", - "sha2-asm", ] [[package]] @@ -8591,15 +8686,6 @@ dependencies = [ "digest 0.11.3", ] -[[package]] -name = "sha2-asm" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b845214d6175804686b2bd482bcffe96651bb2d1200742b712003504a2dac1ab" -dependencies = [ - "cc", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -8743,8 +8829,8 @@ checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "skippy-cache" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "blake3", @@ -8753,40 +8839,41 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "libloading", ] [[package]] name = "skippy-metrics" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "skippy-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "prost 0.14.3", "prost-build 0.14.3", "protoc-bin-vendored", "serde", + "skippy-tokenizer", ] [[package]] name = "skippy-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "libc", @@ -8799,8 +8886,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "ahash", "anyhow", @@ -8811,6 +8898,8 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-native-serving-plugin-api", + "model-artifact", "openai-frontend", "opentelemetry-proto 0.31.0", "serde", @@ -8820,16 +8909,25 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-tokenizer", "socket2", "tokio", "tokio-stream", "tonic", ] +[[package]] +name = "skippy-tokenizer" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "serde", +] + [[package]] name = "skippy-topology" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -9742,6 +9840,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-happy-eyeballs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8564c32dfb6f4257f8bc6edfc178a34af97520e0b7b9815500c55eb3d092f29f" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.7.0" @@ -9810,9 +9917,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -9820,7 +9927,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite 0.28.0", "webpki-roots 0.26.11", ] @@ -9860,6 +9967,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad543404f98bfc969aeb71994105c592acfc6c43323fddcd016bb208d1c65cb" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "bytes", "futures-core", @@ -9877,6 +9985,30 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio_with_wasm" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e40fbbbd95441133fe9483f522db15dbfd26dc636164ebd8f2dd28759a6aa6" +dependencies = [ + "js-sys", + "tokio", + "tokio_with_wasm_proc", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "tokio_with_wasm_proc" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01145a2c788d6aae4cd653afec1e8332534d7d783d01897cefcafe4428de992" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -10178,9 +10310,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -10357,6 +10489,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-time" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a939edecc3c5a7b83c02e5f6b3c31d2bc69eabcc9a87ab12c6d37ee6dbc856" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -11370,20 +11508,18 @@ dependencies = [ [[package]] name = "xet-client" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1e496dcbe6a09017acdfaf48e1a646735e7ff5b2a49e2c7e081cca77a59bc8" +checksum = "c3b8da8cc70aa2e3c500c0400e012df82c656ab9fca47f9f939fffc5afd89aca" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "clap", "crc32fast", "futures", "http", "hyper", - "lazy_static", "more-asserts", "rand 0.10.1", "redb", @@ -11397,8 +11533,8 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-retry", + "tokio_with_wasm", "tracing", - "tracing-subscriber", "url", "urlencoding", "web-time", @@ -11408,24 +11544,21 @@ dependencies = [ [[package]] name = "xet-core-structures" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb838aa8eb67d730af301584cf003caad407487606058292a6750711b603fbee" +checksum = "73503c223783dccc864abde22115e09d12f190448a0baf58ab2c54bc709e2f99" dependencies = [ "async-trait", "base64 0.22.1", "blake3", "bytemuck", "bytes", - "clap", "countio", - "csv", "futures", "futures-util", "getrandom 0.4.3", "heapify", "itertools", - "lazy_static", "lz4_flex", "more-asserts", "rand 0.10.1", @@ -11433,7 +11566,6 @@ dependencies = [ "safe-transmute", "serde", "static_assertions", - "tempfile", "thiserror 2.0.18", "tokio", "tokio-util", @@ -11445,32 +11577,31 @@ dependencies = [ [[package]] name = "xet-data" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fd409bef621411a9d9013798540bb8036cb2678f03ab39af89a5e88034ed8c" +checksum = "c89052ec5dec2187cad30b86af92cc24fd61c4a57a795f1ff7ff5f38d49184eb" dependencies = [ "anyhow", "async-trait", "bytes", "chrono", - "clap", "gearhash", "http", "itertools", - "lazy_static", "more-asserts", "rand 0.10.1", "serde", "serde_json", - "sha2 0.10.9", + "sha2 0.11.0", "tempfile", "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "url", "uuid", - "walkdir", + "web-time", "xet-client", "xet-core-structures", "xet-runtime", @@ -11478,9 +11609,9 @@ dependencies = [ [[package]] name = "xet-runtime" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d8f121c33866f7648b737abe70d0e2dd9c0af4ffdd7219207531d0283aa63d" +checksum = "af5c60d5eed38ab4c576f4421bae835e7bd07631fb381705605529d2015c106b" dependencies = [ "anyhow", "async-trait", @@ -11494,7 +11625,6 @@ dependencies = [ "git-version", "humantime", "konst", - "lazy_static", "libc", "more-asserts", "oneshot", @@ -11508,9 +11638,11 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "tracing-appender", "tracing-subscriber", + "web-time", "whoami", "winapi", ] diff --git a/Justfile b/Justfile index 0a43249d5f..2e62599dac 100644 --- a/Justfile +++ b/Justfile @@ -355,7 +355,6 @@ mesh-dev-fresh: mesh-e2e-hardware: #!/usr/bin/env bash set -euo pipefail - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" cargo run -p buzz-relay --example mesh_serve_client_smoke # Three isolated node processes: trusted member joins and infers; stranger is rejected. @@ -363,14 +362,12 @@ mesh-e2e-hardware: mesh-e2e-admission: #!/usr/bin/env bash set -euo pipefail - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" cargo run -p buzz-relay --example mesh_admission_smoke # Full hardware confidence suite: routing, owner admission, and real agent inference. mesh-e2e-confidence: #!/usr/bin/env bash set -euo pipefail - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" cargo build --release -p buzz-agent -p buzz-dev-mcp cargo run -p buzz-relay --example mesh_serve_client_smoke cargo run -p buzz-relay --example mesh_admission_smoke @@ -457,9 +454,6 @@ dev *ARGS: bootstrap _ensure-sidecar-stubs _ensure-migrations done fi cargo build -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr -p buzz-relay - if [[ -n "{{mesh}}" ]]; then - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" - fi # Docker Desktop's forwarded MinIO port can stall under the deployment # probe's 32 concurrent writers. Keep the gate enabled in local dev, using # the bounded profile already used by the relay test launcher. @@ -536,7 +530,6 @@ staging *ARGS: bootstrap _ensure-sidecar-stubs FEATURES=() if [[ -n "{{mesh}}" ]]; then FEATURES=(--features mesh-llm) - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" fi # Replace 0-byte sidecar stubs with real binaries so tauri dev picks them up. # buzz: the CLI sidecar. buzz-backend-kubernetes: provider discovery scans the @@ -572,7 +565,6 @@ production *ARGS: bootstrap _ensure-sidecar-stubs FEATURES=() if [[ -n "{{mesh}}" ]]; then FEATURES=(--features mesh-llm) - export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)" fi # Replace 0-byte sidecar stubs with real binaries so tauri dev picks them up. # buzz: the CLI sidecar. buzz-backend-kubernetes: provider discovery scans the diff --git a/crates/buzz-agent/src/config.rs b/crates/buzz-agent/src/config.rs index ecd2e88668..a6e1ac7542 100644 --- a/crates/buzz-agent/src/config.rs +++ b/crates/buzz-agent/src/config.rs @@ -833,12 +833,6 @@ pub struct Config { pub anthropic_api_version: String, /// OpenAI endpoint selection. See [`OpenAiApi`]. pub openai_api: OpenAiApi, - /// Prefer mesh-llm's virtual `mesh` model when the configured/effective - /// OpenAI model is `auto` and the live model catalog advertises it. - /// Set by Buzz's relay-mesh provider via - /// `BUZZ_AGENT_PREFER_MESH_FOR_AUTO=1`; other providers keep their - /// existing `auto` semantics. - pub prefer_mesh_for_auto: bool, pub hints_enabled: bool, /// Thinking/reasoning effort level. `None` = use provider default (no /// thinking config sent). Set via `BUZZ_AGENT_THINKING_EFFORT`. @@ -936,7 +930,6 @@ impl Config { base_url, anthropic_api_version: env_or("ANTHROPIC_API_VERSION", "2023-06-01"), openai_api, - prefer_mesh_for_auto: parse_env("BUZZ_AGENT_PREFER_MESH_FOR_AUTO", 0u8)? != 0, max_rounds: parse_env("BUZZ_AGENT_MAX_ROUNDS", 0)?, max_output_tokens: parse_env("BUZZ_AGENT_MAX_OUTPUT_TOKENS", 32_768)?, llm_timeout: Duration::from_secs(parse_env("BUZZ_AGENT_LLM_TIMEOUT_SECS", 240)?), @@ -988,7 +981,6 @@ impl Config { system_prompt: String::new(), anthropic_api_version: "2023-06-01".into(), openai_api: OpenAiApi::Chat, - prefer_mesh_for_auto: false, max_rounds: 0, max_output_tokens: 1, llm_timeout: Duration::from_secs(30), diff --git a/crates/buzz-agent/src/llm.rs b/crates/buzz-agent/src/llm.rs index c8c0a8550f..762d70eed9 100644 --- a/crates/buzz-agent/src/llm.rs +++ b/crates/buzz-agent/src/llm.rs @@ -4,8 +4,6 @@ use std::sync::Arc; use reqwest::Client; use serde_json::{json, Map, Value}; -use tokio::sync::Mutex; -use tokio::time::Instant; use crate::auth::{PkceOAuthConfig, PkceOAuthTokenSource, StaticTokenSource, TokenSource}; use crate::config::{ @@ -25,59 +23,11 @@ const DATABRICKS_OAUTH_SCOPES: &[&str] = &["all-apis", "offline_access"]; const MAX_LLM_RESPONSE_BYTES: usize = 16 * 1024 * 1024; const MAX_LLM_ERROR_BODY_BYTES: usize = 4 * 1024; const STALL_NOTICE_THRESHOLD: std::time::Duration = std::time::Duration::from_secs(300); -const MESH_VIRTUAL_MODEL_ID: &str = "mesh"; -const MESH_AUTO_MODEL_ID: &str = "auto"; -const MESH_AUTO_CATALOG_TTL: std::time::Duration = std::time::Duration::from_secs(5); -const MESH_AUTO_CATALOG_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(2); -const MESH_AUTO_COOLDOWN: std::time::Duration = std::time::Duration::from_secs(30); -const MESH_AUTO_ENABLE_OBSERVATIONS: u8 = 2; -const MESH_MOA_UNAVAILABLE_MESSAGE: &str = "MoA requires ≥2 models available in the mesh"; /// Parser for an OpenAI-family JSON response. Per-endpoint pair lives /// alongside its `_body` serializer. type OpenAiParse = fn(Value) -> Result; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum MeshCatalogObservation { - Available, - Unavailable, - Unknown, -} - -fn mesh_catalog_supports_collective(catalog: &Value) -> Option { - let models = catalog.get("data").and_then(Value::as_array)?; - let mut has_virtual_mesh = false; - let mut physical_models = BTreeSet::new(); - for id in models - .iter() - .filter_map(|model| model.get("id").and_then(Value::as_str)) - .map(str::trim) - .filter(|id| !id.is_empty()) - { - if id == MESH_VIRTUAL_MODEL_ID { - has_virtual_mesh = true; - } else if id != MESH_AUTO_MODEL_ID { - physical_models.insert(id.replace("@main", "")); - } - } - Some(has_virtual_mesh && physical_models.len() >= 2) -} - -fn looks_like_unstructured_tool_call(text: &str) -> bool { - let text = text.trim_start().to_ascii_lowercase(); - text.starts_with("<|tool_call") - || text.starts_with(", - consecutive_available: u8, - collective_enabled: bool, - cooldown_until: Option, -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum DatabricksV2Route { OpenAiResponses, @@ -92,10 +42,6 @@ pub struct Llm { /// == Auto`. Subsequent OpenAI calls then go straight to Responses /// for the lifetime of the process. auto_upgraded: AtomicBool, - /// Hysteretic view of whether mesh-llm currently advertises its virtual - /// Mixture-of-Agents model. A TTL, confirmation count, and failure cooldown - /// let long-running agents adapt without bouncing as peers briefly flap. - mesh_auto_state: Mutex, /// Bearer-token source for OpenAI-family requests. Static for OpenAI /// (the `OPENAI_COMPAT_API_KEY` env var) and Databricks-with-token /// (the `DATABRICKS_TOKEN` env var); a refreshable PKCE engine for @@ -125,7 +71,6 @@ impl Llm { Ok(Self { http, auto_upgraded: AtomicBool::new(false), - mesh_auto_state: Mutex::new(MeshAutoState::default()), auth, }) } @@ -162,37 +107,24 @@ impl Llm { .and_then(parse_openai_with_reasoning_details) } Provider::OpenAi | Provider::Databricks => { - self.openai_request( - cfg, - effective_model, - !tools.is_empty(), - |use_responses, request_model| { - // Normalize effort for model-specific availability. Startup no longer rejects - // `max` for pure OpenAI/Databricks; this per-model table is the single authority - // — it keeps `max` for gpt-5.6, clamps `max`→`xhigh` for other OpenAI-shaped - // models, and still applies corrections like none→minimal on the gpt-5 base. - let e = - effort.map(|ef| normalize_effort_for_openai_route(ef, request_model)); - if use_responses { - ( - responses_body( - cfg, - system_prompt, - history, - tools, - request_model, - e, - ), - parse_responses as OpenAiParse, - ) - } else { - ( - openai_body(cfg, system_prompt, history, tools, request_model, e), - parse_openai as OpenAiParse, - ) - } - }, - ) + self.openai_request(cfg, effective_model, |use_responses, request_model| { + // Normalize effort for model-specific availability. Startup no longer rejects + // `max` for pure OpenAI/Databricks; this per-model table is the single authority + // — it keeps `max` for gpt-5.6, clamps `max`→`xhigh` for other OpenAI-shaped + // models, and still applies corrections like none→minimal on the gpt-5 base. + let e = effort.map(|ef| normalize_effort_for_openai_route(ef, request_model)); + if use_responses { + ( + responses_body(cfg, system_prompt, history, tools, request_model, e), + parse_responses as OpenAiParse, + ) + } else { + ( + openai_body(cfg, system_prompt, history, tools, request_model, e), + parse_openai as OpenAiParse, + ) + } + }) .await } Provider::DatabricksV2 => { @@ -305,37 +237,32 @@ impl Llm { } Provider::OpenAi | Provider::Databricks => { let r = self - .openai_request( - cfg, - effective_model, - false, - |use_responses, request_model| { - if use_responses { - ( - json!({ - "model": request_model, - "max_output_tokens": max_output_tokens, - "instructions": system_prompt, - "input": user_prompt, - }), - parse_responses as OpenAiParse, - ) - } else { - ( - json!({ - "model": request_model, - "stream": false, - "max_completion_tokens": max_output_tokens, - "messages": [ - { "role": "system", "content": system_prompt }, - { "role": "user", "content": user_prompt }, - ], - }), - parse_openai as OpenAiParse, - ) - } - }, - ) + .openai_request(cfg, effective_model, |use_responses, request_model| { + if use_responses { + ( + json!({ + "model": request_model, + "max_output_tokens": max_output_tokens, + "instructions": system_prompt, + "input": user_prompt, + }), + parse_responses as OpenAiParse, + ) + } else { + ( + json!({ + "model": request_model, + "stream": false, + "max_completion_tokens": max_output_tokens, + "messages": [ + { "role": "system", "content": system_prompt }, + { "role": "user", "content": user_prompt }, + ], + }), + parse_openai as OpenAiParse, + ) + } + }) .await?; Ok(r.text) } @@ -396,7 +323,7 @@ impl Llm { async fn post_anthropic(&self, cfg: &Config, body: &Value) -> Result { let url = format!("{}/v1/messages", cfg.base_url.trim_end_matches('/')); - post(&self.http, &url, body, false, cfg.llm_timeout, |r| { + post(&self.http, &url, body, cfg.llm_timeout, |r| { r.header("x-api-key", &cfg.api_key) .header("anthropic-version", &cfg.anthropic_api_version) }) @@ -404,196 +331,21 @@ impl Llm { .map_err(PostError::into_agent) } - /// OpenAI dispatch with Buzz's relay-mesh `auto` policy layered over the - /// normal endpoint selection. When enabled, a live virtual `mesh` model is - /// preferred; if the mesh contracts between discovery and inference, retry - /// the same request once through the router's ordinary `auto` model. + /// OpenAI dispatch. The configured model is sent as given: callers that + /// route through a mesh (Buzz shared compute) resolve their own model name + /// before spawning the agent, so nothing here needs to know about meshes. async fn openai_request( &self, cfg: &Config, effective_model: &str, - tools_supplied: bool, mut build: F, ) -> Result where F: FnMut(bool, &str) -> (Value, OpenAiParse) + Send, { - let request_model = self.resolve_openai_model(cfg, effective_model).await; - let adaptive_mesh = - effective_model == MESH_AUTO_MODEL_ID && request_model == MESH_VIRTUAL_MODEL_ID; - - let first = self - .openai_request_for_model(cfg, &request_model, &mut build) - .await; - match first { - Err(PostError::MeshFallback(detail)) if adaptive_mesh => { - self.cool_down_collective().await; - tracing::warn!( - configured_model = effective_model, - attempted_model = MESH_VIRTUAL_MODEL_ID, - fallback_model = MESH_AUTO_MODEL_ID, - provider_message = detail, - "relay-mesh auto: collective request failed; retrying once with auto" - ); - self.openai_request_for_model(cfg, MESH_AUTO_MODEL_ID, &mut build) - .await - .map_err(PostError::into_agent) - } - Ok(response) - if adaptive_mesh - && tools_supplied - && response.tool_calls.is_empty() - && looks_like_unstructured_tool_call(&response.text) => - { - self.cool_down_collective().await; - tracing::warn!( - configured_model = effective_model, - attempted_model = MESH_VIRTUAL_MODEL_ID, - fallback_model = MESH_AUTO_MODEL_ID, - "relay-mesh auto: collective response emitted unstructured tool markup; retrying once with auto" - ); - self.openai_request_for_model(cfg, MESH_AUTO_MODEL_ID, &mut build) - .await - .map_err(PostError::into_agent) - } - Ok(response) => Ok(response), - Err(error) => Err(error.into_agent()), - } - } - - async fn cool_down_collective(&self) { - let now = Instant::now(); - let mut state = self.mesh_auto_state.lock().await; - state.last_checked = Some(now); - state.consecutive_available = 0; - state.collective_enabled = false; - state.cooldown_until = Some(now + MESH_AUTO_COOLDOWN); - } - - /// Resolve the model for one OpenAI-family request. Explicit model choices - /// are never changed. Relay-mesh `auto` dynamically follows the short-lived - /// `/models` catalog so long-running agents can adopt or leave MoA without - /// being restarted. - async fn resolve_openai_model(&self, cfg: &Config, effective_model: &str) -> String { - if cfg.provider != Provider::OpenAi - || !cfg.prefer_mesh_for_auto - || effective_model != MESH_AUTO_MODEL_ID - { - return effective_model.to_string(); - } - - let mut state = self.mesh_auto_state.lock().await; - let now = Instant::now(); - if let Some(cooldown_until) = state.cooldown_until { - if now < cooldown_until { - return MESH_AUTO_MODEL_ID.to_string(); - } - state.cooldown_until = None; - } - if state - .last_checked - .is_some_and(|checked_at| checked_at.elapsed() < MESH_AUTO_CATALOG_TTL) - { - return if state.collective_enabled { - MESH_VIRTUAL_MODEL_ID.to_string() - } else { - MESH_AUTO_MODEL_ID.to_string() - }; - } - - let observation = self.observe_mesh_virtual_model(cfg).await; - let checked_at = Instant::now(); - state.last_checked = Some(checked_at); - match observation { - MeshCatalogObservation::Available => { - state.consecutive_available = state.consecutive_available.saturating_add(1); - if state.consecutive_available >= MESH_AUTO_ENABLE_OBSERVATIONS { - state.collective_enabled = true; - } - } - MeshCatalogObservation::Unavailable => { - if state.collective_enabled { - state.cooldown_until = Some(checked_at + MESH_AUTO_COOLDOWN); - } - state.consecutive_available = 0; - state.collective_enabled = false; - } - // A failed catalog check is not evidence that a previously stable - // mesh disappeared. Preserve the last confirmed state; inference - // itself remains authoritative and has the narrow 503 fallback. - MeshCatalogObservation::Unknown => {} - } - let request_model = if state.collective_enabled { - MESH_VIRTUAL_MODEL_ID - } else { - MESH_AUTO_MODEL_ID - }; - tracing::debug!( - configured_model = effective_model, - request_model, - "relay-mesh auto: resolved request model from live catalog" - ); - request_model.to_string() - } - - async fn observe_mesh_virtual_model(&self, cfg: &Config) -> MeshCatalogObservation { - let url = format!("{}/models", cfg.base_url.trim_end_matches('/')); - let bearer = match self.auth.bearer().await { - Ok(bearer) => bearer, - Err(error) => { - tracing::debug!( - %error, - "relay-mesh auto: catalog auth unavailable; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - } - }; - let response = match self - .http - .get(&url) - .bearer_auth(bearer) - .timeout(MESH_AUTO_CATALOG_TIMEOUT) - .send() + self.openai_request_for_model(cfg, effective_model, &mut build) .await - { - Ok(response) => response, - Err(error) => { - tracing::debug!( - %error, - "relay-mesh auto: catalog probe failed; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - } - }; - if !response.status().is_success() { - tracing::debug!( - status = %response.status(), - "relay-mesh auto: catalog probe was not successful; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - } - match response.json::().await { - Ok(catalog) => { - let Some(available) = mesh_catalog_supports_collective(&catalog) else { - tracing::debug!( - "relay-mesh auto: catalog response has no data array; preserving last confirmed route" - ); - return MeshCatalogObservation::Unknown; - }; - if available { - MeshCatalogObservation::Available - } else { - MeshCatalogObservation::Unavailable - } - } - Err(error) => { - tracing::debug!( - %error, - "relay-mesh auto: invalid catalog response; preserving last confirmed route" - ); - MeshCatalogObservation::Unknown - } - } + .map_err(PostError::into_agent) } /// Dispatch one OpenAI request for an already-resolved model. Endpoint @@ -697,14 +449,9 @@ impl Llm { let mut bearer = self.auth.bearer().await.map_err(PostError::from)?; let mut refreshed = false; loop { - match post( - &self.http, - &url, - body_ref, - effective_model == MESH_VIRTUAL_MODEL_ID, - cfg.llm_timeout, - |r| r.bearer_auth(&bearer), - ) + match post(&self.http, &url, body_ref, cfg.llm_timeout, |r| { + r.bearer_auth(&bearer) + }) .await { Err(PostError::Agent(AgentError::LlmAuth(_))) if !refreshed => { @@ -1974,23 +1721,16 @@ fn terminal_llm_error(elapsed: std::time::Duration, attempts: u32, detail: &str) )) } -/// Internal HTTP failure that preserves a mesh-specific MoA failure as a -/// typed signal. This covers both pre-inference eligibility rejection and a -/// structured `moa_failure` from workers/reducers. It is consumed inside -/// `openai_request`: an adaptive `auto` call falls back once, while an explicit -/// `mesh` call is surfaced as the ordinary LLM error callers already -/// understand. +/// Internal HTTP failure wrapper for the OpenAI-family `post` helper. #[derive(Debug)] enum PostError { Agent(AgentError), - MeshFallback(String), } impl PostError { fn into_agent(self) -> AgentError { match self { Self::Agent(error) => error, - Self::MeshFallback(detail) => AgentError::Llm(detail), } } } @@ -2001,37 +1741,10 @@ impl From for PostError { } } -fn is_mesh_moa_unavailable_body(body: &str) -> bool { - serde_json::from_str::(body) - .ok() - .and_then(|value| { - value - .pointer("/error/message") - .and_then(Value::as_str) - .map(str::to_owned) - }) - .as_deref() - == Some(MESH_MOA_UNAVAILABLE_MESSAGE) -} - -fn is_mesh_moa_failure_body(body: &str) -> bool { - serde_json::from_str::(body) - .ok() - .and_then(|value| { - value - .pointer("/error/type") - .and_then(Value::as_str) - .map(str::to_owned) - }) - .as_deref() - == Some("moa_failure") -} - async fn post( http: &Client, url: &str, body: &Value, - detect_mesh_fallback: bool, base_timeout: std::time::Duration, apply: F, ) -> Result @@ -2095,13 +1808,6 @@ where } if status.is_server_error() || status == 429 || status.as_u16() == 499 { let body = read_error_body(resp).await; - let mesh_fallback = detect_mesh_fallback - && (status == reqwest::StatusCode::SERVICE_UNAVAILABLE - && is_mesh_moa_unavailable_body(&body) - || status.is_server_error() && is_mesh_moa_failure_body(&body)); - if mesh_fallback { - return Err(PostError::MeshFallback(format!("{status}: {body}"))); - } if attempt + 1 < MAX_RETRIES { tracing::warn!( attempt = attempt + 1, @@ -2810,7 +2516,6 @@ mod tests { base_url: "http://example.invalid".into(), anthropic_api_version: "2023-06-01".into(), openai_api: OpenAiApi::Chat, - prefer_mesh_for_auto: false, hints_enabled: true, thinking_effort: None, thinking_summary: ThinkingSummary::Auto, @@ -2848,24 +2553,6 @@ mod tests { }), } } - - fn moa_failure(status: u16, code: &str, message: &str) -> Self { - Self { - status, - body: json!({ - "choices": [{ - "finish_reason": "error", - "message": { "content": message, "role": "assistant" }, - }], - "error": { - "message": message, - "type": "moa_failure", - "code": code, - }, - "model": "mesh", - }), - } - } } async fn spawn_sequence_stub( @@ -2954,16 +2641,6 @@ mod tests { (base_url, captured) } - fn model_catalog(ids: &[&str]) -> Value { - json!({ - "object": "list", - "data": ids - .iter() - .map(|id| json!({ "id": id, "object": "model" })) - .collect::>(), - }) - } - fn chat_response(text: &str) -> Value { json!({ "choices": [{ @@ -2988,43 +2665,6 @@ mod tests { .await } - async fn complete_model_with_tool( - llm: &Llm, - cfg: &Config, - model: &str, - ) -> Result { - llm.complete( - cfg, - "system", - &[HistoryItem::User("add two numbers".into())], - &[ToolDef { - name: "add_numbers".into(), - description: "Add two numbers".into(), - input_schema: json!({ - "type": "object", - "properties": { - "a": {"type": "number"}, - "b": {"type": "number"} - }, - "required": ["a", "b"] - }), - }], - model, - ) - .await - } - - async fn expire_mesh_catalog_check(llm: &Llm) { - llm.mesh_auto_state.lock().await.last_checked = - Some(Instant::now() - MESH_AUTO_CATALOG_TTL); - } - - async fn expire_mesh_auto_cooldown(llm: &Llm) { - let mut state = llm.mesh_auto_state.lock().await; - state.last_checked = Some(Instant::now() - MESH_AUTO_CATALOG_TTL); - state.cooldown_until = Some(Instant::now() - std::time::Duration::from_secs(1)); - } - fn posted_models(requests: &[CapturedHttpRequest]) -> Vec<&str> { requests .iter() @@ -3033,381 +2673,59 @@ mod tests { .collect() } + /// An explicit model is sent verbatim and never rewritten to something + /// else. A server error is retried under the *same* model (the ordinary + /// transport retry) and then surfaced -- there is no second model to fall + /// back to now that MeshLLM resolves `mesh` server-side. #[tokio::test] - async fn mesh_auto_requires_two_stable_catalog_observations() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("direct")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("collective")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "direct" - ); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "collective" - ); - - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["auto", "mesh"]); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2 - ); - } - - #[tokio::test] - async fn mesh_auto_does_not_enable_while_second_model_flaps() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("one")), - StubHttpResponse::ok(model_catalog(&["model-a"])), - StubHttpResponse::ok(chat_response("two")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("three")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("four")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - for _ in 0..4 { - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - } - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "auto", "auto", "mesh"] - ); - } - - #[test] - fn collective_catalog_requires_two_distinct_physical_models() { - assert_eq!(mesh_catalog_supports_collective(&json!({})), None); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&[])), - Some(false) - ); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&["model-a", "mesh"])), - Some(false) - ); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&[ - "org/model@main:Q4", - "org/model:Q4", - "mesh" - ])), - Some(false), - "two spellings of one model are not collective capacity" - ); - assert_eq!( - mesh_catalog_supports_collective(&model_catalog(&["model-a", "model-b", "mesh"])), - Some(true) - ); - } - - #[tokio::test] - async fn mesh_auto_tracks_models_appearing_disappearing_and_reappearing() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&[])), - StubHttpResponse::ok(chat_response("zero")), - StubHttpResponse::ok(model_catalog(&["model-a", "mesh"])), - StubHttpResponse::ok(chat_response("one")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("two-first-observation")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("two-stable")), - StubHttpResponse::ok(model_catalog(&["model-a", "mesh"])), - StubHttpResponse::ok(chat_response("contracted")), - StubHttpResponse::ok(model_catalog(&[])), - StubHttpResponse::ok(chat_response("empty-again")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("rejoin-first-observation")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("rejoined-stable")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - for call in 0..8 { - if call == 5 { - expire_mesh_auto_cooldown(&llm).await; - } else if call > 0 { - expire_mesh_catalog_check(&llm).await; - } - complete_model(&llm, &config, "auto").await.unwrap(); - } - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "auto", "auto", "mesh", "auto", "auto", "auto", "mesh"] - ); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 8 - ); - } - - #[tokio::test] - async fn mesh_auto_falls_back_once_and_cools_down_when_mesh_contracts() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("warmup")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::error(503, MESH_MOA_UNAVAILABLE_MESSAGE), - StubHttpResponse::ok(chat_response("fallback")), - StubHttpResponse::ok(chat_response("cooldown")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "fallback" - ); - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "cooldown" - ); - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "mesh", "auto", "auto"] - ); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2, - "cooldown request must not re-probe the catalog" - ); - } - - #[tokio::test] - async fn mesh_auto_falls_back_once_when_moa_reducer_fails() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("warmup")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::moa_failure( - 502, - "all_reducers_failed", - "Reducer failed: unsupported chat template", - ), - StubHttpResponse::ok(chat_response("fallback")), - StubHttpResponse::ok(chat_response("cooldown")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "fallback" - ); - assert_eq!( - complete_model(&llm, &config, "auto").await.unwrap().text, - "cooldown" - ); - - let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "mesh", "auto", "auto"] - ); - assert_eq!( - requests - .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2, - "mesh-specific failure must enter cooldown without re-probing" - ); - } - - #[tokio::test] - async fn mesh_auto_retries_unstructured_tool_markup_through_auto() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response("warmup")), - StubHttpResponse::ok(model_catalog(&["model-a", "model-b", "mesh"])), - StubHttpResponse::ok(chat_response( - "<|tool_call>call:add_numbers{a:17,b:25}", - )), - StubHttpResponse::ok(chat_response("safe fallback")), - StubHttpResponse::ok(chat_response("cooldown")), - ]) + async fn explicit_models_are_never_rewritten_or_fallback_retried() { + let (base_url, captured) = spawn_sequence_stub( + (0..MAX_RETRIES) + .map(|_| StubHttpResponse::error(503, "upstream unavailable")) + .collect(), + ) .await; let mut config = cfg(Provider::OpenAi); config.base_url = base_url; - config.prefer_mesh_for_auto = true; let llm = Llm::new(&config).unwrap(); - complete_model(&llm, &config, "auto").await.unwrap(); - expire_mesh_catalog_check(&llm).await; - assert_eq!( - complete_model_with_tool(&llm, &config, "auto") - .await - .unwrap() - .text, - "safe fallback" - ); - assert_eq!( - complete_model_with_tool(&llm, &config, "auto") - .await - .unwrap() - .text, - "cooldown" - ); - + let error = complete_model(&llm, &config, "mesh").await.unwrap_err(); + assert!(error.to_string().contains("upstream unavailable")); let requests = captured.lock().await; - assert_eq!( - posted_models(&requests), - vec!["auto", "mesh", "auto", "auto"] - ); - assert_eq!( + assert!( requests .iter() - .filter(|request| request.path == "/v1/models") - .count(), - 2, - "pseudo tool markup must enter cooldown without another catalog probe" + .filter(|request| request.method == "POST") + .all(|request| request.body.as_ref().and_then(|b| b.get("model")) + == Some(&json!("mesh"))), + "every attempt must keep the explicit model: {:?}", + posted_models(&requests) ); - } - - #[tokio::test] - async fn mesh_auto_catalog_failure_fails_open_to_auto() { - let (base_url, captured) = spawn_sequence_stub(vec![ - StubHttpResponse::error(500, "catalog unavailable"), - StubHttpResponse::ok(chat_response("direct")), - ]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["auto"]); - } - - #[tokio::test] - async fn generic_openai_auto_does_not_probe_or_select_mesh() { - let (base_url, captured) = - spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("provider auto"))]).await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = false; - let llm = Llm::new(&config).unwrap(); - - complete_model(&llm, &config, "auto").await.unwrap(); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["auto"]); assert!(requests.iter().all(|request| request.path != "/v1/models")); } + /// Whatever model the config names is what goes on the wire -- including + /// `auto`, which is a real provider model name for plain OpenAI hosts. A + /// caller routing through a mesh resolves its own name before spawning the + /// agent, so there is nothing to rewrite here and no catalog to probe. #[tokio::test] - async fn explicit_models_are_never_rewritten_or_fallback_retried() { - let (base_url, captured) = spawn_sequence_stub(vec![StubHttpResponse::error( - 503, - MESH_MOA_UNAVAILABLE_MESSAGE, - )]) - .await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - let error = complete_model(&llm, &config, "mesh").await.unwrap_err(); - assert!(error.to_string().contains(MESH_MOA_UNAVAILABLE_MESSAGE)); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["mesh"]); - assert!(requests.iter().all(|request| request.path != "/v1/models")); - } - - #[tokio::test] - async fn explicit_real_model_bypasses_mesh_auto_policy() { - let (base_url, captured) = - spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("explicit"))]).await; - let mut config = cfg(Provider::OpenAi); - config.base_url = base_url; - config.prefer_mesh_for_auto = true; - let llm = Llm::new(&config).unwrap(); - - let response = complete_model(&llm, &config, "model-a").await.unwrap(); - assert_eq!(response.text, "explicit"); - let requests = captured.lock().await; - assert_eq!(posted_models(&requests), vec!["model-a"]); - assert!(requests.iter().all(|request| request.path != "/v1/models")); - } - - #[test] - fn mesh_unavailable_classifier_requires_exact_openai_error_message() { - assert!(is_mesh_moa_unavailable_body( - &StubHttpResponse::error(503, MESH_MOA_UNAVAILABLE_MESSAGE) - .body - .to_string() - )); - assert!(!is_mesh_moa_unavailable_body( - &StubHttpResponse::error(503, "some other outage") - .body - .to_string() - )); - } - - #[test] - fn mesh_failure_classifier_requires_structured_moa_failure_type() { - assert!(is_mesh_moa_failure_body( - &StubHttpResponse::moa_failure( - 502, - "all_reducers_failed", - "Reducer failed: unsupported chat template", - ) - .body - .to_string() - )); - assert!(!is_mesh_moa_failure_body( - &StubHttpResponse::error(502, "some other bad gateway") - .body - .to_string() - )); + async fn the_configured_model_is_sent_verbatim() { + for model in ["auto", "mesh", "unsloth/Qwen3-8B-GGUF:Q4_K_M"] { + let (base_url, captured) = + spawn_sequence_stub(vec![StubHttpResponse::ok(chat_response("ok"))]).await; + let mut config = cfg(Provider::OpenAi); + config.base_url = base_url; + let llm = Llm::new(&config).unwrap(); + + let response = complete_model(&llm, &config, model).await.unwrap(); + assert_eq!(response.text, "ok"); + let requests = captured.lock().await; + assert_eq!(posted_models(&requests), vec![model]); + assert!( + requests.iter().all(|request| request.path != "/v1/models"), + "no catalog probe belongs on this path" + ); + } } fn image_history() -> Vec { @@ -4685,7 +4003,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4756,7 +4073,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4814,7 +4130,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4912,7 +4227,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -4976,7 +4290,6 @@ mod tests { &client, &url, &serde_json::json!({}), - false, Duration::from_secs(5), |b| b, ) @@ -5145,7 +4458,6 @@ mod tests { &client, &url, &serde_json::json!({"model": "x"}), - false, // Small base timeout so the body stall triggers quickly. Duration::from_secs(1), |b| b, @@ -6210,7 +5522,6 @@ mod tests { .build() .unwrap(), auto_upgraded: std::sync::atomic::AtomicBool::new(false), - mesh_auto_state: Mutex::new(MeshAutoState::default()), auth, } } diff --git a/crates/buzz-relay/Cargo.toml b/crates/buzz-relay/Cargo.toml index 65fe32b6b3..825ef2de8e 100644 --- a/crates/buzz-relay/Cargo.toml +++ b/crates/buzz-relay/Cargo.toml @@ -84,8 +84,8 @@ async-compression = { version = "0.4.42", features = ["tokio", "gzip"] } dev = ["buzz-auth/dev"] [dev-dependencies] -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } # Relay-driven mesh lifecycle smoke (examples/mesh_relay_lifecycle_smoke.rs): # the relay client for discovery notes and the exact ed25519 the mesh owner # keys use for binding verification. diff --git a/crates/buzz-relay/examples/mesh_admission_smoke.rs b/crates/buzz-relay/examples/mesh_admission_smoke.rs index a541f35bbc..94c86c2a68 100644 --- a/crates/buzz-relay/examples/mesh_admission_smoke.rs +++ b/crates/buzz-relay/examples/mesh_admission_smoke.rs @@ -54,16 +54,11 @@ fn env(name: &str) -> anyhow::Result { std::env::var(name).map_err(|_| anyhow::anyhow!("{name} is required for this role")) } +/// Installs the signed release runtime when none is cached — the same path the +/// desktop takes. Deliberately no "is it installed?" precondition: this runs in +/// three separate processes, and a guard here would refuse before reaching the +/// call that does the installing. async fn init_native_runtime() -> anyhow::Result<()> { - let cache = mesh_llm_sdk::native_runtime::native_runtime_cache(None)?; - let current = mesh_llm_sdk::native_runtime::CURRENT_MESH_VERSION; - if !cache - .installed()? - .iter() - .any(|runtime| runtime.mesh_version == current) - { - anyhow::bail!("MeshLLM native runtime for MeshLLM {current} is not installed; run `just mesh-e2e-hardware` once to prepare it"); - } mesh_llm_host_runtime::initialize_host_runtime() .await .map_err(|error| anyhow::anyhow!("MeshLLM host runtime init failed: {error}")) diff --git a/crates/buzz-relay/examples/mesh_agent_e2e.rs b/crates/buzz-relay/examples/mesh_agent_e2e.rs index 345ca4c746..db0fc6090b 100644 --- a/crates/buzz-relay/examples/mesh_agent_e2e.rs +++ b/crates/buzz-relay/examples/mesh_agent_e2e.rs @@ -112,10 +112,12 @@ async fn run() -> anyhow::Result<()> { Err(e) => record("P1 explicit-model chat", false, e.to_string()), } - // P2: auto — router picks the model. + // P2: the virtual `mesh` model — exactly what apply_relay_mesh_env now puts + // on the wire for shared compute. With one served model there is no + // committee, so this proves MeshLLM's degrade_to_single_model path. let r = agent_chat( &base, - "auto", + "mesh", None, "Reply with exactly one word: PONG", &[], @@ -123,11 +125,15 @@ async fn run() -> anyhow::Result<()> { .await; match r { Ok(text) => record( - "P2 auto-model chat", + "P2 virtual-mesh chat (degrades to single model)", text.to_uppercase().contains("PONG"), text, ), - Err(e) => record("P2 auto-model chat", false, e.to_string()), + Err(e) => record( + "P2 virtual-mesh chat (degrades to single model)", + false, + e.to_string(), + ), } // P3: regression — an output budget no served model's context can hold @@ -139,7 +145,7 @@ async fn run() -> anyhow::Result<()> { // GLM-4.7-Flash. let r = agent_chat( &base, - &served_id, + "mesh", Some("150000"), "Reply with exactly one word: PONG", &[], @@ -169,7 +175,7 @@ async fn run() -> anyhow::Result<()> { ); let mcp = vec![("dev".to_string(), repo_bin("buzz-dev-mcp")?)]; let (r, marker) = - agent_chat_with_marker(&base, &served_id, None, &prompt, &mcp, &marker_name).await; + agent_chat_with_marker(&base, "mesh", None, &prompt, &mcp, &marker_name).await; let file_ok = std::fs::read_to_string(&marker) .map(|c| c.contains("BUZZ_OK")) .unwrap_or(false); @@ -270,7 +276,9 @@ async fn agent_chat_in_isolated_home( .env_clear() .env("PATH", std::env::var("PATH").unwrap_or_default()) .env("HOME", &home) - // Exactly the environment apply_relay_mesh_env() supplies. + // The transport subset of apply_relay_mesh_env(): provider, base URL, + // model, key, and chat API. Not BUZZ_AGENT_REQUIRE_REPLY, which needs + // Buzz's publish tools to mean anything. .env("BUZZ_AGENT_PROVIDER", "openai") .env("BUZZ_AGENT_MODEL", model) .env("OPENAI_COMPAT_BASE_URL", base) diff --git a/crates/buzz-relay/examples/mesh_serve_client_smoke.rs b/crates/buzz-relay/examples/mesh_serve_client_smoke.rs index 903d1a73c0..19e6c775d7 100644 --- a/crates/buzz-relay/examples/mesh_serve_client_smoke.rs +++ b/crates/buzz-relay/examples/mesh_serve_client_smoke.rs @@ -41,15 +41,11 @@ const CLIENT_CONSOLE_PORT: u16 = 13132; async fn main() -> anyhow::Result<()> { let model = std::env::var("MESH_SMOKE_MODEL").unwrap_or_else(|_| DEFAULT_MODEL.to_string()); eprintln!("[smoke] model: {model}"); - let cache = mesh_llm_sdk::native_runtime::native_runtime_cache(None)?; - let current = mesh_llm_sdk::native_runtime::CURRENT_MESH_VERSION; - if !cache - .installed()? - .iter() - .any(|runtime| runtime.mesh_version == current) - { - anyhow::bail!("MeshLLM native runtime for MeshLLM {current} is not installed; run `just mesh-e2e-hardware` to prepare it"); - } + // No cache precondition: `initialize_host_runtime` installs the signed + // release runtime itself when none is present, which is how the desktop + // gets one too. The guard that used to stand here refused before reaching + // this line and told the reader to run the very recipe that runs this + // example. mesh_llm_host_runtime::initialize_host_runtime() .await .map_err(|error| anyhow::anyhow!("MeshLLM host runtime init failed: {error}"))?; diff --git a/crates/buzz-test-client/tests/e2e_mesh_llm.rs b/crates/buzz-test-client/tests/e2e_mesh_llm.rs index 4b1bfb3d21..21a8ec29d7 100644 --- a/crates/buzz-test-client/tests/e2e_mesh_llm.rs +++ b/crates/buzz-test-client/tests/e2e_mesh_llm.rs @@ -11,7 +11,9 @@ //! # Running (manual / runbook) //! //! ```text -//! # 1. prepare the matching native runtime with `scripts/ensure-mesh-native-runtime.sh` +//! # 1. the mesh-enabled desktop installs the signed native runtime itself on +//! # first init; stale pre-0.75 cache entries are skipped rather than +//! # fatal (MeshLLM >= 0.75.1), so no manual cleanup is needed //! # 2. start the normal membership-gated relay and a mesh-enabled desktop //! # 3. have that desktop publish status, then run the trust assertions: //! RELAY_URL=ws://localhost:3000 \ diff --git a/deny.toml b/deny.toml index c432a20ea4..d3c5fcd4bc 100644 --- a/deny.toml +++ b/deny.toml @@ -15,10 +15,6 @@ ignore = [ # remove these when upstream catches up. { id = "RUSTSEC-2026-0194", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" }, { id = "RUSTSEC-2026-0195", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" }, - # nostr-relay-pool 0.44.3 — informational/unmaintained, not a vulnerability. - # Transitive via mesh-llm 0.74 → nostr-sdk 0.44.1. Remove after mesh-llm - # migrates to nostr-sdk >= 0.45, which absorbed the standalone relay pool. - { id = "RUSTSEC-2026-0243", reason = "transitive via mesh-llm; upstream nostr-sdk 0.45 migration requires API changes" }, ] [licenses] diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index ad7cfed53d..eb7227900b 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -447,20 +447,21 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069" +checksum = "2c713e1f14c7b82e32ea159af1c6e2f070cfadbdf23fb2512acce9af0a26f1a2" dependencies = [ - "async-utility", "futures", "futures-util", "js-sys", "tokio", + "tokio-happy-eyeballs", "tokio-rustls", "tokio-socks", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.28.0", "url", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -507,12 +508,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -762,6 +757,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "beef" version = "0.5.2" @@ -774,7 +775,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "serde", "unicode-normalization", ] @@ -815,7 +816,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2d6094e2a1ba3c93b5a596fe5a10d1a10c3c6e06785cde89f693a044c01aa40" dependencies = [ - "bitcoin-internals", + "bitcoin-internals 0.5.0", ] [[package]] @@ -827,6 +828,12 @@ dependencies = [ "hex-conservative 0.3.2", ] +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" version = "0.1.101" @@ -847,6 +854,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals 0.6.0", + "hex-conservative 1.2.0", + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1046,7 +1065,7 @@ dependencies = [ "chrono", "hex", "hmac 0.13.0", - "nostr", + "nostr 0.44.7", "percent-encoding", "rand 0.10.2", "serde", @@ -1100,7 +1119,7 @@ dependencies = [ "mesh-llm-sdk", "mesh-llm-system", "neteq", - "nostr", + "nostr 0.44.7", "notify-rust", "objc2", "objc2-app-kit", @@ -1168,7 +1187,7 @@ dependencies = [ "imagesize", "infer", "mp4", - "nostr", + "nostr 0.44.7", "rust-s3", "serde", "serde_json", @@ -1197,7 +1216,7 @@ name = "buzz-sdk" version = "0.1.0" dependencies = [ "buzz-core", - "nostr", + "nostr 0.44.7", "serde", "serde_json", "thiserror 2.0.18", @@ -2819,6 +2838,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -3562,6 +3591,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -3620,6 +3658,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0049b265b7f201ca9ab25475b22b47fe444060126a51abe00f77d986fc5cc52e" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -3663,29 +3711,12 @@ dependencies = [ ] [[package]] -name = "hf-hub" -version = "1.0.0-rc.1" +name = "hex-conservative" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89305dc8fe34e165eaf0eb12b6e294e12381d9df9a431bcc52a5809bab4319" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" dependencies = [ - "base64 0.22.1", - "bon", - "bytes", - "futures", - "globset", - "hf-xet", - "hyper", - "pathdiff", - "reqwest 0.13.4", - "serde", - "serde_json", - "sha2 0.11.0", - "thiserror 2.0.18", - "tokio", - "tokio-retry", - "tokio-util", - "tracing", - "url", + "arrayvec", ] [[package]] @@ -4448,7 +4479,7 @@ dependencies = [ "iroh-base", "iroh-dns", "iroh-metrics", - "lru 0.18.1", + "lru", "n0-error", "n0-future", "noq", @@ -4930,12 +4961,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" - [[package]] name = "lru" version = "0.18.1" @@ -5133,8 +5158,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "mesh-llm-client", @@ -5143,8 +5168,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5154,13 +5179,13 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-llm-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5191,8 +5216,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5207,8 +5232,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -5217,8 +5242,8 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "clap", @@ -5229,12 +5254,9 @@ dependencies = [ [[package]] name = "mesh-llm-gpu-bench" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ - "anyhow", - "cc", - "libc", "serde", "serde_json", "tracing", @@ -5242,8 +5264,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -5251,16 +5273,45 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "mesh-llm-native-runtime", ] +[[package]] +name = "mesh-llm-hf-hub" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43088a838cf0c6715c65f65a5ac99045fd6d6e90949a8a4183b8104ab791e96b" +dependencies = [ + "base64 0.22.1", + "bon", + "bytes", + "futures", + "getrandom 0.2.17", + "globset", + "hf-xet", + "hyper", + "pathdiff", + "percent-encoding", + "reqwest 0.13.4", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "tokio-retry", + "tokio-util", + "tracing", + "url", + "wasm-bindgen-futures", +] + [[package]] name = "mesh-llm-host-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "argon2", @@ -5278,7 +5329,6 @@ dependencies = [ "flate2", "futures-util", "hex", - "hf-hub", "http", "http-body-util", "httparse", @@ -5293,6 +5343,7 @@ dependencies = [ "mesh-llm-config", "mesh-llm-events", "mesh-llm-guardrails", + "mesh-llm-hf-hub", "mesh-llm-identity", "mesh-llm-native-runtime", "mesh-llm-node", @@ -5305,6 +5356,7 @@ dependencies = [ "mesh-llm-types", "mesh-llm-ui", "mesh-mixture-of-agents", + "mesh-native-serving-plugin-host", "model-artifact", "model-hf", "model-package", @@ -5352,8 +5404,8 @@ dependencies = [ [[package]] name = "mesh-llm-identity" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "argon2", "base64 0.22.1", @@ -5374,8 +5426,8 @@ dependencies = [ [[package]] name = "mesh-llm-native-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "serde", @@ -5385,8 +5437,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-types", @@ -5399,8 +5451,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5416,8 +5468,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5435,8 +5487,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "hex", @@ -5446,18 +5498,27 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "mesh-llm-release-footer" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "hex", + "sha2 0.10.9", +] + [[package]] name = "mesh-llm-routing" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "iroh", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5479,8 +5540,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5494,8 +5555,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "dirs", @@ -5505,8 +5566,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "chrono", @@ -5514,8 +5575,12 @@ dependencies = [ "dirs", "hex", "libc", + "libloading 0.8.9", "mesh-llm-build-info", "mesh-llm-gpu-bench", + "mesh-llm-native-runtime", + "mesh-llm-release-footer", + "mesh-llm-runtime-install", "reqwest 0.12.28", "semver", "serde", @@ -5528,8 +5593,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "hex", "serde", @@ -5539,13 +5604,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "mesh-mixture-of-agents" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5556,6 +5621,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "mesh-native-serving-plugin-api" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" + +[[package]] +name = "mesh-native-serving-plugin-host" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "anyhow", + "libloading 0.8.9", + "mesh-native-serving-plugin-api", + "skippy-server", +] + [[package]] name = "miette" version = "7.6.0" @@ -5648,8 +5729,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", @@ -5659,14 +5740,14 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", - "hf-hub", + "mesh-llm-hf-hub", "model-artifact", "model-ref", "serde", @@ -5677,14 +5758,14 @@ dependencies = [ [[package]] name = "model-package" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "bytes", "chrono", "futures", - "hf-hub", + "mesh-llm-hf-hub", "model-hf", "model-ref", "reqwest 0.12.28", @@ -5697,16 +5778,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "model-artifact", @@ -6160,6 +6241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" dependencies = [ "aes-gcm", + "aws-lc-rs", "bytes", "derive_more", "enum-assoc", @@ -6200,9 +6282,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" dependencies = [ "base64 0.22.1", - "bech32", + "bech32 0.11.1", "bip39", - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "cbc", "chacha20 0.9.1", "chacha20poly1305", @@ -6210,7 +6292,7 @@ dependencies = [ "hex", "instant", "scrypt", - "secp256k1", + "secp256k1 0.29.1", "serde", "serde_json", "unicode-normalization", @@ -6218,56 +6300,71 @@ dependencies = [ ] [[package]] -name = "nostr-database" -version = "0.44.0" +name = "nostr" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" +checksum = "5dde8c76076d334409d86c2e1db3e97abe5deb8cb92744f939cbc1fa45bd69e7" dependencies = [ - "lru 0.16.4", - "nostr", - "tokio", + "base64 0.22.1", + "bech32 0.12.0", + "bip39", + "bitcoin_hashes 1.2.0", + "cbc", + "chacha20 0.9.1", + "chacha20poly1305", + "faster-hex", + "opaquerr", + "rand 0.10.2", + "secp256k1 0.30.0", + "serde", + "serde_json", + "unicode-normalization", + "universal-time", + "url", + "zeroize", ] [[package]] -name = "nostr-gossip" -version = "0.44.0" +name = "nostr-database" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6" +checksum = "4b1fdb9fcba732e32719662afad1b267e50322dbe89e506017ec13f24361bddf" dependencies = [ - "nostr", + "nostr 0.45.1", + "opaquerr", ] [[package]] -name = "nostr-relay-pool" -version = "0.44.3" +name = "nostr-gossip" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "fa07539e52a71cb91fe0d693facaa298f03fcf9edcd66a521094e18e286e2336" dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "hex", - "lru 0.16.4", - "negentropy", - "nostr", - "nostr-database", - "tokio", - "tracing", + "nostr 0.45.1", + "opaquerr", ] [[package]] name = "nostr-sdk" -version = "0.44.1" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393" +checksum = "26c86342f367bd9b173ec4a697e936e3a82d6dad5b4aa06c0d35d9b4f88a8e72" dependencies = [ "async-utility", - "nostr", + "async-wsocket", + "faster-hex", + "futures", + "lru", + "negentropy", + "nostr 0.45.1", "nostr-database", "nostr-gossip", - "nostr-relay-pool", + "opaquerr", + "rand 0.10.2", "tokio", + "tokio-stream", "tracing", + "universal-time", ] [[package]] @@ -6772,6 +6869,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opaquerr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" + [[package]] name = "open" version = "5.3.6" @@ -6785,8 +6888,8 @@ dependencies = [ [[package]] name = "openai-frontend" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "async-trait", "axum", @@ -8179,7 +8282,7 @@ dependencies = [ "hashbrown 0.17.1", "itertools", "kasuari", - "lru 0.18.1", + "lru", "palette", "serde", "strum", @@ -9009,6 +9112,17 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.101", + "rand 0.8.6", + "secp256k1-sys", +] + [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -9565,8 +9679,8 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skippy-cache" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "blake3", @@ -9575,40 +9689,41 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "libloading 0.8.9", ] [[package]] name = "skippy-metrics" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" [[package]] name = "skippy-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "prost 0.14.4", "prost-build 0.14.4", "protoc-bin-vendored", "serde", + "skippy-tokenizer", ] [[package]] name = "skippy-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "anyhow", "libc", @@ -9621,8 +9736,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "ahash", "anyhow", @@ -9633,6 +9748,8 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-native-serving-plugin-api", + "model-artifact", "openai-frontend", "opentelemetry-proto", "serde", @@ -9642,16 +9759,25 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-tokenizer", "socket2", "tokio", "tokio-stream", "tonic", ] +[[package]] +name = "skippy-tokenizer" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +dependencies = [ + "serde", +] + [[package]] name = "skippy-topology" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.75.1" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" dependencies = [ "serde", "serde_json", @@ -10972,6 +11098,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-happy-eyeballs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8564c32dfb6f4257f8bc6edfc178a34af97520e0b7b9815500c55eb3d092f29f" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.7.0" @@ -11040,9 +11175,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -11050,7 +11185,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite 0.28.0", "webpki-roots 0.26.11", ] @@ -11090,6 +11225,7 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52efb639344a7c6adb8e62c6f3d2c19c001ff1b79a5041ba1c6ed42e19c6aa5" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "bytes", "futures-core", @@ -11480,9 +11616,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -11700,6 +11836,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-time" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a939edecc3c5a7b83c02e5f6b3c31d2bc69eabcc9a87ab12c6d37ee6dbc856" + [[package]] name = "unsafe-libyaml" version = "0.2.11" diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 9b2de6a575..b653c6618b 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -109,14 +109,14 @@ buzz_voice_pkg = { package = "buzz-voice", path = "../../crates/buzz-voice" } buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" } portable-pty = "0.9" iroh = { version = "1.0.2", optional = true } -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } # Model catalog + hardware survey for the Share-compute model picker (same # diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client. -mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-client", optional = true } -mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-node", optional = true } -mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-system", optional = true } -mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-events", optional = true } +mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-client", optional = true } +mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-node", optional = true } +mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-system", optional = true } +mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-events", optional = true } base64 = "0.22" sha2 = "0.11" tar = "0.4" diff --git a/desktop/src-tauri/src/commands/mesh_llm.rs b/desktop/src-tauri/src/commands/mesh_llm.rs index 528ca38767..7356cd7fc0 100644 --- a/desktop/src-tauri/src/commands/mesh_llm.rs +++ b/desktop/src-tauri/src/commands/mesh_llm.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use sha2::{Digest, Sha256}; use tauri::{AppHandle, Manager, State}; +use super::mesh_readiness::wait_for_mesh_inference; use crate::{app_state::AppState, mesh_llm, relay}; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -530,142 +531,6 @@ pub async fn mesh_start_node( Ok(status) } -/// Mesh can bind its HTTP ingress and advertise a model shortly before the -/// router has installed a usable target. Probe the exact chat path agents use -/// so startup cannot race that gap (`single target None unavailable`). -/// Which startup stage a mesh client is stuck at when it never becomes -/// inference-ready. The two live-observed failure modes are physically -/// distinct and want different user copy: -/// -/// * `CatalogNeverSynced` — the local client node came up and connected to -/// the host at the control level (ping/RTT fine), but the served model -/// never appeared in the local `/v1/models` catalog. That catalog is -/// populated by the peer gossip exchange; when the gossip bi-stream can't -/// establish across the network (observed as iroh -/// `MultipathNotNegotiated` / unreachable direct path), the catalog stays -/// empty forever and every request is rejected "model not available". -/// Root cause is the network path between this machine and the host. -/// * `RoutingNeverCompleted` — the model *did* sync into the catalog, but -/// inference requests never completed (routing/transport to the host -/// failing per-request). The host is discoverable and advertised but not -/// actually serving us. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum MeshReadinessFailure { - CatalogNeverSynced, - RoutingNeverCompleted, -} - -/// Pure classifier: given whether the served model was ever observed in the -/// local `/v1/models` catalog during the wait, decide which stage failed. -/// Split out so the diagnosis is unit-testable without a live mesh. -fn classify_mesh_readiness_failure(model_ever_visible: bool) -> MeshReadinessFailure { - if model_ever_visible { - MeshReadinessFailure::RoutingNeverCompleted - } else { - MeshReadinessFailure::CatalogNeverSynced - } -} - -/// Actionable, non-technical copy for a readiness failure. `last_detail` is the -/// last raw transport/HTTP error, appended for support triage. -fn mesh_readiness_failure_message( - failure: MeshReadinessFailure, - model_id: &str, - last_detail: &str, -) -> String { - match failure { - MeshReadinessFailure::CatalogNeverSynced => format!( - "Buzz shared compute connected to the serving member but could not sync \ - the model list for \"{model_id}\" — this is a network path problem \ - between this machine and the host (the compute node is reachable for \ - pings but the model-sync stream did not establish). Try again, or have \ - the host and this machine on a more direct network. (last: {last_detail})" - ), - MeshReadinessFailure::RoutingNeverCompleted => format!( - "Buzz shared compute found \"{model_id}\" on a serving member but inference \ - requests did not complete — the host is discoverable but not currently \ - reachable for requests. Try again shortly. (last: {last_detail})" - ), - } -} - -/// Poll the local mesh OpenAI ingress until a real inference for `model_id` -/// succeeds, or a deadline elapses. On failure, returns a stage-specific, -/// actionable message (see [`MeshReadinessFailure`]) rather than a raw -/// `HTTP 429`, so the UI can tell "still warming up" apart from "can't reach -/// the host". -async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(30)) - .build() - .map_err(|error| format!("failed to build mesh readiness client: {error}"))?; - let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(120); - let models_url = format!("{}/models", crate::managed_agents::RELAY_MESH_API_BASE_URL); - let chat_url = format!( - "{}/chat/completions", - crate::managed_agents::RELAY_MESH_API_BASE_URL - ); - let mut last_error = "mesh inference is not ready".to_string(); - // Track whether the served model ever reached the local catalog — the - // signal that splits "catalog never synced" from "routing never completed". - let mut model_ever_visible = false; - - while tokio::time::Instant::now() < deadline { - // Refresh catalog visibility. "auto" delegates model choice to the - // router, so any advertised model counts as the catalog having synced. - if let Ok(response) = client - .get(&models_url) - .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) - .send() - .await - { - if let Ok(body) = response.json::().await { - if let Some(data) = body.get("data").and_then(|d| d.as_array()) { - let wanted = model_id.trim().replace("@main", ""); - let visible = !data.is_empty() - && (model_id == crate::mesh_llm::AUTO_MODEL_ID - || data.iter().any(|m| { - m.get("id") - .and_then(|id| id.as_str()) - .map(|id| id.replace("@main", "") == wanted) - .unwrap_or(false) - })); - model_ever_visible |= visible; - } - } - } - - match client - .post(&chat_url) - .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) - .json(&serde_json::json!({ - "model": model_id, - "messages": [{"role": "user", "content": "Reply OK"}], - "max_tokens": 1, - "stream": false - })) - .send() - .await - { - Ok(response) if response.status().is_success() => return Ok(()), - Ok(response) => { - let status = response.status(); - let body = response.text().await.unwrap_or_default(); - last_error = format!("HTTP {status}: {body}"); - } - Err(error) => last_error = error.to_string(), - } - tokio::time::sleep(std::time::Duration::from_secs(2)).await; - } - - let failure = classify_mesh_readiness_failure(model_ever_visible); - Err(mesh_readiness_failure_message( - failure, - model_id, - &last_error, - )) -} - pub(crate) async fn ensure_client_node_for_model( state: &AppState, model_id: impl AsRef, diff --git a/desktop/src-tauri/src/commands/mesh_llm_tests.rs b/desktop/src-tauri/src/commands/mesh_llm_tests.rs index 26eb1f5fba..c4e1ae2e42 100644 --- a/desktop/src-tauri/src/commands/mesh_llm_tests.rs +++ b/desktop/src-tauri/src/commands/mesh_llm_tests.rs @@ -178,42 +178,6 @@ fn role_switch_checkpoint_starts_exactly_once_after_restart() { assert_eq!(consumed.relay_url, config.relay_url); } -#[test] -fn readiness_failure_is_catalog_sync_when_model_never_visible() { - assert_eq!( - classify_mesh_readiness_failure(false), - MeshReadinessFailure::CatalogNeverSynced - ); -} - -#[test] -fn readiness_failure_is_routing_when_model_was_visible() { - assert_eq!( - classify_mesh_readiness_failure(true), - MeshReadinessFailure::RoutingNeverCompleted - ); -} - -#[test] -fn readiness_messages_are_distinct_and_actionable() { - let catalog = mesh_readiness_failure_message( - MeshReadinessFailure::CatalogNeverSynced, - "auto", - "HTTP 429", - ); - let routing = mesh_readiness_failure_message( - MeshReadinessFailure::RoutingNeverCompleted, - "auto", - "HTTP 503", - ); - // Distinct diagnoses, each names the model and carries the raw detail. - assert_ne!(catalog, routing); - assert!(catalog.contains("network path")); - assert!(catalog.contains("HTTP 429")); - assert!(routing.contains("did not complete")); - assert!(routing.contains("HTTP 503")); -} - #[test] fn mesh_status_cursor_uses_relay_composite_tiebreak() { let event = nostr::EventBuilder::new(nostr::Kind::TextNote, "status") diff --git a/desktop/src-tauri/src/commands/mesh_readiness.rs b/desktop/src-tauri/src/commands/mesh_readiness.rs new file mode 100644 index 0000000000..023695a6f9 --- /dev/null +++ b/desktop/src-tauri/src/commands/mesh_readiness.rs @@ -0,0 +1,251 @@ +//! Startup readiness for Buzz shared compute. +//! +//! Mesh can bind its HTTP ingress and advertise a model shortly before the +//! router has installed a usable target. These helpers probe the exact chat +//! path agents use, so startup cannot race that gap +//! (`single target None unavailable`), and classify a timeout into copy that +//! names the actual stage rather than a raw `HTTP 429`. + +use super::CmdResult; + +/// Which startup stage a mesh client is stuck at when it never becomes +/// inference-ready. The two live-observed failure modes are physically +/// distinct and want different user copy: +/// +/// * `CatalogNeverSynced` — the local client node came up and connected to +/// the host at the control level (ping/RTT fine), but the served model +/// never appeared in the local `/v1/models` catalog. That catalog is +/// populated by the peer gossip exchange; when the gossip bi-stream can't +/// establish across the network (observed as iroh +/// `MultipathNotNegotiated` / unreachable direct path), the catalog stays +/// empty forever and every request is rejected "model not available". +/// Root cause is the network path between this machine and the host. +/// * `RoutingNeverCompleted` — the model *did* sync into the catalog, but +/// inference requests never completed (routing/transport to the host +/// failing per-request). The host is discoverable and advertised but not +/// actually serving us. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum MeshReadinessFailure { + CatalogNeverSynced, + RoutingNeverCompleted, +} + +/// Pure classifier: given whether the served model was ever observed in the +/// local `/v1/models` catalog during the wait, decide which stage failed. +/// Split out so the diagnosis is unit-testable without a live mesh. +/// Whether the catalog has synced enough to count, for the model actually being +/// requested (a wire name — see `relay_mesh_wire_model`). +/// +/// The virtual `mesh` model delegates the choice to the router, so any +/// advertised model proves the catalog synced. It has to work that way: MeshLLM +/// only advertises `mesh` itself once two non-virtual models are reachable +/// (`should_advertise_virtual_mesh`), so requiring it by name would leave a +/// single-worker mesh looking permanently unsynced and misreport a slow model +/// load as a network path problem. +fn mesh_catalog_shows_model(advertised: &[String], wire_model: &str) -> bool { + if advertised.is_empty() { + return false; + } + if wire_model == crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID { + return true; + } + let wanted = wire_model.trim().replace("@main", ""); + advertised + .iter() + .any(|id| id.replace("@main", "") == wanted) +} + +fn classify_mesh_readiness_failure(model_ever_visible: bool) -> MeshReadinessFailure { + if model_ever_visible { + MeshReadinessFailure::RoutingNeverCompleted + } else { + MeshReadinessFailure::CatalogNeverSynced + } +} + +/// Actionable, non-technical copy for a readiness failure. `last_detail` is the +/// last raw transport/HTTP error, appended for support triage. +fn mesh_readiness_failure_message( + failure: MeshReadinessFailure, + model_id: &str, + last_detail: &str, +) -> String { + match failure { + MeshReadinessFailure::CatalogNeverSynced => format!( + "Buzz shared compute connected to the serving member but could not sync \ + the model list for \"{model_id}\" — this is a network path problem \ + between this machine and the host (the compute node is reachable for \ + pings but the model-sync stream did not establish). Try again, or have \ + the host and this machine on a more direct network. (last: {last_detail})" + ), + MeshReadinessFailure::RoutingNeverCompleted => format!( + "Buzz shared compute found \"{model_id}\" on a serving member but inference \ + requests did not complete — the host is discoverable but not currently \ + reachable for requests. Try again shortly. (last: {last_detail})" + ), + } +} + +/// Poll the local mesh OpenAI ingress until a real inference for `model_id` +/// succeeds, or a deadline elapses. On failure, returns a stage-specific, +/// actionable message (see [`MeshReadinessFailure`]) rather than a raw +/// `HTTP 429`, so the UI can tell "still warming up" apart from "can't reach +/// the host". +pub(crate) async fn wait_for_mesh_inference(model_id: &str) -> CmdResult<()> { + // Probe the name that will actually be requested. Callers pass a stored + // value, which for shared-compute `auto` is not a model the mesh + // advertises: probing it would validate a route no agent uses, and could + // fail readiness while the real route works. Named models pass through + // unchanged, so this is safe for the serve-side callers too. + let requested_model = model_id; + let model_id = crate::managed_agents::relay_mesh_wire_model(model_id); + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .build() + .map_err(|error| format!("failed to build mesh readiness client: {error}"))?; + let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(120); + let models_url = format!("{}/models", crate::managed_agents::RELAY_MESH_API_BASE_URL); + let chat_url = format!( + "{}/chat/completions", + crate::managed_agents::RELAY_MESH_API_BASE_URL + ); + let mut last_error = "mesh inference is not ready".to_string(); + // Track whether the served model ever reached the local catalog — the + // signal that splits "catalog never synced" from "routing never completed". + let mut model_ever_visible = false; + + while tokio::time::Instant::now() < deadline { + // Refresh catalog visibility. The virtual `mesh` model delegates the + // choice to the router, so any advertised model counts as the catalog + // having synced — and it must, because MeshLLM only advertises `mesh` + // itself once two non-virtual models are reachable + // (`should_advertise_virtual_mesh`). Requiring it by name would leave a + // single-worker mesh looking permanently unsynced. + if let Ok(response) = client + .get(&models_url) + .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) + .send() + .await + { + if let Ok(body) = response.json::().await { + if let Some(data) = body.get("data").and_then(|d| d.as_array()) { + let advertised: Vec = data + .iter() + .filter_map(|m| m.get("id").and_then(|id| id.as_str())) + .map(str::to_owned) + .collect(); + model_ever_visible |= mesh_catalog_shows_model(&advertised, model_id); + } + } + } + + match client + .post(&chat_url) + .bearer_auth(crate::managed_agents::RELAY_MESH_API_KEY_PLACEHOLDER) + .json(&serde_json::json!({ + "model": model_id, + "messages": [{"role": "user", "content": "Reply OK"}], + "max_tokens": 1, + "stream": false + })) + .send() + .await + { + Ok(response) if response.status().is_success() => return Ok(()), + Ok(response) => { + let status = response.status(); + let body = response.text().await.unwrap_or_default(); + last_error = format!("HTTP {status}: {body}"); + } + Err(error) => last_error = error.to_string(), + } + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + } + + let failure = classify_mesh_readiness_failure(model_ever_visible); + Err(mesh_readiness_failure_message( + failure, + requested_model, + &last_error, + )) +} +#[cfg(test)] +mod tests { + use super::*; + + /// The regression this guards: MeshLLM only advertises the virtual `mesh` model + /// once two non-virtual models are reachable, so a single-worker mesh never + /// lists it by name. Keying visibility on the literal name would report a lone + /// host that is still loading weights as a network path problem. + #[test] + fn virtual_mesh_counts_any_advertised_model_as_a_synced_catalog() { + let one_worker = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; + assert!(mesh_catalog_shows_model( + &one_worker, + crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID + )); + } + + #[test] + fn an_empty_catalog_is_never_synced_even_for_the_virtual_model() { + assert!(!mesh_catalog_shows_model( + &[], + crate::managed_agents::RELAY_MESH_VIRTUAL_MODEL_ID + )); + } + + #[test] + fn a_named_model_must_actually_be_advertised() { + let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M".to_string()]; + assert!(mesh_catalog_shows_model( + &advertised, + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + )); + assert!(!mesh_catalog_shows_model(&advertised, "some/other-model")); + } + + #[test] + fn a_named_model_ignores_the_main_revision_suffix() { + let advertised = vec!["unsloth/gemma-4-E4B-it-GGUF:Q4_K_M@main".to_string()]; + assert!(mesh_catalog_shows_model( + &advertised, + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + )); + } + + #[test] + fn readiness_failure_is_catalog_sync_when_model_never_visible() { + assert_eq!( + classify_mesh_readiness_failure(false), + MeshReadinessFailure::CatalogNeverSynced + ); + } + + #[test] + fn readiness_failure_is_routing_when_model_was_visible() { + assert_eq!( + classify_mesh_readiness_failure(true), + MeshReadinessFailure::RoutingNeverCompleted + ); + } + + #[test] + fn readiness_messages_are_distinct_and_actionable() { + let catalog = mesh_readiness_failure_message( + MeshReadinessFailure::CatalogNeverSynced, + "auto", + "HTTP 429", + ); + let routing = mesh_readiness_failure_message( + MeshReadinessFailure::RoutingNeverCompleted, + "auto", + "HTTP 503", + ); + // Distinct diagnoses, each names the model and carries the raw detail. + assert_ne!(catalog, routing); + assert!(catalog.contains("network path")); + assert!(catalog.contains("HTTP 429")); + assert!(routing.contains("did not complete")); + assert!(routing.contains("HTTP 503")); + } +} diff --git a/desktop/src-tauri/src/commands/mod.rs b/desktop/src-tauri/src/commands/mod.rs index 322834630a..1ab3bb70d7 100644 --- a/desktop/src-tauri/src/commands/mod.rs +++ b/desktop/src-tauri/src/commands/mod.rs @@ -35,6 +35,8 @@ mod media_transcode; mod media_upload_progress; #[cfg(feature = "mesh-llm")] pub(crate) mod mesh_llm; +#[cfg(feature = "mesh-llm")] +pub(crate) mod mesh_readiness; mod messages; mod notifications; mod observer_archive; diff --git a/desktop/src-tauri/src/managed_agents/relay_mesh.rs b/desktop/src-tauri/src/managed_agents/relay_mesh.rs index 5c246feedc..3858212bbb 100644 --- a/desktop/src-tauri/src/managed_agents/relay_mesh.rs +++ b/desktop/src-tauri/src/managed_agents/relay_mesh.rs @@ -1,9 +1,33 @@ pub const RELAY_MESH_API_BASE_URL: &str = "http://127.0.0.1:9337/v1"; pub const RELAY_MESH_API_KEY_PLACEHOLDER: &str = "buzz-mesh-local"; pub const RELAY_MESH_PROVIDER_ID: &str = "relay-mesh"; +/// Stored value for "let the mesh decide", kept as the user-facing word. pub const RELAY_MESH_AUTO_MODEL_ID: &str = "auto"; +/// MeshLLM's virtual model. It resolves per request: a Mixture-of-Agents +/// committee when two or more workers are reachable, and otherwise degrades to +/// a single served model rather than erroring +/// (`moa_gateway::degrade_to_single_model`). That degradation is a pre-flight +/// capacity decision, so a committee that forms and *then* loses a worker still +/// surfaces as a failed turn — MoA repairs partial results internally +/// (`repair_tool_result_answer`) before it gets that far. Buzz translates the +/// stored `auto` here rather than teaching buzz-agent anything about meshes. #[cfg(feature = "mesh-llm")] -pub const RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV: &str = "BUZZ_AGENT_PREFER_MESH_FOR_AUTO"; +pub const RELAY_MESH_VIRTUAL_MODEL_ID: &str = "mesh"; + +/// The wire name for a stored shared-compute model: `auto` (and a blank legacy +/// value) means "let the mesh decide" and becomes MeshLLM's virtual `mesh` +/// model; anything else is a model the user named and is passed through. +/// +/// The single place this mapping happens. Every consumer that has to name a +/// model to the mesh — the LLM transport env and the ACP harness — goes through +/// here, so they cannot disagree. +#[cfg(feature = "mesh-llm")] +pub fn relay_mesh_wire_model(stored: &str) -> &str { + match stored.trim() { + "" | RELAY_MESH_AUTO_MODEL_ID => RELAY_MESH_VIRTUAL_MODEL_ID, + named => named, + } +} /// Translate the native Buzz shared compute provider into the OpenAI-compatible /// transport understood by buzz-agent. These are derived runtime details, not @@ -17,11 +41,7 @@ pub fn apply_relay_mesh_env( if provider.map(str::trim) != Some(RELAY_MESH_PROVIDER_ID) { return; } - let model = model - .map(str::trim) - .filter(|value| !value.is_empty()) - .unwrap_or(RELAY_MESH_AUTO_MODEL_ID) - .to_string(); + let model = relay_mesh_wire_model(model.unwrap_or(RELAY_MESH_AUTO_MODEL_ID)).to_string(); env.insert("BUZZ_AGENT_PROVIDER".to_string(), "openai".to_string()); env.insert("BUZZ_AGENT_MODEL".to_string(), model.clone()); env.insert( @@ -34,14 +54,6 @@ pub fn apply_relay_mesh_env( RELAY_MESH_API_KEY_PLACEHOLDER.to_string(), ); env.insert("OPENAI_COMPAT_API".to_string(), "chat".to_string()); - // Buzz owns the meaning of relay-mesh `auto`: buzz-agent dynamically uses - // mesh-llm's virtual Mixture-of-Agents model whenever the live catalog says - // at least two distinct models are available, and otherwise keeps the - // router's normal single-model `auto` behavior. - env.insert( - RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV.to_string(), - "1".to_string(), - ); // Keep the requested response inside smaller local-model context windows. // These are defaults, not policy: the effective agent/persona/global env // may deliberately choose a smaller cap or a different effort. This function @@ -128,10 +140,76 @@ mod tests { // stops gemma tool-calling; enabling thinking makes Qwen3 burn ~4x the // output budget). assert_eq!(env.get("BUZZ_AGENT_THINKING_EFFORT"), None); + } + + /// Stored `auto` is translated here, so buzz-agent receives a plain model + /// name and needs no knowledge of the mesh. MeshLLM decides per request + /// whether `mesh` becomes a committee or a single served model. + #[test] + fn stored_auto_becomes_the_virtual_mesh_model_on_the_wire() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some(RELAY_MESH_AUTO_MODEL_ID), + ); + assert_eq!( - env.get(RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV) - .map(String::as_str), - Some("1") + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + assert_eq!( + env.get("OPENAI_COMPAT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + } + + /// A blank stored model is the legacy encoding of the same intent. + #[test] + fn blank_stored_model_becomes_the_virtual_mesh_model() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env(&mut env, Some(RELAY_MESH_PROVIDER_ID), Some(" ")); + + assert_eq!( + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + } + + /// Every consumer that names a model to the mesh goes through one helper, + /// so the LLM transport and the ACP harness cannot be told different things. + #[test] + fn wire_model_maps_auto_and_blank_but_passes_named_through() { + assert_eq!( + relay_mesh_wire_model(RELAY_MESH_AUTO_MODEL_ID), + RELAY_MESH_VIRTUAL_MODEL_ID + ); + assert_eq!(relay_mesh_wire_model(""), RELAY_MESH_VIRTUAL_MODEL_ID); + assert_eq!(relay_mesh_wire_model(" "), RELAY_MESH_VIRTUAL_MODEL_ID); + assert_eq!( + relay_mesh_wire_model("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M"), + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + ); + } + + /// A named model is sent verbatim: picking one is an explicit choice to + /// bypass mesh routing, and must not be rewritten. + #[test] + fn a_named_model_is_sent_verbatim() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M"), + ); + + assert_eq!( + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M") + ); + assert_eq!( + env.get("OPENAI_COMPAT_MODEL").map(String::as_str), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M") ); } diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index ec804869c4..d95c595417 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -713,7 +713,19 @@ pub fn spawn_agent_child( } else { command.env_remove("BUZZ_ACP_SYSTEM_PROMPT"); } - if let Some(model) = effective_model.as_deref() { + // Shared compute stores `auto`, but the wire name is MeshLLM's virtual + // `mesh` model. Translate here too, so the harness and the LLM client are + // told the same thing: `BUZZ_ACP_MODEL=auto` would name a model the mesh + // never advertises, leaving buzz-acp to warn and fall back on every new + // session while `BUZZ_AGENT_MODEL` said `mesh`. + #[cfg(feature = "mesh-llm")] + let acp_model = match (&mesh_model_id, effective_model.as_deref()) { + (Some(mesh_model_id), _) => Some(super::relay_mesh_wire_model(mesh_model_id).to_string()), + (None, model) => model.map(str::to_owned), + }; + #[cfg(not(feature = "mesh-llm"))] + let acp_model = effective_model.as_deref().map(str::to_owned); + if let Some(model) = acp_model.as_deref() { command.env("BUZZ_ACP_MODEL", model); } else { command.env_remove("BUZZ_ACP_MODEL"); diff --git a/scripts/ensure-mesh-native-runtime.sh b/scripts/ensure-mesh-native-runtime.sh deleted file mode 100755 index e2b4fa0dc2..0000000000 --- a/scripts/ensure-mesh-native-runtime.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -BACKEND="${1:-}" -if [[ -z "$BACKEND" ]]; then - case "$(uname -s)" in - Darwin) BACKEND="metal" ;; - *) BACKEND="cpu" ;; - esac -fi - -CACHE_DIR="${MESH_LLM_NATIVE_RUNTIME_CACHE_DIR:-$ROOT/.cache/mesh-llm-native-runtime}" -OUT_DIR="${MESH_LLM_NATIVE_RUNTIME_OUT_DIR:-$ROOT/.cache/mesh-llm-native-runtime-artifacts}" - -metadata="$($ROOT/bin/cargo metadata --manifest-path "$ROOT/desktop/src-tauri/Cargo.toml" --features mesh-llm --format-version 1)" -SDK_MANIFEST="$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(next(p["manifest_path"] for p in data["packages"] if p["name"]=="mesh-llm-sdk"))' <<<"$metadata")" -MESH_ROOT="$(cd "$(dirname "$SDK_MANIFEST")/../.." && pwd)" -MESH_VERSION="$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(next(p["version"] for p in data["packages"] if p["name"]=="mesh-llm-sdk"))' <<<"$metadata")" - -case "$(uname -s)/$(uname -m)/$BACKEND" in - Darwin/arm64/metal) RUNTIME_ID="meshllm-native-runtime-darwin-aarch64-metal" ;; - Darwin/x86_64/metal) RUNTIME_ID="meshllm-native-runtime-darwin-x86_64-metal" ;; - */*/cpu) - case "$(uname -s)/$(uname -m)" in - Darwin/arm64) RUNTIME_ID="meshllm-native-runtime-darwin-aarch64-cpu" ;; - Darwin/x86_64) RUNTIME_ID="meshllm-native-runtime-darwin-x86_64-cpu" ;; - Linux/x86_64) RUNTIME_ID="meshllm-native-runtime-linux-x86_64-cpu" ;; - Linux/aarch64) RUNTIME_ID="meshllm-native-runtime-linux-aarch64-cpu" ;; - *) RUNTIME_ID="" ;; - esac - ;; - *) RUNTIME_ID="" ;; -esac - -if [[ -n "$RUNTIME_ID" && -f "$CACHE_DIR/$MESH_VERSION/$RUNTIME_ID/manifest.json" ]]; then - printf '%s\n' "$CACHE_DIR" - exit 0 -fi - -echo "Preparing MeshLLM native runtime ($BACKEND) for MeshLLM $MESH_VERSION..." >&2 -runtime_dir="$(cd "$MESH_ROOT" && scripts/ci-prepare-native-runtime.sh "$OUT_DIR" "$BACKEND")" -version="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["runtime"].get("mesh_version") or "unknown")' "$runtime_dir/manifest.json")" -id="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["runtime"]["id"])' "$runtime_dir/manifest.json")" -mkdir -p "$CACHE_DIR/$version" -rm -rf "$CACHE_DIR/$version/$id" -cp -a "$runtime_dir" "$CACHE_DIR/$version/$id" -printf '%s\n' "$CACHE_DIR"