Skip to content
Open
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
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ one, and both report live GPU and memory use throughout.
| **Architectures** | x64 and arm64 on all three. Windows on ARM is experimental. |
| **Installers** | Windows `.exe`; Linux `.deb`; macOS `.dmg`. On other Linux distributions, [build from source](docs/building.mdx). |
| **Mixing nodes** | Windows, Linux, and macOS nodes can all be paired with each other |
| **Inference engines** | Ollama and LM Studio |
| **Inference engines** | Ollama, LM Studio, and vLLM (Linux) |

**PAIR running on a machine does not mean an engine will.** PAIR itself runs on
any supported Windows, Linux, or macOS machine. Each engine sets its own requirements
Expand Down Expand Up @@ -100,7 +100,8 @@ you want by its full filename instead.
status there.

- **Get an engine running.** On the node's card, open **Engine settings** and
select **Install** next to Ollama or LM Studio. PAIR downloads and sets the
select **Install** next to Ollama, LM Studio, or (on Linux) vLLM. PAIR
downloads and sets the
engine up for you, so nothing needs to be in place beforehand. If PAIR already
found an engine you installed yourself, start that one instead.

Expand Down Expand Up @@ -149,7 +150,8 @@ The reply is ordinary OpenAI-shaped JSON, abbreviated here:
}
```

If you changed a port, or you are using LM Studio rather than Ollama, copy the
If you changed a port, or you are using LM Studio or vLLM rather than Ollama,
copy the
URL from **Endpoints → API endpoints** instead of assuming the one above.

That is a single machine working. To route across machines, pair a second one
Expand Down
1 change: 1 addition & 0 deletions desktop/docs/services-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
| `engine:remote-stop` | request (we call) | ✅ yes |
| `engine:remote-unload-model` | request (we call) | ✅ yes |
| `engine:restart` | request (we call) | ⚠️ not called |
| `engine:set-model` | request (we call) | ✅ yes |
| `engine:set-port` | request (we call) | ✅ yes |
| `engine:start` | request (we call) | ✅ yes |
| `engine:status` | request (we call) | ✅ yes |
Expand Down
11 changes: 7 additions & 4 deletions desktop/docs/services-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ broker supervises every worker and relays its control plane.
| ------------------------- | --------------------------------------------------------- |
| `nvpair-ui-broker` | Worker supervision and relay |
| `ollama-proxy` | Ollama-compatible routing proxy with cluster-mTLS ingress |
| `lmstudio-proxy` | LM Studio routing proxy with cluster-mTLS ingress |
| `lmstudio-proxy` | OpenAI-compatible routing proxy (LM Studio, vLLM) with cluster-mTLS ingress |
| `nvpair-node-scanner` | Discovery and node announcement |
| `nvpair-node-info` | Node metadata and telemetry |
| `nvpair-manual-nodes` | User-managed node entries |
Expand Down Expand Up @@ -90,8 +90,10 @@ engine, workload, cluster, and error relays. The bridge then emits renderer push
events from backend notifications.

Connector readiness follows the broker contract: `app:ready` establishes the
service connection, while Ollama and LM Studio proxy readiness remains an
asynchronous capability signal. Personal AI Router waits up to the canonical
service connection, while the Ollama and OpenAI-compatible proxy readiness
signals remain asynchronous capability signals. The OpenAI-compatible proxy
fronts LM Studio and vLLM together, so one `lmstudio-proxy:ready` records the
port for both engines. Personal AI Router waits up to the canonical
startup deadline in `src/shared/constants/modular-runtime.ts` for
`app:ready`; an outright failure or stalled broker startup is surfaced in
Settings > Service with retry and log access. If a stalled broker reports ready
Expand Down Expand Up @@ -266,7 +268,8 @@ LAN-reachable. Each node fronts its engine with its `ollama-proxy` /
`lmstudio-proxy`, whose LAN ingress is gated by cluster mTLS: only a pinned
cluster member can send it work. Discovery advertises the promoted **proxy**
port (never the engine port), and the broker hands the private loopback engine to
the local proxy via `node/set-local-backend`. Every cluster-scoped worker derives
the local proxy via `node/set-local-backend`, which is keyed per engine so the
one OpenAI-compatible proxy can hold an LM Studio and a vLLM backend at once. Every cluster-scoped worker derives
its own membership from the cluster directory continuously, so a proxy's mTLS
ingress tracks a create, join, or leave with no worker restart. Loopback
plaintext requests from local clients are unaffected. This is entirely
Expand Down
17 changes: 14 additions & 3 deletions desktop/docs/services-parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ history.
| Manual nodes | Complete with local persistence | Broker owns probing and proxy registration; Electron persists entries for replay |
| Ollama routing | Complete | Broker relay and backend scheduler drive proxy routing |
| LM Studio routing | Complete | Parallel broker relay and scheduler path |
| vLLM routing | Complete on Linux | Shares the LM Studio relay, the OpenAI-compatible proxy, and the same scheduler path |
| Local engine lifecycle | Complete | Install, start, stop, uninstall, update, and port configuration |
| Remote engine lifecycle | Partial | Remote install, start, stop, status, and model pull are supported |
| Engine models | Partial | Core list, pull, load, unload, and supported delete actions are wired |
Expand Down Expand Up @@ -102,7 +103,9 @@ they survive worker restarts.
Both text-engine proxies are broker-owned and cluster-aware:

- `ollama-proxy` serves the Ollama-compatible surface;
- `lmstudio-proxy` serves the LM Studio/OpenAI-compatible surface.
- `lmstudio-proxy` serves the OpenAI-compatible surface for every
OpenAI-compatible engine — LM Studio and vLLM share the one listener, and a
node record can advertise `lm` and `vl` at the same port.

Routing precedence is manual selection, scheduler priority, then deterministic
proxy ordering. Personal AI Router leaves proxies in automatic mode.
Expand Down Expand Up @@ -197,8 +200,16 @@ Personal AI Router uses:
- Ollama `run_model`, `unload_model` (`keep_alive: 0`), and `delete_model`;
- LM Studio `load_model`, `unload_model`, and `delete_model` (`remove_path`).

Both engines expose Load, Eject, and Delete in the model manager when the
backend action exists. Keep-alive / expiry controls remain unsupported.
Ollama and LM Studio expose Load, Eject, and Delete in the model manager when
the backend action exists. Keep-alive / expiry controls remain unsupported.

vLLM has no model operations at all: it declares only `list_models`,
`loaded_models`, and `chat`, because a vLLM process serves exactly one model
chosen at launch and it neither downloads nor deletes weights on request. Its
capability entry therefore sets `hasDeleteModel: false`, `hasEject: false`,
`hasExpiry: false`, and `modelOpsWhenStopped: false`, and adds
`hasServedModel: true` — the model to serve is a *setting* (`engine:set-model`),
not a model action, and changing it restarts the engine.

LM Studio's `delete_model` declares `restart_after`, so the engine manager
restarts a running LM Studio once the files are removed — its `/v1/models` is
Expand Down
18 changes: 18 additions & 0 deletions desktop/src/electron/service-bridge/empty-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,24 @@ function routeEngineManagerCommand(payload: WsInvokeRequest<'engine:command'>):
case 'toggle':
void toggleLocalEngine(engine, payload.engineType)
break
case 'setServedModel':
// vLLM serves one model per process, so the model is a start-time
// setting rather than a model operation. engine:set-model persists it
// as a manifest override — the same mechanism engine:set-port uses —
// and restarts a running engine onto it. Local only: the backend
// exposes no remote served-model control.
supervisor.sendProcess(
'broker',
'engine:set-model',
{ engine, model: payload.model ?? '' },
failAction('set the served model', {
nodeId: payload.nodeId,
engineType: payload.engineType,
modelName: payload.model
}),
true
)
break
case 'setPorts':
// Both ports persist on this node: the engine HTTP server port via
// engine:set-port (manifest override), the proxy port via the broker's
Expand Down
Loading