@mlx-node/server 0.0.9 → 0.0.12
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/dist/chat-session-warm-reuse.d.ts +10 -12
- package/dist/chat-session-warm-reuse.d.ts.map +1 -1
- package/dist/chat-session-warm-reuse.js +10 -12
- package/dist/endpoints/messages.d.ts +2 -2
- package/dist/endpoints/messages.d.ts.map +1 -1
- package/dist/endpoints/messages.js +492 -349
- package/dist/endpoints/responses.d.ts +1 -1
- package/dist/endpoints/responses.d.ts.map +1 -1
- package/dist/endpoints/responses.js +1149 -1055
- package/dist/handler.d.ts.map +1 -1
- package/dist/handler.js +1 -1
- package/dist/health.d.ts +4 -6
- package/dist/health.d.ts.map +1 -1
- package/dist/host/discover.d.ts +1 -2
- package/dist/host/discover.d.ts.map +1 -1
- package/dist/host/discover.js +3 -6
- package/dist/host/index.d.ts +6 -1
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +3 -2
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -9
- package/dist/mappers/anthropic-request.d.ts.map +1 -1
- package/dist/mappers/anthropic-request.js +5 -1
- package/dist/mappers/request.d.ts +12 -2
- package/dist/mappers/request.d.ts.map +1 -1
- package/dist/mappers/request.js +26 -2
- package/dist/model-work-coordinator.d.ts +50 -0
- package/dist/model-work-coordinator.d.ts.map +1 -1
- package/dist/model-work-coordinator.js +161 -0
- package/dist/registry.d.ts +15 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +51 -0
- package/dist/server.d.ts +21 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +30 -6
- package/dist/session-registry.d.ts +266 -86
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +421 -107
- package/dist/streaming.d.ts +37 -2
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +122 -1
- package/dist/transport-visibility.d.ts +5 -4
- package/dist/transport-visibility.d.ts.map +1 -1
- package/dist/transport-visibility.js +5 -4
- package/dist/types-anthropic.d.ts +7 -0
- package/dist/types-anthropic.d.ts.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/presets.d.ts +0 -82
- package/dist/presets.d.ts.map +0 -1
- package/dist/presets.js +0 -98
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAEhD,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,eAAe,EAAE,eAAe,CAAC;CAClC;AAsCD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,EAAE,UAAU,CAAC;IAC9B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IACxD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAoD;IAC7F;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAChE,yDAAyD;IACzD,OAAO,CAAC,cAAc,CAAK;IAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAkF;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.js
CHANGED
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
* and the continuation is accepted.
|
|
38
38
|
*/
|
|
39
39
|
import { SessionRegistry } from './session-registry.js';
|
|
40
|
+
function concurrentDispatchCapacity(model) {
|
|
41
|
+
if (model.hasBlockPagedCache?.() !== true)
|
|
42
|
+
return 1;
|
|
43
|
+
const reported = model.maxConcurrentSequences?.();
|
|
44
|
+
if (reported === undefined || !Number.isSafeInteger(reported) || reported < 2)
|
|
45
|
+
return 1;
|
|
46
|
+
return reported;
|
|
47
|
+
}
|
|
40
48
|
export class ModelRegistry {
|
|
41
49
|
maxQueueDepth;
|
|
42
50
|
models = new Map();
|
|
@@ -88,9 +96,35 @@ export class ModelRegistry {
|
|
|
88
96
|
* hard-timeouts have fired.
|
|
89
97
|
*/
|
|
90
98
|
retiredInstanceIds = new WeakMap();
|
|
99
|
+
modelLoadAdmissionCoordinator;
|
|
91
100
|
constructor(opts) {
|
|
92
101
|
this.maxQueueDepth = opts?.maxQueueDepth;
|
|
93
102
|
}
|
|
103
|
+
/** Configured per-model waiter cap, or `undefined` when unbounded. */
|
|
104
|
+
get queueDepthLimit() {
|
|
105
|
+
return this.maxQueueDepth;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Connect cold-load admission to name registration. The coordinator is
|
|
109
|
+
* attached by the HTTP handler before dispatch; existing names are
|
|
110
|
+
* published immediately and future `register`/`unregister` calls keep the
|
|
111
|
+
* mapping current.
|
|
112
|
+
*/
|
|
113
|
+
setModelLoadAdmissionCoordinator(coordinator) {
|
|
114
|
+
if (this.modelLoadAdmissionCoordinator === coordinator)
|
|
115
|
+
return;
|
|
116
|
+
if (this.modelLoadAdmissionCoordinator) {
|
|
117
|
+
for (const [name, entry] of this.models) {
|
|
118
|
+
this.modelLoadAdmissionCoordinator.unbindRequestLoadAdmissions(name, entry.sessionRegistry);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.modelLoadAdmissionCoordinator = coordinator;
|
|
122
|
+
if (coordinator) {
|
|
123
|
+
for (const [name, entry] of this.models) {
|
|
124
|
+
coordinator.bindRequestLoadAdmissions(name, entry.sessionRegistry);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
94
128
|
/**
|
|
95
129
|
* Register a model under a given name.
|
|
96
130
|
*
|
|
@@ -132,11 +166,13 @@ export class ModelRegistry {
|
|
|
132
166
|
if (opts && 'maxOutputTokens' in opts) {
|
|
133
167
|
existing.sessionRegistry.setMaxOutputTokens(maxOutputTokens);
|
|
134
168
|
}
|
|
169
|
+
this.modelLoadAdmissionCoordinator?.bindRequestLoadAdmissions(name, existing.sessionRegistry);
|
|
135
170
|
return;
|
|
136
171
|
}
|
|
137
172
|
if (existing) {
|
|
138
173
|
// Same name, different model: release the old model's refcount
|
|
139
174
|
// before installing the new binding.
|
|
175
|
+
this.modelLoadAdmissionCoordinator?.unbindRequestLoadAdmissions(name, existing.sessionRegistry);
|
|
140
176
|
this.dropNameReference(existing.model);
|
|
141
177
|
}
|
|
142
178
|
// Look up or allocate the shared binding. If it is still alive
|
|
@@ -151,6 +187,7 @@ export class ModelRegistry {
|
|
|
151
187
|
registry: new SessionRegistry({
|
|
152
188
|
model,
|
|
153
189
|
maxQueueDepth: this.maxQueueDepth,
|
|
190
|
+
maxConcurrentDispatches: concurrentDispatchCapacity(model),
|
|
154
191
|
samplingDefaults,
|
|
155
192
|
maxOutputTokens,
|
|
156
193
|
}),
|
|
@@ -203,6 +240,7 @@ export class ModelRegistry {
|
|
|
203
240
|
createdAt: Math.floor(Date.now() / 1000),
|
|
204
241
|
sessionRegistry: binding.registry,
|
|
205
242
|
});
|
|
243
|
+
this.modelLoadAdmissionCoordinator?.bindRequestLoadAdmissions(name, binding.registry);
|
|
206
244
|
}
|
|
207
245
|
/**
|
|
208
246
|
* Unregister a model by name.
|
|
@@ -219,6 +257,7 @@ export class ModelRegistry {
|
|
|
219
257
|
const entry = this.models.get(name);
|
|
220
258
|
if (!entry)
|
|
221
259
|
return false;
|
|
260
|
+
this.modelLoadAdmissionCoordinator?.unbindRequestLoadAdmissions(name, entry.sessionRegistry);
|
|
222
261
|
this.models.delete(name);
|
|
223
262
|
this.dropNameReference(entry.model);
|
|
224
263
|
return true;
|
|
@@ -270,6 +309,18 @@ export class ModelRegistry {
|
|
|
270
309
|
* persists that crossed the safety breaker.
|
|
271
310
|
*/
|
|
272
311
|
finalizeBindingTeardown(model) {
|
|
312
|
+
const binding = this.sessionRegistriesByModel.get(model);
|
|
313
|
+
if (!binding)
|
|
314
|
+
return;
|
|
315
|
+
binding.registry.clear();
|
|
316
|
+
// The binding maps are removed synchronously below, so no later request can
|
|
317
|
+
// discover this registry and call flushPendingDisposals() for it. Start the
|
|
318
|
+
// flush while the registry is still in hand: the promise retains it until
|
|
319
|
+
// every pending disposal has settled and every transient failure observed
|
|
320
|
+
// during this flush has received its one bounded retry.
|
|
321
|
+
void binding.registry.flushPendingDisposals().catch((error) => {
|
|
322
|
+
console.error('[server] failed to flush final chat-session owner disposals:', error);
|
|
323
|
+
});
|
|
273
324
|
this.sessionRegistriesByModel.delete(model);
|
|
274
325
|
this.instanceIds.delete(model);
|
|
275
326
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -6,6 +6,21 @@ import { type ServerHealth } from './health.js';
|
|
|
6
6
|
import { type LoadModelOptions } from './load-model.js';
|
|
7
7
|
import { ModelWorkCoordinator } from './model-work-coordinator.js';
|
|
8
8
|
import { ModelRegistry } from './registry.js';
|
|
9
|
+
/**
|
|
10
|
+
* Default per-model queue-depth cap (waiters-only; the actively running
|
|
11
|
+
* dispatch does not count). Applied when neither
|
|
12
|
+
* {@link ServerConfig.maxQueueDepthPerModel} nor the
|
|
13
|
+
* `MLX_MAX_QUEUE_DEPTH_PER_MODEL` env var is set; the config sentinel
|
|
14
|
+
* `'unbounded'` opts out entirely.
|
|
15
|
+
*
|
|
16
|
+
* 16, not 8: an agent fan-out (e.g. Claude Code bursting parallel tool
|
|
17
|
+
* calls) can put well over 8 concurrent requests behind one model from a
|
|
18
|
+
* SINGLE client, so 8 risked 429s on a lone user. Over-cap requests get
|
|
19
|
+
* HTTP 429 with `Retry-After: 1`, which the OpenAI/Anthropic SDK retry
|
|
20
|
+
* loops honor — the cap sheds pile-up instead of failing well-behaved
|
|
21
|
+
* clients.
|
|
22
|
+
*/
|
|
23
|
+
export declare const DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL = 16;
|
|
9
24
|
/**
|
|
10
25
|
* Parse a positive integer seconds value; returns undefined for unset/invalid so caller can apply its own default.
|
|
11
26
|
*
|
|
@@ -101,10 +116,13 @@ export interface ServerConfig {
|
|
|
101
116
|
* `Retry-After: 1` header so clients can back off instead of piling
|
|
102
117
|
* into an unbounded queue.
|
|
103
118
|
*
|
|
104
|
-
* Default:
|
|
105
|
-
* `MLX_MAX_QUEUE_DEPTH_PER_MODEL` (positive integer).
|
|
119
|
+
* Default: {@link DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL} (16). Env
|
|
120
|
+
* override: `MLX_MAX_QUEUE_DEPTH_PER_MODEL` (positive integer). Pass
|
|
121
|
+
* `'unbounded'` to opt out of the cap entirely (the pre-cap
|
|
122
|
+
* behaviour); the sentinel also bypasses the env override, because an
|
|
123
|
+
* explicit config value always wins over env.
|
|
106
124
|
*/
|
|
107
|
-
maxQueueDepthPerModel?: number;
|
|
125
|
+
maxQueueDepthPerModel?: number | 'unbounded';
|
|
108
126
|
/**
|
|
109
127
|
* Milliseconds of HTTP inactivity (no request arrivals or completions)
|
|
110
128
|
* before the server issues a single `clearCache()` to drain the MLX
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,0HAA0H;AAI1H,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,WAAW,CAAC;AAIxD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEtE,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA2B9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOhE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOpE;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAUjF;AAED,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -32,6 +32,21 @@ const DEFAULT_CLOSE_TIMEOUT_MS = 5000;
|
|
|
32
32
|
* cost of a one-time prefill on recovery.
|
|
33
33
|
*/
|
|
34
34
|
const DEFAULT_RESPONSE_RETENTION_SECONDS = 7 * 24 * 60 * 60; // 7 days
|
|
35
|
+
/**
|
|
36
|
+
* Default per-model queue-depth cap (waiters-only; the actively running
|
|
37
|
+
* dispatch does not count). Applied when neither
|
|
38
|
+
* {@link ServerConfig.maxQueueDepthPerModel} nor the
|
|
39
|
+
* `MLX_MAX_QUEUE_DEPTH_PER_MODEL` env var is set; the config sentinel
|
|
40
|
+
* `'unbounded'` opts out entirely.
|
|
41
|
+
*
|
|
42
|
+
* 16, not 8: an agent fan-out (e.g. Claude Code bursting parallel tool
|
|
43
|
+
* calls) can put well over 8 concurrent requests behind one model from a
|
|
44
|
+
* SINGLE client, so 8 risked 429s on a lone user. Over-cap requests get
|
|
45
|
+
* HTTP 429 with `Retry-After: 1`, which the OpenAI/Anthropic SDK retry
|
|
46
|
+
* loops honor — the cap sheds pile-up instead of failing well-behaved
|
|
47
|
+
* clients.
|
|
48
|
+
*/
|
|
49
|
+
export const DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL = 16;
|
|
35
50
|
/**
|
|
36
51
|
* Parse a positive integer seconds value; returns undefined for unset/invalid so caller can apply its own default.
|
|
37
52
|
*
|
|
@@ -156,11 +171,20 @@ export async function createServer(config) {
|
|
|
156
171
|
// `normalizePositiveIntConfig` for the failure modes we're guarding.
|
|
157
172
|
const configRetentionSec = normalizePositiveIntConfig(config?.responseRetentionSec, 'responseRetentionSec');
|
|
158
173
|
const responseRetentionSec = configRetentionSec ?? parseEnvSeconds('MLX_RESPONSE_RETENTION_SECONDS') ?? DEFAULT_RESPONSE_RETENTION_SECONDS;
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
174
|
+
// Queue-depth cap; resolved exactly once at server construction so the
|
|
175
|
+
// registry (and its per-model `SessionRegistry` instances allocated on
|
|
176
|
+
// `register()`) all share a single effective value. Precedence:
|
|
177
|
+
// explicit config (the `'unbounded'` sentinel disables the cap
|
|
178
|
+
// outright, short-circuiting env) wins over env wins over the default
|
|
179
|
+
// of {@link DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL}. Bogus numeric config
|
|
180
|
+
// still fails fast in `normalizePositiveIntConfig` before any env /
|
|
181
|
+
// default fallback applies.
|
|
182
|
+
const maxQueueDepthConfig = config?.maxQueueDepthPerModel;
|
|
183
|
+
const maxQueueDepthPerModel = maxQueueDepthConfig === 'unbounded'
|
|
184
|
+
? undefined
|
|
185
|
+
: (normalizePositiveIntConfig(maxQueueDepthConfig, 'maxQueueDepthPerModel') ??
|
|
186
|
+
parseEnvPositiveInt('MLX_MAX_QUEUE_DEPTH_PER_MODEL') ??
|
|
187
|
+
DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL);
|
|
164
188
|
// Idle sweeper wiring. `0` is a legal explicit "off" value so we
|
|
165
189
|
// cannot reuse `normalizePositiveIntConfig` (which rejects 0).
|
|
166
190
|
// Precedence: explicit config value wins over env wins over default.
|
|
@@ -172,7 +196,7 @@ export async function createServer(config) {
|
|
|
172
196
|
// request will not fire `clearCache()`. See the `idle-sweeper.ts`
|
|
173
197
|
// module doc (section "Drain is post-request only") for rationale.
|
|
174
198
|
const registry = new ModelRegistry({ maxQueueDepth: maxQueueDepthPerModel });
|
|
175
|
-
const modelWorkCoordinator = new ModelWorkCoordinator();
|
|
199
|
+
const modelWorkCoordinator = new ModelWorkCoordinator(maxQueueDepthPerModel);
|
|
176
200
|
let store = null;
|
|
177
201
|
if (!disableStore) {
|
|
178
202
|
const storePath = config?.storePath ?? join(homedir(), '.mlx-node', 'responses.db');
|