afd_plugin/config.py: the AFD config schema and parser
Code version: afd-plugin @ 7452d52 · 2026-10-27 · vLLM @ ced6857
In one sentence
This module defines AFDConfig and the functions that parse and validate additional_config["afd"], with no vLLM or CUDA import.
Purpose#
The module turns a raw mapping into a validated AFDConfig. It is CPU-safe, so local
tests parse a config without vLLM.
| Files | afd_plugin/config.py (370 lines), config_utils.py
(75 lines), afd_plugin/envs.py (27 lines). |
|---|---|
| Main class | AFDConfig, a frozen dataclass
(afd_plugin/config.py:41-73). |
| Runs in | Any process (CPU), during config build and worker init. No vLLM import at module load. |
| Called by | The config patch (afd_plugin/compat/patches/config_validation.py:23),
the validation module (afd_plugin/validation.py:12-17), and the connector factory. |
| Calls | The coercers in config_utils.py
(afd_plugin/config.py:13-15). |
| Not in this file | Per-connector option parsing (in each connector), worker
selection (in validation.py), and the vLLM config field itself. |
Where it sits#
Who calls it#
Every part that needs the AFD config parses it here. The config patch parses it to decide the
worker and the NPU path (afd_plugin/compat/patches/config_validation.py:160-172). The
runtime validators parse it with an expected role (afd_plugin/validation.py:93).
What it calls#
It calls the three coercers in config_utils.py for the typed fields
(afd_plugin/config.py:147-174). It imports validate_p2p_topology lazily, only
for the P2P connector (afd_plugin/config.py:335-338).
Key classes and functions#
| Name | Line | Role |
|---|---|---|
AFDConfig | afd_plugin/config.py:41 | The frozen config dataclass with the aliases as properties. |
_ALIASES | afd_plugin/config.py:32-38 | Old afd_* names mapped to the short field names. |
_normalize_mapping | afd_plugin/config.py:120 | Rename aliases, split extra config, coerce types. |
afd_config_from_mapping | afd_plugin/config.py:179 | Build and optionally validate an AFDConfig. |
parse_optional_afd_config | afd_plugin/config.py:244 | Parse when the key is present, else None. |
is_afd_active | afd_plugin/config.py:289 | True only when a present config validates. |
is_afd_async_dp | afd_plugin/config.py:295 | Select the async-DP mode from the flag and the connector. |
validate_afd_config | afd_plugin/config.py:311 | Check roles, connector, host, port and rank counts. |
coerce_extra_int | afd_plugin/config_utils.py:35 | Parse an int; reject a bool or a float. |
force_balanced_topk_ids_enabled | afd_plugin/envs.py:14 | Read a diagnostic env flag for routing. |
Walk through the main methods#
AFDConfig and its aliases#
The dataclass is frozen, so a config cannot change after it is built. Each old
afd_* name is a read-only property that returns the short field
(afd_plugin/config.py:75-89).
@property
def afd_connector(self) -> str:
return self.connector
@property
def afd_role(self) -> AFDRole:
return self.role
@property
def afd_port(self) -> int:
return self.port
@property
def afd_host(self) -> str:
return self.host_normalize_mapping#
This function does the rename, the split and the coercion in one pass.
for key, value in raw.items():
normalized_key = _ALIASES.get(key, key)
if normalized_key == "connector_extra_config":
if not isinstance(value, Mapping):
raise TypeError(f"{key} must be a mapping")
connector_extra_config = dict(value)
continue
if normalized_key not in valid_fields:
raise ValueError(
"unknown AFD config field "
f"{key!r}; put connector-specific values under "
"'connector_extra_config'",
)
if normalized_key in normalized:
raise ValueError(
f"duplicate AFD config field for {normalized_key!r}: "
f"both alias and canonical key were provided",
)
normalized[normalized_key] = value- Line 128. It maps the key through
_ALIASES, so an alias becomes its short name. - Lines 129-133. It pulls
connector_extra_configaside and checks it is a mapping. - Lines 134-139. An unknown field raises, with a message that points to
connector_extra_config. - Lines 140-144. An alias and its canonical key together raise a duplicate error.
After the loop, it coerces the boolean and integer fields
(afd_plugin/config.py:147-174).
validate_afd_config#
The validator runs on the built config. It raises a clear error for each broken rule
(afd_plugin/config.py:311-350). For the P2P connector it also checks the topology
(afd_plugin/config.py:335-338).
config_utils.coerce_extra_int#
The integer coercer rejects a bool or a float before it calls int(), so a wrong
type cannot be truncated silently (afd_plugin/config_utils.py:35-45).
def coerce_extra_int(value: object, *, field_name: str) -> int:
"""Parse an integer option without truncating floats."""
if isinstance(value, bool | float):
raise TypeError(f"{field_name} must be an integer, got {value!r}")
try:
return int(value)
except (TypeError, ValueError, OverflowError) as exc:
raise TypeError(
f"{field_name} must be an integer, got {value!r}",
) from excDeep dive#
Deep diveThe parser entry points, and when each is used
Several functions wrap _normalize_mapping for different needs.
| Function | Returns | Used for |
|---|---|---|
parse_afd_config | An AFDConfig; raises if the key is
missing (afd_plugin/config.py:262-279). | Code that needs AFD to be on. |
parse_optional_afd_config | AFDConfig or None
(afd_plugin/config.py:244-259). | Code that checks whether AFD is on. |
is_afd_active | bool
(afd_plugin/config.py:289-292). | A quick on/off test with validation. |
is_afd_async_dp | bool
(afd_plugin/config.py:295-308). | Import-time async-DP patch selection (parses without validation). |
connector_extra_config_from_source | dict
(afd_plugin/config.py:282-286). | Get only the connector options. |
compute_hash builds a stable hash of the graph-affecting settings, for cache keys
(afd_plugin/config.py:99-114).
Deep diveEnvironment flags in envs.py
envs.py holds two diagnostic flags. Each reads one variable and returns a bool.
| Variable | Read at | Effect |
|---|---|---|
AFD_FORCE_BALANCED_TOPK_IDS | afd_plugin/envs.py:14-15 |
Force balanced top-k ids for the routing simulator. |
AFD_ASYNC_CAM_LAYERED_GMM | afd_plugin/envs.py:18-19 |
Select a layered GMM path in the async CAM connector (NPU). |
A value is true when it is 1, true, yes or on,
in any case (afd_plugin/envs.py:11).
Related concepts#
- AFD configuration: additional_config["afd"]
- Worker selection: choosing the role worker
- AFD config reference: the additional_config["afd"] keys
- afd_plugin/validation.py: runtime wiring checks
Check yourself#
- 1Why iseasy
AFDConfiga frozen dataclass?Show answer
So a config cannot change after it is built (
afd_plugin/config.py:41). The old names are read-only properties (afd_plugin/config.py:75-89). - 2Which function parses the config without validating it, and why?medium
Show answer
is_afd_async_dp. It is a lightweight selector for import-time async-DP patches, so it parses withvalidate=False(afd_plugin/config.py:295-308). - 3What doesmedium
_normalize_mappingdo with a key it does not know?Show answer
It raises
ValueErrorand points toconnector_extra_configfor connector-specific values (afd_plugin/config.py:134-139). - 4A caller passeshard
"num_ffn_ranks": True. Where and why does this fail?Show answer
In
coerce_extra_int: it rejects a bool before callingint(), so the value does not become 1 (afd_plugin/config_utils.py:38-39). The integer fields use this coercer (afd_plugin/config.py:153-162).