mlx-bun 0.3.0 → 0.5.0
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/README.md +57 -32
- package/docs/README.md +58 -42
- package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +3617 -0
- package/docs/archive/investigations/openjarvis-fit.md +198 -0
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
- package/docs/archive/planning/release-notes-v0.4.0.md +45 -0
- package/docs/archive/planning/release-notes-v0.5.0.md +37 -0
- package/docs/design/batching.md +1535 -50
- package/docs/design/decode-speed-program.md +394 -1171
- package/docs/design/generic-model-support.md +195 -3
- package/docs/design/kv-cache.md +538 -225
- package/docs/design/speculative-decoding.md +552 -50
- package/docs/design/turboquant.md +140 -17
- package/docs/design/unified-engine-frontier-plan.md +259 -150
- package/docs/planning/PRODUCT_ROADMAP.md +21 -6
- package/docs/planning/public-readiness-handoff.md +244 -0
- package/docs/planning/public-readiness.md +149 -0
- package/docs/reference/benchmarks.md +3537 -1112
- package/docs/reference/cli.md +102 -2
- package/docs/reference/distribution.md +45 -13
- package/docs/reference/environment.md +159 -13
- package/docs/reference/library-api.md +18 -4
- package/docs/reference/memory.md +1 -1
- package/docs/reference/models.md +28 -7
- package/docs/reference/server-api.md +257 -33
- package/docs/reference/server-config.md +372 -151
- package/docs/reference/troubleshooting.md +13 -21
- package/package.json +2 -2
- package/src/audio/audiotoolbox.ts +112 -0
- package/src/audio/conformer.ts +8 -0
- package/src/audio/mic-capture.ts +98 -0
- package/src/audio/silero-vad.ts +379 -0
- package/src/audio/whisper-decode.ts +774 -0
- package/src/audio/whisper-fast.ts +732 -0
- package/src/audio/whisper-format.ts +73 -0
- package/src/audio/whisper-mel.ts +185 -0
- package/src/audio/whisper-timing.ts +274 -0
- package/src/audio/whisper-tokenizer.ts +239 -0
- package/src/audio/whisper-transcribe.ts +410 -0
- package/src/backends/mlx/assistant-target.ts +46 -0
- package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
- package/src/backends/mlx/autoregressive.ts +22 -1
- package/src/backends/mlx/batch-group.ts +464 -236
- package/src/backends/mlx/cache-attachment.ts +67 -0
- package/src/backends/mlx/cache-layout.ts +31 -0
- package/src/backends/mlx/checkpoint-state.ts +53 -0
- package/src/backends/mlx/continuation-persistence.ts +107 -0
- package/src/backends/mlx/continuation-request.ts +43 -0
- package/src/backends/mlx/continuation.ts +77 -0
- package/src/backends/mlx/draft-target.ts +7 -22
- package/src/backends/mlx/encoder-cache.ts +25 -0
- package/src/backends/mlx/fill-append.ts +21 -8
- package/src/backends/mlx/fill-group.ts +497 -0
- package/src/backends/mlx/full-kv-row-append.ts +32 -0
- package/src/backends/mlx/full-kv-row-donor.ts +54 -0
- package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
- package/src/backends/mlx/gateway-binding.ts +122 -19
- package/src/backends/mlx/kv-codec.ts +9 -0
- package/src/backends/mlx/kv-maintenance.ts +161 -0
- package/src/backends/mlx/kv-tensor-rows.ts +212 -0
- package/src/backends/mlx/mixed-iteration.ts +62 -0
- package/src/backends/mlx/model-prompt.ts +46 -32
- package/src/backends/mlx/model-serving.ts +5 -4
- package/src/backends/mlx/paged-cache-codec.ts +27 -0
- package/src/backends/mlx/prefill-cohort.ts +100 -0
- package/src/backends/mlx/prefill-policy.ts +26 -0
- package/src/backends/mlx/prefill-rows.ts +244 -0
- package/src/backends/mlx/prefill.ts +1 -1
- package/src/backends/mlx/projected-context-rows.ts +72 -0
- package/src/backends/mlx/prompt-input.ts +29 -0
- package/src/backends/mlx/qwen-prompt-input.ts +40 -0
- package/src/backends/mlx/request-state-policy.ts +54 -0
- package/src/backends/mlx/rollback.ts +45 -4
- package/src/backends/mlx/row-sampling.ts +49 -0
- package/src/backends/mlx/serial-executor.ts +51 -73
- package/src/backends/mlx/speculative-group.ts +438 -0
- package/src/backends/mlx/speculative-round.ts +161 -0
- package/src/backends/mlx/speculative.ts +24 -7
- package/src/backends/mlx/state-rows.ts +104 -0
- package/src/backends/mlx/state-views.ts +21 -1
- package/src/backends/mlx/target-layout-capability.ts +35 -0
- package/src/backends/mlx/transitioning-kv-rows.ts +173 -0
- package/src/cli.ts +333 -36
- package/src/contracts/execution.ts +21 -0
- package/src/contracts/object-cache.ts +8 -0
- package/src/contracts/pi-provider.ts +1 -1
- package/src/contracts/pi.ts +3 -1
- package/src/contracts/prefix-cache.ts +33 -0
- package/src/contracts/resources.ts +7 -0
- package/src/contracts/sampling.ts +23 -0
- package/src/contracts/scheduling.ts +27 -0
- package/src/engine/execution-plan.ts +38 -25
- package/src/engine/execution-tasks.ts +44 -0
- package/src/engine/namespaced-cache.ts +13 -0
- package/src/engine/resources.ts +12 -1
- package/src/engine/scheduler.ts +32 -7
- package/src/fill/fill-session.ts +14 -3
- package/src/fit.ts +29 -15
- package/src/generate.ts +105 -195
- package/src/grammar.ts +13 -0
- package/src/inference/draft-acceptance.ts +41 -0
- package/src/inference/draft-output.ts +49 -0
- package/src/inference/prefill.ts +6 -0
- package/src/inference/rollback.ts +2 -2
- package/src/kv-scheme.ts +92 -12
- package/src/kv-store.ts +183 -93
- package/src/lab/README.md +5 -1
- package/src/lab/paged-kv/paged-attention.ts +117 -0
- package/src/lab/paged-kv/paged-kv-rows.ts +111 -0
- package/src/lab/paged-kv/paged-kv.ts +126 -46
- package/src/md-modules.d.ts +5 -0
- package/src/mlx/array.ts +39 -1
- package/src/mlx/ffi.ts +19 -0
- package/src/mlx/normalized-argmax.ts +116 -0
- package/src/mlx/ops.ts +97 -10
- package/src/model/artifact-identity.ts +73 -0
- package/src/model/batched-kv.ts +235 -0
- package/src/model/batched-mask.ts +12 -4
- package/src/model/batched-quant.ts +8 -2
- package/src/model/batched-quantized-kv.ts +144 -0
- package/src/model/batched-rotating-quant.ts +76 -15
- package/src/model/batched-rotating-state.ts +76 -0
- package/src/model/batched-rotating.ts +64 -22
- package/src/model/batched-row-storage.ts +52 -0
- package/src/model/batched-ssm.ts +52 -0
- package/src/model/batched-turboquant-kv.ts +94 -0
- package/src/model/compiled-decode.ts +7 -1
- package/src/model/decoded-kv-donor.ts +12 -0
- package/src/model/delayed-quantized-kv.ts +72 -0
- package/src/model/delayed-rotating-quantized-kv.ts +93 -0
- package/src/model/delayed-turboquant-kv.ts +57 -0
- package/src/model/factory.ts +26 -0
- package/src/model/fingerprint.ts +12 -0
- package/src/model/full-prefill-padding.ts +56 -0
- package/src/model/full-prefill-row.ts +105 -0
- package/src/model/gemma4-base.ts +183 -82
- package/src/model/gemma4.ts +116 -28
- package/src/model/generated/gemma4-12b.ts +4 -1
- package/src/model/generated/gemma4-26b.ts +4 -1
- package/src/model/generated/gemma4-e4b.ts +4 -1
- package/src/model/glm52-cache.ts +80 -4
- package/src/model/glm52-mla.ts +12 -6
- package/src/model/glm52.ts +19 -40
- package/src/model/kv-attention-view.ts +70 -0
- package/src/model/minicpm5.ts +11 -7
- package/src/model/profile.ts +29 -4
- package/src/model/quantized-append-attention.ts +31 -0
- package/src/model/qwen3-delta-state.ts +74 -0
- package/src/model/qwen3-delta.ts +59 -36
- package/src/model/qwen3-mrope.ts +7 -9
- package/src/model/qwen3_5.ts +177 -75
- package/src/model/rotating-kv-donor.ts +35 -0
- package/src/model/rotating-kv-layout.ts +262 -0
- package/src/model/rotating-row-transaction.ts +35 -0
- package/src/model/speculative-rotating-kv.ts +101 -0
- package/src/model/ssm-prefill-padding.ts +39 -0
- package/src/model/support.ts +8 -0
- package/src/model/token-groups.ts +66 -0
- package/src/model/trellis-linear.ts +4 -4
- package/src/model/turboquant-codec.ts +89 -0
- package/src/model/universal/dense.ts +14 -13
- package/src/model/whisper.ts +435 -0
- package/src/native/mic_capture.swift +92 -0
- package/src/native-pack.ts +6 -5
- package/src/pi-web.ts +9 -1
- package/src/preflight.ts +31 -15
- package/src/prompt-cache.ts +306 -86
- package/src/runtime-config.ts +1 -5
- package/src/safetensors.ts +9 -2
- package/src/sampler.ts +37 -25
- package/src/serve/audio-routes.ts +323 -0
- package/src/serve/chat-request.ts +2 -0
- package/src/serve/chat-stage.ts +19 -14
- package/src/serve/completion-executor.ts +9 -1
- package/src/serve/discovery-routes.ts +17 -1
- package/src/serve/generated-token-history.ts +57 -0
- package/src/serve/generation-gateway.ts +88 -38
- package/src/serve/model-binding.ts +7 -1
- package/src/serve/model-host.ts +3 -4
- package/src/serve/preparation.ts +3 -3
- package/src/serve/prompt-response-trace.ts +14 -0
- package/src/serve/request-plan.ts +6 -3
- package/src/serve/request-prep.ts +40 -25
- package/src/serve/text-completion-stage.ts +2 -2
- package/src/serve/transcription-server.ts +64 -0
- package/src/serve/transcription-service.ts +502 -0
- package/src/server.ts +185 -261
- package/src/spec/assistant-rows.ts +93 -0
- package/src/spec/assistant-source.ts +28 -18
- package/src/spec/cache-identity.ts +22 -0
- package/src/spec/deepspec-rows.ts +29 -0
- package/src/spec/deepspec-source.ts +12 -10
- package/src/spec/dflash-source.ts +28 -4
- package/src/spec/drafter.ts +153 -142
- package/src/spec/dspark/deepspec-module.ts +95 -120
- package/src/spec/dspark/module-dflash.ts +126 -170
- package/src/spec/glm52-mtp-graph.ts +29 -0
- package/src/spec/glm52-mtp-rows.ts +132 -0
- package/src/spec/glm52-mtp-source.ts +54 -136
- package/src/spec/glm52-mtp-state.ts +33 -0
- package/src/spec/ngram-source.ts +115 -21
- package/src/spec/projected-draft-rows.ts +73 -0
- package/src/spec/qwen-mtp-module.ts +171 -0
- package/src/spec/qwen-mtp-rows.ts +230 -0
- package/src/spec/qwen-mtp-source.ts +84 -226
- package/src/spec/qwen-mtp-state.ts +30 -0
- package/src/spec/serve-loop.ts +73 -69
- package/src/spec/source.ts +93 -15
- package/src/spec/two-model-rows.ts +62 -0
- package/src/spec/two-model.ts +45 -103
- package/src/ssd-cache.ts +107 -36
- package/src/ssd-durability.ts +29 -27
- package/src/storage/host-buffer.ts +24 -0
- package/src/storage/kv-writer.ts +83 -0
- package/src/storage/kv-writer.worker.js +264 -0
- package/src/storage/retention-policy.ts +37 -0
- package/src/tiered-prompt-cache.ts +45 -0
- package/src/vision/embedder.ts +8 -0
- package/src/vision/prompt.ts +101 -65
- package/src/vision/qwen3vl-prompt.ts +101 -64
- package/src/vision/qwen3vl-tower.ts +11 -0
- package/src/vision/siglip.ts +8 -0
- package/src/web/app.html +12 -0
- package/src/web/app.js +217 -57
- package/src/web/src/chat.ts +4 -1
- package/src/web/src/composer.ts +3 -0
- package/src/web/src/voice.ts +141 -0
- package/src/web/sw.js +1 -1
- package/src/backends/mlx/memory-guard.ts +0 -40
- package/src/spec/prefix-state.ts +0 -45
- /package/docs/{planning → archive/planning}/release-notes-v0.3.0.md +0 -0
|
@@ -30,6 +30,10 @@ every other path is unique. Unmatched paths return
|
|
|
30
30
|
| POST | `/v1/messages` | [Anthropic Messages](#post-v1messages-anthropic-messages-api) |
|
|
31
31
|
| POST | `/v1/responses` | [OpenAI Responses](#post-v1responses-openai-responses-api) |
|
|
32
32
|
| POST | `/v1/embeddings` | [Embeddings](#post-v1embeddings-openai-embeddings-api) |
|
|
33
|
+
| POST | `/v1/audio/transcriptions`, `/v1/audio/translations` | [Audio transcription](#post-v1audiotranscriptions--post-v1audiotranslations-speech-to-text) |
|
|
34
|
+
| POST | `/v1/audio/sessions`, `/v1/audio/sessions/<id>/audio`, `/v1/audio/sessions/<id>/finish` | [Streaming sessions](#streaming-transcription-sessions-v1audiosessions) |
|
|
35
|
+
| DELETE | `/v1/audio/sessions/<id>` | [Streaming sessions](#streaming-transcription-sessions-v1audiosessions) |
|
|
36
|
+
| POST | `/admin/transcription/unload` | [Audio transcription](#post-v1audiotranscriptions--post-v1audiotranslations-speech-to-text) |
|
|
33
37
|
| GET | `/v1` | [API index](#get-v1) |
|
|
34
38
|
| GET | `/v1/models`, `/v1/models/<id>` | [Models](#get-v1models) |
|
|
35
39
|
| GET / POST | `/v1/adapters` | [Adapters](#adapters-lora-hot-swap) |
|
|
@@ -40,6 +44,7 @@ every other path is unique. Unmatched paths return
|
|
|
40
44
|
| GET | `/fit` | [Fit](#get-fit) |
|
|
41
45
|
| GET | `/library` | [Library](#get-library) |
|
|
42
46
|
| GET | `/downloads` | [Downloads](#get-downloads) |
|
|
47
|
+
| POST | `/admin/cache/session/close` | [Close cache session](#post-admincachesessionclose) |
|
|
43
48
|
| POST | `/admin/cache/flush` | [SSD cache flush](#post-admincacheflush) |
|
|
44
49
|
| GET | `/api/jobs`, `/api/jobs/<id>`, `/api/jobs/<id>/stream` | [Jobs](#jobs-apijobs) |
|
|
45
50
|
| POST | `/api/finetune/submit`, `/inspect-dataset`, `/merge`, `/export` | [Fine-tune](#fine-tune-apifinetune) |
|
|
@@ -117,8 +122,8 @@ Request body (OpenAI chat schema; unknown fields ignored):
|
|
|
117
122
|
"stream": false,
|
|
118
123
|
"max_tokens": 1024, // or max_completion_tokens (wins); unset =
|
|
119
124
|
// no cap — generate until EOS or the
|
|
120
|
-
//
|
|
121
|
-
//
|
|
125
|
+
// an explicit operator limit is reached;
|
|
126
|
+
// --max-tokens sets a server default
|
|
122
127
|
"temperature": 0.7, // 0 = greedy
|
|
123
128
|
"top_p": 0, "top_k": 0, // 0 = off
|
|
124
129
|
"seed": 1234, // omit for time-derived
|
|
@@ -369,15 +374,34 @@ Speculative decoding is a **server-level mode** (`serve --draft-model`,
|
|
|
369
374
|
or the model-free `serve --draft-kind ngram`, which mounts no draft
|
|
370
375
|
model); there is no per-request draft field. The `speculation` usage
|
|
371
376
|
extension appears on chat and text completions alike, non-streaming and on
|
|
372
|
-
the final stream chunk. Spec-eligible requests are text-only on base weights
|
|
373
|
-
|
|
374
|
-
uniform KV4
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
377
|
+
the final stream chunk. Spec-eligible requests are text-only on base weights.
|
|
378
|
+
In the shared executor, Qwen grouped MTP, prompt lookup and standalone drafting support logprobs and
|
|
379
|
+
bf16, uniform KV4/KV8 or per-layer affine KV, or TurboQuant. Qwen MTP and prompt lookup also support
|
|
380
|
+
positive library conversion thresholds for uniform KV4/KV8. Per-layer mixed KV and
|
|
381
|
+
adapters remain incompatible with speculation. Strict legacy serial execution
|
|
382
|
+
retains its narrower bf16/KV4 policy. The KV4 compatibility control is
|
|
383
|
+
`MLX_BUN_QWEN_SPEC_KV4=0`.
|
|
384
|
+
Ineligible requests decode normally and omit the field. Qwen MTP reuses paired
|
|
385
|
+
target/draft prefill and completed decode state through the shared RAM/SSD
|
|
386
|
+
cache by default in v0.4.0, reported in `cached_tokens`. Configured SSD persistence
|
|
387
|
+
supports reuse after restart; `MLX_BUN_MTP_PROMPT_CACHE=0` disables reuse.
|
|
388
|
+
Generated checkpoints cover processed tokens and use queued SSD persistence.
|
|
389
|
+
Long-conversation performance acceptance remains open. Qwen prompt lookup also
|
|
390
|
+
retains prefill and completed-output state in the common RAM/SSD cache, with
|
|
391
|
+
its committed token history stored as a companion attachment. Standalone drafting
|
|
392
|
+
on Qwen targets stores the draft model’s attention/recurrent state and pending
|
|
393
|
+
last draft token as companion state, including restoration after restart. Qualified Qwen
|
|
394
|
+
MTP, prompt lookup and standalone drafting use shared execution at one or several active rows and
|
|
395
|
+
report `usage.lane: "batched"`. Supported full-attention and rotating targets, including
|
|
396
|
+
Llama, MiniCPM and Gemma, use shared bf16, uniform KV4/KV8 or per-layer affine KV or TurboQuant lookup and standalone drafting, with
|
|
397
|
+
logprobs and generated RAM/SSD checkpoints. Delayed and per-layer affine rotating speculation uses the same row lifecycle; positive conversion thresholds are a library setting, while served schemes default to zero. Gemma assistant drafting also shares execution with bf16, uniform KV4/KV8 or per-layer affine KV or TurboQuant target storage and stores its
|
|
398
|
+
last true target hidden alongside generated RAM/SSD prefixes. Donor attention
|
|
399
|
+
handles row validity independently of the draft graph. DeepSpec and DSpark also use these shared target layouts and the common RAM/SSD cache
|
|
400
|
+
for projected context, with independent confidence-based proposal lengths.
|
|
401
|
+
Other provider/target combinations still
|
|
402
|
+
use the serial executor.
|
|
403
|
+
Prompt-lookup concurrent performance acceptance remains open. Ineligible requests can use continuous ordinary execution
|
|
404
|
+
when their features support it, even while a draft is mounted.
|
|
381
405
|
|
|
382
406
|
`usage.fill` reports **token fast-forwarding** (`MLX_BUN_FILL=strict|echo`).
|
|
383
407
|
Strict mode appends structural spans from the request's tools and chat
|
|
@@ -387,19 +411,45 @@ using its qualified append chunk size, then resumes sampling
|
|
|
387
411
|
(design: [speculative-decoding.md](../design/speculative-decoding.md) §"Token
|
|
388
412
|
fast-forwarding"). Injected tokens are billed in `completion_tokens` like any
|
|
389
413
|
other generated token; `decodeSteps` counts ordinary sampling steps, so
|
|
390
|
-
`injected / (injected + decodeSteps)` is the share
|
|
414
|
+
`injected / (injected + decodeSteps)` is the share emitted through fill.
|
|
415
|
+
Strict spans bypass sampling; echo spans still undergo sampled verification.
|
|
391
416
|
Injected positions still run through the model layers. The field is absent when the feature is off or the
|
|
392
|
-
request's shape
|
|
393
|
-
|
|
417
|
+
request's shape excludes it, including `logprobs`, structured output or media.
|
|
418
|
+
Strict shared fill supports bf16, affine KV and
|
|
419
|
+
TurboQuant layouts and retains absolute sampling positions for explicit seeds.
|
|
420
|
+
Its `usage.fill` statistics use the same fields as explicit serial execution.
|
|
421
|
+
Known tokens still advance model state. All-known steps skip intermediate
|
|
422
|
+
vocabulary heads and use the model-qualified append geometry, including the
|
|
423
|
+
specialized Qwen B1 append. Wider Qwen rows retain one-position arithmetic. Compiled
|
|
424
|
+
replay, paging and interruption checkpoints remain outside this shared method.
|
|
425
|
+
Explicit serial fill still excludes user-fixed seeds.
|
|
394
426
|
|
|
395
427
|
Under `MLX_BUN_FILL=echo` a second, weaker source joins: spans copied from
|
|
396
428
|
earlier in the same session, carried under policy `verify`. Those ride the same
|
|
397
|
-
single forward, are checked
|
|
429
|
+
single forward, are checked by the request's sampler on its logits, and have
|
|
398
430
|
their rejected tail rewound — so `verifyAccepted + verifyRejected` is what the
|
|
399
431
|
echo index proposed and `injected` is what survived. `wastedSamples` counts
|
|
400
432
|
only ASSERT fills (a verify fill consumes the in-flight sample as its first
|
|
401
433
|
position's check instead of discarding it).
|
|
402
434
|
|
|
435
|
+
Qwen MTP can alternate learned draft rounds with verified echo rounds. The
|
|
436
|
+
provider consumes accepted copied tokens and target context before drafting
|
|
437
|
+
again. Its asserted template spans remain ordinary sampled output. Learned
|
|
438
|
+
draft counts, acceptance histograms and `speculation.rounds` exclude echo;
|
|
439
|
+
`targetCalls`, `tokensPerForward` and `forwardsSaved` cover both round types.
|
|
440
|
+
Echo activity remains in `usage.fill`, including matching prefixes established
|
|
441
|
+
by ordinary MTP samples, so it is not additive with learned-draft counts.
|
|
442
|
+
A request fully served by echoes can
|
|
443
|
+
have zero learned drafts. Other mounted providers do not yet support this
|
|
444
|
+
combination.
|
|
445
|
+
|
|
446
|
+
An explicit `seed` controls a request-local random stream and can use
|
|
447
|
+
continuous execution. Repeating a request reproduces sampling at the same
|
|
448
|
+
model arithmetic and batch composition. Different batch shapes can change
|
|
449
|
+
floating-point results even with identical random draws; `--batch 1` retains
|
|
450
|
+
the existing fixed-shape control. This differs from mlx-lm's policy of routing
|
|
451
|
+
explicit seeds to its single-request generator.
|
|
452
|
+
|
|
403
453
|
### logprobs / top_logprobs
|
|
404
454
|
|
|
405
455
|
With `"logprobs": true` and/or `"top_logprobs": k` (1–11), the
|
|
@@ -432,8 +482,8 @@ penalties), **before** the sampler's temperature/top-p/top-k/min-p/XTC.
|
|
|
432
482
|
mlx-lm's top-k order is unspecified (argpartition); ours is sorted
|
|
433
483
|
descending — the same set, so the entry is deterministically the argmax.
|
|
434
484
|
Stream chunks never carry logprobs (mlx-lm's streaming responses don't
|
|
435
|
-
either). Requests with logprobs
|
|
436
|
-
|
|
485
|
+
either). Requests with logprobs can use ordinary continuous execution under
|
|
486
|
+
`--batch N`; other feature exclusions still apply. Invalid values are rejected
|
|
437
487
|
with mlx-lm's exact messages (see Errors).
|
|
438
488
|
|
|
439
489
|
### Streaming
|
|
@@ -544,11 +594,10 @@ Design and fidelity notes: [docs/reference/server-api.md](./server-api.md).
|
|
|
544
594
|
|
|
545
595
|
### Errors
|
|
546
596
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
generation checkpoints remain available for an identical retry.
|
|
597
|
+
Memory estimates do not reject or shorten requests by default. Actual runtime
|
|
598
|
+
failures use the request/stream error path when recoverable; native Metal
|
|
599
|
+
allocation failure can terminate the process. Saved generation checkpoints
|
|
600
|
+
remain available for an identical retry.
|
|
552
601
|
|
|
553
602
|
All errors are `{ "error": { "message": …, ... } }`.
|
|
554
603
|
|
|
@@ -567,10 +616,12 @@ All errors are `{ "error": { "message": …, ... } }`.
|
|
|
567
616
|
'low', 'medium', 'high', 'xhigh'`).
|
|
568
617
|
- `400` with `"type": "memory_admission"`, `"code":
|
|
569
618
|
"context_over_budget"` — the PROMPT itself leaves no generation slot
|
|
570
|
-
within
|
|
619
|
+
within an explicitly configured context/budget limit (`src/serve/request-plan.ts`). A prompt that
|
|
571
620
|
fits is never rejected for a broad `max_tokens`: the upper bound is
|
|
572
621
|
capped to the remaining room (`max_tokens` is a ceiling, not a promise)
|
|
573
|
-
and generation proceeds.
|
|
622
|
+
and generation proceeds. This is opt-in via an operator budget/context cap
|
|
623
|
+
(or the fixed GLM-5.2 layout), never enforced from default fit estimates.
|
|
624
|
+
`/stats.admission.enforced_context_tokens` is null when no cap applies.
|
|
574
625
|
- `429` with `"type": "resource_admission"`, `"code": "queue_full"` when
|
|
575
626
|
a preparation or generation queue has 64 waiting requests. If SSE headers
|
|
576
627
|
are already sent, the stream emits its terminal error instead.
|
|
@@ -590,7 +641,7 @@ and adapter selection as chat.
|
|
|
590
641
|
"prompt": "Once upon a time", // REQUIRED, non-empty string only (token
|
|
591
642
|
// arrays rejected, matching mlx_lm.server)
|
|
592
643
|
"max_tokens": 512, // unset = no cap: generate until EOS or the
|
|
593
|
-
//
|
|
644
|
+
// an explicit operator limit (DEVIATION: mlx_lm.server
|
|
594
645
|
// defaults 512 — pass --max-tokens 512 to
|
|
595
646
|
// reproduce); max_completion_tokens wins
|
|
596
647
|
"stream": false,
|
|
@@ -737,6 +788,97 @@ curl localhost:8080/v1/embeddings -H 'content-type: application/json' \
|
|
|
737
788
|
-d '{"input": ["the cat sat on the mat", "a kitten by the window"]}'
|
|
738
789
|
```
|
|
739
790
|
|
|
791
|
+
## POST /v1/audio/transcriptions · POST /v1/audio/translations (speech-to-text)
|
|
792
|
+
|
|
793
|
+
OpenAI-compatible Whisper transcription (`translations` = the same route with
|
|
794
|
+
task `translate`, English output). Served by every `mlx-bun serve` beside the
|
|
795
|
+
chat model — the Whisper checkpoint (`--whisper-model`, default: the first
|
|
796
|
+
downloaded `whisper` model) loads for each take and is released right after
|
|
797
|
+
it unless `--whisper-idle-unload`/`--whisper-resident` say otherwise — and by the transcription-only server that
|
|
798
|
+
`serve <whisper checkpoint>` starts ([server-config.md](server-config.md)).
|
|
799
|
+
Model support and the parity contract: [models.md](models.md#supported-models);
|
|
800
|
+
CLI equivalent: [`mlx-bun transcribe`](cli.md#transcribe--speech-to-text).
|
|
801
|
+
|
|
802
|
+
**Request** — `multipart/form-data` (OpenAI's shape) or JSON:
|
|
803
|
+
|
|
804
|
+
| Field | Meaning |
|
|
805
|
+
| --- | --- |
|
|
806
|
+
| `file` | audio upload (multipart) or base64 / `data:` URL string (JSON; `input_audio.data` and `audio` are accepted aliases). WAV decodes through the exact PCM parser; any other CoreAudio-readable container (mp3/m4a/aac/flac/ogg/aiff/caf, mp4/mov audio tracks…) decodes in-process through AudioToolbox (`src/audio/audiotoolbox.ts`, 3.7 ms for an 11 s mp3). Undecodable bytes are a `400`. |
|
|
807
|
+
| `model` | ignored — the configured Whisper checkpoint is used |
|
|
808
|
+
| `language` | ISO code or name; omitted / `auto` detects from the first 30 s |
|
|
809
|
+
| `prompt` | initial prompt (vocabulary, style); the last 223 tokens are kept |
|
|
810
|
+
| `response_format` | `json` (default) · `verbose_json` · `text` · `srt` · `vtt` |
|
|
811
|
+
| `temperature` | one temperature in [0, 1]; omitted = mlx-whisper's `(0, 0.2, …, 1.0)` fallback ladder |
|
|
812
|
+
| `stream` | `true` → server-sent events (below) |
|
|
813
|
+
| `timestamp_granularities[]` | `segment` (default); `word` adds `words` (`word, start, end, probability`) to `verbose_json` segments via cross-attention DTW on the checkpoint's alignment heads — one extra decoder pass per window |
|
|
814
|
+
| `beam_size` | **non-standard**: beam search width (default greedy); requires temperature 0 |
|
|
815
|
+
| `vocabulary` | **non-standard**: array (JSON) or comma-separated (multipart) recognition hints, fitted whole-term into the 223-token prompt budget after `prompt`; usage is reported |
|
|
816
|
+
| `condition_on_previous_text` | **non-standard**, default `true` |
|
|
817
|
+
| `no_speech_threshold`, `without_timestamps` | **non-standard** mlx-whisper decode options |
|
|
818
|
+
| `vad`, `vad_threshold`, `vad_min_speech_ms`, `vad_trim` | **non-standard**: Silero VAD gate (v6.2, `ggml-org/whisper-vad` weights in the HF cache). No detected speech → `{"text": ""}` with `mlx_bun.vad.speech = false` and Whisper never runs (≈25 ms for 11 s). `vad_trim` crops to the speech span ± 0.5 s first; timestamps stay clip-relative. Segments are reported in `mlx_bun.vad.segments` (seconds). Defaults: threshold 0.5, min speech 250 ms |
|
|
819
|
+
| `faithful` | **non-standard**: `true` runs the oracle-parity graph instead of the fast path (fused attention, compiled decoder step); same tokens up to documented near-ties, slower |
|
|
820
|
+
| `audio_ctx` | **non-standard, Lab**: encode only the first n of 1500 encoder positions (whisper.cpp `-ac`). Cheaper for short clips; measured hallucinated repeats below ~1024 on the fixture clips — off unless set |
|
|
821
|
+
|
|
822
|
+
**Response** — `json`: `{ "text": "…", "mlx_bun": { "model", "language",
|
|
823
|
+
"duration", "timings": { "load_ms", "transcribe_ms", "total_ms" },
|
|
824
|
+
"vocabulary"?: { "included", "omitted", "token_count", "token_budget" } } }`.
|
|
825
|
+
`verbose_json` adds `task`, `language`, `duration` and openai-whisper's
|
|
826
|
+
`segments` (`id, seek, start, end, text, tokens, temperature, avg_logprob,
|
|
827
|
+
compression_ratio, no_speech_prob`). `text`/`srt`/`vtt` return the bare body
|
|
828
|
+
(`text/plain`, `application/x-subrip`, `text/vtt`) with `x-mlx-bun-language`
|
|
829
|
+
and `x-mlx-bun-timings` headers. `load_ms` is non-zero exactly when the
|
|
830
|
+
request paged the weights in.
|
|
831
|
+
|
|
832
|
+
**Streaming** (`stream=true`): `text/event-stream` with `transcript.text.delta`
|
|
833
|
+
`{ "delta": "<segment text>", "segment": { "id", "start", "end" } }` per
|
|
834
|
+
finalized segment, `transcript.progress` `{ "done", "total" }` (mel frames),
|
|
835
|
+
and a final `transcript.text.done` carrying the non-streaming body.
|
|
836
|
+
|
|
837
|
+
**Errors**: `400 invalid_request_error` (fields, undecodable audio, clips
|
|
838
|
+
shorter than 0.1 s), `415` (content type), `499` on client cancel, and
|
|
839
|
+
`503 model_unavailable` when no Whisper checkpoint is on disk (the message
|
|
840
|
+
carries the `mlx-bun get` command).
|
|
841
|
+
|
|
842
|
+
`POST /admin/transcription/unload` pages the weights out now and returns
|
|
843
|
+
`{ "unloaded": bool, "resident", "loads", "unloads", "requests",
|
|
844
|
+
"last_load_ms", "idle_unload_sec" }` — the same block `/health` and
|
|
845
|
+
`/stats` report as `transcription` on the transcription-only server.
|
|
846
|
+
Transcriptions run one at a time and never overlap chat generation on the
|
|
847
|
+
GPU (the generation gateway's exclusive lock).
|
|
848
|
+
|
|
849
|
+
```sh
|
|
850
|
+
curl localhost:8080/v1/audio/transcriptions -F file=@meeting.m4a -F language=en -F response_format=srt
|
|
851
|
+
curl localhost:8080/v1/audio/transcriptions -H 'content-type: application/json' \
|
|
852
|
+
-d '{"file":"<base64 wav>","beam_size":5,"vocabulary":["Sotto","SwiftUI","Metal"]}'
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
## Streaming transcription sessions (`/v1/audio/sessions`)
|
|
856
|
+
|
|
857
|
+
For audio that is being recorded while it is sent (dictation). Every
|
|
858
|
+
completed 30 s window is transcribed as soon as it exists, under the same
|
|
859
|
+
exclusive lock as everything else, so `finish` costs one window regardless
|
|
860
|
+
of take length (measured: a 62 s take that needs 1165 ms in batch returns
|
|
861
|
+
327 ms after its last chunk, identical text — M1 Max). With `vad: true` the
|
|
862
|
+
Silero gate runs on the stream: nothing is transcribed until speech has
|
|
863
|
+
been seen, and a take with no speech finishes as `{"text": ""}` without
|
|
864
|
+
ever running Whisper. The one-shot parameters (`language`, `prompt`,
|
|
865
|
+
`vocabulary`, `beam_size`, `temperature`, `vad*`, `timestamp_granularities`,
|
|
866
|
+
`faithful`) apply; `response_format` is `json` or `verbose_json`.
|
|
867
|
+
|
|
868
|
+
| Route | Body / response |
|
|
869
|
+
| --- | --- |
|
|
870
|
+
| `POST /v1/audio/sessions` | JSON params → `{ "id", "model", "vocabulary" }` (the weights load here if they were paged out) |
|
|
871
|
+
| `POST /v1/audio/sessions/<id>/audio` | one chunk: `content-type: audio/pcm;rate=16000` raw little-endian float32 mono (sotto's format, no decode), or any CoreAudio container. → `{ "samples", "duration", "speech", "segments": [...] }` with every segment finalized so far |
|
|
872
|
+
| `POST /v1/audio/sessions/<id>/finish` | → the one-shot response body (`json`/`verbose_json` with `mlx_bun`), closes the session |
|
|
873
|
+
| `DELETE /v1/audio/sessions/<id>` | discard, `204` |
|
|
874
|
+
|
|
875
|
+
Chunk order is the caller's responsibility (send sequentially and await
|
|
876
|
+
each receipt). Unknown ids are `404`; a finished session is `409`; at most
|
|
877
|
+
64 sessions are open at once (`429`). Windows fed early are normalized
|
|
878
|
+
against the audio received so far (the log-mel clamp is relative to the
|
|
879
|
+
clip maximum), which can differ from a batch transcription in near-silent
|
|
880
|
+
mel bins — the fixture takes decode identically.
|
|
881
|
+
|
|
740
882
|
## GET /v1
|
|
741
883
|
|
|
742
884
|
API index for discovery tooling:
|
|
@@ -744,7 +886,8 @@ API index for discovery tooling:
|
|
|
744
886
|
```jsonc
|
|
745
887
|
{ "name": "mlx-bun", "version": "<package version>", "model": "<served model id>",
|
|
746
888
|
"endpoints": ["POST /v1/chat/completions", "POST /v1/completions", "POST /v1/messages",
|
|
747
|
-
"POST /v1/responses", "POST /v1/embeddings", "
|
|
889
|
+
"POST /v1/responses", "POST /v1/embeddings", "POST /v1/audio/transcriptions",
|
|
890
|
+
"POST /v1/audio/translations", "GET /v1/models",
|
|
748
891
|
"GET/POST/DELETE /v1/adapters", "GET /health", "GET /stats", "GET /fit",
|
|
749
892
|
"GET /library", "GET /downloads"] }
|
|
750
893
|
```
|
|
@@ -836,11 +979,11 @@ vision/audio, adapters, and training are not emulated by the serving port.
|
|
|
836
979
|
```jsonc
|
|
837
980
|
{
|
|
838
981
|
"server": { "owner": "serve" | "pi-session" | "embedded", "model": "...", "started_at": 0 },
|
|
839
|
-
"prompt_cache": { "entries": 0, "bytes": 0, "max_bytes": 0, "hits": 0, "misses": 0 },
|
|
982
|
+
"prompt_cache": { "entries": 0, "bytes": 0, "max_bytes": 0, "hits": 0, "misses": 0, "object_hits": 0, "object_misses": 0, "object_restores": 0 },
|
|
840
983
|
"response_store": { "entries": 0, "bytes": 0, "max_bytes": 33554432, "ttl_ms": 3600000 },
|
|
841
984
|
"kv_quant": { "mode": "bf16" | "uniform-kv8" | "turbo k8v3" | "mixed (kv_config.json)",
|
|
842
985
|
"layers": { "kv4": 8, "bf16": 40 }, // turbo: { "turbo-k8v3": 8, "bf16": 40 }
|
|
843
|
-
"attention": { "global": 10, "sliding_window": 38 } },
|
|
986
|
+
"attention": { "global": 10, "sliding_window": 38 }, "recurrent_layers": 0 },
|
|
844
987
|
// present only when --ssd-cache is on:
|
|
845
988
|
"ssd_cache": { "dir": "...", "entries": 0, "bytes": 0, "max_bytes": 0,
|
|
846
989
|
"restores": 0, "spills": 0, "restore_ms_last": 0, "demotions": 0,
|
|
@@ -849,8 +992,9 @@ vision/audio, adapters, and training are not emulated by the serving port.
|
|
|
849
992
|
"pending_spill_bytes": 0, "dropped_spills": 0,
|
|
850
993
|
"failed_spills": 0, "longest_durable_prefix_tokens": 0 },
|
|
851
994
|
"admission": {
|
|
852
|
-
"max_safe_context": 0, //
|
|
853
|
-
"
|
|
995
|
+
"max_safe_context": 0, // advisory fit estimate, tokens
|
|
996
|
+
"enforced_context_tokens": null, // explicit cap or fixed layout; null = none
|
|
997
|
+
"memory_budget_bytes": null, // explicit budget, or null
|
|
854
998
|
"usable_bytes": 0,
|
|
855
999
|
"weights_bytes": 0
|
|
856
1000
|
},
|
|
@@ -898,6 +1042,12 @@ appears only when `--kv-quant config` was passed explicitly and the
|
|
|
898
1042
|
checkpoint ships a per-layer `kv_config.json` — per-layer KV quantization
|
|
899
1043
|
is never selected automatically.
|
|
900
1044
|
|
|
1045
|
+
`kv_quant.layers` counts configured attention KV codecs; `attention` separates
|
|
1046
|
+
full and sliding attention. `recurrent_layers` counts linear/recurrent layers
|
|
1047
|
+
whose convolution and recurrent state retain their own precision. They do not
|
|
1048
|
+
count as bf16 or quantized attention KV. These are model/policy counts, not
|
|
1049
|
+
live allocation measurements or a per-request conversion-progress report.
|
|
1050
|
+
|
|
901
1051
|
`pending_snapshots` counts prompt-cache entries scheduled for persistence but
|
|
902
1052
|
not yet confirmed by the atomic SSD store. `longest_durable_prefix_tokens` is
|
|
903
1053
|
the longest prefix already present in the SSD index, not merely queued.
|
|
@@ -924,7 +1074,7 @@ generic all-resident SKU estimator is inapplicable
|
|
|
924
1074
|
```jsonc
|
|
925
1075
|
{
|
|
926
1076
|
"machine": { "chip": "M4 Pro", "ram_bytes": 0, "bandwidth_gbs": 0.0 },
|
|
927
|
-
"context_tokens": 8192, //
|
|
1077
|
+
"context_tokens": 8192, // estimated context capacity
|
|
928
1078
|
"typical_context_tokens": 8192, // min(8192, context_tokens)
|
|
929
1079
|
"typical_decode_tps": 0.0, // predicted at typical_context_tokens
|
|
930
1080
|
"measured_decode_tps": null, // real number from eval DB, or null
|
|
@@ -959,7 +1109,11 @@ generic all-resident SKU estimator is inapplicable
|
|
|
959
1109
|
## Adapters (LoRA hot-swap)
|
|
960
1110
|
|
|
961
1111
|
Routes in `src/serve/model-admin-routes.ts`; the manager is
|
|
962
|
-
`AdapterManager` in `src/lora.ts`.
|
|
1112
|
+
`AdapterManager` in `src/lora.ts`. Requests selecting the same ordered adapter
|
|
1113
|
+
set can share a continuous group. Different sets wait for the current group
|
|
1114
|
+
to finish and then use the same executor. Prefix-cache identity is resolved
|
|
1115
|
+
under the execution lease, so replacing an adapter while a request is queued
|
|
1116
|
+
cannot reuse state from its previous weights.
|
|
963
1117
|
|
|
964
1118
|
- `GET /v1/adapters` — `{ adapters: [{ id, path, rank, scale, size_bytes,
|
|
965
1119
|
mounted_layers, ram_bytes }] }` — currently-mounted adapters only.
|
|
@@ -998,6 +1152,50 @@ makes it the default for requests that send no `adapter` field; an
|
|
|
998
1152
|
explicit `adapter` (including `"none"`) always wins, and hot-swap via
|
|
999
1153
|
these endpoints is unchanged.
|
|
1000
1154
|
|
|
1155
|
+
## Cache session affinity
|
|
1156
|
+
|
|
1157
|
+
Completion requests may supply `session_id` or `prompt_cache_key` in the JSON
|
|
1158
|
+
body, or `x-session-affinity` / `session_id` in a header. Precedence is body
|
|
1159
|
+
`session_id`, body `prompt_cache_key`, `x-session-affinity`, then `session_id`.
|
|
1160
|
+
This applies to chat completions, text completions, Responses and Messages.
|
|
1161
|
+
The shared mlx-bun Pi provider sends session-affinity headers automatically.
|
|
1162
|
+
|
|
1163
|
+
A session indexes its latest published checkpoint within each numerical cache
|
|
1164
|
+
namespace. Compatible continuations use that checkpoint directly; edited,
|
|
1165
|
+
branched or missing histories use ordinary prefix matching. The full intended
|
|
1166
|
+
input is still required: a session label does not append omitted messages,
|
|
1167
|
+
isolate access, change model settings or bypass token compatibility.
|
|
1168
|
+
|
|
1169
|
+
The cache prefers session-referenced checkpoints when choosing RAM residents,
|
|
1170
|
+
but can evict them when required. SSD persistence remains independent. Session
|
|
1171
|
+
indexes are process-local; after restart, the first request uses ordinary SSD
|
|
1172
|
+
lookup and re-establishes the association. Entries remain shareable across
|
|
1173
|
+
sessions and requests without session metadata. `MLX_BUN_SESSION_CACHE` provides
|
|
1174
|
+
the A/B control described in server-config.md.
|
|
1175
|
+
|
|
1176
|
+
`GET /stats` adds `prompt_cache.session_hits`, `session_misses` and
|
|
1177
|
+
`prefix_scans`. A session hit counts direct RAM checkpoint selection, including
|
|
1178
|
+
state prepared from SSD before execution. A session miss counts execution
|
|
1179
|
+
lookups with session metadata that need ordinary candidate search. Prefix scans
|
|
1180
|
+
count ordinary execution searches, not tokenization or preparation probes.
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
Qwen image/video encoder reuse reports `prompt_cache.object_hits`,
|
|
1184
|
+
`object_misses` and `object_restores`. A hit borrows an exact RAM tensor object;
|
|
1185
|
+
a miss may restore from SSD or recompute. Object entries and bytes are included
|
|
1186
|
+
in the existing cache totals and share its RAM budget and persistence queue.
|
|
1187
|
+
These counters do not count language-model KV prefix hits. Media prompt KV
|
|
1188
|
+
reuse remains disabled because token IDs alone do not identify media contents.
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
## POST /admin/cache/session/close
|
|
1192
|
+
|
|
1193
|
+
Send `{ "session_id": "agent-42" }` to remove that session's cache association
|
|
1194
|
+
and retention preference. Returns `{ "closed": true }` when an ID is supplied,
|
|
1195
|
+
or `{ "closed": false }` otherwise. Closing does not delete checkpoints from
|
|
1196
|
+
RAM or SSD. Applications can close idle or completed sessions; a later request
|
|
1197
|
+
with the same ID establishes a new association.
|
|
1198
|
+
|
|
1001
1199
|
## POST /admin/cache/flush
|
|
1002
1200
|
|
|
1003
1201
|
Forces every pending SSD prompt-cache snapshot through the serial write queue
|
|
@@ -1621,7 +1819,11 @@ repetition_context_size?, presence_penalty?, frequency_penalty?, seed?}`
|
|
|
1621
1819
|
`delete_session {path}`, `regenerate`, `edit_resend {text}`,
|
|
1622
1820
|
`switch_sibling {entryId}`, `context {context}` (app-aware assistant, below).
|
|
1623
1821
|
|
|
1624
|
-
Server → client: `ready {model, vision, audio, thinking, genDefaults}
|
|
1822
|
+
Server → client: `ready {model, vision, audio, thinking, genDefaults, transcription}`
|
|
1823
|
+
(`transcription` is true when a Whisper checkpoint serves [`/v1/audio/sessions`](#streaming-transcription-sessions-v1audiosessions);
|
|
1824
|
+
the web composer then shows a hold-to-talk mic that streams 16 kHz float32
|
|
1825
|
+
into a session and inserts the transcript into the message box — voice
|
|
1826
|
+
input for any chat model),
|
|
1625
1827
|
`turn_start`, `text_delta {delta}`, `thinking_delta {delta}`, `tool_start
|
|
1626
1828
|
{callId, tool, args}`, `tool_approval_request {callId, tool, args}`,
|
|
1627
1829
|
`tool_update {callId, chunk}`, `tool_end {callId, ok, result}`, `turn_end
|
|
@@ -1719,3 +1921,25 @@ Under `--isolate`, the parent owns Responses continuation history and exposes it
|
|
|
1719
1921
|
bounded store counters in `GET /engine` as `response_store`. Model-worker restart
|
|
1720
1922
|
or eviction preserves that history; restarting the parent clears it. Direct
|
|
1721
1923
|
serving retains the same process-local TTL and byte limits.
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
Supported grouped TurboQuant drafting retains `usage.speculation`, seeded
|
|
1927
|
+
sampling, logprobs, structured output and generated RAM/SSD continuations.
|
|
1928
|
+
The cache codec does not select the scheduler or sampler implementation.
|
|
1929
|
+
Composition coverage: [batching design](../design/batching.md).
|
|
1930
|
+
|
|
1931
|
+
With paged KV enabled, supported Gemma4 bf16 text requests report the batched lane when submitted through continuous scheduling. Seeded sampling, logprobs and grammar use the same interfaces. These requests bypass prompt-cache lookup/publication; paging does not provide SSD persistence.
|
|
1932
|
+
|
|
1933
|
+
Ordinary adapter checkpoints are isolated by ordered adapter IDs and mounted
|
|
1934
|
+
content revisions. Base-model requests cannot restore or remove an adapter checkpoint.
|
|
1935
|
+
With generation checkpointing enabled, repeating an identical eligible request
|
|
1936
|
+
replays its saved completion prefix and resumes from the saved pending token.
|
|
1937
|
+
Cancellation retains queued snapshots; successful completion removes them.
|
|
1938
|
+
`POST /admin/cache/flush` drains both prompt spills and generation checkpoints,
|
|
1939
|
+
and its elapsed time and durability statistics include both queues. A hard
|
|
1940
|
+
process exit can lose snapshots that have not drained.
|
|
1941
|
+
|
|
1942
|
+
For resumed generation checkpoints, `usage.prompt_tokens_details.cached_tokens`
|
|
1943
|
+
counts only the reused original prompt, bounded by `usage.prompt_tokens`. Saved
|
|
1944
|
+
completion tokens are replayed as completion output and do not increase that
|
|
1945
|
+
prompt-cache usage counter.
|