veloxquant_mlx.cli.serve module¶
veloxquant serve — OpenAI-compatible server with a VeloxQuant KV cache.
Thin launcher, per option (a) of issue #27: load the model, wire our cache in
via patch_model_kv_cache, hand off to mlx_lm.server. The serving
contract itself is inherited — 35 of 40 caches subclass mlx_lm’s KVCache
and get update_and_fetch / trim / nbytes for free.
What is not thin is the honesty layer, and it is the reason this file is longer than the ten lines #27 predicted:
An unsupported method must fail before the model loads, not with an
AttributeErroron the first HTTP request.Byte counters are accounting-only. The cache stores dequantized fp16, so
nbytesover-reports and no memory is actually saved. A server is exactly where users assume otherwise, so the warning is unconditional.There is no fp16 fallback, silent or otherwise. A method that cannot serve stops the process.
Model-identity keying for prefix-cache reuse (issue #310): mlx_lm.server
keys its prompt-prefix cache on model_key = (model_path, adapter_path,
draft_model_path) – a plain path tuple, not a content hash. Swapping
weights at the same path between requests could serve a stale prefix cache.
This is pre-existing mlx_lm behavior, not something this file introduces
or fixes – doing so would mean forking ModelProvider/APIHandler
internals, against this file’s own “thin launcher” design above. Track
upstream if this needs fixing.
- veloxquant_mlx.cli.serve.READY_PREFIX = 'VELOXQUANT_READY '¶
stdout handshake for the control panel. Printed once the model is loaded and the cache is wired, immediately before mlx_lm.server binds the port. Lets the panel distinguish “starting” from “running” without racing a health poll against a slow model load.
- veloxquant_mlx.cli.serve.build_parser()[source]¶
Build the argparse parser for
veloxquant serve(model, method, bits, host/port, –set).- Return type:
- veloxquant_mlx.cli.serve.parse_overrides(pairs, method=None)[source]¶
Turn
FIELD=VALUEstrings into typedKVCacheConfigkwargs.Types come from the dataclass via the registry, so a value that the config would reject is refused here — with the field name in the message — instead of surfacing as an opaque failure deep in cache construction.
When
methodis given, a field with no effect for that method (e.g.--set kivi_group_size=64while--method h2o) prints a warning instead of silently doing nothing (issue #345) — a warning, not a hard error, since_CONFIG_FIELDSdoes not yet curate every method and a false positive there must not block a legitimate override.
- veloxquant_mlx.cli.serve.validate_method(method)[source]¶
Reject unservable methods before the expensive model load.
Deliberately fatal. #27’s crash tier includes
turboquant_prod— the library’s default — so silently substituting a working method here would mean users think they benchmarked one thing and served another.
- veloxquant_mlx.cli.serve.build_config(args)[source]¶
Build a KVCacheConfig from –method/–bits/–seed plus parsed –set overrides.
- veloxquant_mlx.cli.serve.attach_cache(model, config)[source]¶
Point
model.make_cacheat a freshly built VeloxQuant cache list.Not
patch_model_kv_cache: that helper builds one list eagerly and pins it, which suitsgenerate()but would make every concurrent request share one cache. Building per call gives each request its own.Must run on the thread that will generate — see
_Provider._load.Returns
(n_layers, is_batchable).is_batchableis derived from this same probe list rather than by callingmake_prompt_cache(model)again after the patch below is applied: that second call would invoke the now-self-referentialmodel.make_cache, which recurses intoKVCacheBuilder.for_modeluntil Python’s recursion limit is hit (VeloxQuant-MLX#506; confirmed 333 full re-executions, masked by a broadexcept Exceptionfurther down the call stack, costing 30-70s per real server cold start). Reusing the probe list is strictly cheaper than the old second call, not just recursion-free.
- veloxquant_mlx.cli.serve.emit_ready(args, n_caches)[source]¶
Print the machine-readable handshake the control panel waits for.
- veloxquant_mlx.cli.serve.run_server(args)[source]¶
Hand off to mlx_lm’s server, wiring our cache in as it loads the model.
The model is loaded by
mlx_lmitself, inside its generation thread, rather than up front on the main thread. MLX arrays are bound to the thread that created them, so a main-thread model produces “There is no Stream(gpu, N) in current thread.” the first time the generation thread evaluates cache state. Upstream sidesteps this by callingload_default()from within_generate; hooking_loadputs our patch on that same thread.
- veloxquant_mlx.cli.serve.main(argv=None)[source]¶
Parse CLI args and launch the OpenAI-compatible server with a VeloxQuant KV cache.
Validates –method fails fast before the model loads, prints the unconditional accounting-only warning, and hands off to mlx_lm.server (via run_server) with the cache wired in through attach_cache.