v1/worker: the attention worker and runner
Code version: afd-plugin @ 7452d52 · 2026-10-27 · vLLM @ ced6857
In one sentence
The attention role is a native vLLM worker with the AFD Model Runner V2 swapped in, plus a shared mixin that publishes the control plane.
Purpose#
These three files add the connector and control plane to the attention side without rewriting native vLLM. The concept behind them is on The attention runtime: a seam over Model Runner V2; this page maps the files.
| Item | Value |
|---|---|
| Files (lines, origin) | attention_worker.py (93), attention_model_runner_v2.py (622), attention_metadata.py (357); all AFD-owned |
| Main classes | AFDAttentionWorker (afd_plugin/v1/worker/attention_worker.py:22), AFDAttentionModelRunnerV2 (afd_plugin/v1/worker/attention_model_runner_v2.py:358), AFDMetadataProviderMixin (afd_plugin/v1/worker/attention_metadata.py:21) |
| Runs in | each attention worker process (one per rank); transfers on the current CUDA stream |
| Called by | vLLM, through the rewritten worker_cls (afd_plugin/validation.py:290-291) |
| Calls | native Worker and GPUModelRunner (vllm:vllm/v1/worker/gpu_worker.py:469-482), the connector factory, the model wrapper |
| Not in this file | the FFN runtime (v1/worker: the FFN worker and runner), the connector transport (connectors/gpu/p2p.py: the GPU P2P connector), the model split (model_executor/models/deepseek_v2.py: the DeepSeek wrapper) |
Where it sits#
Who calls it#
The AFD config patch rewrites worker_cls to AFDAttentionWorker when the role is
attention (afd_plugin/validation.py:280-291). vLLM then builds this worker like any other. The worker
builds the runner inside init_device.
What it calls#
The worker delegates device setup to native Worker.init_device, which local-imports and builds
GPUModelRunner (vllm:vllm/v1/worker/gpu_worker.py:469-482). The runner builds the
connector through the factory (afd_plugin/connectors/factory.py:49-68) and reads the model wrapper.
Key classes and functions#
| Name | Line | Role |
|---|---|---|
AFDAttentionWorker.init_device | afd_plugin/v1/worker/attention_worker.py:60-90 | swap in the AFD runner for the native construction window |
AFDAttentionModelRunnerV2.__init__ | afd_plugin/v1/worker/attention_model_runner_v2.py:369-411 | build the connector; require a control plane |
load_model | afd_plugin/v1/worker/attention_model_runner_v2.py:454-462 | native load, then connector rendezvous at the end |
execute_model | afd_plugin/v1/worker/attention_model_runner_v2.py:569-595 | scope the AFD context, delegate to native V2 |
capture_model | afd_plugin/v1/worker/attention_model_runner_v2.py:509-553 | publish graph warm-up/capture control |
AFDMetadataProviderMixin.send_dp_metadata | afd_plugin/v1/worker/attention_metadata.py:75-116 | publish one control payload |
install_afd_metadata_on_forward_context | afd_plugin/v1/worker/attention_metadata.py:211-257 | attach the sidecar, publish on the non-DBO path |
Walk through the main methods#
AFDAttentionWorker.init_device: swap the runner class#
vLLM 0.30.0 builds its runner from a module symbol inside Worker.init_device, with no injection
hook. The worker replaces that symbol for the native construction window, then restores it
(afd_plugin/v1/worker/attention_worker.py:60-90).
native_runner_cls = gpu_model_runner_v2.GPUModelRunner
gpu_model_runner_v2.GPUModelRunner = AFDAttentionModelRunnerV2
try:
super().init_device()
finally:
gpu_model_runner_v2.GPUModelRunner = native_runner_cls
# ### PATCH END: select and directly construct the AFD runner.
torch.accelerator.empty_cache()- Line 82. Save the native runner class.
- Line 83. Point the module symbol at
AFDAttentionModelRunnerV2. - Line 85. Native
init_devicelocal-imports and builds the runner, now the AFD class. - Lines 86-87. Restore the native class in a
finally, so the patch is scoped to the window.
Before the swap, the worker asserts the AFD stack and validates the V2 config
(afd_plugin/v1/worker/attention_worker.py:64-73).
execute_model: scope the context, then delegate#
The runner adds nothing to the forward itself. It opens the AFD context and calls super
(afd_plugin/v1/worker/attention_model_runner_v2.py:580-595).
# ### PATCH START: scope AFD metadata provider/replay and profiler step.
step_afd_gpu_profiler(self.prof)
with _use_afd_execution_context(
self,
int(scheduler_output.total_num_scheduled_tokens),
):
return super().execute_model(
scheduler_output,
intermediate_tensors,
dummy_run=dummy_run,
skip_attn_for_dummy_run=skip_attn_for_dummy_run,
is_profile=is_profile,
context_len=context_len,
valid_dummy_state_slots=valid_dummy_state_slots,
)
# ### PATCH END: scope AFD metadata provider/replay and profiler step.send_dp_metadata: publish one control payload#
The mixin builds one payload with the per-stage DP metadata and the graph flags, updates the connector state,
then sends (afd_plugin/v1/worker/attention_metadata.py:75-116).
payload = AFDControlPayload(
dp_metadata_list=dp_metadata_list,
is_graph_capturing=is_graph_capturing,
is_warmup=is_warmup,
is_graph_replaying=is_graph_replaying,
is_profile=self._afd_is_profile,
)
self.connector.control_plane.update_state_from_dp_metadata(payload)
self.connector.control_plane.send_dp_metadata_list(payload)- Lines 108-114. Build the
AFDControlPayloadwith the DP metadata and the warm-up/capture/replay flags. - Line 115.
update_state_from_dp_metadatapre-allocates the wire buffers. - Line 116.
send_dp_metadata_listsends on the designated sender ranks.
Deep dive#
Deep diveThe overrides and what each adds
| Override | Adds | Line |
|---|---|---|
__init__ | connector + profiler; control-plane check | afd_plugin/v1/worker/attention_model_runner_v2.py:369-411 |
load_model | deferred connector rendezvous | afd_plugin/v1/worker/attention_model_runner_v2.py:454-462 |
initialize_kv_cache | DP1 ubatch factory for DBO | afd_plugin/v1/worker/attention_model_runner_v2.py:473-486 |
capture_model | graph warm-up/capture control | afd_plugin/v1/worker/attention_model_runner_v2.py:509-553 |
execute_model | AFD context scope | afd_plugin/v1/worker/attention_model_runner_v2.py:569-595 |
shutdown | profiler stop + connector close | afd_plugin/v1/worker/attention_model_runner_v2.py:609-619 |
Every override calls super() for the real work. The runner's docstring states the delegation
boundary (afd_plugin/v1/worker/attention_model_runner_v2.py:358-365).
Deep diveThe shared mixin, used by GPU and NPU
AFDMetadataProviderMixin holds the connector-facing plumbing: build the sidecar, publish control,
and attach the sidecar to a context (afd_plugin/v1/worker/attention_metadata.py:21-28). The GPU V2
runner and the Ascend runners both reuse it, so the control-plane logic lives in one place. The module also has
the DP-metadata builders for DBO stages
(afd_plugin/v1/worker/attention_metadata.py:308-351).
Related concepts#
- The attention runtime: a seam over Model Runner V2 — the seam and the two publish paths.
- The control plane: DP metadata once per step — the DP-metadata path.
- v1/worker: the FFN worker and runner — the FFN side.
- model_executor/models/deepseek_v2.py: the DeepSeek wrapper — the model split the runner drives.
Check yourself#
- 1How does the worker install the AFD runner?easy
Show answer
It replaces the module
GPUModelRunnersymbol aroundsuper().init_device(), then restores it (afd_plugin/v1/worker/attention_worker.py:82-87). - 2Which class holds the control-plane publish logic, and why is it a mixin?medium
Show answer
AFDMetadataProviderMixin(afd_plugin/v1/worker/attention_metadata.py:21). It is a mixin so the GPU V2 runner and the NPU runners share one implementation (afd_plugin/v1/worker/attention_metadata.py:21-28). - 3Why does the module-symbol swap inhard
init_devicework at all?Show answer
Because native
Worker.init_devicelocal-importsGPUModelRunnerfrom the module just before it constructs the runner, so the attribute swap is seen at construction (vllm:vllm/v1/worker/gpu_worker.py:469-482,afd_plugin/v1/worker/attention_worker.py:82-87).