feat(crd): extend InferenceService vLLM config for agentic-coding flags#306
Conversation
Adds typed CRD fields to VLLMConfig so agentic-coding workloads can reach
vLLM's throughput-critical flags without dropping to extraArgs:
- kvCacheDtype (auto | fp8_e5m2 | fp8_e4m3, default auto) -- unlocks
128K+ context on consumer VRAM
- enableChunkedPrefill -- opt-in, keeps long prefill from starving decode
- maxNumBatchedTokens (min 512, no default) -- main throughput knob
- speculative.{enabled,model,numSpeculativeTokens} -- draft-model
speculative decoding as a sub-struct
- enableExpertParallel -- MoE-specific shard toggle
Also:
- Widens VLLMConfig.Quantization enum to accept fp8, nvfp4, and
compressed-tensors so recent Qwen/Llama/NVFP4/Unsloth releases are
referenceable.
- Extends AttentionBackend enum with uppercase vLLM-native forms
(FLASH_ATTN, FLASHINFER, XFORMERS) while keeping lowercase values
working for backwards compatibility.
- Emits flags deterministically in BuildArgs; opt-in booleans (prefix
caching, chunked prefill, expert parallel) are only emitted when
explicitly set to true so vLLM upstream defaults stay authoritative.
- Surfaces structurally invalid speculative config (enabled without a
draft model) as a VLLMSpecValid=False status condition on the
InferenceService rather than failing reconcile; the flags are simply
skipped.
Tests:
- internal/controller/runtime_vllm_test.go: 20-row table covering
every new flag's emit / do-not-emit paths, plus a determinism test
and table-driven coverage of ValidateVLLMConfig.
Sample manifest:
- config/samples/inferenceservice_qwen36_35b_fp8_agentic.yaml targeting
2x RTX 5060 Ti with Qwen3.6-35B-A3B-FP8, TP=2, FP8 KV cache, prefix
caching + chunked prefill on, FLASHINFER, max-num-batched-tokens=8192.
Speculative decoding is commented out with a pointer to the CRD spec.
No breaking change: every new field is optional and defaults to vLLM's
current behavior if unset. CLI integration is deferred to a follow-up PR.
Closes #305
Signed-off-by: Christopher Maher <chris@mahercode.io>
Live smoke test on Shadowstack — validation completeBuilt the PR as `registry.defilan.net/llmkube-controller:0.7.1-dev-vllm-flags` and rolled it onto a real 2× RTX 5060 Ti microk8s cluster. End-to-end validation summary: ✅ What this PR proves
🪲 Smoke test found two things worth fixing (not in this PR)1. Sample YAML had wrong `kvCacheDtype`. Pushed
Fixed in the sample to `fp8_e4m3` with an inline comment explaining: `fp8_e5m2` is for BF16/FP16 weights where you're compressing only the KV cache; `fp8_e4m3` is required when weights themselves are FP8. This is a good argument for typed CRD fields with docs — the sample's comments now teach this constraint. 2. Hardware budget on 2× 5060 Ti is tighter than the original sample assumed. Qwen3.6-35B-A3B-FP8 weights are ~36 GB; TP=2 → ~18 GB/shard > 15.48 GB usable on a 5060 Ti. Retargeted the sample to `Qwen3-Coder-30B-A3B-Instruct-FP8` (~30.5 GB, ~15.25 GB/shard). Even that hit OOM during vLLM's profile run due to activation + cudagraph overhead — needs `--gpu-memory-utilization 0.95` and `--cpu-offload-gb` support, which this PR intentionally doesn't add. Filed as follow-up #307: add `gpuMemoryUtilization` and `cpuOffloadGB` to VLLMConfig. Merge readinessAll 15 CI checks pass, 1 skipping. Core PR functionality fully validated on real hardware. The sample YAML was updated from `inferenceservice_qwen36_35b_fp8_agentic.yaml` → `inferenceservice_qwen3_coder_30b_fp8_agentic.yaml` with corrected `kvCacheDtype` and VRAM-budget math in the comment block. Recommending: merge this PR as-is. #307 will follow up for the two vLLM memory-management flags needed to actually boot 30B+ models on this specific hardware. |
…type
Two smoke-test findings from running the original sample on real 2x RTX
5060 Ti hardware:
1. Qwen3.6-35B-A3B-FP8 weights are ~36 GB; TP=2 per-shard ~18 GB >
15.48 GiB usable on a 5060 Ti. The model doesn't fit on this hardware.
Retargeting the sample to Qwen3-Coder-30B-A3B-Instruct-FP8 (~30.5 GB
total, ~15.25 GB/shard) which is the actual production agentic-coding
model for this size class. Inline VRAM-budget comment teaches future
users how to check the fit.
2. vLLM 0.19.1 rejects the combination `--kv-cache-dtype fp8_e5m2` +
`--quantization fp8`:
ValueError: fp8_e5m2 kv-cache is not supported with fp8 checkpoints.
Correct value for FP8-weight checkpoints is fp8_e4m3 (matches the FP8
encoding of the weights). fp8_e5m2 is only correct when weights are
bf16/fp16 and you're compressing only the KV cache. Sample comment
now teaches this distinction.
Context: weight allocation still OOMs during vLLM's profile run at
maxModelLen=65536 due to activation + cudagraph overhead. Filed #307 to
add --gpu-memory-utilization and --cpu-offload-gb to VLLMConfig so this
hardware can actually run 30B+ FP8 models. That's a follow-up, not in
scope for #305.
Signed-off-by: Christopher Maher <chris@mahercode.io>
Why
Running Qwen3.6-35B-A3B-FP8 on a dual RTX 5060 Ti for agentic-coding workloads exposed the current VLLMConfig as too narrow. Users had to drop to `extraArgs` escape hatches to get prefix caching, chunked prefill, FP8 KV cache, FLASHINFER attention, or speculative decoding — defeating the point of a typed CRD.
This PR makes those first-class. Closes #305.
What's added
New `VLLMConfig` fields (all optional, all nil-default)
Model `Quantization` enum
Added `fp8`, `nvfp4`, `compressed-tensors`. Existing values unchanged.
New status condition
`VLLMSpecValid`. Flips to `False / SpeculativeMissingModel` when `speculative.enabled=true` but `speculative.model` is empty. Does not block reconcile — the Deployment still renders with the speculative flags omitted.
Sample manifest
`config/samples/inferenceservice_qwen36_35b_fp8_agentic.yaml` — full agentic-coding config targeting 2× RTX 5060 Ti with Qwen3.6-35B-A3B-FP8, TP=2, FP8 KV cache, all the performance flags above.
Rules for `BuildArgs`
Tests
`TestVLLMBuildArgs`: 20 table-driven rows covering every new flag × emit/no-emit combinations. `TestVLLMBuildArgsDeterministic` asserts stable ordering. `TestValidateVLLMConfig` covers the speculative-misconfig path.
```
$ make manifests generate fmt vet lint test
(all clean; internal/controller at 83.1% coverage)
$ make docker-build
(ghcr.io/defilantech/llmkube-controller built, distroless multi-arch)
```
Live smoke test on Shadowstack
Built as `registry.defilan.net/llmkube-controller:0.7.1-dev-vllm-flags`, rolled out on a real 2× RTX 5060 Ti microk8s cluster. Verified:
```
--model Qwen/Qwen3.6-35B-A3B-FP8 --host 0.0.0.0 --port 8000
--tensor-parallel-size 2 --max-model-len 131072
--quantization fp8 --dtype bfloat16
--kv-cache-dtype fp8_e5m2
--enable-prefix-caching --enable-chunked-prefill
--max-num-batched-tokens 8192
--attention-backend FLASHINFER
```
Back-compat
Out of scope (follow-ups)