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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ jobs:
go test -v -count=1 -timeout=15m -json ./... 2>&1 \
| tee "$RUNNER_TEMP/cross-process.jsonl" \
| jq --unbuffered -j 'select(.Output != null) | .Output'
- name: Require llama.cpp broker routing coverage
if: success()
shell: bash
run: |
jq -e 'select(.Action == "pass" and .Test == "TestBrokerLlamaCPPProxyRoutesOpenAI")' "$RUNNER_TEMP/cross-process.jsonl"
- name: Upload cross-process diagnostics
if: always()
uses: actions/upload-artifact@v4.6.2
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ change belongs in a Go service instead.
service. Operator tools are separate from this supervised service inventory.
- The broker supervises the other Go workers: discovery, proxies, engines,
cluster, settings, manual nodes, workloads, errors, and the scheduler.
- The broker attempts to start 12 workers, including `omlx-proxy`. Standalone,
- The broker attempts to start 13 workers, including `omlx-proxy` and `llamacpp-proxy`. Standalone,
only the scanner is required: missing default auxiliary binaries and initial
auxiliary spawn failures are non-fatal. Invalid explicit worker paths are
fatal; scanner's initial spawn failure is also fatal. Optional initial spawn
Expand All @@ -61,9 +61,9 @@ change belongs in a Go service instead.
interval. Two consecutive failures report `degraded`; a successful probe
restores `running` and clears the failure count. Three failures restart scanner,
errors, settings, node-info, workload-manager, manual-nodes, cluster-manager,
and scheduler. Engine-manager and the three proxies never auto-restart on a
and scheduler. Engine-manager and the four proxies never auto-restart on a
hang; desktop shows "Not responding — restart manually".
- `workers:get-status` and full-snapshot `workers:update` expose all 12 workers;
- `workers:get-status` and full-snapshot `workers:update` expose all 13 workers;
`workers:restart` requests an explicit retry/restart, including after exhaustion.
Desktop renders these under Settings → Service → Workers; TUI Overview uses
the same registry, with `R` and confirmation to restart. Unix cross-process
Expand Down Expand Up @@ -102,7 +102,7 @@ Never edit `desktop/docs/services-api.md` by hand. It is generated by

## Services (`services/`)

Fourteen service executables: broker + 12 workers + TUI. Each component is its
Fifteen service executables: broker + 13 workers + TUI. Each component is its
own module, with its tests beside its source and a `README.md` describing its
JSON-RPC surface. Shared packages live in
`shared/`, and `tests/` holds cross-process tests that drive real binaries.
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Remove unrelated personal information, prompts, model data, and secrets.

- Electron's renderer reaches main through a typed preload bridge. Electron
starts only `nvpair-ui-broker`; the terminal client starts its own broker.
- The broker supervises **12 workers**: scanner, node-info, three engine proxies,
- The broker supervises **13 workers**: scanner, node-info, four engine proxies,
engine manager, cluster manager, workload manager, errors, node settings,
manual nodes, and job scheduler. Scanner startup is required; others are
optional. Workers run under the user's operating-system account.
Expand Down
2 changes: 1 addition & 1 deletion desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ docs/ Desktop-specific architecture and contract documentation

The renderer reaches services only through the preload bridge; it never talks to
a Go worker directly. Electron starts `nvpair-ui-broker` and the broker
supervises up to 12 workers. The service bundle contains 14 executables, including
supervises up to 13 workers. The service bundle contains 15 executables, including
`omlx-proxy` and the separate, unsupervised `nvpair-tui`. Desktop prevalidates
broker, scanner, Ollama proxy, node-info, and node-settings; standalone broker
requirements are narrower.
Expand Down
5 changes: 3 additions & 2 deletions desktop/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The canonical runtime inventory is
| `ollama-proxy` | Broker | Ollama-compatible proxy and cluster routing |
| `lmstudio-proxy` | Broker, optional | LM Studio OpenAI-compatible proxy |
| `omlx-proxy` | Broker, optional | oMLX OpenAI-compatible proxy |
| `llamacpp-proxy` | Broker, optional | llama.cpp OpenAI-compatible proxy |
| `nvpair-node-scanner` | Broker | LAN discovery and announcement |
| `nvpair-node-info` | Broker | Node metadata and telemetry endpoint |
| `nvpair-workload-manager` | Broker, optional | Workload replication |
Expand Down Expand Up @@ -122,9 +123,9 @@ from worker crash supervision or hang detection.

The Go broker separately probes `worker/health` every 30 s (5 s deadline),
reports `degraded` after two failures, and restarts the eight eligible workers
after three. Engine-manager and the three proxies never auto-restart on a hang.
after three. Engine-manager and the four proxies never auto-restart on a hang.
The bridge recovers `workers:get-status` on each epoch and replaces its cache on
full `workers:update` snapshots. Settings → Service → Workers renders all twelve
full `workers:update` snapshots. Settings → Service → Workers renders all thirteen
rows and relays `workers:restart`; ineligible degraded rows say "Not responding —
restart manually". Engine-manager and proxy restarts require confirmation because
they may interrupt engines or inference. Health, initial-spawn retry, missing
Expand Down
33 changes: 33 additions & 0 deletions desktop/docs/services-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
- none ✅

### Requests the backend handles but the bridge never calls (unused capability)
- ⚠️ llamacpp-proxy → node/selected
- ⚠️ llamacpp-proxy → node/set-local-backend
- ⚠️ llamacpp-proxy → telemetry/configure
- ⚠️ llamacpp-proxy → telemetry/status
- ⚠️ lmstudio-proxy → node/selected
- ⚠️ lmstudio-proxy → node/set-local-backend
- ⚠️ lmstudio-proxy → telemetry/configure
Expand Down Expand Up @@ -70,6 +74,34 @@
### Backend binaries not listed in `modular-binaries.ts`
- none ✅

## llamacpp-proxy

| Method | Direction | In bridge? |
|---|---|---|
| `error` | notification (we consume) | ✅ yes |
| `errors:clear` | notification (we consume) | ✅ yes |
| `errors:report` | notification (we consume) | ✅ yes |
| `node/discovered` | notification (we consume) | ✅ yes |
| `node/removed` | notification (we consume) | ✅ yes |
| `node/selection-changed` | notification (we consume) | ➖ ignored |
| `node/updated` | notification (we consume) | ✅ yes |
| `proxy/request` | notification (we consume) | ✅ yes |
| `proxy/request-started` | notification (we consume) | ➖ ignored |
| `ready` | notification (we consume) | ✅ yes |
| `workload:progress` | notification (we consume) | ➖ ignored |
| `node/add-manual` | request (we call) | ✅ yes |
| `node/remove-manual` | request (we call) | ✅ yes |
| `node/select` | request (we call) | ✅ yes |
| `node/selected` | request (we call) | ⚠️ not called |
| `node/set-local-backend` | request (we call) | ⚠️ not called |
| `node/set-priority` | request (we call) | ✅ yes |
| `nodes/list` | request (we call) | ✅ yes |
| `telemetry/configure` | request (we call) | ⚠️ not called |
| `telemetry/status` | request (we call) | ⚠️ not called |

**Dynamic / unresolved notify sites (verify by hand — `npm run service-contracts` prints the line numbers):**
- `method (var) (proxy.go)`

## lmstudio-proxy

| Method | Direction | In bridge? |
Expand Down Expand Up @@ -403,6 +435,7 @@ Client surface: requests are sent to the broker; notifications are consumed from

**Dynamic / unresolved notify sites (verify by hand — `npm run service-contracts` prints the line numbers):**
- `proxy:* (broker.go)`
- `llamacpp-proxy:* (llamacppproxy.go)`
- `lmstudio-proxy:* (lmstudioproxy.go)`
- `omlx-proxy:* (omlxproxy.go)`
- `WorkerRunning (var) (supervisor.go, 2 sites)`
Expand Down
11 changes: 6 additions & 5 deletions desktop/docs/services-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ broker supervises every worker and relays its control plane.
| `ollama-proxy` | Ollama-compatible routing proxy with cluster-mTLS ingress |
| `lmstudio-proxy` | LM Studio routing proxy with cluster-mTLS ingress |
| `omlx-proxy` | oMLX routing proxy with cluster-mTLS ingress |
| `llamacpp-proxy` | llama.cpp routing proxy 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 All @@ -46,7 +47,7 @@ flowchart TB
Broker["nvpair-ui-broker"]
Scanner["nvpair-node-scanner"]
NodeInfo["nvpair-node-info"]
Proxies["ollama-proxy / lmstudio-proxy / omlx-proxy"]
Proxies["ollama-proxy / lmstudio-proxy / omlx-proxy / llamacpp-proxy"]
Engines["nvpair-engine-manager"]
Cluster["nvpair-cluster-manager"]
Settings["nvpair-node-settings"]
Expand Down Expand Up @@ -137,12 +138,12 @@ inbound-silence deadline and frontend-owned recovery. This is not per-worker han

Worker health is independently broker-owned: `worker/health` probes every 30 s
with a 5 s deadline; two failures degrade, three restart eligible workers only.
Engine-manager and the three proxies never auto-restart on a hang. Optional
Engine-manager and the four proxies never auto-restart on a hang. Optional
initial spawn failures share the bounded crash/hang budget, while missing default
binaries remain `missing` until explicit retry. The bridge hydrates
`workers:get-status` on every epoch, replaces rows on full `workers:update`
snapshots, and relays `workers:restart {name}`. Settings → Service → Workers
shows all twelve, attempts and last errors, including "Not responding — restart
shows all thirteen, attempts and last errors, including "Not responding — restart
manually" where applicable. Engine-manager and proxy restarts ask for
confirmation. No health/retry policy is duplicated in TypeScript; see the
[broker contract](../../services/nvpair-ui-broker/README.md#worker-lifecycle-registry).
Expand All @@ -161,7 +162,7 @@ consent policies, loopback-only Arize Phoenix deployment, and trace storage purg
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `app:ready` | Complete broker startup and refresh snapshots | `state:request-refresh` |
| `discovery:nodes-changed` | Replace discovery snapshot and diff nodes | `discovery:nodes-changed`, `nodes:upsert`, `nodes:remove` |
| `proxy:ready` / `lmstudio-proxy:ready` / `omlx-proxy:ready` | Record engine proxy port | `engines:state-changed` |
| `proxy:ready` / `lmstudio-proxy:ready` / `omlx-proxy:ready` / `llamacpp-proxy:ready` | Record engine proxy port | `engines:state-changed` |
| proxy `node/*` | Update per-engine node presence; the advertised port is the peer's promoted proxy port (not the engine's private loopback port) | node and engine pushes |
| `engine:ready` / `engine:state-changed` | Update engine facts and models | `engines:state-changed` |
| `engine:install-progress` / `engine:remote-progress` | Update operation progress | engine progress pushes |
Expand Down Expand Up @@ -213,7 +214,7 @@ the orphan-reclaim path, for a process whose `exec.Cmd` handle was lost.

Personal AI Router calls `engine:prepare-shutdown` before broker teardown so local processes
stop without clearing their persisted desired state. The broker also
first closes its three proxies concurrently, then self-initiates
first closes its four proxies concurrently, then self-initiates
`engine:prepare-shutdown` and joins started workers concurrently. Worker joins use
bounded stdin-close, terminate, and kill escalation, clipped together with the
engine shutdown request to a shared 10 s budget. Ingress (including the heartbeat
Expand Down
2 changes: 1 addition & 1 deletion desktop/docs/services-parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Personal AI Router supports local:
Before shutdown, Personal AI Router calls `engine:prepare-shutdown`. This stops managed engine
processes without changing the persisted desired state; the broker restores
enabled engines on the next launch. The broker also self-initiates
`engine:prepare-shutdown` after closing all three proxies concurrently. Started
`engine:prepare-shutdown` after closing all four proxies concurrently. Started
workers then join concurrently with bounded stdin-close, terminate, and kill
escalation. All waits share a 10 s teardown deadline. The first 2 s bound ingress
and the heartbeat join (proxy EOF grace 1 s, terminate/kill waits 500 ms each),
Expand Down
1 change: 1 addition & 0 deletions desktop/native/PrivilegedHelper/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum Firewall {
"ollama-proxy",
"lmstudio-proxy",
"omlx-proxy",
"llamacpp-proxy",
"nvpair-node-info",
"nvpair-node-scanner",
"nvpair-workload-manager",
Expand Down
Loading
Loading