@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.
Files changed (53) hide show
  1. package/dist/chat-session-warm-reuse.d.ts +10 -12
  2. package/dist/chat-session-warm-reuse.d.ts.map +1 -1
  3. package/dist/chat-session-warm-reuse.js +10 -12
  4. package/dist/endpoints/messages.d.ts +2 -2
  5. package/dist/endpoints/messages.d.ts.map +1 -1
  6. package/dist/endpoints/messages.js +492 -349
  7. package/dist/endpoints/responses.d.ts +1 -1
  8. package/dist/endpoints/responses.d.ts.map +1 -1
  9. package/dist/endpoints/responses.js +1149 -1055
  10. package/dist/handler.d.ts.map +1 -1
  11. package/dist/handler.js +1 -1
  12. package/dist/health.d.ts +4 -6
  13. package/dist/health.d.ts.map +1 -1
  14. package/dist/host/discover.d.ts +1 -2
  15. package/dist/host/discover.d.ts.map +1 -1
  16. package/dist/host/discover.js +3 -6
  17. package/dist/host/index.d.ts +6 -1
  18. package/dist/host/index.d.ts.map +1 -1
  19. package/dist/host/index.js +3 -2
  20. package/dist/index.d.ts +2 -4
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -9
  23. package/dist/mappers/anthropic-request.d.ts.map +1 -1
  24. package/dist/mappers/anthropic-request.js +5 -1
  25. package/dist/mappers/request.d.ts +12 -2
  26. package/dist/mappers/request.d.ts.map +1 -1
  27. package/dist/mappers/request.js +26 -2
  28. package/dist/model-work-coordinator.d.ts +50 -0
  29. package/dist/model-work-coordinator.d.ts.map +1 -1
  30. package/dist/model-work-coordinator.js +161 -0
  31. package/dist/registry.d.ts +15 -0
  32. package/dist/registry.d.ts.map +1 -1
  33. package/dist/registry.js +51 -0
  34. package/dist/server.d.ts +21 -3
  35. package/dist/server.d.ts.map +1 -1
  36. package/dist/server.js +30 -6
  37. package/dist/session-registry.d.ts +266 -86
  38. package/dist/session-registry.d.ts.map +1 -1
  39. package/dist/session-registry.js +421 -107
  40. package/dist/streaming.d.ts +37 -2
  41. package/dist/streaming.d.ts.map +1 -1
  42. package/dist/streaming.js +122 -1
  43. package/dist/transport-visibility.d.ts +5 -4
  44. package/dist/transport-visibility.d.ts.map +1 -1
  45. package/dist/transport-visibility.js +5 -4
  46. package/dist/types-anthropic.d.ts +7 -0
  47. package/dist/types-anthropic.d.ts.map +1 -1
  48. package/dist/types.d.ts +9 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +4 -4
  51. package/dist/presets.d.ts +0 -82
  52. package/dist/presets.d.ts.map +0 -1
  53. package/dist/presets.js +0 -98
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
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,UAAU,6BAA6B;IACrC,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IAC5E,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CAC/E;AASD,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;IACrH,OAAO,CAAC,6BAA6B,CAA4C;IAEjF,YAAY,IAAI,CAAC,EAAE,oBAAoB,EAEtC;IAED,sEAAsE;IACtE,IAAI,eAAe,IAAI,MAAM,GAAG,SAAS,CAExC;IAED;;;;;OAKG;IACH,gCAAgC,CAAC,WAAW,EAAE,6BAA6B,GAAG,SAAS,GAAG,IAAI,CAa7F;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,IAAI,CA4FzE;IAED;;;;;;;;;;OAUG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOhC;IAED;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;;;;;;;;;;;;;;;;;OAkBG;IACH,oBAAoB,CAClB,IAAI,EAAE,MAAM,GACX;QAAE,KAAK,EAAE,aAAa,CAAC;QAAC,QAAQ,EAAE,eAAe,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CASrF;IAED;;;;;;;;OAQG;IACH,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAQ/C;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAIxC;IAED;;;;;;;;OAQG;IACH,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAQzC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,+BAA+B,CAAC,KAAK,EAAE,aAAa,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAUxF;IAED;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAO3C;IAED;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAE3C;IAED;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI9C;IAED;;;;;;;;;;;;;;OAcG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE5D;IAED;;;;;;;;;OASG;IACH,qBAAqB,IAAI,eAAe,EAAE,CAMzC;IAED;;OAEG;IACH,IAAI,IAAI;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAgB1E;IAED;;;;;;;;;OASG;IACH,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAG9C;CACF"}
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: `undefined` (unbounded — current behaviour). Env override:
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
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
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
- // Opt-in queue-depth cap; resolved exactly once at server construction
160
- // so the registry (and its per-model `SessionRegistry` instances
161
- // allocated on `register()`) all share a single effective value.
162
- const configMaxQueueDepth = normalizePositiveIntConfig(config?.maxQueueDepthPerModel, 'maxQueueDepthPerModel');
163
- const maxQueueDepthPerModel = configMaxQueueDepth ?? parseEnvPositiveInt('MLX_MAX_QUEUE_DEPTH_PER_MODEL');
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');