Skip to content

Commit cd0c76e

Browse files
committed
Enable prompt-end checkpoints by default for Muse-Glimmer serving
1 parent 57cdb87 commit cd0c76e

7 files changed

Lines changed: 68 additions & 49 deletions

File tree

‎examples/llm_server/python/README.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ limit; choose a value supported by the export. Capacity defaults are
6868
bounded by `min(max_sessions, max_inflight_requests)`. A positive value overrides
6969
this allowance. Alternatively, `--prefix-checkpoint-prompt-end` retains initial,
7070
continuation, and replacement prompt boundaries separately for each named session.
71+
This mode is opt-in.
7172
`--prefix-cache-entries` defaults to eight snapshots per session; duplicates
7273
are skipped and LRU eviction stays within the owning session. Close/reset clears
7374
that session's snapshots. Anonymous requests neither use nor retain them.
@@ -99,8 +100,7 @@ event loop. Post-spawn startup failures also await worker cleanup. CLI validatio
99100
and chat-template preflight still happen before any worker is started.
100101

101102
The batching guide covers the native build, supported export contract, and
102-
integration checks. The existing generic launcher below and Muse-specific
103-
launcher are unchanged.
103+
integration checks. The legacy launcher below is unchanged.
104104

105105
## Run a legacy worker
106106

@@ -257,8 +257,8 @@ worker JSONL protocol is covered separately by `tests/test_worker_client.py`,
257257
and `tests/test_stream_disconnect.py` uses real loopback Uvicorn/TCP plus a
258258
model-free subprocess to verify disconnect cancellation end to end.
259259
The BPE splice tests use an in-memory tokenizer with no model downloads. Optional
260-
integration tests use local tokenizer directories set with `QWEN_HF_DIR`,
261-
`GEMMA_HF_DIR`, or `MUSE_GLIMMER_HF_DIR` and require `transformers`.
260+
integration tests require `transformers` and local tokenizer directories; see the
261+
environment variables in `tests/test_warm_resume_scaffold.py`.
262262

263263
## Architecture
264264

@@ -313,8 +313,8 @@ the generic control plane never imports an example. Backend specifics
313313

314314
The dedicated batching launcher supports concurrent text requests on one shared
315315
runner, ordered persistent-session continuation, targeted in-band cancellation,
316-
and optional prefix reuse for new sessions and clean prompt replacements. It
317-
does not yet migrate Muse image or DFlash execution. The serialized-worker
316+
and optional prefix reuse for new sessions and clean prompt replacements. Image
317+
and speculative execution require a model-specific worker. The serialized-worker
318318
limitations below describe the legacy path, not the new batching worker.
319319

320320
Deliberately narrow (reliability-first): Hermes/Qwen tool calling only;

‎examples/llm_server/spec/README.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ keepalive comments every 15 seconds. Model launchers may bound initial readiness
139139
waiting to return headers during prefill; errors detected after headers arrive
140140
as SSE error events.
141141

142-
The generic and Muse batching launchers accept `--api-dialect openai|openrouter`.
142+
The batching launcher accepts `--api-dialect openai|openrouter`.
143143
The `openrouter` dialect attaches requested usage to the finish chunk, once, for
144144
clients that stop consuming on `finish_reason`, and supplies overflow metadata
145145
for context recovery. The default `openai` dialect retains the separate
@@ -385,8 +385,8 @@ loads only the program before ModuleExecutor construction, reads activation dtyp
385385
and context length, resolves model/tokenizer EOS, binds the backend's default
386386
batched cache via `cache::kind::kBatched`, derives scheduler width from
387387
`preferred_batch_tokens()`, and constructs
388-
one `ServingRuntime`. It does not preload `forward`, run token-step loops, or use
389-
Glimmer's legacy session implementation. Prefix caching is disabled by default.
388+
one `ServingRuntime`. It does not preload `forward` or run token-step loops.
389+
Prefix caching is disabled by default.
390390
`--prefix_cache_entries=N` enables retained-prefix reuse for both greedy and
391391
sampled requests. With `--prefix_capture_slots=C`, it adds `N + C` physical rows
392392
for retained snapshots and transient captures; logical `--max_sessions` remains
@@ -426,7 +426,7 @@ contains multiple session slices. Overlapping HTTP requests or sequential
426426
single-session forwards are not proof. Real-model smoke testing is explicitly
427427
deferred: no compatible local PTE/tokenizer has been confirmed, and this stack
428428
must not download/export weights or claim that a real combined forward was
429-
verified. A Glimmer `embed_text`/`forward_from_embeddings` last-logits artifact is
429+
verified. An `embed_text`/`forward_from_embeddings` last-logits artifact is
430430
not assumed compatible with this packed construction path.
431431

432432
Idle-session recovery retries at most once and only for native

‎examples/llm_server/spec/batching.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ of N and at the initial prompt end. It requires `--prefix-cache-entries > 0`.
3636
Zero (the default) disables periodic capture. With prefix caching enabled and no
3737
other capture options, only initial prompts are retained.
3838
`--prefix-checkpoint-prompt-end` instead retains prompt ends within each named
39-
session, including continuations and replacements. `--prefix-cache-entries`
39+
session, including continuations and replacements. This mode is opt-in.
40+
`--prefix-cache-entries`
4041
defaults to eight per session. Checkpoints cannot be reused or evicted by another
4142
session, and close/reset clears them. Anonymous requests do not use this cache.
4243
This mode cannot be combined with a nonzero interval or custom checkpoint policy.

‎examples/models/muse-glimmer/BATCHING_SERVING.md‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,34 +219,41 @@ submitted history; server-side image references are not supported.
219219

220220
The base64/framing check does not guarantee that an arbitrarily long text history
221221
fits. Whole-frame, decoded-pixel, vision-patch, and model context limits still
222-
apply. Text/image prefix snapshots are opt-in via `--prefix-cache-entries`
223-
(default 0).
222+
apply. Text/image prompt-end checkpoints are enabled by default, with
223+
`--prefix-cache-entries` defaulting to eight snapshots per named session.
224224

225225
## Session Behavior
226226

227-
Without prompt-end mode, nonzero `--prefix-cache-entries` enables shared prefix
228-
caching; without other capture options it retains initial prompts. `--prefix-checkpoint-prompt-end` instead
229-
retains prompt boundaries separately for each named session, including tool-result
227+
The Muse-Glimmer batching launcher and native workers enable
228+
`--prefix-checkpoint-prompt-end` by default. General LLM serving keeps it opt-in.
229+
This mode retains prompt boundaries separately for each named session, including tool-result
230230
continuations and replacements. `--prefix-cache-entries` defaults to eight
231231
checkpoints per session. Repeated or branched requests using the same session ID can
232232
reuse its snapshots; other sessions and anonymous requests cannot. New tool-result
233233
text still needs prefill. Exact prompt ends already retained in that session are
234234
not cloned again. Close/reset, including idle-session eviction, clears its snapshots.
235235

236+
Use `--prefix-checkpoint-prompt-end=false` to disable this mode in the Python launcher,
237+
or `--prefix_checkpoint_prompt_end=false` in a native worker. With it disabled,
238+
`--prefix-cache-entries` defaults to zero; a positive value enables shared prefix
239+
caching, which retains initial prompts unless periodic capture is configured.
240+
236241
Prompt-end-only capture preserves per-request capture concurrency and the default
237242
transient-row budget. LRU eviction stays within each session, so one session cannot
238243
evict another's checkpoints. Capture slots are released after clone collection,
239244
without waiting for decoding to finish. Capture is best effort: concurrent captures
240245
or session replacements can occupy the available rows. Measure capture overhead
241-
and reuse for your workload before enabling it.
246+
and reuse to choose the entry limit for your workload.
242247

243248
`--prefix-checkpoint-prompt-end` cannot be combined with a nonzero
244249
`--prefix-checkpoint-interval`; both the launcher and native worker reject that combination.
250+
To use periodic capture, disable prompt-end capture and set a positive
251+
`--prefix-cache-entries` limit.
245252
With prompt-end mode, `--prefix-cache-entries` is the per-session budget (default eight);
246253
without it, the same option is the shared-cache budget (default zero). This mode reserves
247254
`max_sessions * prefix_cache_entries` snapshot rows plus working sessions
248255
and capture slots: eight sessions, eight checkpoints each, and eight automatic
249-
capture slots require 80 physical rows. The feature remains disabled by default.
256+
capture slots require 80 physical rows.
250257
Periodic checkpoints use one serialized lane and may skip a boundary while an
251258
earlier clone is pending. Automatic transient-row sizing is capped at two in
252259
periodic mode; prompt-end capture retains the ordinary concurrency budget.

‎examples/models/muse-glimmer/runtime/runners/batching_bootstrap.cpp‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ DEFINE_int32(max_inflight_requests, 4, "Maximum admitted requests");
3535
DEFINE_int32(
3636
prefix_cache_entries,
3737
0,
38-
"Retained snapshots: shared by default (0), per session with prompt-end checkpoints (8)");
38+
"Retained snapshots per session (default: 8); shared with prompt-end checkpoints disabled (default: 0)");
3939
DEFINE_int32(
4040
prefix_checkpoint_interval,
4141
0,
@@ -46,8 +46,8 @@ DEFINE_int32(
4646
"Transient capture rows; 0: size automatically from session/request limits");
4747
DEFINE_bool(
4848
prefix_checkpoint_prompt_end,
49-
false,
50-
"Retain prompt-end checkpoints separately for each named session");
49+
true,
50+
"Retain prompt-end checkpoints separately for each named session (enabled by default)");
5151
DEFINE_uint64(max_image_bytes, 20 * 1024 * 1024, "Maximum encoded image bytes");
5252
DEFINE_uint64(
5353
max_input_frame_bytes,

‎examples/models/muse-glimmer/serving/serve_batching.py‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def _parse_args(argv=None):
7676
parser.add_argument(
7777
"--prefix-cache-entries",
7878
type=int,
79-
help="Retained snapshots: shared by default (0), per named session with prompt-end checkpoints (8)",
79+
help="Retained snapshots per named session (default: 8); shared with prompt-end checkpoints disabled (default: 0)",
8080
)
8181
parser.add_argument(
8282
"--prefix-capture-slots",
@@ -87,8 +87,11 @@ def _parse_args(argv=None):
8787
parser.add_argument("--max-vision-patches", type=int, default=4096)
8888
parser.add_argument(
8989
"--prefix-checkpoint-prompt-end",
90-
action="store_true",
91-
help="Retain prompt-end checkpoints separately for each named session",
90+
nargs="?",
91+
choices=("true", "false"),
92+
const="true",
93+
default="true",
94+
help="Retain prompt-end checkpoints separately for each named session (default: true; false disables)",
9295
)
9396
parser.add_argument(
9497
"--prefix-checkpoint-interval",
@@ -124,6 +127,7 @@ def _parse_args(argv=None):
124127
parser.add_argument("--host", default="127.0.0.1")
125128
parser.add_argument("--port", type=int, default=8000)
126129
args = parser.parse_args(argv)
130+
args.prefix_checkpoint_prompt_end = args.prefix_checkpoint_prompt_end == "true"
127131
if args.prefix_cache_entries is None:
128132
args.prefix_cache_entries = 8 if args.prefix_checkpoint_prompt_end else 0
129133
_validate_limits(
@@ -207,7 +211,7 @@ def _worker_command(args):
207211
("prefix_checkpoint_interval", args.prefix_checkpoint_interval or None),
208212
(
209213
"prefix_checkpoint_prompt_end",
210-
"true" if args.prefix_checkpoint_prompt_end else None,
214+
str(args.prefix_checkpoint_prompt_end).lower(),
211215
),
212216
("max_vision_patches", args.max_vision_patches),
213217
("max_image_bytes", args.max_image_bytes),

‎examples/models/muse-glimmer/tests/test_serve_batching.py‎

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,47 +53,51 @@ def test_prefix_capture_override_reaches_worker(arguments, captures):
5353
assert f"--prefix_capture_slots={captures}" in serve_batching._worker_command(args)
5454

5555

56-
def test_prompt_end_capture_is_opt_in_and_reaches_worker(arguments, capsys):
57-
default = serve_batching._parse_args(arguments + ["--prefix-cache-entries", "4"])
58-
assert not default.prefix_checkpoint_prompt_end
59-
assert not any(
60-
"prefix_checkpoint_prompt_end" in flag
61-
for flag in serve_batching._worker_command(default)
62-
)
63-
enabled = serve_batching._parse_args(arguments + ["--prefix-checkpoint-prompt-end"])
56+
@pytest.mark.parametrize(
57+
"options",
58+
[[], ["--prefix-checkpoint-prompt-end"], ["--prefix-checkpoint-prompt-end=true"]],
59+
)
60+
def test_prompt_end_capture_defaults_on_and_reaches_worker(arguments, options):
61+
enabled = serve_batching._parse_args(arguments + options)
62+
assert enabled.prefix_checkpoint_prompt_end
6463
assert enabled.prefix_cache_entries == 8
6564
assert "--prefix_checkpoint_prompt_end=true" in serve_batching._worker_command(
6665
enabled
6766
)
6867
assert "--prefix_cache_entries=8" in serve_batching._worker_command(enabled)
68+
69+
70+
@pytest.mark.parametrize("capacity", [None, 0, 3])
71+
def test_prompt_end_capture_opt_out_reaches_worker(arguments, capacity):
72+
options = ["--prefix-checkpoint-prompt-end=false"]
73+
if capacity is not None:
74+
options += ["--prefix-cache-entries", str(capacity)]
75+
disabled = serve_batching._parse_args(arguments + options)
76+
assert not disabled.prefix_checkpoint_prompt_end
77+
assert disabled.prefix_cache_entries == (capacity or 0)
78+
command = serve_batching._worker_command(disabled)
79+
assert "--prefix_checkpoint_prompt_end=false" in command
80+
assert f"--prefix_cache_entries={capacity or 0}" in command
81+
assert ("--prefix_capture_slots=0" in command) == bool(capacity)
82+
83+
84+
def test_prompt_end_capture_rejects_invalid_combinations(arguments, capsys):
6985
with pytest.raises(SystemExit):
70-
serve_batching._parse_args(
71-
arguments
72-
+ ["--prefix-checkpoint-prompt-end", "--prefix-cache-entries", "0"]
73-
)
86+
serve_batching._parse_args(arguments + ["--prefix-cache-entries", "0"])
87+
assert "requires --prefix-cache-entries > 0" in capsys.readouterr().err
7488
with pytest.raises(SystemExit):
75-
serve_batching._parse_args(
76-
arguments
77-
+ [
78-
"--prefix-checkpoint-prompt-end",
79-
"--prefix-checkpoint-interval",
80-
"64",
81-
]
82-
)
89+
serve_batching._parse_args(arguments + ["--prefix-checkpoint-interval", "64"])
8390
assert "cannot be combined" in capsys.readouterr().err
8491

8592

8693
def test_session_checkpoint_capacity_override_and_row_overflow(arguments):
87-
args = serve_batching._parse_args(
88-
arguments + ["--prefix-checkpoint-prompt-end", "--prefix-cache-entries", "2"]
89-
)
94+
args = serve_batching._parse_args(arguments + ["--prefix-cache-entries", "2"])
9095
assert "--prefix_cache_entries=2" in serve_batching._worker_command(args)
9196
for capacity in (0, -1, (1 << 31) - 1):
9297
with pytest.raises(SystemExit):
9398
serve_batching._parse_args(
9499
arguments
95100
+ [
96-
"--prefix-checkpoint-prompt-end",
97101
"--prefix-cache-entries",
98102
str(capacity),
99103
]
@@ -110,6 +114,7 @@ def test_worker_forwards_mg_options(arguments):
110114
"2048",
111115
"--prefix-cache-entries",
112116
"3",
117+
"--prefix-checkpoint-prompt-end=false",
113118
"--prefix-checkpoint-interval",
114119
"512",
115120
]
@@ -127,6 +132,7 @@ def test_worker_forwards_mg_options(arguments):
127132
assert flags["--bos_id"] == "200000"
128133
assert flags["--eos_id"] == "200001"
129134
assert flags["--prefix_checkpoint_interval"] == "512"
135+
assert flags["--prefix_checkpoint_prompt_end"] == "false"
130136

131137

132138
@pytest.mark.parametrize("options,enabled", [([], "false"), (["--dflash"], "true")])
@@ -160,6 +166,7 @@ def test_draft_count_forwarded(arguments, n_draft):
160166
("--max-vision-patches", str(1 << 31)),
161167
("--prefix-checkpoint-interval", "8"),
162168
("--prefix-checkpoint-interval", "-1"),
169+
("--prefix-checkpoint-prompt-end", "invalid"),
163170
("--prefix-capture-slots", "-1"),
164171
("--prefix-capture-slots", str(1 << 31)),
165172
],

0 commit comments

Comments
 (0)