@qvac/ggml-rpc-server 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +26 -20
- package/index.d.ts +9 -3
- package/index.js +19 -28
- package/package.json +1 -1
- package/prebuilds/android-arm64/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/darwin-arm64/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/darwin-arm64/qvac__ggml-rpc-server.bare.exports +1 -2
- package/prebuilds/darwin-x64/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/darwin-x64/qvac__ggml-rpc-server.bare.exports +1 -2
- package/prebuilds/ios-arm64/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/ios-arm64/qvac__ggml-rpc-server.bare.exports +1 -2
- package/prebuilds/ios-arm64-simulator/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/ios-arm64-simulator/qvac__ggml-rpc-server.bare.exports +1 -2
- package/prebuilds/ios-x64-simulator/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/ios-x64-simulator/qvac__ggml-rpc-server.bare.exports +1 -2
- package/prebuilds/linux-arm64/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/linux-x64/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/win32-x64/qvac__ggml-rpc-server.bare +0 -0
- package/prebuilds/win32-x64/qvac__ggml-rpc-server.bare.exports +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.1] - 2026-10-15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- On Windows, the addon now binds the delay-loaded `@qvac/fabric` runtime
|
|
8
|
+
when it loads, so a first fabric call on a worker thread cannot fail with
|
|
9
|
+
`0xC06D007E` ([#5121](https://github.com/tetherto/qvac/pull/5121)). Loading throws an error naming the fabric
|
|
10
|
+
module if the bind fails.
|
|
11
|
+
|
|
12
|
+
## [0.5.0] - 2026-10-15
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Without a `port`, the native server binds a free port itself and reports it,
|
|
17
|
+
instead of this package picking a free port and releasing it before the
|
|
18
|
+
server bound it. Another process could take the port in between, which showed
|
|
19
|
+
up as a rare `RpcServerStartError`. `allocateFreePort()` is still exported.
|
|
20
|
+
- `rdmaCapable` and `expectRdma` now use the capability that `@qvac/fabric`
|
|
21
|
+
reports, replacing a search of the RPC module for a log line. They mean the
|
|
22
|
+
server will try RDMA, which on Linux also needs `libibverbs.so.1` installed,
|
|
23
|
+
an RDMA device, and `GGML_RPC_NO_RDMA` unset. A host with the library but no
|
|
24
|
+
RDMA device now reports `rdmaCapable: false`, so `expectRdma: true` fails
|
|
25
|
+
closed there.
|
|
26
|
+
- Requires `@qvac/fabric` 0.22.0, whose RPC backend exports
|
|
27
|
+
`ggml_backend_rpc_server_get_port` and `ggml_backend_rpc_rdma_supported`.
|
|
28
|
+
A mobile app that still installs `@qvac/fabric-android-arm64` or
|
|
29
|
+
`@qvac/fabric-ios` 0.21 fails to start the server with
|
|
30
|
+
`RpcServerStartError`.
|
|
31
|
+
|
|
3
32
|
## [0.4.0] - 2026-10-14
|
|
4
33
|
|
|
5
34
|
### Changed
|
package/README.md
CHANGED
|
@@ -24,6 +24,11 @@ try {
|
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
Without a `port`, the server binds a free port itself and reports it in
|
|
28
|
+
`server.port`, so no other process can take the port before the server binds
|
|
29
|
+
it. `allocateFreePort()` only finds a port that is free now; prefer leaving
|
|
30
|
+
`port` unset.
|
|
31
|
+
|
|
27
32
|
The listener accepts IPv4 addresses; `localhost` is normalized to `127.0.0.1`.
|
|
28
33
|
The default host is `127.0.0.1`, but loopback is not private to the host
|
|
29
34
|
application: other local processes (including apps with local TCP access on
|
|
@@ -48,9 +53,10 @@ Native server output is written to the host application's platform log.
|
|
|
48
53
|
|
|
49
54
|
Errors are classes that can be matched by `name` or `instanceof`. Invalid
|
|
50
55
|
options throw `RpcServerInvalidHostError`, `RpcServerNonLoopbackHostError`,
|
|
51
|
-
`RangeError` (port) or `TypeError` (threads);
|
|
52
|
-
`RpcServerPortAllocationError
|
|
53
|
-
|
|
56
|
+
`RangeError` (port) or `TypeError` (threads); `allocateFreePort()` throws
|
|
57
|
+
`RpcServerPortAllocationError` when it cannot find a port; and
|
|
58
|
+
`expectRdma: true` when the server would not try RDMA throws
|
|
59
|
+
`RpcServerRdmaUnavailableError`. Failures reported by the native server
|
|
54
60
|
extend `RpcServerNativeError`, which carries the native error as `cause` and a
|
|
55
61
|
`code` equal to its `name`:
|
|
56
62
|
|
|
@@ -58,31 +64,31 @@ extend `RpcServerNativeError`, which carries the native error as `cause` and a
|
|
|
58
64
|
|---|---|
|
|
59
65
|
| `RpcServerDeviceError` | No requested device exists, or no device is available |
|
|
60
66
|
| `RpcServerCacheError` | The cache directory cannot be resolved or created |
|
|
61
|
-
| `RpcServerStartError` | The server cannot be created or bound,
|
|
67
|
+
| `RpcServerStartError` | The server cannot be created or bound, the RPC backend is missing, or the installed `@qvac/fabric` is too old |
|
|
62
68
|
| `RpcServerBackendError` | The Fabric backends directory is invalid or cannot be inspected |
|
|
63
69
|
| `RpcServerStopError` | The server does not stop cleanly |
|
|
64
70
|
|
|
65
71
|
Anything else from the native addon, such as an out-of-memory failure, is
|
|
66
72
|
rethrown unchanged.
|
|
67
73
|
|
|
68
|
-
## Linux
|
|
69
|
-
|
|
70
|
-
The Linux RPC backend in `@qvac/fabric` links `libibverbs.so.1`, so Linux
|
|
71
|
-
hosts must provide it (`libibverbs1` on Debian/Ubuntu) even when connections
|
|
72
|
-
use TCP. Without it, `startRpcServer()` rejects with an error whose `code` is
|
|
73
|
-
`RpcServerStartError`.
|
|
74
|
-
RDMA also requires the provider package for the host's hardware.
|
|
74
|
+
## RDMA on Linux
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
The Linux RPC backend in `@qvac/fabric` is built with RDMA and loads
|
|
77
|
+
`libibverbs.so.1` (`libibverbs1` on Debian/Ubuntu) at runtime. When the library
|
|
78
|
+
is installed and the host has an RDMA device, the server tries RDMA with each
|
|
79
|
+
client that also supports it and falls back to TCP otherwise. Without the
|
|
80
|
+
library or a device, the server runs over TCP only. RDMA also requires the
|
|
81
|
+
provider package for the host's hardware.
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
`rdmaCapable: true` does not guarantee that a given connection uses
|
|
82
|
-
|
|
83
|
+
`rdmaCapable` reports whether the server will try RDMA: the library loaded, it
|
|
84
|
+
lists an RDMA device, and `GGML_RPC_NO_RDMA` is not set. A host with libibverbs
|
|
85
|
+
but no RDMA device, common where MPI or UCX installs the library, reports
|
|
86
|
+
`false`. `rdmaCapable: true` does not guarantee that a given connection uses
|
|
87
|
+
RDMA, since the client and the link must support it too. Android, iOS, macOS
|
|
88
|
+
and Windows always report `false`.
|
|
83
89
|
|
|
84
90
|
To fail closed when RDMA is required, pass `expectRdma: true`. Startup rejects
|
|
85
|
-
with `RpcServerRdmaUnavailableError` when the
|
|
91
|
+
with `RpcServerRdmaUnavailableError` when the server would not try RDMA:
|
|
86
92
|
|
|
87
93
|
```js
|
|
88
94
|
const server = await startRpcServer({
|
|
@@ -95,8 +101,8 @@ const server = await startRpcServer({
|
|
|
95
101
|
console.log(server.rdmaCapable)
|
|
96
102
|
```
|
|
97
103
|
|
|
98
|
-
Without `expectRdma`, the server starts either way and reports
|
|
99
|
-
|
|
104
|
+
Without `expectRdma`, the server starts either way and reports in
|
|
105
|
+
`rdmaCapable` whether it will try RDMA.
|
|
100
106
|
|
|
101
107
|
## Testing
|
|
102
108
|
|
package/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class RpcServerInvalidHostError extends Error {
|
|
|
9
9
|
constructor(host: string);
|
|
10
10
|
}
|
|
11
11
|
export declare class RpcServerRdmaUnavailableError extends Error {
|
|
12
|
-
constructor();
|
|
12
|
+
constructor(cause?: unknown);
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* A failure reported by the native server. The addon raises plain errors with a
|
|
@@ -55,8 +55,10 @@ export interface RpcServer {
|
|
|
55
55
|
readonly url: string;
|
|
56
56
|
readonly device?: string;
|
|
57
57
|
/**
|
|
58
|
-
* Whether the
|
|
59
|
-
*
|
|
58
|
+
* Whether new connections will try RDMA: the Fabric RPC backend was built
|
|
59
|
+
* with it, loaded libibverbs, found an RDMA device, and `GGML_RPC_NO_RDMA` is
|
|
60
|
+
* unset. Each connection still falls back to TCP when the client or the link
|
|
61
|
+
* cannot use RDMA.
|
|
60
62
|
*/
|
|
61
63
|
readonly rdmaCapable: boolean;
|
|
62
64
|
stop(): Promise<void>;
|
|
@@ -64,5 +66,9 @@ export interface RpcServer {
|
|
|
64
66
|
export interface AllocateFreePortOptions {
|
|
65
67
|
readonly allowNonLoopbackHost?: boolean;
|
|
66
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Finds a port that is free now. Another process can take it before you bind
|
|
71
|
+
* it, so `startRpcServer()` without a `port` lets the server bind one itself.
|
|
72
|
+
*/
|
|
67
73
|
export declare function allocateFreePort(host?: string, options?: AllocateFreePortOptions): Promise<number>;
|
|
68
74
|
export declare function startRpcServer(options?: StartRpcServerOptions): Promise<RpcServer>;
|
package/index.js
CHANGED
|
@@ -23,9 +23,6 @@ const TRUSTED_LAN_WARNING_CODE = "QVAC_GGML_RPC_SERVER_TRUSTED_LAN";
|
|
|
23
23
|
// Keep native handles alive until an explicit stop finishes. Otherwise their
|
|
24
24
|
// finalizer can synchronously stop and join a live server during garbage collection.
|
|
25
25
|
const activeServerHandles = new Set();
|
|
26
|
-
// Fabric never unloads its RPC module, so its RDMA build cannot change within a
|
|
27
|
-
// process. Cache the native check, which reads the module from disk.
|
|
28
|
-
let rdmaSupport;
|
|
29
26
|
class RpcServerPortAllocationError extends Error {
|
|
30
27
|
constructor(cause) {
|
|
31
28
|
super("Failed to allocate a free port for ggml-rpc-server", { cause });
|
|
@@ -50,8 +47,12 @@ class RpcServerInvalidHostError extends Error {
|
|
|
50
47
|
}
|
|
51
48
|
exports.RpcServerInvalidHostError = RpcServerInvalidHostError;
|
|
52
49
|
class RpcServerRdmaUnavailableError extends Error {
|
|
53
|
-
constructor() {
|
|
54
|
-
super(
|
|
50
|
+
constructor(cause) {
|
|
51
|
+
super(
|
|
52
|
+
"RDMA is not available: the @qvac/fabric RPC backend lacks it, libibverbs.so.1 " +
|
|
53
|
+
"could not be loaded, the host has no RDMA device, or GGML_RPC_NO_RDMA is set",
|
|
54
|
+
{ cause },
|
|
55
|
+
);
|
|
55
56
|
this.name = "RpcServerRdmaUnavailableError";
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -115,6 +116,9 @@ const nativeErrorClasses = new Map([
|
|
|
115
116
|
]);
|
|
116
117
|
function toTypedError(error) {
|
|
117
118
|
const code = error?.code;
|
|
119
|
+
if (code === "RpcServerRdmaUnavailableError") {
|
|
120
|
+
return new RpcServerRdmaUnavailableError(error);
|
|
121
|
+
}
|
|
118
122
|
const ErrorClass =
|
|
119
123
|
typeof code === "string" ? nativeErrorClasses.get(code) : undefined;
|
|
120
124
|
return ErrorClass === undefined
|
|
@@ -192,17 +196,10 @@ function validateThreads(threads) {
|
|
|
192
196
|
throw new TypeError("threads must be a positive integer");
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
rdmaSupport = binding.rpcBackendSupportsRdma({});
|
|
200
|
-
} catch (error) {
|
|
201
|
-
throw toTypedError(error);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return rdmaSupport;
|
|
205
|
-
}
|
|
199
|
+
/**
|
|
200
|
+
* Finds a port that is free now. Another process can take it before you bind
|
|
201
|
+
* it, so `startRpcServer()` without a `port` lets the server bind one itself.
|
|
202
|
+
*/
|
|
206
203
|
function allocateFreePort(
|
|
207
204
|
host = exports.DEFAULT_RPC_SERVER_HOST,
|
|
208
205
|
options = {},
|
|
@@ -231,23 +228,17 @@ async function startRpcServer(options = {}) {
|
|
|
231
228
|
assertLoopbackHost(host, options.allowNonLoopbackHost);
|
|
232
229
|
warnForTrustedLanHost(host, options.allowNonLoopbackHost);
|
|
233
230
|
validateThreads(options.threads);
|
|
234
|
-
|
|
235
|
-
if (options.expectRdma === true && !rdmaCapable) {
|
|
236
|
-
throw new RpcServerRdmaUnavailableError();
|
|
237
|
-
}
|
|
238
|
-
const port =
|
|
239
|
-
options.port ??
|
|
240
|
-
(await allocateFreePort(host, {
|
|
241
|
-
allowNonLoopbackHost: options.allowNonLoopbackHost,
|
|
242
|
-
}));
|
|
243
|
-
validatePort(port);
|
|
231
|
+
if (options.port !== undefined) validatePort(options.port);
|
|
244
232
|
const device = normalizeDevice(options.device);
|
|
245
|
-
|
|
233
|
+
// Without a port the server binds port 0 itself and reports the port it got,
|
|
234
|
+
// so no other process can take the port between choosing and binding it.
|
|
235
|
+
const { handle, port, rdmaCapable } = await callNative(() =>
|
|
246
236
|
binding.startServer({
|
|
247
|
-
endpoint: `${host}:${port}`,
|
|
237
|
+
endpoint: `${host}:${options.port ?? 0}`,
|
|
248
238
|
device,
|
|
249
239
|
cache: options.cache ?? false,
|
|
250
240
|
threads: options.threads,
|
|
241
|
+
expectRdma: options.expectRdma === true,
|
|
251
242
|
}),
|
|
252
243
|
);
|
|
253
244
|
activeServerHandles.add(handle);
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
],
|
|
8
8
|
"current_versions": [
|
|
9
9
|
{
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.5.1"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"exported_symbols": [
|
|
14
14
|
{
|
|
15
15
|
"text": {
|
|
16
16
|
"global": [
|
|
17
|
-
"__ZN10rpc_server23rpcBackendHasRdmaMarkerERKNSt3__14__fs10filesystem4pathE",
|
|
18
17
|
"__ZN10rpc_server12ServerRunner4stopEv",
|
|
19
18
|
"__ZN10rpc_server13selectDevicesERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
|
|
20
19
|
"_bare_register_module_v0",
|
|
Binary file
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
],
|
|
8
8
|
"current_versions": [
|
|
9
9
|
{
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.5.1"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"exported_symbols": [
|
|
14
14
|
{
|
|
15
15
|
"text": {
|
|
16
16
|
"global": [
|
|
17
|
-
"__ZN10rpc_server23rpcBackendHasRdmaMarkerERKNSt3__14__fs10filesystem4pathE",
|
|
18
17
|
"__ZN10rpc_server12ServerRunner4stopEv",
|
|
19
18
|
"__ZN10rpc_server13selectDevicesERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
|
|
20
19
|
"_bare_register_module_v0",
|
|
Binary file
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
],
|
|
8
8
|
"current_versions": [
|
|
9
9
|
{
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.5.1"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"exported_symbols": [
|
|
14
14
|
{
|
|
15
15
|
"text": {
|
|
16
16
|
"global": [
|
|
17
|
-
"__ZN10rpc_server23rpcBackendHasRdmaMarkerERKNSt3__14__fs10filesystem4pathE",
|
|
18
17
|
"__ZN10rpc_server12ServerRunner4stopEv",
|
|
19
18
|
"__ZN10rpc_server13selectDevicesERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
|
|
20
19
|
"_bare_register_module_v0",
|
|
Binary file
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
],
|
|
8
8
|
"current_versions": [
|
|
9
9
|
{
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.5.1"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"exported_symbols": [
|
|
14
14
|
{
|
|
15
15
|
"text": {
|
|
16
16
|
"global": [
|
|
17
|
-
"__ZN10rpc_server23rpcBackendHasRdmaMarkerERKNSt3__14__fs10filesystem4pathE",
|
|
18
17
|
"__ZN10rpc_server12ServerRunner4stopEv",
|
|
19
18
|
"__ZN10rpc_server13selectDevicesERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
|
|
20
19
|
"_bare_register_module_v0",
|
|
Binary file
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
],
|
|
8
8
|
"current_versions": [
|
|
9
9
|
{
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.5.1"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"exported_symbols": [
|
|
14
14
|
{
|
|
15
15
|
"text": {
|
|
16
16
|
"global": [
|
|
17
|
-
"__ZN10rpc_server23rpcBackendHasRdmaMarkerERKNSt3__14__fs10filesystem4pathE",
|
|
18
17
|
"__ZN10rpc_server12ServerRunner4stopEv",
|
|
19
18
|
"__ZN10rpc_server13selectDevicesERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
|
|
20
19
|
"_bare_register_module_v0",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|