The vLLM v1 map
Code version: afd-plugin @ 7452d52 · 2026-10-27 · vLLM @ ced6857
In one sentence
AFD hooks into one chain of vLLM v1 parts: API server, EngineCore, executor, worker, Model Runner V2 and the model.
The idea#
vLLM v1 runs a request through a fixed chain of parts. The API server process builds the config and owns HTTP. The EngineCore process holds the scheduler and the executor. The executor starts the workers. Each worker owns one device and builds a model runner, which runs the model.
afd-plugin does not replace this chain. It changes six small points along it: the config, the EngineCore start, the worker class, the model runner class and the model class. This page names each box once, gives its vLLM line, and points to the AFD hook.
An everyday comparison. Think of a factory line with six stations. AFD does not rebuild the line; it changes the tool at a few stations. The comparison stops here: one station (the FFN EngineCore) stops the line early and runs its own loop.
The picture#
Read the boxes top to bottom. Each box runs in a process or is built inside one. The violet row on each box is the one place AFD changes it. The afd-plugin loads once in each process, so the hooks take effect everywhere they are needed.
In the code#
The table names each box, its vLLM line at commit ced6857, and the AFD hook. The vLLM lines are in the pinned vLLM checkout; the hook lines are in afd-plugin.
| Box | What it does (vLLM) | AFD hook |
|---|---|---|
API server processAsyncLLM |
The engine client in the API server process; it drives the EngineCore
(vllm:vllm/v1/engine/async_llm.py:78). |
The plugin loads here, while the CLI parser is built, and installs the config patch
(vllm:vllm/engine/arg_utils.py:3009). See The plugin entry point: register_afd. |
| EngineCore process | Builds the model_executor and the scheduler
(vllm:vllm/v1/engine/core.py:134, vllm:vllm/v1/engine/core.py:162). |
The FFN EngineCore returns after the executor, with no real scheduler (a no-op stand-in) or KV cache
(afd_plugin/compat/patches/engine_core.py:59-68). See Attention and FFN roles: what each server owns. |
| Executor | mp starts worker processes; uni runs one worker
in-process (vllm:vllm/v1/executor/abstract.py:70-77). |
No AFD change. AFD works through the worker the executor builds. |
| Worker (per device) | Imports parallel_config.worker_cls by its qualified name
(vllm:vllm/v1/worker/worker_base.py:273-277). |
worker_cls was rewritten to the AFD role worker by the config patch
(afd_plugin/compat/patches/config_validation.py:92-93). See
Worker selection: choosing the role worker. |
| Model Runner V2 | Built when the V2 model runner is on; vLLM reads VLLM_USE_V2_MODEL_RUNNER
(vllm:vllm/config/vllm.py:692-694, vllm:vllm/v1/worker/gpu_worker.py:469-482). |
The AFD worker swaps the runner class in init_device
(afd_plugin/v1/worker/attention_worker.py:82-87). See The attention runtime: a seam over Model Runner V2. |
| The model | ModelRegistry resolves the architecture name to a class
(vllm:vllm/model_executor/models/registry.py:1126-1141). |
AFD registers an AFD<arch> model wrapper
and swaps the model identity (afd_plugin/__init__.py:211-214). See
Model registration: the AFD<arch> wrappers. |
The executor chooses between separate worker processes and one in-process worker.
elif distributed_executor_backend == "mp":
from vllm.v1.executor.multiproc_executor import MultiprocExecutor
executor_class = MultiprocExecutor
elif distributed_executor_backend == "uni":
from vllm.v1.executor.uniproc_executor import UniProcExecutor
executor_class = UniProcExecutorThe worker then imports the class named in worker_cls. AFD has already set
this field to its role worker, so vLLM builds the AFD worker here.
parallel_config = vllm_config.parallel_config
if isinstance(parallel_config.worker_cls, str):
worker_class: type[WorkerBase] = resolve_obj_by_qualname(
parallel_config.worker_cls
)Deep dive#
Deep diveHow many processes, and where each hook runs
The number of processes depends on the executor
(vllm:vllm/v1/executor/abstract.py:70-77).
- Multi-process executor (
mp). Each worker is a separate process. The plugin loads again in each one, so the worker and runner hooks run in the worker process (vllm:vllm/v1/worker/worker_base.py:271). - Single-device executor (
uni). The worker runs inside the EngineCore process (vllm:vllm/v1/executor/abstract.py:74-77). The worker's plugin load hits the guard, so it does not reload the plugins.
The config hook is different from the others. It runs in the API server process, while
create_engine_config builds the config
(afd_plugin/compat/patches/config_validation.py:51-113). vLLM then pickles the
config and sends it to the EngineCore and worker processes, so those processes do not rebuild
it. The worker rewrite must therefore happen in the API server process, before the config is
sent.
Deep diveThe FFN EngineCore is the one box that breaks the chain
Five of the six boxes keep their normal vLLM job and only change a class or a field. The
FFN EngineCore is different. After it builds the executor, the AFD patch returns before the
scheduler and the KV cache are set up (afd_plugin/compat/patches/engine_core.py:56-68).
The FFN side then runs a connector loop instead of vLLM's request loop
(afd_plugin/compat/patches/engine_core.py:411-419). The attention EngineCore keeps the
normal chain. The Attention and FFN roles: what each server owns page covers this split.
In afd-plugin#
Each hook has its own page. Start here:
- The config and the worker rewrite: AFD configuration: additional_config["afd"] and Worker selection: choosing the role worker.
- The model wrappers: Model registration: the AFD<arch> wrappers.
- The two roles and the FFN EngineCore: Attention and FFN roles: what each server owns.
- The module that applies the hooks at load time: afd_plugin/__init__.py: the plugin entry module.
Check yourself#
- 1Which vLLM box holds the scheduler and the executor?easy
Show answer
The EngineCore process. It builds both (
vllm:vllm/v1/engine/core.py:134,vllm:vllm/v1/engine/core.py:162). - 2When is the Model Runner V2 used?easy
Show answer
When the V2 model runner is on; vLLM reads it from
VLLM_USE_V2_MODEL_RUNNER(vllm:vllm/config/vllm.py:692-694,vllm:vllm/v1/worker/gpu_worker.py:469-482). AFD needs it on both roles. - 3Why must the worker-class rewrite happen in the API server process?medium
Show answer
vLLM builds the config there, then pickles it to the other processes. The rewrite runs inside
create_engine_configbefore the config is sent (afd_plugin/compat/patches/config_validation.py:51-113). - 4Which box does AFD not change?medium
Show answer
The executor. AFD works through the worker the executor builds; it adds no executor patch (
vllm:vllm/v1/executor/abstract.py:70-77). - 5The FFN EngineCore and the attention EngineCore are the same vLLM class. What makes them behave differently?hard
Show answer
The AFD EngineCore patch checks the role. For the FFN role it returns after building the executor and skips the scheduler and KV cache (
afd_plugin/compat/patches/engine_core.py:56-68); the FFN side then runs a connector loop (afd_plugin/compat/patches/engine_core.py:411-419). The attention role keeps the normal chain.