Environment Variables¶
Spyre Inference reads the following SPYRE_* environment variables to configure
the plugin. Each is evaluated the first time it is read, so set it before
launching vLLM. The list below is generated from spyre_inference/envs.py; the
comment above each entry documents its effect and default.
environment_variables: dict[str, Callable[[], Any]] = {
# Comma-separated Spyre device indices to run on, mapping each tensor-parallel
# local rank to a physical card. Unset lets the runtime pick the default device(s).
"SPYRE_DEVICES": lambda: os.getenv("SPYRE_DEVICES"),
# Granularity of the decoder's torch.compile graph:
# - "block": compile one transformer block at a time (default)
# - "model": compile the whole model as a single graph
"SPYRE_COMPILE_GRANULARITY": lambda: os.getenv("SPYRE_COMPILE_GRANULARITY") or "block",
# What to do when a model block, attention kernel or the lm_head compiles *after*
# warmup, which costs a full Inductor compile mid-request:
# - "off": nothing (default)
# - "warn": log each distinct violation
# - "error": raise. Use in CI to keep a warmup-coverage regression from landing,
# bearing in mind it catches most but not all: torch runs its compile-start
# callbacks only when a process-wide pending counter goes 0 -> 1, so a compile
# starting while another is in flight goes unreported.
# torch-spyre compiles every eager aten op, so those compiles continue for the
# whole run; they are never reported.
"SPYRE_COMPILE_GUARD": lambda: os.getenv("SPYRE_COMPILE_GUARD") or "off",
# When "1", wrap attention forward/softmax in torch.profiler.record_function
# spans for kineto trace capture. Off by default: profiled runs are not
# wall-clock comparable.
"SPYRE_ATTN_PROFILING": lambda: bool(int(os.getenv("SPYRE_ATTN_PROFILING", "0"))),
# When "1" (default), pre-compile every attention variant the run can need during
# warmup, so no request pays an Inductor compile mid-serving. "0" falls back to
# compiling each variant lazily on first use.
"SPYRE_ATTN_RECORD": lambda: bool(int(os.getenv("SPYRE_ATTN_RECORD", "1"))),
# Comma-separated kv_len buckets to record, unset uses the default buckets of
# powers of two from block_size up to max_model_len.
"SPYRE_ATTN_KV_BUCKETS": lambda: os.getenv("SPYRE_ATTN_KV_BUCKETS"),
# Comma-separated query_len buckets to record, unset uses the default buckets
# [1] + multiples of min(512, max_num_batched_tokens) up to max_num_batched_tokens.
"SPYRE_ATTN_QUERY_BUCKETS": lambda: os.getenv("SPYRE_ATTN_QUERY_BUCKETS"),
# Comma-separated num_seqs buckets for the batched decode kernel, unset uses the
# default buckets of powers of two from 4 up to max_num_seqs.
"SPYRE_ATTN_NUM_SEQS_BUCKETS": lambda: os.getenv("SPYRE_ATTN_NUM_SEQS_BUCKETS"),
# Which KV cache layout the decoder attention backend uses, within a page:
# - "token_major": [num_blocks, block_size, num_kv_heads, head_size] (default)
# - "head_major": [num_blocks, num_kv_heads, block_size, head_size], which drops
# the per-page permute the kernels do before the matmuls
"SPYRE_ATTN_KV_LAYOUT": lambda: os.getenv("SPYRE_ATTN_KV_LAYOUT") or "token_major",
# Core cap for the attention compile only, leaving the rest of the model on all 32.
# "0" (default) lets the LX path pick its own cap and leaves the others uncapped.
"SPYRE_ATTN_MAX_CORES": lambda: int(os.getenv("SPYRE_ATTN_MAX_CORES", "0")),
# When "1" (default), enables the batched multi-sequence decode kernel for
# batches of at least _MIN_BATCHED_SEQS sequences; smaller batches take the
# per-seq loop either way. "0" forces the loop for all batch sizes.
"SPYRE_BATCHED_DECODE": lambda: bool(int(os.getenv("SPYRE_BATCHED_DECODE", "1"))),
# When "1", reuse compiled Spyre kernels across processes by caching them on
# disk. Off by default. TORCHINDUCTOR_FORCE_DISABLE_CACHES=1 disables the cache
# even when this flag is enabled.
"SPYRE_KERNEL_CACHE": lambda: os.getenv("SPYRE_KERNEL_CACHE", "0") == "1",
# Maximum number of sequences allowed to prefill in the same batch. "1" (default)
# serialises prefills, so a batch spends the whole token budget on one prompt
# instead of topping itself up with a short chunk of the next. Any non-positive
# value removes the cap, as does a pooling runner, which never decodes.
"SPYRE_MAX_NUM_PARTIAL_PREFILLS": lambda: int(os.getenv("SPYRE_MAX_NUM_PARTIAL_PREFILLS", "1")),
# CPU budget used to size thread pools. "0" (default) auto-detects the budget
# (cgroup CPU quota, then physical core count).
"SPYRE_NUM_CPUS": lambda: int(os.getenv("SPYRE_NUM_CPUS", "0")),
# When "1" (default), clamp the CPU threading env vars (OMP_NUM_THREADS and
# friends) to the detected budget to avoid thread oversubscription in
# CPU-limited containers. Set to "0" to leave them untouched and only warn.
"SPYRE_UPDATE_THREAD_CONFIG": lambda: bool(int(os.getenv("SPYRE_UPDATE_THREAD_CONFIG", "1"))),
}