Repository navigation
refactor: run mlxcel generate on the engine and retire CxxGenerator #2176
Description
Part of #2166. Phase 6. Depends on #2173 (Phase 5: run and chat on the server engine), and through it on the engine API from #2172.
Problem / Background
After Phase 5, only mlxcel generate (raw completion, --profile, benchmarks) and the speculative CLI features still decode through CxxGenerator. While those four loops exist, every decode fix can still need a CLI-only twin, and bench_decode keeps timing a path the server does not run. llama.cpp kept a thin raw loop, llama-completion, for exactly this use after its CLI moved onto the server (ggml-org/llama.cpp#17824). In mlxcel that raw path should run on the engine at B=1.
Current Behavior
CxxGeneratorhas four decode loops insrc/lib/mlxcel-core/src/generate.rs::1914,:2233,:2416and:2683. It also hasevaluate_loglikelihoodsat:2991, reached throughMlxInferenceSession(src/lib/mlxcel-core/src/session.rs:338) andsrc/backend/session.rs:437.src/bin/bench_decode.rs:381and:412constructCxxGeneratordirectly, andsrc/bin/speculative_bench.rsuses it too.- The CLI-only speculative features are prompt-lookup decoding (
PromptLookupGenerator,src/lib/mlxcel-core/src/speculative/prompt_lookup.rs:669, wired byPromptLookupOptionsinsrc/main.rs:376) and the classic draft-modelSpeculativeGenerator(src/lib/mlxcel-core/src/speculative/mod.rs:225). The server never uses the latter:SpeculativeDispatch::Classicfalls back to plain decode (src/server/batch/speculative_burst.rs:924). mlxcel generate --draft-kind mtpdoes not useCxxGenerator:run_offline_mtp(src/commands/generate.rs:2233, routed at:1791) drivesMtpGeneratorover the same per-target adapters the server burst uses. Since fix(gemma4): make MTP verify byte-identical to decode on CUDA #2185 the Gemma 4 adapter takes an optional history-boundary prefill split (with_prefill_boundary,src/models/gemma4_mtp_target.rs:392). The server sets it and the offline CLI path leaves it unset, which matches classic CLI prefill (never split there).src/backend/mod.rs:31-43andsession.rs:47-60describe the CLI as wrappingCxxGenerator.
Proposed Solution
mlxcel generateruns on theEnginefrom refactor: batch-native engine step API with BatchScheduler on top #2172 at B=1:open, thenprefillthrough thePrefillPlan, thenstepuntilStepOutcome.finishisSome, thenclose.--profileand TTFT phase reporting (TtftPhases,generate.rs:1608) read timings from the engine. VLM embeddings come from the server'sprepare_request_vlm_embeddings, as Phase 5 set up.MlxInferenceSessionwrapsEngineinstead ofCxxGenerator.evaluate_loglikelihoodsbecomes an engine prefill-only scoring call.- Prompt-lookup decoding is ported to an engine-level drafter that implements the same
Drafterinterface the server's MTP and DFlash bursts use, so the server can offer it too. Rejected alternative: keeping it CLI-only, which would recreate a CLI-only loop. - Deprecate the classic draft-model
SpeculativeGenerator. Using it prints a deprecation notice that points to--draft-kind mtpordflash. Remove it in the next minor release, and record the removal version inCHANGELOG.md. If the maintainer prefers a port instead, the issue comment that decides this replaces this item. bench_decodeandspeculative_benchmeasure the engine path.- Remove the four
CxxGeneratorloops, then the type, or keep a shim with a removal date in its doc comment. Remove dead helpers and the stale comments that describe the old split (includingsrc/server/model_worker.rs:1115, andsrc/backend/mod.rsandsession.rsabove).
Scope
In scope: src/commands/generate.rs, generate_vlm.rs and generate_dflash.rs; src/bin/bench_decode.rs and speculative_bench.rs; the session wrappers; the prompt-lookup drafter; the SpeculativeGenerator deprecation; removing CxxGenerator; and docs.
Out of scope: the remote --server-base client, and new speculative methods.
Implementation Notes
- Reuse: the engine API, the
Draftertrait the server burst uses (src/lib/mlxcel-core/src/drafter/mod.rs:840), and the existingbench_decodeCSV schema, so historical results stay comparable. Add one column that names the path. - Constraints:
generateoutput underMLXCEL_SDPA_DETERMINISTIC=1must equalrun -pand the server for the same request. Single-stream decode must stay within the Phase 0 threshold of the pre-epic baseline. - Edge cases: families with model-owned state that today reset through
CxxGenerator(src/models/gemma4.rs:6210,src/vision/gemma4_vl.rs:652) must reset through the engine'sopenandclose. Gemma 3 builds its CLI fallback slot without the rotating undo log (make_fallback_caches,src/models/gemma3.rs:1569, used byreset_caches) and its scheduler sequences with it when every sliding layer is FP16 (make_configured_caches,:1555, perf(scheduler): restore Gemma 3 decode lookahead via ring rewind #2182). Oncegenerateopens engine sequences it gets the second form. Row copies happen only inside the lookahead'sDecodeLookaheadAppendScope, so a B=1 run without lookahead pays only the cursor bookkeeping. Confirm that with thebench_decoderun below.
Acceptance Criteria
-
rg -n 'CxxGenerator' srcreturns nothing, or only a documented shim with a removal date -
mlxcel generate,--profile,bench_decode,speculative_benchand--prompt-lookupwork on the engine, each checked on a real checkpoint - Using the classic draft-model path prints the deprecation notice, and
CHANGELOG.mdrecords it - Under
MLXCEL_SDPA_DETERMINISTIC=1, the Phase 0 harness showsgenerate,run -pand the server giving identical greedy output on Qwen3-1.7B and Llama-3.2-1B -
bench_decodeon the engine is within the Phase 0 threshold of the pre-epicCxxGeneratorbaseline at short and long context. The results are committed underdocs/benchmark_results/ - No stale comment describes a CLI/server decode split.
rg -n 'CxxGenerator|SpeculativeGenerator' src/serverreturns nothing
Verification
make verify # macOS gate
make verify-fmt verify-test-cuda # Linux/CUDA gate
MLXCEL_SDPA_DETERMINISTIC=1 make <Phase 0 parity target> MODEL=<Qwen3-1.7B-4bit path>
./scripts/bench_decode.sh <Llama-3.2-1B-4bit path>References verified against main at fc21784 (2026-10-07).
Activity
feat: add ADR 0007, CLI/server parity harness and B=1 decode bench (#…
docs: record the parity harness render's enable_thinking divergence
refactor: run mlxcel run and chat through the in-process server engine (
refactor: wrap the inference session around the engine client
refactor: time the engine's raw-completion client in the decode bench…
refactor: run prompt lookup as an engine drafter and deprecate the cl…
refactor: remove CxxGenerator and the CLI/server decode split
refactor: pipeline the raw-completion client's B=1 decode
refactor: pipeline the plain rounds of the engine's drafter loop
fix: inject KV modes before the engine client opens a sequence
fix: address the remaining review findings on the engine generate path
fix(perf): pipeline generate for model-owned families without a rewind
fix: harden the engine client's verify and scoring paths
docs: align ADR 0009, README and CHANGELOG with the engine generate path
refactor: run mlxcel generate on the engine and retire CxxGenerator (l…