Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 42 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ curl -fsSL https://raw.githubusercontent.com/michaelneale/mesh-llm/main/install.
Then start a node:

```bash
mesh-llm --auto
mesh-llm serve --auto
```

Inspect local GPU identity:

```bash
mesh-llm gpus
```

That command:
Expand Down Expand Up @@ -60,15 +66,15 @@ curl http://localhost:9337/v1/chat/completions \
### 1. Try the public mesh

```bash
mesh-llm --auto
mesh-llm serve --auto
```

This is the easiest way to see the system working end to end.

### 2. Start a private mesh

```bash
mesh-llm --model Qwen2.5-32B
mesh-llm serve --model Qwen2.5-32B
```

This starts serving a model, opens the local API and console, and prints an invite token for other machines.
Expand All @@ -89,31 +95,31 @@ Windows source builds are also supported for `cuda`, `rocm`/`hip`, `vulkan`, and
Once installed, you can run:

```bash
mesh-llm --auto # join the best public mesh, start serving
mesh-llm serve --auto # join the best public mesh, start serving
```

That's it. Downloads a model for your hardware, connects to other nodes, and gives you an OpenAI-compatible API at `http://localhost:9337`.

Or start your own:
```bash
mesh-llm --model Qwen2.5-32B # downloads model (~20GB), starts API + web console
mesh-llm --model Qwen2.5-3B # or a small model first (~2GB)
mesh-llm serve --model Qwen2.5-32B # downloads model (~20GB), starts API + web console
mesh-llm serve --model Qwen2.5-3B # or a small model first (~2GB)
```

Add another machine:
```bash
mesh-llm --join <token> # token printed by the first machine
mesh-llm serve --join <token> # token printed by the first machine
```

Or discover and join public meshes:
```bash
mesh-llm --auto # find and join the best mesh
mesh-llm --client --auto # join as API-only client (no GPU)
mesh-llm serve --auto # find and join the best mesh
mesh-llm client --auto # join as API-only client (no GPU)
```

## How it works

Every node gets an OpenAI-compatible API at `http://localhost:9337/v1`. Distribution is automatic — you just say `mesh-llm --model X` and the mesh figures out the best strategy:
Every node gets an OpenAI-compatible API at `http://localhost:9337/v1`. Distribution is automatic — you just say `mesh-llm serve --model X` and the mesh figures out the best strategy:

- **Model fits on one machine?** → runs solo, full speed, no network overhead
- **Dense model too big?** → pipeline parallelism — layers split across nodes
Expand Down Expand Up @@ -143,43 +149,51 @@ Currently using a lightly forked version of llama.cpp (see the Justfile for wher

### Start a mesh
```bash
mesh-llm --model Qwen2.5-32B
mesh-llm serve --model Qwen2.5-32B
```
Starts serving a model and prints an invite token. This mesh is **private** — only people you share the token with can join.

To make it **public** (discoverable by others via `--auto`):
```bash
mesh-llm --model Qwen2.5-32B --publish
mesh-llm serve --model Qwen2.5-32B --publish
```

### Join a mesh
```bash
mesh-llm --join <token> # join with invite token (GPU node)
mesh-llm --client --join <token> # join as API-only client (no GPU)
mesh-llm serve --join <token> # join with invite token (GPU node)
mesh-llm client --join <token> # join as API-only client (no GPU)
```

### Named mesh (buddy mode)
```bash
mesh-llm --auto --model GLM-4.7-Flash-Q4_K_M --mesh-name "poker-night"
mesh-llm serve --auto --model GLM-4.7-Flash-Q4_K_M --mesh-name "poker-night"
```
Everyone runs the same command. First person creates it, everyone else discovers "poker-night" and joins automatically. `--mesh-name` implies `--publish` — named meshes are always published to the directory.

### Auto-discover
```bash
mesh-llm --auto # discover, join, and serve a model
mesh-llm --client --auto # join as API-only client (no GPU)
mesh-llm serve --auto # discover, join, and serve a model
mesh-llm client --auto # join as API-only client (no GPU)
mesh-llm discover # browse available meshes
mesh-llm gpus # inspect local GPUs and stable IDs
```

### Multi-model
```bash
mesh-llm --model Qwen2.5-32B --model GLM-4.7-Flash
mesh-llm serve --model Qwen2.5-32B --model GLM-4.7-Flash

# Route by model name
curl localhost:9337/v1/chat/completions -d '{"model":"GLM-4.7-Flash-Q4_K_M", ...}'
```
Different nodes serve different models. The API proxy routes by the `model` field.

### Inspect local GPUs
```bash
mesh-llm gpus
```

Prints local GPU entries, backend device names, stable IDs, VRAM, and cached bandwidth if a benchmark fingerprint is already available.

### No-arg behavior
```bash
mesh-llm # no args — prints --help and exits
Expand All @@ -197,7 +211,7 @@ curl -fsSL https://raw.githubusercontent.com/michaelneale/mesh-llm/main/install.
To seed the service with a custom startup command on first install:

```bash
curl -fsSL https://raw.githubusercontent.com/michaelneale/mesh-llm/main/install.sh | bash -s -- --service --service-args '--model Qwen2.5-3B'
curl -fsSL https://raw.githubusercontent.com/michaelneale/mesh-llm/main/install.sh | bash -s -- --service --service-args 'serve --model Qwen2.5-3B'
```

Service installs are user-scoped:
Expand All @@ -208,7 +222,7 @@ Service installs are user-scoped:

The two platforms handle launch args differently:

- macOS: `launchd` runs `~/.config/mesh-llm/run-service.sh`, which reads `~/.config/mesh-llm/service.args`. `service.args` is one `mesh-llm` CLI argument per line. The installer creates it with `--auto` by default and preserves your edits on reinstall unless you pass `--service-args` again.
- macOS: `launchd` runs `~/.config/mesh-llm/run-service.sh`, which reads `~/.config/mesh-llm/service.args`. `service.args` is one `mesh-llm` CLI argument per line. The installer creates it with `serve --auto` by default and preserves your edits on reinstall unless you pass `--service-args` again.
- Linux: the installer writes the `mesh-llm` argv directly into `ExecStart=` in `~/.config/systemd/user/mesh-llm.service`. If you pass `--service-args`, those replace the current unit args; otherwise the installer preserves the existing unit args on reinstall.

`service.env` is optional and shared by both platforms. Use plain `KEY=value` lines, for example:
Expand All @@ -233,7 +247,7 @@ sudo loginctl enable-linger "$USER"
## Web console

```bash
mesh-llm --model Qwen2.5-32B # dashboard at http://localhost:3131
mesh-llm serve --model Qwen2.5-32B # dashboard at http://localhost:3131
```

Live topology, VRAM bars per node, model picker, built-in chat. Everything comes from `/api/status` (JSON) and `/api/events` (SSE).
Expand Down Expand Up @@ -308,7 +322,7 @@ This command writes/updates `~/.config/goose/custom_providers/mesh.json` and lau

1. Start a mesh client:
```bash
mesh-llm --client --auto --port 9337
mesh-llm client --auto --port 9337
```

2. Check what models are available:
Expand All @@ -319,33 +333,33 @@ curl -s http://localhost:9337/v1/models | jq '.data[].id'
If you want the mesh to be discoverable via `--auto`, publish it:

```bash
mesh-llm --model Qwen2.5-32B --publish
mesh-llm serve --model Qwen2.5-32B --publish
```

### 3. Add another machine

```bash
mesh-llm --join <token>
mesh-llm serve --join <token>
```

Use `--client` if the machine should join without serving a model:
Use `mesh-llm client` if the machine should join without serving a model:

```bash
mesh-llm --client --join <token>
mesh-llm client --join <token>
```

### 4. Create a named mesh for a group

```bash
mesh-llm --auto --model GLM-4.7-Flash-Q4_K_M --mesh-name "poker-night"
mesh-llm serve --auto --model GLM-4.7-Flash-Q4_K_M --mesh-name "poker-night"
```

Everyone runs the same command. The first node creates the mesh, the rest discover and join it automatically.

### 5. Serve more than one model

```bash
mesh-llm --model Qwen2.5-32B --model GLM-4.7-Flash
mesh-llm serve --model Qwen2.5-32B --model GLM-4.7-Flash
```

Requests are routed by the `model` field:
Expand Down
4 changes: 2 additions & 2 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ mesh-llm claude --model MiniMax-M2.5-Q4_K_M
Start a mesh client:

```bash
mesh-llm --client --auto --port 9337
mesh-llm client --auto --port 9337
```

Check available models:
Expand Down Expand Up @@ -115,7 +115,7 @@ Mesh LLM can also share status, findings, and questions across the mesh through
This works even if you are not using Mesh LLM for model serving. A client-only node is enough:

```bash
mesh-llm --client
mesh-llm client
```

Install the agent skill:
Expand Down
37 changes: 23 additions & 14 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Release bundles install flavor-specific llama.cpp binaries:
If you keep more than one flavor in the same `bin` directory, choose one explicitly:

```bash
mesh-llm --llama-flavor vulkan --model Qwen2.5-32B
mesh-llm serve --llama-flavor vulkan --model Qwen2.5-32B
```

Source builds must use `just`:
Expand Down Expand Up @@ -59,10 +59,11 @@ For full build details, see [CONTRIBUTING.md](../CONTRIBUTING.md).
## Common commands

```bash
mesh-llm --auto
mesh-llm --model Qwen2.5-32B
mesh-llm --join <token>
mesh-llm --client --auto
mesh-llm serve --auto
mesh-llm serve --model Qwen2.5-32B
mesh-llm serve --join <token>
mesh-llm client --auto
mesh-llm gpus
mesh-llm discover
```

Expand All @@ -79,7 +80,7 @@ curl -fsSL https://raw.githubusercontent.com/michaelneale/mesh-llm/main/install.
To seed the service with a custom startup command on first install:

```bash
curl -fsSL https://raw.githubusercontent.com/michaelneale/mesh-llm/main/install.sh | bash -s -- --service --service-args '--model Qwen2.5-3B'
curl -fsSL https://raw.githubusercontent.com/michaelneale/mesh-llm/main/install.sh | bash -s -- --service --service-args 'serve --model Qwen2.5-3B'
```

Service installs are user-scoped:
Expand Down Expand Up @@ -133,15 +134,15 @@ Draft pairings for speculative decoding:

## Specifying models

`--model` accepts several formats. Hugging Face-backed models are cached in the standard Hugging Face cache on first use.
`mesh-llm serve --model` accepts several formats. Hugging Face-backed models are cached in the standard Hugging Face cache on first use.

```bash
mesh-llm --model Qwen3-8B
mesh-llm --model Qwen3-8B-Q4_K_M
mesh-llm --model https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf
mesh-llm --model bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q4_K_M.gguf
mesh-llm --gguf ~/my-models/custom-model.gguf
mesh-llm --gguf ~/my-models/qwen3.5-4b.gguf --mmproj ~/my-models/mmproj-BF16.gguf
mesh-llm serve --model Qwen3-8B
mesh-llm serve --model Qwen3-8B-Q4_K_M
mesh-llm serve --model https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf
mesh-llm serve --model bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q4_K_M.gguf
mesh-llm serve --gguf ~/my-models/custom-model.gguf
mesh-llm serve --gguf ~/my-models/qwen3.5-4b.gguf --mmproj ~/my-models/mmproj-BF16.gguf
```

Useful model commands:
Expand All @@ -165,9 +166,17 @@ mesh-llm models updates Qwen/Qwen3-8B-GGUF
- Hugging Face repo snapshots are the canonical managed model store.
- Flat `~/.models/` storage is no longer scanned for managed models.
- If you still have legacy files there, use `mesh-llm models migrate --apply`.
- Arbitrary local GGUF files still work through `--gguf`.
- Arbitrary local GGUF files still work through `mesh-llm serve --gguf`.
- MoE split artifacts are cached under `~/.cache/mesh-llm/splits/`.

## Inspect local GPUs

```bash
mesh-llm gpus
```

This prints the local GPU inventory with stable IDs, backend device names, VRAM, unified-memory status, and cached bandwidth if a benchmark fingerprint is already present.

## Local runtime control

Stage one supports local-only hot load and unload on a running node.
Expand Down
4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ <h3 style="font-size:0.95rem; color:#fff; margin:2rem 0 1rem;">Built-in catalog<
<div class="section-tag">Collaboration</div>
<h2>Blackboard</h2>
<p class="section-lead">Have your agents gossip over the mesh to collaborate amongst themselves. Status updates, findings, and questions propagate across the mesh — no central server, no cloud.</p>
<p style="font-size:0.82rem; color:var(--text2); margin-top:0.8rem;">Works standalone — using your own API keys or cloud models? Just run <code>mesh-llm --client</code> to give your agents a gossip layer. No GPU needed, no model needed. Blackboard is on by default for private meshes.</p>
<p style="font-size:0.82rem; color:var(--text2); margin-top:0.8rem;">Works standalone — using your own API keys or cloud models? Just run <code>mesh-llm client</code> to give your agents a gossip layer. No GPU needed, no model needed. Blackboard is on by default for private meshes.</p>

<div class="features" style="margin-bottom:2rem;">
<div class="feature">
Expand All @@ -367,7 +367,7 @@ <h3>Stays in the mesh</h3>

<div class="code-block">
<span class="comment"># Start a node — blackboard is on by default</span><br>
<span class="cmd">mesh-llm</span> <span class="flag">--client --join</span> <span class="val">&lt;token&gt;</span><br><br>
<span class="cmd">mesh-llm</span> <span class="flag">client --join</span> <span class="val">&lt;token&gt;</span><br><br>
<span class="comment"># Install the agent skill (works with pi, goose, others)</span><br>
<span class="cmd">mesh-llm</span> blackboard <span class="val">install-skill</span><br><br>
<span class="comment"># Post, search, read (talks to the running node)</span><br>
Expand Down
9 changes: 5 additions & 4 deletions mesh-llm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ The blackboard plugin is auto-registered unless explicitly disabled in config. U
mesh-llm plugin list
mesh-llm blackboard
mesh-llm blackboard --search "routing"
mesh-llm --client --join <token> blackboard --mcp
mesh-llm client --join <token> blackboard --mcp
```

External plugins are configured as executables, for example:
Expand All @@ -88,16 +88,17 @@ args = ["--stdio"]
Opt-in Nostr discovery:

```bash
mesh-llm --model Qwen2.5-3B --publish --mesh-name "Sydney Lab" --region AU
mesh-llm serve --model Qwen2.5-3B --publish --mesh-name "Sydney Lab" --region AU
mesh-llm discover
mesh-llm discover --model GLM --region AU
mesh-llm --auto
mesh-llm serve --auto
mesh-llm gpus
```

Named meshes still work as a strict discovery filter:

```bash
mesh-llm --auto --model GLM-4.7-Flash-Q4_K_M --mesh-name "poker-night"
mesh-llm serve --auto --model GLM-4.7-Flash-Q4_K_M --mesh-name "poker-night"
```

No-arg behavior remains intentionally simple:
Expand Down
2 changes: 1 addition & 1 deletion mesh-llm/docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ When joining an existing mesh, a tunnel-only API proxy starts immediately on the
local port — before rpc-server or llama-server are ready. Requests are tunneled to
mesh hosts via QUIC. When the real `api_proxy` is ready, it takes over the listener.

This gives instant API access (within seconds of `mesh-llm --join`) while the local
This gives instant API access (within seconds of `mesh-llm serve --join`) while the local
GPU loads its model in the background.

## Passive Mode
Expand Down
4 changes: 2 additions & 2 deletions mesh-llm/docs/MoE_DEPLOY_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ This documents how MoE expert sharding is implemented in mesh-llm. Originally a

```bash
# MoE auto-detected — splits if needed, runs solo if it fits
mesh-llm --model Qwen3-30B-A3B-Q4_K_M
mesh-llm serve --model Qwen3-30B-A3B-Q4_K_M

# Force splitting even if model fits locally
mesh-llm --model Qwen3-30B-A3B-Q4_K_M --split
mesh-llm serve --model Qwen3-30B-A3B-Q4_K_M --split
```

The system detects MoE from the GGUF header, computes expert assignments, splits the GGUF per node, and each node runs its own llama-server. Sessions are hash-routed. No manual steps.
Expand Down
Loading
Loading