diff --git a/.gitignore b/.gitignore
index 88a42a5c0f64..96b97a552c54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -146,6 +146,7 @@ venv.bak/
# mkdocs documentation
/site
+docs/argparse
docs/examples
# mypy
diff --git a/docs/configuration/engine_args.md b/docs/configuration/engine_args.md
index a0e3594cd581..c3c1d5a1c362 100644
--- a/docs/configuration/engine_args.md
+++ b/docs/configuration/engine_args.md
@@ -1,3 +1,7 @@
+---
+toc_depth: 3
+---
+
# Engine Arguments
Engine arguments control the behavior of the vLLM engine.
@@ -5,11 +9,12 @@ Engine arguments control the behavior of the vLLM engine.
- For [offline inference](../serving/offline_inference.md), they are part of the arguments to [LLM][vllm.LLM] class.
- For [online serving](../serving/openai_compatible_server.md), they are part of the arguments to `vllm serve`.
-You can look at [EngineArgs][vllm.engine.arg_utils.EngineArgs] and [AsyncEngineArgs][vllm.engine.arg_utils.AsyncEngineArgs] to see the available engine arguments.
+The engine argument classes, [EngineArgs][vllm.engine.arg_utils.EngineArgs] and [AsyncEngineArgs][vllm.engine.arg_utils.AsyncEngineArgs], are a combination of the configuration classes defined in [vllm.config][]. Therefore, if you are interested in developer documentation, we recommend looking at these configuration classes as they are the source of truth for types, defaults and docstrings.
+
+## `EngineArgs`
-However, these classes are a combination of the configuration classes defined in [vllm.config][]. Therefore, we would recommend you read about them there where they are best documented.
+--8<-- "docs/argparse/engine_args.md"
-For offline inference you will have access to these configuration classes and for online serving you can cross-reference the configs with `vllm serve --help`, which has its arguments grouped by config.
+## `AsyncEngineArgs`
-!!! note
- Additional arguments are available to the [AsyncLLMEngine][vllm.engine.async_llm_engine.AsyncLLMEngine] which is used for online serving. These can be found by running `vllm serve --help`
+--8<-- "docs/argparse/async_engine_args.md"
diff --git a/docs/mkdocs/hooks/generate_argparse.py b/docs/mkdocs/hooks/generate_argparse.py
new file mode 100644
index 000000000000..64120f2d1513
--- /dev/null
+++ b/docs/mkdocs/hooks/generate_argparse.py
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
+import logging
+import sys
+from argparse import SUPPRESS, HelpFormatter
+from pathlib import Path
+from typing import Literal
+from unittest.mock import MagicMock, patch
+
+ROOT_DIR = Path(__file__).parent.parent.parent.parent
+ARGPARSE_DOC_DIR = ROOT_DIR / "docs/argparse"
+
+sys.path.insert(0, str(ROOT_DIR))
+sys.modules["aiohttp"] = MagicMock()
+sys.modules["blake3"] = MagicMock()
+sys.modules["vllm._C"] = MagicMock()
+
+from vllm.engine.arg_utils import AsyncEngineArgs, EngineArgs # noqa: E402
+from vllm.utils import FlexibleArgumentParser # noqa: E402
+
+logger = logging.getLogger("mkdocs")
+
+
+class MarkdownFormatter(HelpFormatter):
+ """Custom formatter that generates markdown for argument groups."""
+
+ def __init__(self, prog):
+ super().__init__(prog,
+ max_help_position=float('inf'),
+ width=float('inf'))
+ self._markdown_output = []
+
+ def start_section(self, heading):
+ if heading not in {"positional arguments", "options"}:
+ self._markdown_output.append(f"\n### {heading}\n\n")
+
+ def end_section(self):
+ pass
+
+ def add_text(self, text):
+ if text:
+ self._markdown_output.append(f"{text.strip()}\n\n")
+
+ def add_usage(self, usage, actions, groups, prefix=None):
+ pass
+
+ def add_arguments(self, actions):
+ for action in actions:
+
+ option_strings = f'`{"`, `".join(action.option_strings)}`'
+ self._markdown_output.append(f"#### {option_strings}\n\n")
+
+ if choices := action.choices:
+ choices = f'`{"`, `".join(str(c) for c in choices)}`'
+ self._markdown_output.append(
+ f"Possible choices: {choices}\n\n")
+
+ self._markdown_output.append(f"{action.help}\n\n")
+
+ if (default := action.default) != SUPPRESS:
+ self._markdown_output.append(f"Default: `{default}`\n\n")
+
+ def format_help(self):
+ """Return the formatted help as markdown."""
+ return "".join(self._markdown_output)
+
+
+def create_parser(cls, **kwargs) -> FlexibleArgumentParser:
+ """Create a parser for the given class with markdown formatting.
+
+ Args:
+ cls: The class to create a parser for
+ **kwargs: Additional keyword arguments to pass to `cls.add_cli_args`.
+
+ Returns:
+ FlexibleArgumentParser: A parser with markdown formatting for the class.
+ """
+ parser = FlexibleArgumentParser()
+ parser.formatter_class = MarkdownFormatter
+ with patch("vllm.config.DeviceConfig.__post_init__"):
+ return cls.add_cli_args(parser, **kwargs)
+
+
+def on_startup(command: Literal["build", "gh-deploy", "serve"], dirty: bool):
+ logger.info("Generating argparse documentation")
+ logger.debug("Root directory: %s", ROOT_DIR.resolve())
+ logger.debug("Output directory: %s", ARGPARSE_DOC_DIR.resolve())
+
+ # Create the ARGPARSE_DOC_DIR if it doesn't exist
+ if not ARGPARSE_DOC_DIR.exists():
+ ARGPARSE_DOC_DIR.mkdir(parents=True)
+
+ # Create parsers to document
+ parsers = {
+ "engine_args": create_parser(EngineArgs),
+ "async_engine_args": create_parser(AsyncEngineArgs,
+ async_args_only=True),
+ }
+
+ # Generate documentation for each parser
+ for stem, parser in parsers.items():
+ doc_path = ARGPARSE_DOC_DIR / f"{stem}.md"
+ with open(doc_path, "w") as f:
+ f.write(parser.format_help())
+ logger.info("Argparse generated: %s", doc_path.relative_to(ROOT_DIR))
diff --git a/docs/mkdocs/hooks/generate_examples.py b/docs/mkdocs/hooks/generate_examples.py
index 14a28f944d98..0ee52bb34603 100644
--- a/docs/mkdocs/hooks/generate_examples.py
+++ b/docs/mkdocs/hooks/generate_examples.py
@@ -161,8 +161,8 @@ def on_startup(command: Literal["build", "gh-deploy", "serve"], dirty: bool):
for example in sorted(examples, key=lambda e: e.path.stem):
example_name = f"{example.path.stem}.md"
doc_path = EXAMPLE_DOC_DIR / example.category / example_name
- logger.debug("Example generated: %s", doc_path.relative_to(ROOT_DIR))
if not doc_path.parent.exists():
doc_path.parent.mkdir(parents=True)
with open(doc_path, "w+") as f:
f.write(example.generate())
+ logger.debug("Example generated: %s", doc_path.relative_to(ROOT_DIR))
diff --git a/docs/mkdocs/overrides/partials/toc-item.html b/docs/mkdocs/overrides/partials/toc-item.html
new file mode 100644
index 000000000000..284af59cbe2c
--- /dev/null
+++ b/docs/mkdocs/overrides/partials/toc-item.html
@@ -0,0 +1,21 @@
+
+