From 9f0a856f9a36b09055d5a734f12a9ab9ed1d3400 Mon Sep 17 00:00:00 2001 From: iroiro147 Date: Sat, 1 Aug 2026 17:31:01 +0530 Subject: [PATCH] fix(desktop): stop promising local-file model refs the SDK can't consume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Share compute UI advertised "enter a model reference or local file" and `classifyModelRef` had a `local-path` branch. But the pinned mesh-llm SDK at v0.74.0 only knows two `ExactModelRef` variants — `Catalog` and `HuggingFace` — so every local path routed through `mesh_start_node` fails with "Expected an exact model ref" (issue #4049, sub-issue 2 of 2). Two-part fix: 1. **Copy.** Drop "or local file" from the descriptive text and add "local files are not supported yet" so the form no longer promises a capability we cannot deliver. 2. **Validator.** Remove the `local-path` branch from `classifyModelRef`; local-path inputs (`/`, `./`, `../`, `~`, `.gguf`, `file://`) now classify as `unknown`, which keeps the Share button disabled with a clear UI-level signal instead of running and dumping a cryptic SDK error at the user. Classifier doc comment explains the SDK constraint and points reclassification at the SDK gaining a `LocalPath` variant. Tests updated: classifier unit tests assert `unknown` for all path shapes (plus `file://` and `../` for completeness); Playwright e2e copy assertion matches the new text. Local verification: 10/10 node-test pass; tsc clean; 2/2 playwright mesh-compute smoke pass. Partial fix for #4049. The other half — the underlying ~70 MB layer-download stall inside the vendored mesh-llm download loop — is a separate, SDK-side investigation and is explicitly left out of scope here. Signed-off-by: Sarthak Singh --- .../mesh-compute/classifyModelRef.test.mjs | 42 +++++++++++-------- .../features/mesh-compute/classifyModelRef.ts | 19 +++++---- .../ui/MeshComputeSettingsCard.tsx | 5 ++- desktop/tests/e2e/mesh-compute.spec.ts | 2 +- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/desktop/src/features/mesh-compute/classifyModelRef.test.mjs b/desktop/src/features/mesh-compute/classifyModelRef.test.mjs index 9876678c8a..448888def0 100644 --- a/desktop/src/features/mesh-compute/classifyModelRef.test.mjs +++ b/desktop/src/features/mesh-compute/classifyModelRef.test.mjs @@ -15,32 +15,38 @@ test("hf:// prefix → huggingface", () => { }); }); -test("absolute path → local-path", () => { +// The vendored mesh-llm SDK's `parse_exact_model_ref` has no local-path +// variant — feeding any of these into `mesh_start_node` always fails with +// "Expected an exact model ref" at runtime. We therefore classify them as +// unknown so the UI keeps the Start button disabled with a clear signal +// instead of letting the submit bubble up a cryptic SDK error. +// See https://github.com/block/buzz/issues/4049. + +test("absolute path → unknown", () => { assert.deepEqual(classifyModelRef("/Users/me/models/qwen.gguf"), { - kind: "local-path", - path: "/Users/me/models/qwen.gguf", + kind: "unknown", }); }); -test("relative path with ./ → local-path", () => { - assert.deepEqual(classifyModelRef("./models/qwen.gguf"), { - kind: "local-path", - path: "./models/qwen.gguf", - }); +test("relative path with ./ → unknown", () => { + assert.deepEqual(classifyModelRef("./models/qwen.gguf"), { kind: "unknown" }); }); -test("home shortcut → local-path", () => { - assert.deepEqual(classifyModelRef("~/models/qwen.gguf"), { - kind: "local-path", - path: "~/models/qwen.gguf", - }); +test("relative path with ../ → unknown", () => { + assert.deepEqual(classifyModelRef("../models/qwen.gguf"), { kind: "unknown" }); +}); + +test("home shortcut → unknown", () => { + assert.deepEqual(classifyModelRef("~/models/qwen.gguf"), { kind: "unknown" }); +}); + +test(".gguf extension without path prefix → unknown", () => { + assert.deepEqual(classifyModelRef("my-model.gguf"), { kind: "unknown" }); }); -test(".gguf extension without path prefix → local-path", () => { - // Bare filename ending in .gguf — user clearly means a file. - assert.deepEqual(classifyModelRef("my-model.gguf"), { - kind: "local-path", - path: "my-model.gguf", +test("file:// URL → unknown", () => { + assert.deepEqual(classifyModelRef("file:/// Users/me/models/qwen.gguf"), { + kind: "unknown", }); }); diff --git a/desktop/src/features/mesh-compute/classifyModelRef.ts b/desktop/src/features/mesh-compute/classifyModelRef.ts index 666c56b309..84aee6865e 100644 --- a/desktop/src/features/mesh-compute/classifyModelRef.ts +++ b/desktop/src/features/mesh-compute/classifyModelRef.ts @@ -5,17 +5,21 @@ export type ModelRefKind = | { kind: "catalog"; name: string } | { kind: "huggingface"; ref: string } - | { kind: "local-path"; path: string } | { kind: "unknown" }; /** * Classify a model-ref string the way mesh-llm's runtime does: * - `hf://…` → HuggingFace ref - * - starts with `/` or `./` or `~`, OR ends with `.gguf` → local file * - otherwise non-empty → catalog name * - empty/whitespace → unknown * - * Source: mesh runtime/mod.rs:3390 ("local file, catalog name, or HuggingFace URL"). + * Local filesystem paths are classified `unknown`, NOT `local-path`: the + * vendored mesh-llm SDK's `parse_exact_model_ref` has no local-path variant, + * so feeding a path into `mesh_start_node` always fails with "Expected an + * exact model ref". Treating paths as unknown keeps the Start button + * disabled with a clear UI-level signal instead of a runtime error at start. + * Reclassify-and-accept if/when the SDK gains a local-path branch. + * See https://github.com/block/buzz/issues/4049. * * This is validation-only — canonical resolution happens server-side via * `mesh_start_node`. @@ -28,16 +32,17 @@ export function classifyModelRef(raw: string): ModelRefKind { if (trimmed.startsWith("hf://")) { return { kind: "huggingface", ref: trimmed }; } - // Local path heuristics. Conservative: only mark as path when there are - // unambiguous signals (leading separator, home shortcut, .gguf extension). + // Local path heuristics — classify as unknown so the form refuses to + // submit. See the function-level doc-comment above and issue #4049. const looksLikePath = trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("~") || - trimmed.toLowerCase().endsWith(".gguf"); + trimmed.toLowerCase().endsWith(".gguf") || + trimmed.startsWith("file://"); if (looksLikePath) { - return { kind: "local-path", path: trimmed }; + return { kind: "unknown" }; } return { kind: "catalog", name: trimmed }; } diff --git a/desktop/src/features/mesh-compute/ui/MeshComputeSettingsCard.tsx b/desktop/src/features/mesh-compute/ui/MeshComputeSettingsCard.tsx index fd7550eff0..ef7552a986 100644 --- a/desktop/src/features/mesh-compute/ui/MeshComputeSettingsCard.tsx +++ b/desktop/src/features/mesh-compute/ui/MeshComputeSettingsCard.tsx @@ -301,8 +301,9 @@ export function MeshComputeSettingsCard() { value={modelInput} />

- Choose a suggested model below, or enter a model reference or - local file. Buzz downloads remote models when sharing starts. + Choose a suggested model below, or enter a catalog name or + HuggingFace ref. Buzz downloads remote models when sharing + starts; local files are not supported yet.

{catalog && catalog.entries.length > 0 ? (