vLLM Explained

Python entry points

BeginnerLearn the basicsabout 5 min read + 4 min deep dives

Code version: vLLM @ 3e44c0d · 2026-10-22

In one sentence

An entry point is a named pointer to a Python object in the metadata of an installed package; other code finds and loads it.

The idea#

A name, a group and a target#

When you install a Python package, the installer writes a small metadata folder next to the code. One file in that folder can list entry points. Each entry point has three parts:

  • a group, for example vllm.general_plugins or console_scripts (an entry-point group);
  • a name inside the group, for example lora_filesystem_resolver;
  • a value of the form module.path:attribute, the object to load.

Any program can ask: "Which entry points of group X are installed?" It gets the names and values without an import of the packages. Only when it calls load() on one entry point does Python import that module.

Two uses in vLLM#

  1. The vllm command. A console script is an entry point in the group console_scripts. The installer makes a small program called vllm that calls vllm.entrypoints.cli.main:main (pyproject.toml:42-43).
  2. Plugins. vLLM reads its own groups. A package that you install next to vLLM adds an entry point to such a group, and vLLM calls it at start-up. vLLM itself uses this for its two built-in LoRA resolvers (pyproject.toml:45-47).

So the plugin system of vLLM needs no configuration file. "Install the package" is enough. The page How vLLM finds and loads plugins explains when each process loads each group.

An everyday comparison#

Think of the directory board in the hall of an office building. Each company writes its name and its room number on the board when it moves in. A visitor reads the board and then goes to the room. The visitor does not open every door to find the company.

The comparison stops here: a visitor can only read the board. load() also "opens the door": it imports the module, and the import runs the module's top-level code.

The picture#

Figure: an entry point goes from the source declaration into installed metadata (panel 1). At run time importlib.metadata finds it, and load() imports the target (panel 2).
Open full size ↗

Read panel 1 at install time and panel 2 at run time. The green box is the only link between them: a text file in the installed metadata. That is why a plugin works without a change to vLLM: the installer writes the file, and vLLM reads it.

In vLLM#

This is a basics page, so this part gives only pointers. The concept page How vLLM finds and loads plugins and the module page plugins/__init__.py: the plugin loaders explain the loading code in full.

  1. vLLM's own entry points. The pyproject.toml of vLLM declares the console script and two general plugins:
    [project.scripts]
    vllm = "vllm.entrypoints.cli.main:main"
    
    [project.entry-points."vllm.general_plugins"]
    lora_filesystem_resolver = "vllm.plugins.lora_resolvers.filesystem_resolver:register_filesystem_resolver"
    lora_hf_hub_resolver = "vllm.plugins.lora_resolvers.hf_hub_resolver:register_hf_hub_resolver"
  2. A plugin package in the tests. The test package vllm_add_dummy_model declares its entry point with setup.py (tests/plugins/vllm_add_dummy_model/setup.py:6-13). Its function registers extra model classes (tests/plugins/vllm_add_dummy_model/vllm_add_dummy_model/__init__.py:7-12).
  3. The loader. load_plugins_by_group() asks importlib.metadata.entry_points(group=...), filters by name, and calls load() on each entry point (vllm/plugins/__init__.py:36-74).

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 dive

Declare an entry point: pyproject.toml or setup.py

pyproject.toml. The [project] table of the packaging standard has two places for entry points:

  • [project.scripts]: each key becomes a console script (pyproject.toml:42-43). The installer writes it into the group console_scripts.
  • [project.entry-points."<group>"]: any other group. The group name has dots, so it needs quotes (pyproject.toml:45-47).

setup.py. Older packages pass a dictionary to setup(): group name → list of "name = module:attr" strings. One package can fill several groups. The dummy platform plugin of the tests fills two groups at once (tests/plugins/vllm_add_dummy_platform/setup.py:6-18):

setup(
    name="vllm_add_dummy_platform",
    version="0.1",
    packages=["vllm_add_dummy_platform"],
    entry_points={
        "vllm.platform_plugins": [
            "dummy_platform_plugin = vllm_add_dummy_platform:dummy_platform_plugin"  # noqa
        ],
        "vllm.general_plugins": [
            "dummy_custom_ops = vllm_add_dummy_platform:register_ops"
        ],
    },
)

What the installer writes. In the installed <name>-<version>.dist-info/ folder, the file entry_points.txt has one INI section per group. For vLLM it looks like this (the file is not in the repo; it comes from the build):

[console_scripts]
vllm = vllm.entrypoints.cli.main:main

[vllm.general_plugins]
lora_filesystem_resolver = vllm.plugins.lora_resolvers.filesystem_resolver:register_filesystem_resolver
lora_hf_hub_resolver = vllm.plugins.lora_resolvers.hf_hub_resolver:register_hf_hub_resolver

An editable install (pip install -e .) also writes this file. So when you add or rename an entry point, install the package again. A change to the code of the target needs no new install.

Deep dive

Read entry points with importlib.metadata: a tiny example

vLLM needs Python 3.11 or newer (pyproject.toml:34). From Python 3.10, importlib.metadata.entry_points(group=...) returns only the entry points of one group. Each item is an EntryPoint with these attributes:

AttributeExample
ep.group"vllm.general_plugins"
ep.name"lora_filesystem_resolver"
ep.value"vllm.plugins.lora_resolvers.filesystem_resolver:register_filesystem_resolver"
ep.module, ep.attrthe parts before and after the colon
ep.load()imports ep.module, then returns the attribute ep.attr of it

A tiny plugin package (example files, not in the repo). Two files are enough:

# my_vllm_plugin/pyproject.toml
[project]
name = "my-vllm-plugin"
version = "0.1"

[project.entry-points."vllm.general_plugins"]
hello = "my_vllm_plugin:register"
# my_vllm_plugin/my_vllm_plugin/__init__.py
def register():
    print("hello from my_vllm_plugin")

Install it with pip install -e my_vllm_plugin. Then read the group from any Python process:

from importlib.metadata import entry_points

eps = entry_points(group="vllm.general_plugins")
for ep in eps:
    print(ep.name, "->", ep.value)

ep = next(ep for ep in eps if ep.name == "hello")
func = ep.load()   # imports my_vllm_plugin now
func()             # prints "hello from my_vllm_plugin"

Install no other plugin of this group. Then the loop prints three lines: hello and the two LoRA resolvers of vLLM. The order of the lines depends on the order of the packages on sys.path; do not depend on it.

Deep dive

How vLLM reads a group, line by line

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
  1. Line 40. VLLM_PLUGINS is a list of names, or None when the variable is not set (vllm/envs.py:1163-1167).
  2. Line 42. entry_points(group=group) reads the metadata of all installed packages. Nothing is imported yet.
  3. Lines 43-45. An empty group returns an empty dict.
  4. Lines 52-54. The loader logs each ep.name and ep.value: at DEBUG for vllm.general_plugins, at INFO for other groups.
  5. Line 64. The filter compares ep.name, not the package name, with VLLM_PLUGINS.
  6. Lines 68-72. plugin.load() imports the module. An exception is logged with logger.exception, and the loop goes on with the next plugin.
  7. Line 74. The result maps each name to the loaded object. The caller decides what to do with it. For general plugins, load_general_plugins() calls each function once per process (vllm/plugins/__init__.py:77-90).

The groups. vLLM reads five groups through this function (vllm/plugins/__init__.py:16-30). A sixth group, vllm.logits_processors, has its own loader that calls entry_points() directly (vllm/v1/worker/gpu/sample/logits_processor/loader.py:46). Which process loads which group, when, and what the loaded object must be, is the topic of How vLLM finds and loads plugins. This page does not repeat it.

Deep dive

The console script vllm, and other uses of package metadata

For each console script, the installer writes a small executable into the bin/ folder of the environment (Scripts\ on Windows). It imports vllm.entrypoints.cli.main and calls main(). So vllm serve ... and python -m vllm.entrypoints.cli.main serve ... run the same function (vllm/entrypoints/cli/main.py:122-123).

The same metadata gives the version. vllm --version prints importlib.metadata.version("vllm"): the version of the installed distribution, not a constant in the code (vllm/entrypoints/cli/main.py:92-97).

Worked example. The value vllm.entrypoints.cli.main:main splits at the colon. The module part is vllm.entrypoints.cli.main, and the attribute part is main. So ep.load() does the same as importlib.import_module("vllm.entrypoints.cli.main").main. A value can also name a dotted attribute, for example pkg.mod:Class.method; then load() follows each dot.

What can go wrong#

  • The plugin is installed in another environment. entry_points() reads only the packages on the sys.path of the running interpreter. Install the plugin with the same pip as vLLM.
  • The metadata is old. You renamed the entry point or the function, but did not install the package again. The old name is still in entry_points.txt, and load() fails.
  • A wrong value fails quietly. When load() raises, vLLM logs the exception and skips the plugin (vllm/plugins/__init__.py:68-72). The server starts without your plugin. Read the log for "Failed to load plugin".
  • VLLM_PLUGINS uses entry-point names. It matches ep.name, for example register_dummy_model, not the package name vllm_add_dummy_model (vllm/plugins/__init__.py:64, tests/plugins/vllm_add_dummy_model/setup.py:11).
  • Heavy imports. load() runs the top-level code of the module, in every process that loads the group. Keep the module light and do the work inside the function.
  • The function runs more than once. A general plugin runs in several processes. The docstring says that plugins "should be designed in a way that they can be loaded multiple times" (vllm/plugins/__init__.py:78-80). The test plugin checks before it registers a model (tests/plugins/vllm_add_dummy_model/vllm_add_dummy_model/__init__.py:11-12).

Why it matters. Every vLLM plugin starts as an entry point. When a plugin does not load, the reason is almost always one of the points above.

Check yourself#

  1. 1
    What are the three parts of an entry point?
    easy
    Show answer

    A group (for example vllm.general_plugins), a name (for example lora_filesystem_resolver) and a value module:attr (pyproject.toml:45-47).

  2. 2
    Which function and which group make the vllm command?
    easy
    Show answer

    vllm.entrypoints.cli.main:main, declared in [project.scripts] (pyproject.toml:42-43). The installer puts it into the group console_scripts and writes a vllm executable.

  3. 3
    Does entry_points(group="vllm.general_plugins") import the plugin modules?
    medium
    Show answer

    No. It reads metadata only. The import happens in plugin.load() (vllm/plugins/__init__.py:42, vllm/plugins/__init__.py:69). That is why vLLM can filter by name before any plugin code runs (vllm/plugins/__init__.py:64).

  4. 4
    You set VLLM_PLUGINS=vllm_add_dummy_model and install the test package. The model is not registered, and there is no error. Why?
    hard
    Show answer

    The filter compares entry-point names (vllm/plugins/__init__.py:64). The name in the test package is register_dummy_model; vllm_add_dummy_model is the package name (tests/plugins/vllm_add_dummy_model/setup.py:7-11). So the loader skips the plugin. Set VLLM_PLUGINS=register_dummy_model.

  5. 5
    Your plugin module has a syntax error. What happens when vllm serve starts, and where do you see it?
    hard
    Show answer

    plugin.load() raises during the import. The loader catches it, logs "Failed to load plugin ..." with the traceback, and goes on (vllm/plugins/__init__.py:68-72). The server starts without the plugin, so you see the problem only in the log.