apiVersion: serving.kserve.io/v1beta1 kind: InferenceService metadata: annotations: serving.kserve.io/deploymentMode: RawDeployment labels: app.kubernetes.io/name: llm-reasoning app.kubernetes.io/part-of: llm-serving name: reasoning namespace: llm-serving spec: predictor: containers: - args: # Swapped off DeepSeek-R1-Distill-Qwen-32B: tool_choice="auto" (what pi # sends) hit a documented vLLM architecture conflict for R1-family # models -- the model narrated fake tool-call completions in its # block instead of emitting real tool_calls, regardless of # parser combo tried (deepseek_v3 400s outright, hermes parsed but the # model itself never called out to the real tool-call path). Root # cause is upstream in the R1 distillation, not this config -- Qwen3's # own tool-call format is natively supported instead. # # First attempt was a Kimi-K2.6-distilled Qwen3.6-35B-A3B checkpoint # (lordx64/Qwen3.6-35B-A3B-Kimi-K2.6-Reasoning-Distilled) -- crashed on # boot: `model type qwen3_5_moe` unrecognized by transformers/vLLM # 0.11.0, not a config problem, a genuinely unsupported/obscure # architecture. # # Second attempt was cpatonn's pre-quantized "AWQ-4bit" build of the # official Qwen3-30B-A3B-Thinking-2507 -- also crashed, on two # different real issues in sequence: (1) it's actually compressed-tensors # format despite the repo name, and (2) once that was fixed, vLLM raised # `RuntimeError: Quantization scheme is not supported for the current # GPU. Min capability: 80. Current capability: 70.` -- worker-1's GPU is # compute capability 7.0 (V100), and AWQ/GPTQ/compressed-tensors marlin # INT4 kernels all require sm80+ (Ampere or newer). This node cannot run # any of those quant formats, full stop -- only bitsandbytes or full # precision work here (which is exactly why the old DeepSeek config used # bitsandbytes to begin with). # # Using the official full-precision Qwen/Qwen3-30B-A3B-Thinking-2507 # checkpoint with on-the-fly bitsandbytes quantization instead -- # architecture itself already confirmed good (Qwen3MoeForCausalLM # resolved cleanly in both prior attempts). - --model=Qwen/Qwen3-30B-A3B-Thinking-2507 - --served-model-name=reasoning - --quantization=bitsandbytes - --dtype=bfloat16 - --kv-cache-dtype=auto - --tensor-parallel-size=1 - --max-model-len=16384 - --gpu-memory-utilization=0.90 # 30B total MoE at pre-quantized AWQ-4bit is ~15-16GB weights on a # 32GB card at 0.90 util (~29.5GB budget) -- meaningfully more KV-cache # headroom than the old DeepSeek-32B config had, so restoring the # original max-num-seqs=4 rather than starting conservative again. - --max-num-seqs=4 - --enable-chunked-prefill - --enable-prefix-caching # qwen3 is vLLM's dedicated reasoning parser for this family's # blocks -- official pairing per Qwen3-Thinking's own deployment docs. - --reasoning-parser=qwen3 # hermes is the documented tool-call parser for general Qwen3 models # (qwen3_coder/qwen3_xml are Coder-variant-only). - --enable-auto-tool-choice - --tool-call-parser=hermes # vLLM 0.11.0's native OffloadingConnector -- spills KV cache blocks # to CPU DRAM instead of discarding them on preemption (max-num-seqs=4 # + max-model-len=16384 means concurrent long sequences compete for # the same GPU KV space). No extra dependency, built into vLLM core. # num_cpu_blocks=2000 hung the pod at startup on the old model (one # pinned host tensor per layer, sized per CPU block -- oversized enough # to stall pinning that much host memory). 32 is a small, known-safe # starting point independent of this model's own layer count -- # confirm it comes up healthy, then watch real host memory usage and # raise it deliberately rather than guessing a round number again. # block_size=128 tokens matches vLLM's own example. # Note: 0.11.0 ships the original (fragmented, small-transfer-block) # version of this connector -- 0.12.0 consolidates KV data into one # contiguous block per request and is reported an order of magnitude # faster for this specific feature, so this is a real but not yet # optimal implementation until the image gets bumped. - --kv-transfer-config={"kv_connector":"OffloadingConnector","kv_role":"kv_both","kv_connector_extra_config":{"num_cpu_blocks":32,"block_size":128}} - --host=0.0.0.0 - --port=8080 env: - name: VLLM_USE_FLASHINFER_SAMPLER value: '0' - name: VLLM_ATTENTION_BACKEND value: TRITON_ATTN - name: HF_HOME value: /mnt/models image: vllm/vllm-openai:v0.11.0@sha256:014a95f21c9edf6abe0aea6b07353f96baa4ec291c427bb1176dc7c93a85845c name: kserve-container ports: - containerPort: 8080 protocol: TCP readinessProbe: httpGet: path: /health port: 8080 periodSeconds: 10 resources: limits: cpu: '16' # Bumped from 36Gi -- on-the-fly bnb quantization stages full # bf16 shards (~60GB total model) transiently in host RAM during # load before discarding them, unlike loading an already-quantized # checkpoint. Request left unchanged (node is already at 84% memory # request allocation) -- only the OOM ceiling moves. memory: 48Gi nvidia.com/gpu: '1' requests: cpu: '8' memory: 12Gi nvidia.com/gpu: '1' startupProbe: failureThreshold: 80 httpGet: path: /health port: 8080 periodSeconds: 15 volumeMounts: - mountPath: /mnt/models name: models - mountPath: /dev/shm name: shm deploymentStrategy: type: Recreate maxReplicas: 2 minReplicas: 2 nodeSelector: kubernetes.io/hostname: worker-1 runtimeClassName: nvidia volumes: - name: models persistentVolumeClaim: claimName: llm-models - emptyDir: medium: Memory sizeLimit: 2Gi name: shm