fix(p2p): print the p2p-worker command, not the pre-v4 worker one

Root cause: #9124 moved the llama.cpp P2P worker under `p2p-worker` and
made `worker` the generic distributed worker, but the hint printed after
`run --p2p`, the Swarm page's command builder and the docs kept the old
`local-ai worker p2p-llama-cpp-rpc`, which the parser now rejects with
"unexpected argument p2p-llama-cpp-rpc". The MLX shard command had the
same stale prefix.

The hint becomes a constant that a test parses against the real CLI
tree, so the printed command cannot drift from the command set again.

Fixes #13032

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Vivek1898 <viveksingh27795@gmail.com>
This commit is contained in:
Vivek1898 committed 2026-11-02 22:42:52 +05:30
1 parent 16acc8926b
commit 3c11ab96a0
7 files changed
+53 -9

No files matched your search

+36
View File
@@ -0,0 +1,36 @@
package cli
import (
"strings"
"github.com/alecthomas/kong"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/mudler/LocalAI/core/config"
)
var _ = Describe("p2p worker hint", func() {
It("names a command the CLI accepts", func() {
// Parse a copy so the package-level CLI is left untouched. The vars
// are the ones cmd/local-ai/main.go supplies; the tags cannot be
// resolved without them.
app := CLI
k, err := kong.New(&app, kong.Vars{
"basepath": kong.ExpandPath("."),
"generatedcontentpath": DefaultGeneratedContentPath(),
"uploadpath": DefaultUploadPath(),
"galleries": config.DefaultGalleriesJSON,
"backends": config.DefaultBackendGalleriesJSON,
"version": "test",
})
Expect(err).ToNot(HaveOccurred())
args := strings.Fields(p2pWorkerCommand)
Expect(args[0]).To(Equal("local-ai"))
ctx, err := k.Parse(args[1:])
Expect(err).ToNot(HaveOccurred())
Expect(ctx.Command()).To(Equal("p2p-worker p2p-llama-cpp-rpc"))
})
})
+8 -1
View File
@@ -32,6 +32,13 @@ import (
// When renaming flags, add the old name as an alias for backward compatibility
// and document the deprecation in the help text.
// p2pWorkerCommand is the command a user runs on another node to join the
// P2P network with a llama.cpp worker. It is printed after `run --p2p`
// generates a token, and a test parses it against the CLI so it cannot drift
// from the command tree again (it named the pre-v4 `worker` command for a
// release cycle).
const p2pWorkerCommand = "local-ai p2p-worker p2p-llama-cpp-rpc"
type RunCMD struct {
AddonHostOps string `name:"addon-host-ops" env:"LOCALAI_ADDON_HOST_OPS" default:"fine-tuning=finetune.,quantization=quantize.,sandbox=workspace.,sandbox=egress.,office=workspace.,web=workspace.,telegram=connector." help:"Add-ons that may call host operations, as a comma list of addon-id=operation-prefix. An add-on that is not listed gets no host operation channel. Empty gives none" group:"addons"`
AddonsEnabled bool `name:"addons-enabled" env:"LOCALAI_ADDONS_ENABLED" default:"true" help:"Run the add-on owner, which installs and supervises add-ons. Turn it off with --addons-enabled=false" group:"addons"`
@@ -627,7 +634,7 @@ func (r *RunCMD) Run(ctx *cliContext.Context) (result error) {
fmt.Println(token)
xlog.Info("To use the token, you can run the following command in another node or terminal:")
fmt.Printf("export TOKEN=\"%s\"\nlocal-ai worker p2p-llama-cpp-rpc\n", token)
fmt.Printf("export TOKEN=\"%s\"\n%s\n", token, p2pWorkerCommand)
}
opts = append(opts, config.WithP2PToken(token))
}
+2 -2
View File
@@ -111,9 +111,9 @@ test.describe('Add a node', () => {
test('a memory shard offers llama.cpp RPC and MLX', async ({ page }) => {
await mockSwarm(page, { p2p })
await page.goto('/app/nodes/add?join=shard')
await expect(command(page)).toContainText('worker p2p-llama-cpp-rpc', { timeout: 15_000 })
await expect(command(page)).toContainText('p2p-worker p2p-llama-cpp-rpc', { timeout: 15_000 })
await page.getByRole('radio', { name: 'MLX (Apple Silicon)' }).click()
await expect(command(page)).toContainText('worker p2p-mlx')
await expect(command(page)).toContainText('p2p-worker p2p-mlx')
await expect(command(page)).toContainText('latest-metal-darwin-arm64')
// MLX runs on a Mac: GPU build targets do not apply.
await expect(page.getByRole('radio', { name: 'CUDA 12', exact: true })).toHaveCount(0)
+2 -2
View File
@@ -249,8 +249,8 @@ export function workerCommand({ flavor, nodeType, option, dev, frontend, natsUrl
export function p2pCommand({ method, option, dev, token, flavor }) {
const suffix = method === 'peer' ? 'run --federated --p2p'
: method === 'server' ? 'federated'
: method === 'mlx' ? 'worker p2p-mlx'
: 'worker p2p-llama-cpp-rpc'
: method === 'mlx' ? 'p2p-worker p2p-mlx'
: 'p2p-worker p2p-llama-cpp-rpc'
const name = method === 'peer' ? 'local-ai' : method === 'server' ? 'local-ai-federated' : method === 'mlx' ? 'local-ai-mlx-worker' : 'local-ai-worker'
const tokenValue = token || 'your-token-here'
if (flavor === 'cli') return `TOKEN="${tokenValue}" local-ai ${suffix}`
+2 -1
View File
@@ -140,5 +140,6 @@ test('builds join commands that carry no secret', () => {
assert.match(p2pCommand({ method: 'peer', option, dev: false, token: 'abc', flavor: 'docker' }), /run --federated --p2p$/)
assert.match(p2pCommand({ method: 'mlx', option, dev: true, token: '', flavor: 'docker' }), /your-token-here/)
assert.match(p2pCommand({ method: 'server', option, dev: false, token: 't', flavor: 'docker' }), /--name local-ai-federated[\s\S]*federated$/)
assert.match(p2pCommand({ method: 'shard', option, dev: false, token: 't', flavor: 'cli' }), /^TOKEN="t" local-ai worker p2p-llama-cpp-rpc$/)
assert.match(p2pCommand({ method: 'shard', option, dev: false, token: 't', flavor: 'cli' }), /^TOKEN="t" local-ai p2p-worker p2p-llama-cpp-rpc$/)
assert.match(p2pCommand({ method: 'mlx', option, dev: false, token: 't', flavor: 'cli' }), /^TOKEN="t" local-ai p2p-worker p2p-mlx$/)
})
@@ -119,7 +119,7 @@ To reuse the same token later, restart the server with `--p2ptoken` or `P2P_TOKE
2. Start the workers. Copy the `local-ai` binary to other hosts and run as many workers as needed using the token:
```bash
TOKEN=XXX ./local-ai worker p2p-llama-cpp-rpc --llama-cpp-args="-m <memory>"
TOKEN=XXX ./local-ai p2p-worker p2p-llama-cpp-rpc --llama-cpp-args="-m <memory>"
```
(Note: You can also supply the token via command-line arguments)
+2 -2
View File
@@ -43,7 +43,7 @@ On each additional Mac:
docker run -ti --net host \
-e TOKEN="<your-token>" \
--name local-ai-mlx-worker \
localai/localai:latest-metal-darwin-arm64 worker p2p-mlx
localai/localai:latest-metal-darwin-arm64 p2p-worker p2p-mlx
```
Workers auto-register on the P2P network. The LocalAI server discovers them and generates a hostfile for MLX distributed.
@@ -192,7 +192,7 @@ Starts a worker or manual rank 0 process.
| `--addr` | `MLX_DISTRIBUTED_ADDR` | `localhost:50051` | gRPC API listen address (rank 0 only, for LocalAI or external access) |
| `--coordinator` | `MLX_JACCL_COORDINATOR` | | JACCL coordinator `ip:port` - rank 0's address for RDMA setup (all ranks must use the same value) |
### `worker p2p-mlx`
### `p2p-worker p2p-mlx`
P2P mode - auto-discovers peers and generates hostfile.