Page MenuHomePhabricator

Host Qwen 3.6-27B as an inference service
Closed, ResolvedPublic

Description

Summary

Deploy Qwen 3.6-27B (official FP8 quantized weights, ~30 GB) on Lift Wing using vLLM AsyncLLMEngine with tensor parallelism across 2 GPUs, supporting configurable reasoning and non-reasoning modes per request.

Technical notes

Model: Qwen/Qwen3.6-27B-FP8 (Apache 2.0), 27B dense parameters, hybrid attention (Gated DeltaNet + Gated Attention), 262K native context. Official fine-grained FP8 quantization (block size 128). Text-only serving with vision encoder disabled. Same FP8 model used everywhere.

Serving: vLLM AsyncLLMEngine via the same KServe + Blubber pattern as gpt-oss-safeguard-20b. Initial base image is amd-vllm014 (vLLM 0.14); if FP8 support is absent, a new base image with vLLM >= 0.19.0 will be needed. Reasoning mode toggled via reasoning field in the request payload. GPU_MEMORY_UTILIZATION set to 0.85, MAX_MODEL_LEN to 32768
(conservative).

Deployment: Model got deployed on 1x 192GM VRAM GPU partition of MI300X. It is deployed in llm namespace under the following name: llm-qwen36-27b. It's using FP8 quantization.

Acceptance criteria

  • Model server loads Qwen/Qwen3.6-27B-FP8 and serves predictions via vLLM AsyncLLMEngine
  • Upload model to swift -> s3://wmf-ml-models/llm/Qwen3.6-27B-FP8
  • CI pipeline publishes the machinelearning-liftwing-inference-services-qwen36 image
  • Service deployed in experimental namespace on ml-serve-eqiad and verified with curl

Details

Related Changes in Gerrit:
SubjectAuthorRepoBranchLines +/-
Bartosz Wójtowiczoperations/deployment-chartsmaster+85 -85
Bartosz Wójtowiczoperations/deployment-chartsmaster+1 -1
Gkyziridisoperations/deployment-chartsmaster+3 -1
Gkyziridismachinelearning/liftwing/inference-servicesmain+5 -0
Gkyziridisoperations/deployment-chartsmaster+103 -1
Gkyziridisoperations/deployment-chartsmaster+0 -102
Gkyziridisoperations/deployment-chartsmaster+4 -4
Gkyziridisoperations/deployment-chartsmaster+5 -4
Gkyziridismachinelearning/liftwing/inference-servicesmain+10 -0
Gkyziridisoperations/deployment-chartsmaster+5 -1
Gkyziridismachinelearning/liftwing/inference-servicesmain+1 -1
Kevin Baziramachinelearning/liftwing/inference-servicesmain+0 -11
Gkyziridisoperations/deployment-chartsmaster+1 -1
Gkyziridismachinelearning/liftwing/inference-servicesmain+1 -0
Kevin Baziramachinelearning/liftwing/inference-servicesmain+11 -3
Gkyziridisoperations/deployment-chartsmaster+3 -3
Gkyziridismachinelearning/liftwing/inference-servicesmain+1 -1
Gkyziridismachinelearning/liftwing/inference-servicesmain+9 -5
Gkyziridisoperations/deployment-chartsmaster+98 -0
Gkyziridisoperations/deployment-chartsmaster+2 -2
Ilias Sarantopoulosmachinelearning/liftwing/inference-servicesmain+59 -13
Ilias Sarantopoulosoperations/docker-images/production-imagesmaster+204 -0
Clément Goubertoperations/puppetproduction+3 -0
Gkyziridisoperations/deployment-chartsmaster+30 -0
Clément Goubertoperations/puppetproduction+2 -53
Gkyziridisoperations/deployment-chartsmaster+98 -0
Ilias Sarantopoulosoperations/deployment-chartsmaster+98 -0
Ilias Sarantopoulosmachinelearning/liftwing/inference-servicesmain+1 K -0
Gkyziridisintegration/configmaster+15 -0
Ilias Sarantopoulosmachinelearning/liftwing/inference-servicesmain+696 -0
Ilias Sarantopoulosmachinelearning/liftwing/inference-servicesmain+695 -0
Show related patches Customize query in gerrit

Related Objects

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

Change #1303357 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6-27B-FP8 model in experimental ns.

https://gerrit.wikimedia.org/r/1303357

Change #1303390 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6-27B-FP8 model in experimental ns.

https://gerrit.wikimedia.org/r/1303390

Change #1303390 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6-27B-FP8 model in experimental ns.

https://gerrit.wikimedia.org/r/1303390

Change #1303400 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[machinelearning/liftwing/inference-services@main] qwen36-27b: copy build venv packages into production

https://gerrit.wikimedia.org/r/1303400

Change #1303400 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] qwen36-27b: copy build venv packages into production

https://gerrit.wikimedia.org/r/1303400

CI Failure Root Cause

The postmerge of this patch failed check the full logs here.
There is an open discussion for this issue since it affects multiple repos in this slack thread.
The vLLM base image (amd-vllm022, built June 11) has /etc/apt/sources.list pointing to mirrors.wikimedia.org, which was disabled on June 17 (T416707).
The base image's sources.list contains 5 entries — 4 broken, 1 working:

deb http://mirrors.wikimedia.org/debian/ bookworm main                   → 404
deb http://mirrors.wikimedia.org/debian/ bookworm-updates main           → 404
deb http://mirrors.wikimedia.org/debian/ bookworm-backports main contrib → 404
deb http://security.debian.org/debian-security bookworm-security main    → DNS failure (webproxy)
deb http://apt.wikimedia.org/wikimedia bookworm-wikimedia main           → WORKS

Blubber's apt step runs apt-get update which fails on the broken entries.

Workarounds attempted (all failed)

  1. Blubber apt.sources — Only *adds* sources alongside existing ones. Does not remove or replace the broken entries from the base image's /etc/apt/sources.list, so apt-get update still fails on them.
  1. Overwrite /etc/apt/sources.list via copies — Created a clean sources.list (pointing to deb.debian.org + apt.wikimedia.org) and added a copies entry in the build variant. Confirmed with --progress=plain that Blubber does NOT generate COPY instructions for copies in the build variant — these only mark artifacts for from: build consumption in downstream variants. The apt step runs first, before any user-controllable step.
  1. Custom builder to run apt-get — Removed apt.packages (which successfully prevents Blubber from running its own apt-get update). Moved apt package installation into a builders.custom entry. Failed because custom builders run as the non-root user (UID 65533) set by insecurely: true, and cannot write to /etc/apt/ or /var/lib/apt/.
  1. Override insecurely per-variant — Attempted to set runs.insecurely: false on the build variant only, or move insecurely: true from top-level to only production/test variants. Both failed — Blubber's buildkit frontend caches the generated Dockerfile and the user setup steps persisted as UID 65533 even with --no-cache.
  1. Redirect apt state to /tmp — Used apt-get -o Dir::Etc=/tmp/apt -o Dir::State::lists=/tmp/apt/lists to point apt at writable paths. While this allows apt-get update to work, apt-get install still requires root (writes to /usr, /etc, /var/lib/dpkg), so the non-root builder cannot complete installation.

Conclusion

There is no Blubber-level workaround for this. The base image's broken apt sources are baked in and run before any configurable step in the build variant. The only fix is updating the base image chain (amd-vllm022 → amd-pytorch-common → python3-bookworm → WMF bookworm) to use a bookworm image built after June 17 with working apt mirrors.

Change #1304729 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[machinelearning/liftwing/inference-services@main] qwen36-27b: copy build venv packages into production

https://gerrit.wikimedia.org/r/1304729

Change #1304729 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] qwen36-27b: copy build venv packages into production

https://gerrit.wikimedia.org/r/1304729

Change #1304758 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6-27B-FP8 model in experimental ns.

https://gerrit.wikimedia.org/r/1304758

Change #1304758 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6-27B-FP8 model in experimental ns.

https://gerrit.wikimedia.org/r/1304758

Update

After the vllm image is updated and tested in the policy-violation model service in this patch we proceeded to the deployment of the Qwen model using the updated vllm image, see here.

The patch is merged and the postmerge was successful but the actual deployment on ml-serve-eqiad under the experimental namespace failed with the following errors:

1$ kubectl logs -n experimental qwen36-27b-predictor-00001-deployment-c8fcc8778-9nzb5 -c kserve-container -f
2
3+ source common_settings.sh
4+++ /srv/venv/bin/python -c 'from python.resource_utils import get_cpu_count; print(get_cpu_count())'
5++ CPU_COUNT=16
6++ echo 'CPU count detected from get_cpu_count: 16'
7CPU count detected from get_cpu_count: 16
8OMP_NUM_THREADS set to: 16
9++ export OMP_NUM_THREADS=16
10++ OMP_NUM_THREADS=16
11++ echo 'OMP_NUM_THREADS set to: 16'
12+ MODEL_SERVER_PATH=model.py
13+ exec /srv/venv/bin/python model.py
14/srv/venv/lib/python3.12/site-packages/torchvision/io/image.py:14: UserWarning: Failed to load image Python extension: 'Could not load this library: /srv/venv/lib/python3.12/site-packages/torchvision/image.so'If you don't plan on using image functionality from `torchvision.io`, you can ignore this warning. Otherwise, there might be something wrong with your environment. Did you have `libjpeg` or `libpng` installed before building `torchvision` from source?
15 warn(
16INFO:root:Loading Qwen 3.6 model from /mnt/models...
17ERROR 06-22 10:46:39 [registry.py:951] Error in inspecting model architecture 'Qwen3_5ForConditionalGeneration'
18ERROR 06-22 10:46:39 [registry.py:951] Traceback (most recent call last):
19ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 1386, in _run_in_subprocess
20ERROR 06-22 10:46:39 [registry.py:951] returned.check_returncode()
21ERROR 06-22 10:46:39 [registry.py:951] File "/opt/python3.12/lib/python3.12/subprocess.py", line 502, in check_returncode
22ERROR 06-22 10:46:39 [registry.py:951] raise CalledProcessError(self.returncode, self.args, self.stdout,
23CRITICAL:root:Failed to load model. Reason: 1 validation error for ModelConfig
24 Value error, Model architectures ['Qwen3_5ForConditionalGeneration'] failed to be inspected. Please check the logs for more details. [type=value_error, input_value=ArgsKwargs((), {'model': ...nderer_num_workers': 1}), input_type=ArgsKwargs]
25 For further information visit https://errors.pydantic.dev/2.13/v/value_error
26ERROR 06-22 10:46:39 [registry.py:951] subprocess.CalledProcessError: Command '['/srv/venv/bin/python', '-m', 'vllm.model_executor.models.registry']' returned non-zero exit status 1.
27ERROR 06-22 10:46:39 [registry.py:951]
28ERROR 06-22 10:46:39 [registry.py:951] The above exception was the direct cause of the following exception:
29ERROR 06-22 10:46:39 [registry.py:951]
30ERROR 06-22 10:46:39 [registry.py:951] Traceback (most recent call last):
31ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 949, in _try_inspect_model_cls
32ERROR 06-22 10:46:39 [registry.py:951] return model.inspect_model_cls()
33ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^
34ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/logging_utils/log_time.py", line 21, in _wrapper
35ERROR 06-22 10:46:39 [registry.py:951] result = func(*args, **kwargs)
36ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^
37ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 910, in inspect_model_cls
38ERROR 06-22 10:46:39 [registry.py:951] mi = _run_in_subprocess(
39ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^
40ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 1389, in _run_in_subprocess
41ERROR 06-22 10:46:39 [registry.py:951] raise RuntimeError(
42ERROR 06-22 10:46:39 [registry.py:951] RuntimeError: Error raised in subprocess:
43ERROR 06-22 10:46:39 [registry.py:951] /srv/venv/lib/python3.12/site-packages/torchvision/io/image.py:14: UserWarning: Failed to load image Python extension: 'Could not load this library: /srv/venv/lib/python3.12/site-packages/torchvision/image.so'If you don't plan on using image functionality from `torchvision.io`, you can ignore this warning. Otherwise, there might be something wrong with your environment. Did you have `libjpeg` or `libpng` installed before building `torchvision` from source?
44ERROR 06-22 10:46:39 [registry.py:951] warn(
45ERROR 06-22 10:46:39 [registry.py:951] <frozen runpy>:128: RuntimeWarning: 'vllm.model_executor.models.registry' found in sys.modules after import of package 'vllm.model_executor.models', but prior to execution of 'vllm.model_executor.models.registry'; this may result in unpredictable behaviour
46ERROR 06-22 10:46:39 [registry.py:951] Traceback (most recent call last):
47ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/aiter/jit/utils/cpp_extension.py", line 90, in get_hip_version
48ERROR 06-22 10:46:39 [registry.py:951] hipconfig = executable_path("hipconfig")
49ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/aiter/jit/utils/cpp_extension.py", line 83, in executable_path
51ERROR 06-22 10:46:39 [registry.py:951] path is not None
52ERROR 06-22 10:46:39 [registry.py:951] AssertionError: Could not find hipconfig in PATH or ROCM_HOME(/opt/rocm-7.2.0)
53ERROR 06-22 10:46:39 [registry.py:951]
54ERROR 06-22 10:46:39 [registry.py:951] During handling of the above exception, another exception occurred:
55ERROR 06-22 10:46:39 [registry.py:951]
56ERROR 06-22 10:46:39 [registry.py:951] Traceback (most recent call last):
57ERROR 06-22 10:46:39 [registry.py:951] File "<frozen runpy>", line 198, in _run_module_as_main
58ERROR 06-22 10:46:39 [registry.py:951] File "<frozen runpy>", line 88, in _run_code
59ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 1412, in <module>
60ERROR 06-22 10:46:39 [registry.py:951] _run()
61ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 1405, in _run
62ERROR 06-22 10:46:39 [registry.py:951] result = fn()
63ERROR 06-22 10:46:39 [registry.py:951] ^^^^
64ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 911, in <lambda>
65ERROR 06-22 10:46:39 [registry.py:951] lambda: _ModelInfo.from_model_cls(self.load_model_cls())
66ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^
67ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/registry.py", line 924, in load_model_cls
68ERROR 06-22 10:46:39 [registry.py:951] mod = importlib.import_module(self.module_name)
69ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
70ERROR 06-22 10:46:39 [registry.py:951] File "/opt/python3.12/lib/python3.12/importlib/__init__.py", line 90, in import_module
71ERROR 06-22 10:46:39 [registry.py:951] return _bootstrap._gcd_import(name[level:], package, level)
72ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
73ERROR 06-22 10:46:39 [registry.py:951] File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
74ERROR 06-22 10:46:39 [registry.py:951] File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
75ERROR 06-22 10:46:39 [registry.py:951] File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
76ERROR 06-22 10:46:39 [registry.py:951] File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
77ERROR 06-22 10:46:39 [registry.py:951] File "<frozen importlib._bootstrap_external>", line 999, in exec_module
78ERROR 06-22 10:46:39 [registry.py:951] File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
79ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/qwen3_5.py", line 43, in <module>
80ERROR 06-22 10:46:39 [registry.py:951] from vllm.model_executor.layers.mamba.gdn.qwen_gdn_linear_attn import (
81ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/layers/mamba/gdn/qwen_gdn_linear_attn.py", line 73, in <module>
82ERROR 06-22 10:46:39 [registry.py:951] GDN_AITER_TRITON_AVAILABLE = rocm_aiter_ops.are_gdn_triton_kernels_available()
83ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
84ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/_aiter_ops.py", line 120, in wrapper
85ERROR 06-22 10:46:39 [registry.py:951] return func(*args, **kwargs)
86Traceback (most recent call last):
87 File "/srv/app/model.py", line 87, in load
88ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^
89 self.model = AsyncLLMEngine.from_engine_args(engine_args)
90 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
91 File "/srv/venv/lib/python3.12/site-packages/vllm/v1/engine/async_llm.py", line 242, in from_engine_args
92ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/vllm/_aiter_ops.py", line 1565, in are_gdn_triton_kernels_available
93ERROR 06-22 10:46:39 [registry.py:951] import aiter.ops.triton.causal_conv1d_update_single_token # noqa: F401
94ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
95ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/aiter/__init__.py", line 75, in <module>
96ERROR 06-22 10:46:39 [registry.py:951] from .jit import core as core # noqa: E402
97ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
98ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/aiter/jit/core.py", line 23, in <module>
99ERROR 06-22 10:46:39 [registry.py:951] from chip_info import get_gfx, get_gfx_list # noqa: E402
100ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
101ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/aiter/jit/utils/chip_info.py", line 9, in <module>
102ERROR 06-22 10:46:39 [registry.py:951] from cpp_extension import executable_path
103 vllm_config = engine_args.create_engine_config(usage_context)
104ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/aiter/jit/utils/cpp_extension.py", line 169, in <module>
105ERROR 06-22 10:46:39 [registry.py:951] HIP_VERSION = get_hip_version()
106ERROR 06-22 10:46:39 [registry.py:951] ^^^^^^^^^^^^^^^^^
107ERROR 06-22 10:46:39 [registry.py:951] File "/srv/venv/lib/python3.12/site-packages/aiter/jit/utils/cpp_extension.py", line 94, in get_hip_version
108ERROR 06-22 10:46:39 [registry.py:951] raise RuntimeError("ROCm version file not found")
109ERROR 06-22 10:46:39 [registry.py:951] RuntimeError: ROCm version file not found
110ERROR 06-22 10:46:39 [registry.py:951]
111 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
112 File "/srv/venv/lib/python3.12/site-packages/vllm/engine/arg_utils.py", line 1730, in create_engine_config
113 model_config = self.create_model_config()
114 ^^^^^^^^^^^^^^^^^^^^^^^^^^
115 File "/srv/venv/lib/python3.12/site-packages/vllm/engine/arg_utils.py", line 1560, in create_model_config
116 return ModelConfig(
117 ^^^^^^^^^^^^
118 File "/srv/venv/lib/python3.12/site-packages/pydantic/_internal/_dataclasses.py", line 121, in __init__
119 s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s)
120pydantic_core._pydantic_core.ValidationError: 1 validation error for ModelConfig
121 Value error, Model architectures ['Qwen3_5ForConditionalGeneration'] failed to be inspected. Please check the logs for more details. [type=value_error, input_value=ArgsKwargs((), {'model': ...nderer_num_workers': 1}), input_type=ArgsKwargs]
122 For further information visit https://errors.pydantic.dev/2.13/v/value_error
123
124During handling of the above exception, another exception occurred:
125
126Traceback (most recent call last):
127 File "/srv/app/model.py", line 405, in <module>
128 model.load()
129 File "/srv/app/model.py", line 94, in load
130 raise kserve.errors.ModelMissingError(error_message)
131kserve.errors.ModelMissingError: Failed to load model. Reason: 1 validation error for ModelConfig
132 Value error, Model architectures ['Qwen3_5ForConditionalGeneration'] failed to be inspected. Please check the logs for more details. [type=value_error, input_value=ArgsKwargs((), {'model': ...nderer_num_workers': 1}), input_type=ArgsKwargs]
133 For further information visit https://errors.pydantic.dev/2.13/v/value_error

It seems that the issue seems to be that aiter library cannot find hipconfig — in the ROCm.
The image has /opt/rocm-7.2.0 on disk but the hipconfig binary is missing from its bin/ directory.
This means the ROCm 7.2.0 installation inside this container image seems to be incomplete or broken.
It seems that the latest image needs a rebuild with hipconfig properly installed.

Change #1304823 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[machinelearning/liftwing/inference-services@main] qwen36: fix aiter import crash by symlinking version-hip to version

https://gerrit.wikimedia.org/r/1304823

Change #1304827 had a related patch set uploaded (by Kevin Bazira; author: Kevin Bazira):

[machinelearning/liftwing/inference-services@main] qwen36: add hipconfig shim to entrypoint.sh to unblock AITER import

https://gerrit.wikimedia.org/r/1304827

Change #1304827 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] qwen36: add hipconfig shim to entrypoint.sh to unblock AITER import

https://gerrit.wikimedia.org/r/1304827

Change #1304823 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] qwen36: Retrigger CI/CD

https://gerrit.wikimedia.org/r/1304823

Change #1304810 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] Deploy Qwen3.6-27b-FP8 on experimental ns.

https://gerrit.wikimedia.org/r/1304810

Change #1304810 merged by jenkins-bot:

[operations/deployment-charts@master] Deploy Qwen3.6-27b-FP8 on experimental ns.

https://gerrit.wikimedia.org/r/1304810

Change #1305071 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] Deploy Qwen3.6-27b-FP8 on experimental ns.

https://gerrit.wikimedia.org/r/1305071

Reporting back here after some slack conversations.
The reported errors in the related phabricator pastes https://phabricator.wikimedia.org/P94363 show 2 things:

  1. we need to disable multimodal abilites since we want to use this model for text only for now. this is handled by @gkyziridis in https://gerrit.wikimedia.org/r/c/operations/deployment-charts/+/1305071 and indeed the ModuleNotFoundError goes away as it was related to the model's vision capabilities.
  2. The base image is probably not setup right as hipconfig and hipcc cannot be found. They don't exist in the PATH and neither in the image's filesystem. I found past references about hipcc on our stack T381567: Debian hipcc package conflicts with hipcc from AMD's ROCm repository & https://phabricator.wikimedia.org/T385173#10568321 but don't know if they are of much help. It seems that we might be missing a debian package, either rocm-dev or rocm-device-libs (don't know which is the correct one for bookworm + rocm7.2). In the base production image we are using rocm-libs https://gerrit.wikimedia.org/r/plugins/gitiles/operations/docker-images/production-images/+/refs/heads/master/ml/vllm022/Dockerfile.template#84

Change #1305142 had a related patch set uploaded (by Kevin Bazira; author: Kevin Bazira):

[machinelearning/liftwing/inference-services@main] qwen36: remove hipconfig shim as it's no longer needed

https://gerrit.wikimedia.org/r/1305142

Change #1305142 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] qwen36: remove hipconfig shim as it's no longer needed

https://gerrit.wikimedia.org/r/1305142

Change #1305360 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[machinelearning/liftwing/inference-services@main] qwen36: Use the latest vllm0.22.1.-3 base image.

https://gerrit.wikimedia.org/r/1305360

Change #1305360 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] qwen36: Use the latest vllm0.22.1.-3 base image.

https://gerrit.wikimedia.org/r/1305360

Change #1305071 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6 model.

https://gerrit.wikimedia.org/r/1305071

After using the latest vllm0.22.1-3 with the hipcc fix, the deployment failed again.
I am reporting here the changes I've made:

  1. Based on this patch the affinity was targeting ml-serve1013.eqiad.wmnet , ml-serve1014.eqiad.wmnet, ml-serve1015.eqiad.wmnet. This implementation threw ImagePullBackOff check the logs:

1gkyziridis@deploy1003:/srv/deployment-charts/helmfile.d/ml-services/experimental$ kubectl logs qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g -c queue-proxy
2aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
3context deadline exceeded
4aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
5context deadline exceeded
6aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
7context deadline exceeded
8aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
9context deadline exceeded
10aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
11context deadline exceeded
12aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
13context deadline exceeded
14aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
15context deadline exceeded
16aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
17context deadline exceeded
18aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
19context deadline exceeded
20aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
21context deadline exceeded
22aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
23context deadline exceeded
24aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
25context deadline exceeded
26aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
27context deadline exceeded
28
29---
30---
31gkyziridis@deploy1003:/srv/deployment-charts/helmfile.d/ml-services/experimental$ kubectl get events -n experimental --field-selector involvedObject.name=qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g --sort-by='.lastTimestamp'
32LAST SEEN TYPE REASON OBJECT MESSAGE
3319m Normal Scheduled pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Successfully assigned experimental/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g to ml-serve1015.eqiad.wmnet
3419m Normal Pulled pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Container image "docker-registry.discovery.wmnet/istio/proxyv2:1.24.2-1" already present on machine
3519m Normal Created pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Created container: istio-validation
3619m Normal Started pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Started container istio-validation
3719m Normal Pulled pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Container image "docker-registry.wikimedia.org/kserve-storage-initializer:0.17.0-0" already present on machine
3819m Normal Created pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Created container: storage-initializer
3919m Normal Started pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Started container storage-initializer
4018m Normal Pulling pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Pulling image "docker-registry.discovery.wmnet/wikimedia/machinelearning-liftwing-inference-services-qwen36@sha256:1b8f8b419e4d94e54f86d0fc1fa7e6156f73f34b48a7f36f93bdd4c66bf00e41"
4112m Normal Pulled pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Container image "docker-registry.discovery.wmnet/knative-serving-queue:1.21.2-1" already present on machine
4212m Warning Failed pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Error: ErrImagePull
4312m Warning Failed pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Failed to pull image "docker-registry.discovery.wmnet/wikimedia/machinelearning-liftwing-inference-services-qwen36@sha256:1b8f8b419e4d94e54f86d0fc1fa7e6156f73f34b48a7f36f93bdd4c66bf00e41": failed to pull and unpack image "docker-registry.discovery.wmnet/wikimedia/machinelearning-liftwing-inference-services-qwen36@sha256:1b8f8b419e4d94e54f86d0fc1fa7e6156f73f34b48a7f36f93bdd4c66bf00e41": failed to read expected number of bytes: unexpected EOF
4412m Normal Created pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Created container: queue-proxy
4512m Normal Started pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Started container queue-proxy
4612m Normal Pulled pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Container image "docker-registry.discovery.wmnet/istio/proxyv2:1.24.2-1" already present on machine
4712m Normal Created pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Created container: istio-proxy
4812m Normal Started pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Started container istio-proxy
4912m Warning Unhealthy pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Startup probe failed: Get "http://10.67.22.33:15021/healthz/ready": dial tcp 10.67.22.33:15021: connect: connection refused
5012m Normal BackOff pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Back-off pulling image "docker-registry.discovery.wmnet/wikimedia/machinelearning-liftwing-inference-services-qwen36@sha256:1b8f8b419e4d94e54f86d0fc1fa7e6156f73f34b48a7f36f93bdd4c66bf00e41"
5112m Warning Failed pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Error: ImagePullBackOff
524m42s Warning Unhealthy pod/qwen36-27b-predictor-00004-deployment-6cc55bcbb8-knr6g Readiness probe failed: Get "http://10.67.22.33:15020/app-health/queue-proxy/readyz": context deadline exceeded (Client.Timeout exceeded while awaiting headers)

  1. I removed the ml-serve1015.eqiad.wmnet from the deployment and left only the ml-serve1013.eqiad.wmnet and ml-serve1014.eqiad.wmnet. The ImagePullBackOff error went away. The issue on this iteration was that the JIT compilation taking too long. On first startup, aiter needed to compile ROCm attention kernels (mha_varlen_fwd) using hipcc. This takes 10-15 minutes because it compiles dozens of kernel variants for different head dimensions (d64, d80, d96, d128, d192, d256) and GPU targets (gfx9, gfx950). I used this command to watch the files added in the container one by one. :
watch -n 30 "kubectl exec qwen36-27b-predictor-00005-deployment-cdb555d8d-vsjzc  -c kserve-container -- ls -la /srv/venv/lib/python3.12/site-packages/aiter/jit/build/mha_varlen_fwd_bf16_nlogits_nbias_nmask_nlse_ndropout_nskip_nqscale/build/ | tail -5"

That hit a timeout: Readiness probe failed: Get "http://10.67.19.117:15020/app-health/queue-proxy/readyz": context deadline exceeded (Client.Timeout exceeded while awaiting headers) and No available shared memory broadcast block found in 60 seconds. This typically happens when some processes are hanging or doing some time-consuming work (e.g. compilation, weight/kv cache quantization). See here the logs in the comment: https://phabricator.wikimedia.org/P94417#384661

  1. The final trial I set a high timeout=600 (10 mins, since this is the upper limit that we can use), and using the command watch -n 30 "kubectl exec qwen36-27b-predictor-00006-deployment-54945d656-5wd6t -c kserve-container -- ls -la /srv/venv/lib/python3.12..., I saw all the files (d64, d80, ...) added but in the end it failed with these logs: https://phabricator.wikimedia.org/P94417#384706

Thoughts:
The basic issue which is common in the last two trials is shown in these lines:

(Worker_TP0) [aiter] start build [mha_varlen_fwd_bf16_...]
(Worker_TP1) [aiter] waiting for baton release
(Worker_TP0) [aiter] Current hipcc not support: -mllvm -amdgpu-coerce-illegal-types=1, skip it.

Apart from that, after the kernels finished compiling vLLM crashed with Engine core initialization failedand leaked shared memory objects:

RuntimeError: Engine core initialization failed. See root cause above. Failed core proc(s): {}
kserve.errors.ModelMissingError: Failed to load model. Reason: Engine core initialization failed.
resource_tracker: There appear to be 2 leaked shared_memory objects
Last State: Terminated
  Reason: Error
  Message: RuntimeError: Engine core initialization failed...

Suggestions:

  1. Something is going wrong again with the hipcc with our Rocm version or the gpus.
  2. Add more shared memory 16G -> 32G
  3. Add higher timeout limit.

I removed the ml-serve1015.eqiad.wmnet from the deployment and left only the ml-serve1013.eqiad.wmnet and ml-serve1014.eqiad.wmnet. The ImagePullBackOff error went away. The issue on this iteration was that the JIT compilation taking too long. On first startup, aiter needed to compile ROCm attention kernels (mha_varlen_fwd) using hipcc. This takes 10-15 minutes because it compiles dozens of kernel variants for different head dimensions (d64, d80, d96, d128, d192, d256) and GPU targets (gfx9, gfx950). I used this command to watch the files added in the container one by one.

This is a bit of a "side quest", but should we maybe precompile these? I feel ml-build is sortof the ideal place to do this. How we then distribute it to the pod that needs it is another question, but we could take a similar approach as for the base model (storage initializer and S3/Ceph).

This is a bit of a "side quest", but should we maybe precompile these? I feel ml-build is sortof the ideal place to do this. How we then distribute it to the pod that needs it is another question, but we could take a similar approach as for the base model (storage initializer and S3/Ceph).

@klausman thnx your comment and your idea!
I was thinking if it would be a good idea to add the kernels in the base image of vllm in the Dockerfile at production-images repo.
So, we could pre-compile aiter so JIT kernels avoid the slow compilation during the pod start.
Adding something like this in the Dockerfile:

RUN /srv/venv/bin/python -c \
    "from vllm.model_executor.layers.mamba.gdn.qwen_gdn_linear_attn import *"

I am suggesting this in order to avoid messing with compiling things on ml-build and distribute it later on.
Tagging @kevinbazira if he has any thoughts on that.

Tagging @kevinbazira if he has any thoughts on that.

Must you use AITER in this isvc? We have experienced AITER compilation issues before in the embeddings isvc (T418976#11705174) and ended up dropping AITER in favor of a Triton backend. I tested this Qwen/Qwen3.6-27B-FP8 LLM and was able to run it with the standard backend as shown in: P94313#384135

If you don't strictly need to use AITER, you can change the deployment config

from:

...
- name: ATTENTION_BACKEND
  value: "ROCM_AITER_UNIFIED_ATTN"
...
- name: VLLM_ROCM_USE_AITER
  value: "1"
...

to:

...
- name: ATTENTION_BACKEND
  value: "TRITON_ATTN"
...
- name: VLLM_ROCM_USE_AITER
  value: "0"
...

Thnx for your comment @kevinbazira, I tried the above as well but I am having errors (full logs):

(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962] torch.OutOfMemoryError: HIP out of memory. Tried to allocate 576.00 MiB. GPU 0 has a total capacity of 24.00 GiB of which 324.00 MiB is free. Of the allocated memory 20.89 GiB is allocated by PyTorch, and 1.32 GiB is reserved by PyTorch but unallocated.

The chain led to this one:

(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962]   File "/srv/venv/lib/python3.12/site-packages/vllm/v1/worker/gpu_model_runner.py", line 6152, in profile_run
(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962]     dummy_encoder_outputs = self.model.embed_multimodal(
(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962]   File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/qwen3_vl.py", line 2690, in embed_multimodal
(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962]   File "/srv/venv/lib/python3.12/site-packages/vllm/distributed/device_communicators/pynccl.py", line 184, in all_reduce
(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962]     out_tensor = torch.empty_like(in_tensor)
(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962] torch.OutOfMemoryError: HIP out of memory.
...
CRITICAL:root:Failed to load model. Reason: Engine core initialization failed. See root cause above. Failed core proc(s): {}

What I also parsed is that even we use the LANGUAGE_MODEL_ONLY: true, it still tries to load the visual encoder shown in the following logs:

INFO 06-25 09:12:41 [model.py:617] Resolved architecture: Qwen3_5ForConditionalGeneration

and

(Worker_TP0 pid=292) INFO 06-25 09:13:27 [rocm.py:606] Using Flash Attention backend for ViT model.

crashes in

(Worker_TP0 pid=292) ERROR 06-25 09:13:47 [multiproc_executor.py:962]   File "/srv/venv/lib/python3.12/site-packages/vllm/model_executor/models/qwen3_vl.py", line 2690, in embed_multimodal

So summarising, the model.py reads LANGUAGE_MODEL_ONLY from the environment but never passes it to AsyncEngineArgs. Because of this, vLLM always loads the full vision-language model (qwen3_vl.py) including the visual encoder (ViT). During startup, vLLM runs a memory profiling pass (profile_run) that tries to run 4 dummy images through the visual encoder. With 14.9 GiB already used by model weights on a 24 GiB GPU, this profiling pass runs out of memory and crashes.

I found these two suggestions:
Add directly the LANGUAGE_MODEL_ONLY in the model.py:

engine_args = AsyncEngineArgs(
    ...
    language_model_only=strtobool(os.environ.get("LANGUAGE_MODEL_ONLY", "True")),
)

There is also this argument which explicitly tells vLLM to skip the multimodal profiling pass:

engine_args = AsyncEngineArgs(
    ...
    skip_mm_profiling=True,
)

Change #1305638 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[machinelearning/liftwing/inference-services@main] Qwen3.6-27b-FP8: Add hardcoded arguments for language model only.

https://gerrit.wikimedia.org/r/1305638

Change #1305638 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] Qwen3.6-27b-FP8: Add env variables directly in the load function.

https://gerrit.wikimedia.org/r/1305638

Change #1305886 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6 latest image.

https://gerrit.wikimedia.org/r/1305886

Change #1305886 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Deploy Qwen3.6 latest image.

https://gerrit.wikimedia.org/r/1305886

Update

Qwen3.6-27b-FP8 model server is deployed successfully in experimental namespace at ml-serve-eqiad.
The errors during deployment went away I am reporting here the Logs and the corresponding fixes I've made:

Error 1: GPU OOM during multimodal profiling
Log evidence:

(Worker_TP0 pid=277) INFO [rocm.py:606] Using Flash Attention backend for ViT model.
(Worker_TP0 pid=277) ERROR torch.OutOfMemoryError: HIP out of memory. Tried to allocate 576.00 MiB. GPU 0 has a total capacity of 24.00 GiB of which 324.00 MiB is free.

Root cause: config.json declares Qwen3_5ForConditionalGeneration (multimodal arch), so vLLM loads the ViT visual encoder and tries to profile it with dummy images during startup, OOM'ing with only 324MB free after loading 14.45GiB of weights.
Fix: Added language_model_only=True and skip_mm_profiling=True to AsyncEngineArgs in model.py, and exposed them as env vars LANGUAGE_MODEL_ONLY and SKIP_MM_PROFILING (both defaulting to True).
Confirmation in later logs:

(EngineCore pid=218) INFO [registry.py:134] All limits of multimodal modalities supported by the model are set to 0, running in text-only mode.

Error 2: GPU OOM during profiling run with MAX_NUM_BATCHED_TOKENS=65536
Log evidence:

(Worker_TP0 pid=277) INFO [gpu_worker.py:466] Available KV cache memory: 1.22 GiB
torch.OutOfMemoryError: HIP out of memory. Tried to allocate 2.12 GiB.

Root cause: With MAX_NUM_BATCHED_TOKENS=65536, the dummy profiling forward pass allocated more memory than available after weights were loaded.
Fix: Reduced MAX_NUM_BATCHED_TOKENS and MAX_MODEL_LEN from 65536 to 32768 in the ISVC. Confirmed by torch.compile targeting the correct range:

(Worker_TP0 pid=277) INFO [backends.py:378] Cache the graph of compile range (1, 32768) for later use

Error 3: Mamba cache blocks insufficient for MAX_NUM_SEQS=128
Log evidence:

(Worker_TP0 pid=277) INFO [gpu_worker.py:466] Available KV cache memory: 1.25 GiB
(EngineCore pid=218) INFO [kv_cache_utils.py:1733] GPU KV cache size: 34,192 tokens

ValueError: max_num_seqs (128) exceeds available Mamba cache blocks (48). 
Each decode sequence requires one Mamba cache block, so CUDA graph capture 
cannot proceed. Please lower max_num_seqs to at most 48 or increase 
gpu_memory_utilization.

Root cause: With only 1.25 GiB of KV cache memory available (after weights + CUDA graphs), vLLM could only allocate 48 Mamba cache blocks, but MAX_NUM_SEQS=128 required 128 blocks — one per concurrent sequence.
Fix: Set MAX_NUM_SEQS=48 in the ISVC, matching exactly what vLLM reported as available.

Final successful startup

Capturing CUDA graphs (mixed prefill-decode, PIECEWISE): 100%|██████████| 15/15 [00:06<00:00,  2.37it/s]
Capturing CUDA graphs (decode, FULL): 100%|██████████| 9/9 [00:03<00:00,  2.58it/s]
(Worker_TP0 pid=277) INFO [gpu_model_runner.py:6456] Graph capturing finished in 11 secs, took 2.06 GiB
(EngineCore pid=218) INFO [registry.py:134] All limits of multimodal modalities supported by the model are set to 0, running in text-only mode.
INFO:root:Model loaded successfully!
INFO:uvicorn.error:Uvicorn running on http://0.0.0.0:8080

and

$ kube_env experimental ml-serve-eqiad
$ kubectl get pods
NAME                                                              READY   STATUS    RESTARTS   AGE
cope-a-9b-predictor-00001-deployment-7c484bf7b4-mckvm             3/3     Running   0          23d
editing-suggestions-predictor-00003-deployment-77d55c79d8-9cc5f   3/3     Running   0          3d4h
qwen3-14b-predictor-00001-deployment-5684cb7866-476dh             3/3     Running   0          10d
qwen36-27b-predictor-00011-deployment-5bd6c6db5c-w6m8f            3/3     Running   0          36m

The problem is that the model is not performing correctly see the example below:

curl -s -i https://inference.svc.eqiad.wmnet:30443/v1/models/qwen36-27b:predict \
-X POST \
-H "Content-Type: application/json" \
-H "Host: qwen36-27b.experimental.wikimedia.org" \
-d '{"prompt": "What is the capital of France?", "max_tokens": 50}'

HTTP/2 200 
content-length: 133
content-type: application/json
date: Fri, 26 Jun 2026 14:19:42 GMT
server: istio-envoy
x-envoy-upstream-service-time: 1745

{"model_name":"qwen36-27b","response":"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!","prompt_tokens":17,"completion_tokens":50}

I summarise what the issue probably is:
The Qwen3.6-27B-FP8 model weights are stored in FP8 format and vLLM uses W8A8 Block FP8 matrix multiplication kernels to run them. On AMD Instinct MI300X with ROCm, these kernels require tuned configuration files (JSON files specifying optimal kernel parameters per matrix shape) to produce correct results. These config files are missing from the vLLM installation for the specific matrix shapes used by this model (N=8192,K=5120, N=5120,K=3072, N=17408,K=5120, N=5120,K=8704, N=7168,K=5120), as confirmed by the startup warnings:

WARNING [fp8_utils.py:833] Using default W8A8 Block FP8 kernel config. Performance might be sub-optimal!
Config file not found at ...N=8192,K=5120,device_name=AMD_Instinct_MI300X,dtype=fp8_w8a8,block_shape=[128,128].json

The fallback "default" kernel config produces numerically incorrect results on MI300X — not just degraded performance, but completely wrong outputs (the model generates "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" regardless of input, prompt format, or sampling parameters).
Why float16: Setting DTYPE=float16 forces vLLM to convert the FP8 weights to float16 at load time and run standard fp16 matrix multiplications, bypassing the broken FP8 kernels entirely. This is a temporary workaround to confirm the model itself is correct — the proper fix is generating and installing the tuned FP8 kernel config files for AMD MI300X for this model's matrix shapes.

I will make a change directly to isvc changing the DTYPE and test it.

Change #1305919 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] ml-services: Switch to float16 and reduce context length for Qwen3.6-27B deployment

https://gerrit.wikimedia.org/r/1305919

Change #1305919 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Switch to float16 and reduce context length for Qwen3.6-27B deployment

https://gerrit.wikimedia.org/r/1305919

Change #1306172 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] ml-services: Remove qwen completely from experiental.

https://gerrit.wikimedia.org/r/1306172

Change #1306172 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Remove qwen completely from experiental.

https://gerrit.wikimedia.org/r/1306172

Change #1306220 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] ml-services: increase helmfile timeout and redeploy qwen36-27b in float16

https://gerrit.wikimedia.org/r/1306220

Change #1306220 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: increase helmfile timeout and redeploy qwen36-27b in float16

https://gerrit.wikimedia.org/r/1306220

Thanks for all the work here!
Regarding the OOM errors: since we’re using 2x24 GB partitions, shouldn’t the model weights be split across both partitions thus avoiding running out of memory?
The fp8 model is already ~27GB so it definitely wouldn't fit in one 24GB partition.

@gkyziridis if we need to go with 16 bit floats we could aim at the original non-quantized version of this model that uses bloat16 as its dtype https://huggingface.co/Qwen/Qwen3.6-27B. However this model would be double the size (~54GB) which needs additional provisioning of VRAM.
I initially proposed using the FP8 version so we could start with a smaller model and then gradually deploy larger ones. I assumed this would be easier, but perhaps I was mistaken so let's revisit this if you think that we need to.

Update

Qwen3.6-27B-FP8 KServe Deployment — Blocking Issue
The Problem I parsed is that even the logs of the pod shows that it is completed (see here) the pod is terminating.
It seems that the model cannot be deployed because Knative's queue-proxy sidecar kills the pod after ~21 minutes, before the model finishes loading. The model itself loads successfully in ~20 minutes but the pod is terminated right as it becomes ready.
Knative's queue-proxy performs an "aggressive probe" against port 8080 during startup. After a hardcoded number of failures it gives up and the pod is terminated. Is this is a cluster-level Knative setting? Can i configure it anywhere myself?
Evidence from queue-proxy logs:

aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused
aggressive probe error (failed 201 times): dial tcp 127.0.0.1:8080: connect: connection refused

Evidence that the model itself loads fine (from kserve-container logs):

(EngineCore pid=218) INFO 06-29 12:56:14 [core.py:302] init engine (profile, create kv cache, warmup model) took 1186.41 s
INFO:root:Model loaded successfully!
INFO:kserve:Registering model: qwen36-27b
INFO:uvicorn.error:Application startup complete.
INFO:uvicorn.error:Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)

...but the pod was already terminating at this point.
Evidence from pod describe:

Warning  Unhealthy  11m (x7 over 19m)    kubelet   Readiness probe failed: HTTP probe failed with statuscode: 503
Warning  Unhealthy  96s (x113 over 19m)  kubelet   Readiness probe failed: Get "http://10.67.18.137:15020/app-health/queue-proxy/readyz": context deadline exceeded

Thnx for your comment @isarantopoulos, we do not have any OOM issues, these issues were not from model weights — tensor parallelism handles that fine. It was from KV cache size. Fixed by reducing MAX_MODEL_LEN from 65536 to 19136.

Attempt: deploy qwen36-27b (FP8) on ml-serve1012
Changes (vs ml-serve1013/1014:

  • affinity → ml-serve1012
  • TENSOR_PARALLEL_SIZE 2→1
  • amd.com/gpu 2→1 (limits+requests)
  • DISABLE_CUSTOM_ALL_REDUCE True →False

Result: Pod qwen36-27b-predictor-00005 scheduled fine (192 GB partition free), pulled the image (one-time, 16.5 GB, ~4m14s), then was evicted at 86% of weight loading — before torch.compile/graph capture.

Reason:   Evicted
Message:  The node was low on resource: ephemeral-storage.
          Threshold quantity: 14650510349, available: 12034436Ki.
          Container kserve-container was using 317968Ki, request is 0, has larger consumption of ephemeral-storage.
          Container istio-proxy was using 3096Ki, request is 0, ...
          Container queue-proxy was using 28Ki, request is 0, ...

Container termination (exit 137 = SIGKILL, at 86% model load):

Last State:  Terminated
Reason:      Error
Exit Code:   137
Message:     Loading safetensors checkpoint shards:  86% Completed | 57/66 [00:10<00:01, 5.31it/s]

Image pull (one-time, first pull on this node):

Successfully pulled image "...machinelearning-liftwing-inference-services-qwen36@sha256:c5aaacbe..."
in 4m13.914s. Image size: 16483746045 bytes

Since CUDA-graph capture is the ~968s bottleneck killing the pod before readiness, I am planning to use enforce_eager=True as the next step: based on documentation "disable CUDA graph and always execute the model in eager mode", so it skips capture entirely → startup drops to ~4-5 min (under the queue-proxy deadline), with no node/cluster change and no model change, staying on the current FP8 model on ml-serve1013/1014 where capacity is reliable. Tradeoff is lower inference throughput (no CUDA graphs), acceptable for a first working deployment to validate correctness.

The above is an endeavour to find a workaround in order to deploy this current model Qwen3.6-27b-FP8. The next iteration would be to try the un-quantised model version: Qwen3.6-27B.

Change #1306680 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[machinelearning/liftwing/inference-services@main] Qwen-model: add ENFORCE_EAGER env var to optionally disable CUDA graph capture

https://gerrit.wikimedia.org/r/1306680

Change #1306680 merged by jenkins-bot:

[machinelearning/liftwing/inference-services@main] Qwen-model: add ENFORCE_EAGER env var to optionally disable CUDA graph capture

https://gerrit.wikimedia.org/r/1306680

Change #1306699 had a related patch set uploaded (by Gkyziridis; author: Gkyziridis):

[operations/deployment-charts@master] ml-services: Deploy qwen36-27b in eager mode to fix startup timeout

https://gerrit.wikimedia.org/r/1306699

Change #1306699 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Deploy qwen36-27b in eager mode to fix startup timeout

https://gerrit.wikimedia.org/r/1306699

qwen36-27b-FP8 — deployed (baseline) via eager mode

qwen36-27b now deploys and serves correctly on ml-serve1013/1014 in eager mode. This is the first working deployment across all seven revisions.

Root cause (confirmed)

On the 24GB CPX slices (TP=2), CUDA graph capture took ~968s — ~81% of a ~1186–1250s engine init. The server binds :8080 only after capture, so it lost the Knative queue-proxy readiness-probe race and the pod was terminated at ~21 min, every time. The ISVC probe-timeout annotations are not honored in this Knative version; the proper cluster fix (config-deployment progress-deadline in knative-serving) needs access we don't have. enforce_eager=True skips capture entirely, bringing startup well under the probe deadline.

Three follow-ups remain, all deferred:

1. Latency — ~34s for 85 tokens (~2.5 tok/s)

Two independent mechanisms, both direct consequences of the current config/hardware:
(a) eager mode skips CUDA graphs (CUDAGraphMode.NONE) — the same thing that fixed startup time — so each decode step dispatches kernels one-by-one instead of replaying a graph

(b) the 24GB CPX slice is 1/8 of a physical MI300, and TP=2 adds a per-layer all-reduce, while a full-GPU (TP=1) run wouldn't. The tight KV cache (1.83x) is a throughput/batching ceiling, not the cause here.

Possible fix: re-enable CUDA graphs.

INFO:kserve.trace:kserve.io.openai.v1.completions: 33.96046233177185 ['http_status:200', 'http_method:POST', 'time:wall']
(Worker_TP0 pid=277) INFO 07-01 08:09:14 [gpu_worker.py:466] Available KV cache memory: 1.36 GiB
(EngineCore pid=218) INFO 07-01 08:09:14 [kv_cache_utils.py:1733] GPU KV cache size: 34,972 tokens
(EngineCore pid=218) INFO 07-01 08:09:14 [kv_cache_utils.py:1734] Maximum concurrency for 19,136 tokens per request: 1.83x
2. FP8 kernel configs — still on default W8A8 block-FP8

The MI300X-specific .json is missing, so the kernel falls back to an untuned default. float16 works as the interim; generating MI300X configs restores true FP8 and is the leading candidate to (probably) also fix the slow capture (per-shape FP8 autotuning).

(Worker_TP0 pid=277) WARNING 07-01 08:07:54 [fp8_utils.py:833] Using default W8A8 Block FP8 kernel config. Performance might be sub-optimal! Config file not found at /srv/venv/lib/python3.12/site-packages/vllm/model_executor/layers/quantization/utils/configs/N=8192,K=5120,device_name=AMD_Instinct_MI300X,dtype=fp8_w8a8,block_shape=[128,128].json
3. ml-serve1012 ephemeral-storage — needs ops fix

Node is now auto-tainted disk-pressure; it evicted the full-GPU test (00005) at 86% of model loading. Needs disk freed (~16.5GB image + ~28.75 GiB model) before the full-GPU capture experiment can run.

Reason:   Evicted
Message:  The node was low on resource: ephemeral-storage. Threshold quantity: 14650510349, available: 12034436Ki. Container kserve-container was using 317968Ki, request is 0, has larger consumption of ephemeral-storage.
0/17 nodes are available: 1 node(s) had untolerated taint {node.kubernetes.io/disk-pressure: }, 14 node(s) didn't match Pod's node affinity/selector, 2 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }.

Next steps

1. Re-run the exact same ISVC on ml-serve1013/1014 without eager mode, with more startup headroom
Same config, same nodes, ENFORCE_EAGER=False (CUDA graphs on). The goal is to let capture run to completion and see what happens, so we need to give the pod more time before the queue-proxy probe kills it.
Open question: the ISVC probe-timeout annotations seem to be in the earlier revisions, so we need to confirm what actually controls the deadline. Candidate is progress-deadline in the knative-serving config-deployment configmap. I am still not sure where to configure this.

2. Full-GPU test on ml-serve1012 (needs disk freed first)
I am not sure if this is feasible, but it would be nice to test this model without TP=2 and deploy it on the ml-serve1012. I faced the issue of "evicted: node low on resource" explained in the 3rd point above.

3. Generate MI300X W8A8 block-FP8 kernel configs
The missing file is the device-named tuning config for the model's GEMM shapes, e.g. N=8192,K=5120,device_name=AMD_Instinct_MI300X,dtype=fp8_w8a8,block_shape=[128,128].json. Without it vLLM logs Using default W8A8 Block FP8 kernel config and falls back to an untuned kernel.
These configs are generated by running vLLM's FP8 kernel-tuning/benchmark script on an MI300X for the shapes this model uses, then committing the resulting JSON(s) to the vLLM configs path in the image.
Two payoffs:

(a) restores the tuned FP8 compute path (currently DTYPE=float16 interim)

(b) candidate fix for the slow capture, if per-shape FP8 autotuning is what made capture slow.

Timeout ceiling blocks the graphs-on test on 1013/1014

Tried to re-deploy qwen36-27b with ENFORCE_EAGER=False (CUDA graphs on) this patch with a higher timeout to let capture (~968s) finish before the probe kills the pod. The Knative admission webhook rejected it — revision timeout is hard-capped at 600s cluster-wide:

lastTransitionTime: "2026-07-03T06:16:00Z"
      message: 'fails to reconcile predictor: fails to reconcile knative service:
        admission webhook "validation.webhook.serving.knative.dev" denied the request:
        validation failed: expected 0 <= 2000 <= 600: spec.template.spec.timeoutSeconds'
      reason: ReconcileFailed
      status: "False"

Also tried at the ISVC level; same cap.

Consequence: 600s < ~968s capture, so CUDA graphs-on cannot come up within the allowed timeout. This is a hard limit we can't raise from my side.

I am tagging here @klausman, @DPogorzelski-WMF for visibility. Is there any way to set higher time limits?

Update

We've noticed similar issue with corrupt responses when deploying qwen3-14b model on 2x 24GB VRAM partitions of MI300X - https://phabricator.wikimedia.org/T431268.

The issue went away after moving the model to a single host. I did the same for qwen36-27b in this patch by moving it to a single 192GB VRAM partition of MI300X. Model server started correctly in ~170 seconds, reports 128GiB KV cache and the responds correctly (3.4s cold, ~0.3s warm on the example below):

~ curl -s "https://inference.svc.eqiad.wmnet:30443/openai/v1/completions" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Host: qwen36-27b.experimental.wikimedia.org" \
  -d '{"model": "qwen36-27b", "prompt": "What is the capital of France?", "max_tokens": 200}'

{"id":"80843f0b440fd5cd","object":"text_completion","created":1783511126,"model":"qwen36-27b","choices":[{"index":0,"text":"\n\n<think>\n\n</think>\n\nThe capital of France is **Paris**.","logprobs":null,"finish_reason":"stop","stop_reason":null,"token_ids":null,"prompt_logprobs":null,"prompt_token_ids":null,"routed_experts":null}],"service_tier":null,"system_fingerprint":null,"usage":{"prompt_tokens":7,"total_tokens":21,"completion_tokens":14,"prompt_tokens_details":null},"kv_transfer_params":null}

Startup logs confirm FP8 execution is active despite DTYPE=float16 in env vars: quantization=fp8, Selected TritonFp8BlockScaledMMKernel for Fp8LinearMethod and weights occupy 28.5GiB on GPU, expected for FP8 dtype. The default W8A8 configs are still in the hot path, but the output is correct so this is rather performance-only issue we can follow up on.

Change #1309133 had a related patch set uploaded (by Bartosz Wójtowicz; author: Bartosz Wójtowicz):

[operations/deployment-charts@master] ml-services: Update dtype to auto for qwen36-27b.

https://gerrit.wikimedia.org/r/1309133

Change #1309133 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: Update dtype to auto for qwen36-27b.

https://gerrit.wikimedia.org/r/1309133

I removed the DTYPE=float16 and exchanged it with DTYPE=auto env var and service still responds happily, which confirms that the FP8 quantization works.

In a next step, I'll move qwen36-27b from experimental namespace to llm namespace and expose it via REST Gateway with our LLM rate limiting policy.

Change #1309554 had a related patch set uploaded (by Bartosz Wójtowicz; author: Bartosz Wójtowicz):

[operations/deployment-charts@master] ml-services: move qwen36-27b to llm namespace.

https://gerrit.wikimedia.org/r/1309554

Change #1309554 merged by jenkins-bot:

[operations/deployment-charts@master] ml-services: move qwen36-27b to llm namespace.

https://gerrit.wikimedia.org/r/1309554

The model is now deployed in the llm namespace and is exposed via REST Gateway with LiftWingLLM rate limits. Additionally, I've added it to LiftWing studio so everyone can play around with it via UI.
Model can be queried like below:

curl -s https://api.wikimedia.org/service/lw/inference/v1/models/llm-qwen36-27b/openai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"llm-qwen36-27b","messages":[{"role":"user","content":"What is the capital of France?"}],"max_tokens":200}' | jq .

Closing the task! 🎉