plugins/__init__.py: the plugin loaders
Code version: vLLM @ 3e44c0d · 2026-10-22
Purpose#
vllm/plugins/__init__.py finds the plugins that other Python packages publish as entry points, filters them with VLLM_PLUGINS, and imports them for the code that asks.
| File | vllm/plugins/__init__.py, 159 lines of Python. Five group-name constants, one flag and three functions. |
|---|---|
| Main functions | load_plugins_by_group (vllm/plugins/__init__.py:36), load_general_plugins (vllm/plugins/__init__.py:77), load_endpoint_plugins (vllm/plugins/__init__.py:93). There is no class. |
| Runs in | Every process of vllm serve: the API server (the front end), the EngineCore process, each worker process, and the model registry subprocess (only when it starts). With one GPU, the worker runs inside the engine core process. The code runs on the CPU, in the thread of the caller. |
| Called by | Nine call sites in vllm/: the engine arguments, EngineCore, the worker wrapper, the model registry, the platform detection, the stat loggers, the IO processor loader and the endpoint plugin wiring (see below). |
| Calls | importlib.metadata.entry_points, envs.VLLM_PLUGINS, EntryPoint.load(), and the loaded plugin functions or factories. |
| Not in this file | The platform detection rules (vllm/platforms/__init__.py), the stat logger type check (vllm/v1/metrics/loggers.py), the IO processor choice (vllm/plugins/io_processors/__init__.py), the route wiring of endpoint plugins (vllm/plugins/endpoint_plugins/interface.py), and the group vllm.logits_processors. |
The file only finds, filters and imports. Each caller decides what to do with the loaded objects. The page How vLLM finds and loads plugins explains the same code from the view of the processes. This page reads the file line by line.
Where it sits#
Read panel 1 row by row. Green is the path of the general plugins, blue is the shared loader, and pink is the path of the endpoint plugins. Grey boxes are code outside the plugin loaders. Panel 2 is a short form of the walk-through below.
Who calls it#
load_general_plugins() has five callers in vllm/:
AsyncEngineArgs.add_cli_argsin the front end, before the CLI parser gets its flags. The comment says that a plugin can add a choice to--quantizationor--device(vllm/engine/arg_utils.py:3240-3243).EngineArgs.__post_init__, again in the front end, and also for the offlineLLMclass, which builds no CLI parser (vllm/engine/arg_utils.py:921-924). The same file also imports the function at module level (vllm/engine/arg_utils.py:123).EngineCore.__init__in the engine core process, before it makes the executor (vllm/v1/engine/core.py:129-132,vllm/v1/engine/core.py:147).WorkerWrapperBase.init_workerin each worker, before it resolvesworker_cls(vllm/v1/worker/worker_base.py:282-284,vllm/v1/worker/worker_base.py:292-296)._run()of the model registry subprocess, before it runs the function that the parent process sent (vllm/model_executor/models/registry.py:1608-1616). vLLM starts this subprocess only when it cannot use the cached model information: the cache has no valid entry, or vLLM cannot find the source file of the model (vllm/model_executor/models/registry.py:1086-1109). This is true for built-in models too, because every built-in model is a lazy registry entry (vllm/model_executor/models/registry.py:1565-1573).
load_plugins_by_group() has three callers outside this file. Each one imports the group constant from this file:
- Platform detection.
resolve_current_platform_cls_qualname()loadsvllm.platform_plugins(vllm/platforms/__init__.py:10,vllm/platforms/__init__.py:250). It runs on the first read ofcurrent_platformin a process (vllm/platforms/__init__.py:316-319). WithVLLM_TARGET_DEVICE=cpu, the function returns the CPU platform before it loads any platform plugin (vllm/platforms/__init__.py:244-248). - Stat loggers.
load_stat_logger_plugin_factories()loadsvllm.stat_logger_plugins(vllm/v1/metrics/loggers.py:84-98).AsyncLLM.__init__calls it (vllm/v1/engine/async_llm.py:155-156). - IO processors.
get_io_processor()loadsvllm.io_processor_plugins, but only when a plugin name comes from the model config (io_processor_plugin) or from the Hugging Face config of the model (vllm/plugins/io_processors/__init__.py:42-61). The pooling serving code calls it (vllm/entrypoints/pooling/pooling/io_processor.py:51-55).
load_endpoint_plugins() has one caller: attach_endpoint_plugins() (vllm/plugins/endpoint_plugins/interface.py:106-108). build_app calls that function after the core routes (vllm/entrypoints/launchers/app.py:45-51).
Tests also call the loaders. For example, a test resets the flag plugins_loaded by hand, so that load_general_plugins() runs again (tests/v1/kv_connector/unit/test_scheduler_kv_connector_override.py:113-119).
What it calls#
importlib.metadata.entry_points(group=...), imported inside the functions (vllm/plugins/__init__.py:38,vllm/plugins/__init__.py:120).vllm.envsforVLLM_PLUGINS(vllm/plugins/__init__.py:8,vllm/plugins/__init__.py:40,vllm/plugins/__init__.py:122).EntryPoint.load(), which imports the module of the plugin (vllm/plugins/__init__.py:69).- The plugin objects: each general plugin function (
vllm/plugins/__init__.py:89-90) and each endpoint plugin factory (vllm/plugins/__init__.py:138). - The standard
loggingmodule. The file useslogging.getLogger(__name__)(vllm/plugins/__init__.py:14), not vLLM'sinit_logger, which adds helper methods to the same kind of logger (vllm/logger.py:328-335).
The subfolders of vllm/plugins/#
Three subfolders hold plugin code that vLLM itself ships. The page Plugins in the API server explains how to write such plugins.
lora_resolvers/: two general plugins that vLLM publishes in its ownpyproject.toml(pyproject.toml:45-47). Each one registers a LoRA resolver.register_filesystem_resolverregisters a resolver only whenVLLM_LORA_RESOLVER_CACHE_DIRis set (vllm/plugins/lora_resolvers/filesystem_resolver.py:49-61). The resolver accepts a folder when itsadapter_config.jsonhaspeft_typeLORAand the same base model (vllm/plugins/lora_resolvers/filesystem_resolver.py:30-45).register_hf_hub_resolverregisters a resolver for Hugging Face repositories only whenVLLM_LORA_RESOLVER_HF_REPO_LISTis set andVLLM_PLUGINSnameslora_hf_hub_resolver(vllm/plugins/lora_resolvers/hf_hub_resolver.py:131-145).OpenAIServingModelsin the API server gets the registered resolvers (vllm/entrypoints/openai/models/serving.py:113-117).io_processors/: the abstract classIOProcessor(vllm/plugins/io_processors/interface.py:18-24), with the abstract methodspre_processandpost_process(vllm/plugins/io_processors/interface.py:41-65), and the loaderget_io_processor(). An IO processor plugin changes the input and the output of a pooling model. The model config can name the plugin inio_processor_plugin(vllm/config/model.py:410).endpoint_plugins/: the protocolEndpointPlugin(vllm/plugins/endpoint_plugins/interface.py:49-94) and the two wiring phases. Phase A,attach_endpoint_plugins(), adds the routes of each endpoint plugin to the FastAPI app (vllm/plugins/endpoint_plugins/interface.py:97-146). Phase B,init_endpoint_plugins_state(), callsinit_stateof each plugin (vllm/plugins/endpoint_plugins/interface.py:149-164).init_app_stateruns Phase B (vllm/entrypoints/launchers/api_server/app_state.py:173). The render server runs it too, with no engine client (vllm/entrypoints/launchers/render/app_state.py:124).
Key classes and functions#
| Name | Line | What it does |
|---|---|---|
DEFAULT_PLUGINS_GROUP | vllm/plugins/__init__.py:18 | "vllm.general_plugins". The comment says: loaded in all processes. |
IO_PROCESSOR_PLUGINS_GROUP | vllm/plugins/__init__.py:20 | "vllm.io_processor_plugins". The comment says: loaded in "process0" only, which is the front end. |
PLATFORM_PLUGINS_GROUP | vllm/plugins/__init__.py:23 | "vllm.platform_plugins". Loaded in all processes, on the first read of current_platform. Not loaded with VLLM_TARGET_DEVICE=cpu. |
STAT_LOGGER_PLUGINS_GROUP | vllm/plugins/__init__.py:26 | "vllm.stat_logger_plugins". Loaded in the front end, with the async engine. |
ENDPOINT_PLUGINS_GROUP | vllm/plugins/__init__.py:30 | "vllm.endpoint_plugins". Loaded in the API server only. Each entry point gives a factory that returns an EndpointPlugin. |
plugins_loaded | vllm/plugins/__init__.py:33 | Module-level flag. It makes sure that one process loads the general plugins only once. |
load_plugins_by_group(group) | vllm/plugins/__init__.py:36-74 | Finds the entry points of one group, filters them by name, imports each one. Returns dict[str, Callable]. |
load_general_plugins() | vllm/plugins/__init__.py:77-90 | Loads vllm.general_plugins once per process and calls each function. |
load_endpoint_plugins(supported_tasks) | vllm/plugins/__init__.py:93-159 | Loads endpoint plugins only when VLLM_PLUGINS is set. Calls each factory and keeps the plugins whose tasks match. |
Walk through the main methods#
load_plugins_by_group: find, filter, import#
def load_plugins_by_group(group: str) -> dict[str, Callable[[], Any]]:
"""Load plugins registered under the given entry point group."""
from importlib.metadata import entry_points
allowed_plugins = envs.VLLM_PLUGINS
discovered_plugins = entry_points(group=group)
if len(discovered_plugins) == 0:
logger.debug("No plugins for group %s found.", group)
return {}
# Check if the only discovered plugin is the default one
is_default_group = group == DEFAULT_PLUGINS_GROUP
# Use INFO for non-default groups and DEBUG for the default group
log_level = logger.debug if is_default_group else logger.info
log_level("Available plugins for group %s:", group)
for plugin in discovered_plugins:
log_level("- %s -> %s", plugin.name, plugin.value)
if allowed_plugins is None:
log_level(
"All plugins in this group will be loaded. "
"Set `VLLM_PLUGINS` to control which plugins to load."
)
plugins = dict[str, Callable[[], Any]]()
for plugin in discovered_plugins:
if allowed_plugins is None or plugin.name in allowed_plugins:
if allowed_plugins is not None:
log_level("Loading plugin %s", plugin.name)
try:
func = plugin.load()
plugins[plugin.name] = func
except Exception:
logger.exception("Failed to load plugin %s", plugin.name)
return plugins- Line 38. The import of
entry_pointsis inside the function, so it runs only on a call. - Line 40.
envs.VLLM_PLUGINSisNonewhen the variable is not set, else the value split at commas (vllm/envs.py:1163-1167). - Lines 42-45.
entry_points(group=group)returns the entry points of all installed packages in the group. No entry points: a DEBUG log and an empty dict. - Lines 48-50. The log level is DEBUG for
vllm.general_pluginsand INFO for every other group. The comment on line 47 does not match the code: the check is only for the group name. - Lines 52-60. The function logs each found plugin as
name -> value. When the allowlist is not set, it also logs that it loads all plugins. - Lines 63-66. A plugin passes when there is no allowlist, or when its entry-point name is in the list. The test is an exact string match. With an allowlist, line 66 logs "Loading plugin" for each name that passes. A name that does not pass gets no such line.
- Lines 68-72.
plugin.load()imports the module and gets the attribute. An exception is logged with its traceback, and the loop continues. - Line 74. The result maps the entry-point name to the loaded object. The function does not call the objects.
load_general_plugins: once per process#
def load_general_plugins():
"""WARNING: plugins can be loaded for multiple times in different
processes. They should be designed in a way that they can be loaded
multiple times without causing issues.
"""
global plugins_loaded
if plugins_loaded:
return
plugins_loaded = True
plugins = load_plugins_by_group(group=DEFAULT_PLUGINS_GROUP)
# general plugins, we only need to execute the loaded functions
for func in plugins.values():
func()- Lines 78-80. The docstring warns that plugins can load many times, in different processes. So a plugin function must be safe to run again.
- Lines 82-85. The guard. The first call sets the module global; each later call in the same process returns at once.
- Line 87. The shared loader gets the functions of
vllm.general_plugins, filtered byVLLM_PLUGINS. - Lines 89-90. Each function runs with no arguments and no
tryblock. An exception goes up to the caller, for exampleEngineCore.__init__.
load_endpoint_plugins: opt-in only#
The docstring gives the reason for a stricter rule: endpoint plugins add HTTP routes, so a plugin loads only when VLLM_PLUGINS names it (vllm/plugins/__init__.py:98-103).
from importlib.metadata import entry_points
if envs.VLLM_PLUGINS is None:
discovered = entry_points(group=ENDPOINT_PLUGINS_GROUP)
if discovered:
logger.warning(
"Found endpoint plugin(s) %s but VLLM_PLUGINS is not set. "
"Endpoint plugins add HTTP routes and must be explicitly "
"allowlisted via VLLM_PLUGINS to be loaded.",
[p.name for p in discovered],
)
return []
factories = load_plugins_by_group(ENDPOINT_PLUGINS_GROUP)
endpoint_plugins: list[EndpointPlugin] = []
for name, factory in factories.items():
try:
plugin = factory()
except Exception:
logger.exception("Failed to instantiate endpoint plugin %s", name)
continue
required_tasks = plugin.required_tasks
if required_tasks is not None and (
supported_tasks is None or not set(required_tasks) & set(supported_tasks)
):
logger.info(
"Skipping endpoint plugin %s: requires one of tasks %s, "
"server supports %s",
name,
required_tasks,
supported_tasks,
)
continue
logger.info("Loaded endpoint plugin %s", name)
endpoint_plugins.append(plugin)
return endpoint_plugins- Lines 122-131.
VLLM_PLUGINSnot set: the function still lists the group. If it finds plugins, it logs a WARNING with their names. Then it returns an empty list. So an endpoint plugin never loads by accident. - Line 133.
VLLM_PLUGINSset: the shared loader imports the named entry points. An empty string gives[""], which matches no name (vllm/plugins/__init__.py:105-109). - Lines 136-141. Each factory runs with no arguments. If it raises, the function logs the error and skips the plugin.
- Lines 143-154. The task gate.
required_tasks = Nonemeans "always eligible". Otherwise, the plugin needs at least one task in common withsupported_tasks. If the caller givessupported_tasks = None, every plugin with tasks is skipped. A skip writes an INFO log. - Lines 156-159. The function logs each loaded plugin and returns them in the order of discovery.
Deep dive#
The sections below give the full detail. Open them one at a time, or use Open all deep dives at the top of the page.
Deep diveData flow: types in and out
| Value | Type | Where |
|---|---|---|
group | str, one of the five constants | argument of load_plugins_by_group (vllm/plugins/__init__.py:36) |
envs.VLLM_PLUGINS | list[str] | None | declared at vllm/envs.py:116, parsed at vllm/envs.py:1163-1167 |
discovered_plugins | importlib.metadata.EntryPoints; each item has .name, .value and .load() | vllm/plugins/__init__.py:42 |
return of load_plugins_by_group | dict[str, Callable[[], Any]]: entry-point name to the loaded object | vllm/plugins/__init__.py:62, vllm/plugins/__init__.py:74 |
supported_tasks | tuple[SupportedTask, ...] | None; a task is for example "generate" | vllm/plugins/__init__.py:94, vllm/tasks.py:7, vllm/tasks.py:43 |
return of load_endpoint_plugins | list[EndpointPlugin], in discovery order | vllm/plugins/__init__.py:95, vllm/plugins/__init__.py:159 |
The "callable" in the type hint of line 36 is only true for some groups. The table shows what the object behind each entry point must be, from the code of each caller:
| Group | The loaded object | What the caller does with it |
|---|---|---|
vllm.general_plugins | a function with no arguments | calls it (vllm/plugins/__init__.py:89-90) |
vllm.platform_plugins | a function that returns a class name or None | calls it to detect the platform (vllm/platforms/__init__.py:254-259) |
vllm.stat_logger_plugins | a subclass of StatLoggerBase (a class, not a function) | checks the type and keeps the class (vllm/v1/metrics/loggers.py:87-96) |
vllm.io_processor_plugins | a function that returns a class name or None | calls it, then resolves the requested class (vllm/plugins/io_processors/__init__.py:63-88) |
vllm.endpoint_plugins | a factory with no arguments (a class or a function) | calls it to get an EndpointPlugin (vllm/plugins/__init__.py:138, vllm/plugins/endpoint_plugins/interface.py:53-56) |
Deep diveProcesses and threads
The file has no thread and no lock. Each function runs in the thread of its caller. plugins_loaded is a global of the module, so each Python process has its own copy (vllm/plugins/__init__.py:33).
| Process | Functions that run there | Call site |
|---|---|---|
| API server (front end) | load_general_plugins (the second call returns at once), platform loading, stat loggers, endpoint plugins, IO processors (pooling only) | vllm/engine/arg_utils.py:3243, vllm/engine/arg_utils.py:924, vllm/platforms/__init__.py:250, vllm/v1/engine/async_llm.py:156, vllm/entrypoints/launchers/app.py:51, vllm/entrypoints/pooling/pooling/io_processor.py:51 |
| Engine core process | load_general_plugins, platform loading | vllm/v1/engine/core.py:132, vllm/platforms/__init__.py:250 |
| Worker process | load_general_plugins, platform loading | vllm/v1/worker/worker_base.py:284, vllm/platforms/__init__.py:250 |
| Model registry subprocess | load_general_plugins, platform loading (through the module-level import of vllm.config) | vllm/model_executor/models/registry.py:1612, vllm/model_executor/models/registry.py:26 |
With one GPU, the executor is uni (vllm/config/parallel.py:1099-1100), and init_worker runs inside the engine core process (vllm/v1/executor/uniproc_executor.py:68). There, the flag is already True, so line 284 of worker_base.py does nothing. How spawn and fork change the flag in a child process is on How vLLM finds and loads plugins.
The platform comes first. In the processes of vllm serve, the platform plugins load before the general plugins. A module-level from vllm.platforms import current_platform calls the module __getattr__ and resolves the platform at import time (vllm/platforms/__init__.py:303-319). The package vllm.config has such an import: it imports vllm.config.compilation (vllm/config/__init__.py:7), which has the import (vllm/config/compilation.py:22). Each process imports vllm.config or vllm.platforms before it calls load_general_plugins():
- The front end:
arg_utils.pyimportscurrent_platformat module level (vllm/engine/arg_utils.py:122). - The engine core process: the package
vllm.v1.engineimportsvllm.config.kv_events(vllm/v1/engine/__init__.py:14). Python runs this package file beforecore.py, which also importsvllm.config(vllm/v1/engine/core.py:24). - A worker process: the package
vllm.v1.executorimportsabstract.py, which importsvllm.config(vllm/v1/executor/__init__.py:3,vllm/v1/executor/abstract.py:11).multiproc_executor.pyalso importscurrent_platform(vllm/v1/executor/multiproc_executor.py:46).
So a general plugin cannot change which platform vLLM detects. (Derived from the imports above.)
envs.VLLM_PLUGINS is read again on each access until enable_envs_cache() runs. After that, the value is cached (vllm/envs.py:2291-2298, vllm/envs.py:2308-2326). Only the engine core and the multiproc workers call enable_envs_cache() (vllm/v1/engine/core.py:243, vllm/v1/executor/multiproc_executor.py:710). The front end never calls it. The docstring says that the cache starts "after service initialization" (vllm/envs.py:2313-2315). So change VLLM_PLUGINS only before you start vLLM.
Deep diveA worked example: three plugins and two settings of VLLM_PLUGINS
Assume this machine has vLLM and two more packages. Package acme_hw publishes acme = acme_hw:detect in vllm.platform_plugins. Package acme_routes publishes acme_admin = acme_routes:make in vllm.endpoint_plugins, with required_tasks = None. vLLM publishes lora_filesystem_resolver and lora_hf_hub_resolver in vllm.general_plugins (pyproject.toml:45-47). The names acme... are examples, not real packages.
Case 1: VLLM_PLUGINS is not set.
- General plugins: both vLLM functions run (line 64 passes every name). The filesystem resolver registers only if
VLLM_LORA_RESOLVER_CACHE_DIRis set (vllm/plugins/lora_resolvers/filesystem_resolver.py:51-59). The Hugging Face resolver checks the allowlist itself:VLLM_PLUGINSisNone, so it does not register. IfVLLM_LORA_RESOLVER_HF_REPO_LISTis set, it logs a warning (vllm/plugins/lora_resolvers/hf_hub_resolver.py:131-142). - Platform plugins:
acmeloads, and the platform code callsdetect()(vllm/platforms/__init__.py:254-259). - Endpoint plugins: none load. The log shows a WARNING with
['acme_admin'](vllm/plugins/__init__.py:122-131).
Case 2: VLLM_PLUGINS=acme,acme_admin. The parsed value is ["acme", "acme_admin"].
- General plugins: line 64 rejects both vLLM names, so
load_plugins_by_groupreturns{}, and no LoRA resolver registers. No error and no warning appears. The only log lines for this group are at DEBUG level (lines 50-54), and the default log level is INFO (vllm/envs.py:860). - Platform plugins:
acmeloads, as before. - Endpoint plugins:
acme_adminloads,make()runs, and withrequired_tasks = Nonethe plugin is kept. The log shows "Loaded endpoint plugin acme_admin" (vllm/plugins/__init__.py:156).
To keep the filesystem LoRA resolver in case 2, add its name: VLLM_PLUGINS=acme,acme_admin,lora_filesystem_resolver. (Derived from lines 63-64 and vllm/envs.py:1166. The code does not strip spaces, so do not put a space after a comma.)
Deep diveError paths: what raises, what only logs
| Situation | Result | Where |
|---|---|---|
| No entry points in the group | DEBUG log, empty result | vllm/plugins/__init__.py:43-45 |
The import of a plugin fails (load() raises) | ERROR log with traceback ("Failed to load plugin ..."); the plugin is skipped | vllm/plugins/__init__.py:68-72 |
| A general plugin function raises | not caught here; the caller gets the exception, and start-up of that process stops. The engine core process logs "EngineCore failed to start." and raises again (vllm/v1/engine/core.py:1570-1576). A worker process logs "WorkerProc failed to start." (vllm/v1/executor/multiproc_executor.py:950-957). | vllm/plugins/__init__.py:89-90 |
Endpoint plugins exist, VLLM_PLUGINS not set | WARNING log; no plugin loads | vllm/plugins/__init__.py:122-131 |
| An endpoint factory raises | ERROR log with traceback; the plugin is skipped | vllm/plugins/__init__.py:137-141 |
| Endpoint tasks do not match | INFO log; the plugin is skipped | vllm/plugins/__init__.py:143-154 |
A stat logger plugin is not a StatLoggerBase subclass | TypeError (in the caller) | vllm/v1/metrics/loggers.py:88-94 |
| Two out-of-tree platform plugins are active | RuntimeError (in the caller) | vllm/platforms/__init__.py:272-276 |
| The model needs an IO processor that is not installed | ValueError (in the caller) | vllm/plugins/io_processors/__init__.py:73-84 |
VLLM_LORA_RESOLVER_CACHE_DIR is set, but it is not a folder | ValueError from the general plugin, so start-up stops | vllm/plugins/lora_resolvers/filesystem_resolver.py:52-57 |
Deep diveEnvironment variables
| Variable | Default | Read at | Effect |
|---|---|---|---|
VLLM_PLUGINS | not set, which gives None (vllm/envs.py:1163-1167) | vllm/plugins/__init__.py:40, vllm/plugins/__init__.py:122 | One comma-separated allowlist for every group of this file. It does not filter vllm.logits_processors, which does not use this file. Not set: load all, except endpoint plugins. "": load none. The comment above the definition gives the first and the last rule, but not the endpoint exception (vllm/envs.py:1160-1162). |
VLLM_LORA_RESOLVER_CACHE_DIR | None (vllm/envs.py:1171-1172) | vllm/plugins/lora_resolvers/filesystem_resolver.py:51 | Folder in which the filesystem resolver looks for LoRA adapters. |
VLLM_LORA_RESOLVER_HF_REPO_LIST | None (vllm/envs.py:1178-1179) | vllm/plugins/lora_resolvers/hf_hub_resolver.py:131 | Comma-separated Hugging Face repositories for the Hub resolver. Works only when VLLM_PLUGINS names lora_hf_hub_resolver. |
The tests show the edge cases of VLLM_PLUGINS for endpoint plugins: not set gives no plugin, and an empty string gives no plugin (tests/plugins_tests/test_endpoint_plugins.py:101-115).
Related concepts#
- How vLLM finds and loads plugins: the same code from the view of the processes, with fork and spawn.
- All the ways to extend vLLM: all extension points, with and without entry points.
- Platform plugins and current_platform: what the platform code does with the loaded detectors.
- Plugins in the API server: endpoint plugins, IO processors, stat loggers and LoRA resolvers.
- Serving LoRA adapters: from the model name to the wrapped layer: how the API server uses the LoRA resolvers.
- Python entry points: the Python packaging basics under all of this.
- Tour: Life of a plugin and Tour: Build a toy plugin: the same code as guided tours.
Check yourself#
- 1Which function in this file calls the plugin functions, and which function only imports them?easy
Show answer
load_general_plugins()calls each function (vllm/plugins/__init__.py:89-90).load_plugins_by_group()only imports the objects and returns a dict (vllm/plugins/__init__.py:68-74). The caller decides what to do with them. - 2You install an endpoint plugin and do not seteasy
VLLM_PLUGINS. Does the route appear?Show answer
No.
load_endpoint_plugins()returns an empty list and logs a warning with the plugin names (vllm/plugins/__init__.py:122-131). Endpoint plugins load only whenVLLM_PLUGINSnames them. - 3A general plugin function raisesmedium
KeyError. A test catches the exception and callsload_general_plugins()again in the same process. Do the other general plugins run?Show answer
No. Line 85 sets
plugins_loaded = Truebefore the loop, so the second call returns at once (vllm/plugins/__init__.py:83-85). The functions after the failed one never run in that process. To load again, code must reset the flag by hand, as a test does (tests/v1/kv_connector/unit/test_scheduler_kv_connector_override.py:113-119). - 4An endpoint plugin hasmedium
required_tasks = ("embed",). The server supports only("generate",). What happens, and at which log level?Show answer
The factory runs, but the sets
{"embed"}and{"generate"}have nothing in common. So the function skips the plugin with an INFO log (vllm/plugins/__init__.py:143-154). A test checks the same rule with the two tasks swapped (tests/plugins_tests/test_endpoint_plugins.py:129-140). - 5You sethard
VLLM_PLUGINS=acme,acme_adminto enable your platform and endpoint plugins, andVLLM_LORA_RESOLVER_CACHE_DIR=/loras. Requests for an unknown adapter in/lorasnow fail. Why, and what is the fix?Show answer
VLLM_PLUGINSis one allowlist for every group that goes throughload_plugins_by_group(), the general plugins too. The namelora_filesystem_resolveris not in it, so line 64 skips vLLM's own general plugin, andregister_filesystem_resolvernever runs (vllm/plugins/__init__.py:63-64,pyproject.toml:46). No resolver registers, and nothing logs an error. Add the name:VLLM_PLUGINS=acme,acme_admin,lora_filesystem_resolver. This is the worked example in the deep dive.