cli_args.py 12.2 KB
Newer Older
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4
5
6
7
8
9
10
11
"""
This file contains the command line arguments for the vLLM's
OpenAI-compatible server. It is kept in a separate file for documentation
purposes.
"""

import argparse
import json
import ssl
12
13
from collections.abc import Sequence
from typing import Optional, Union, get_args
14

15
import vllm.envs as envs
16
from vllm.engine.arg_utils import AsyncEngineArgs, optional_type
17
18
from vllm.entrypoints.chat_utils import (ChatTemplateContentFormatOption,
                                         validate_chat_template)
19
from vllm.entrypoints.openai.serving_models import (LoRAModulePath,
20
                                                    PromptAdapterPath)
21
from vllm.entrypoints.openai.tool_parsers import ToolParserManager
22
from vllm.logger import init_logger
23
from vllm.utils import FlexibleArgumentParser
24

25
26
logger = init_logger(__name__)

27
28
29

class LoRAParserAction(argparse.Action):

30
31
32
33
34
35
36
37
38
39
40
41
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: Optional[Union[str, Sequence[str]]],
        option_string: Optional[str] = None,
    ):
        if values is None:
            values = []
        if isinstance(values, str):
            raise TypeError("Expected values to be a list")

42
        lora_list: list[LoRAModulePath] = []
43
        for item in values:
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
            if item in [None, '']:  # Skip if item is None or empty string
                continue
            if '=' in item and ',' not in item:  # Old format: name=path
                name, path = item.split('=')
                lora_list.append(LoRAModulePath(name, path))
            else:  # Assume JSON format
                try:
                    lora_dict = json.loads(item)
                    lora = LoRAModulePath(**lora_dict)
                    lora_list.append(lora)
                except json.JSONDecodeError:
                    parser.error(
                        f"Invalid JSON format for --lora-modules: {item}")
                except TypeError as e:
                    parser.error(
                        f"Invalid fields for --lora-modules: {item} - {str(e)}"
                    )
61
62
63
        setattr(namespace, self.dest, lora_list)


64
65
class PromptAdapterParserAction(argparse.Action):

66
67
68
69
70
71
72
73
74
75
76
77
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: Optional[Union[str, Sequence[str]]],
        option_string: Optional[str] = None,
    ):
        if values is None:
            values = []
        if isinstance(values, str):
            raise TypeError("Expected values to be a list")

78
        adapter_list: list[PromptAdapterPath] = []
79
80
81
82
83
84
        for item in values:
            name, path = item.split('=')
            adapter_list.append(PromptAdapterPath(name, path))
        setattr(namespace, self.dest, adapter_list)


Ethan Xu's avatar
Ethan Xu committed
85
def make_arg_parser(parser: FlexibleArgumentParser) -> FlexibleArgumentParser:
86
    parser.add_argument("--host",
87
                        type=optional_type(str),
88
                        default=None,
89
90
                        help="Host name.")
    parser.add_argument("--port", type=int, default=8000, help="Port number.")
91
92
93
94
95
    parser.add_argument(
        "--uvicorn-log-level",
        type=str,
        default="info",
        choices=['debug', 'info', 'warning', 'error', 'critical', 'trace'],
96
        help="Log level for uvicorn.")
97
98
99
    parser.add_argument("--disable-uvicorn-access-log",
                        action="store_true",
                        help="Disable uvicorn access log.")
100
101
    parser.add_argument("--allow-credentials",
                        action="store_true",
102
                        help="Allow credentials.")
103
104
105
    parser.add_argument("--allowed-origins",
                        type=json.loads,
                        default=["*"],
106
                        help="Allowed origins.")
107
108
109
    parser.add_argument("--allowed-methods",
                        type=json.loads,
                        default=["*"],
110
                        help="Allowed methods.")
111
112
113
    parser.add_argument("--allowed-headers",
                        type=json.loads,
                        default=["*"],
114
                        help="Allowed headers.")
115
    parser.add_argument("--api-key",
116
                        type=optional_type(str),
117
118
119
120
121
                        default=None,
                        help="If provided, the server will require this key "
                        "to be presented in the header.")
    parser.add_argument(
        "--lora-modules",
122
        type=optional_type(str),
123
124
125
        default=None,
        nargs='+',
        action=LoRAParserAction,
126
127
        help="LoRA module configurations in either 'name=path' format"
        "or JSON format. "
128
        "Example (old format): ``'name=path'`` "
129
        "Example (new format): "
130
        "``{\"name\": \"name\", \"path\": \"lora_path\", "
131
        "\"base_model_name\": \"id\"}``")
132
133
    parser.add_argument(
        "--prompt-adapters",
134
        type=optional_type(str),
135
136
137
138
139
        default=None,
        nargs='+',
        action=PromptAdapterParserAction,
        help="Prompt adapter configurations in the format name=path. "
        "Multiple adapters can be specified.")
140
    parser.add_argument("--chat-template",
141
                        type=optional_type(str),
142
143
144
                        default=None,
                        help="The file path to the chat template, "
                        "or the template in single-line form "
145
                        "for the specified model.")
146
147
148
149
150
151
152
153
    parser.add_argument(
        '--chat-template-content-format',
        type=str,
        default="auto",
        choices=get_args(ChatTemplateContentFormatOption),
        help='The format to render message content within a chat template.'
        '\n\n'
        '* "string" will render the content as a string. '
154
        'Example: ``"Hello World"``\n'
155
156
        '* "openai" will render the content as a list of dictionaries, '
        'similar to OpenAI schema. '
157
        'Example: ``[{"type": "text", "text": "Hello world!"}]``')
158
    parser.add_argument("--response-role",
159
                        type=optional_type(str),
160
161
                        default="assistant",
                        help="The role name to return if "
162
                        "``request.add_generation_prompt=true``.")
163
    parser.add_argument("--ssl-keyfile",
164
                        type=optional_type(str),
165
                        default=None,
166
                        help="The file path to the SSL key file.")
167
    parser.add_argument("--ssl-certfile",
168
                        type=optional_type(str),
169
                        default=None,
170
                        help="The file path to the SSL cert file.")
171
    parser.add_argument("--ssl-ca-certs",
172
                        type=optional_type(str),
173
                        default=None,
174
                        help="The CA certificates file.")
175
176
177
178
179
    parser.add_argument(
        "--enable-ssl-refresh",
        action="store_true",
        default=False,
        help="Refresh SSL Context when SSL certificate files change")
180
181
182
183
    parser.add_argument(
        "--ssl-cert-reqs",
        type=int,
        default=int(ssl.CERT_NONE),
184
        help="Whether client certificate is required (see stdlib ssl module's)."
185
186
187
    )
    parser.add_argument(
        "--root-path",
188
        type=optional_type(str),
189
        default=None,
190
191
        help="FastAPI root_path when app is behind a path based routing proxy."
    )
192
193
    parser.add_argument(
        "--middleware",
194
        type=optional_type(str),
195
196
197
198
199
200
        action="append",
        default=[],
        help="Additional ASGI middleware to apply to the app. "
        "We accept multiple --middleware arguments. "
        "The value should be an import path. "
        "If a function is provided, vLLM will add it to the server "
201
        "using ``@app.middleware('http')``. "
202
        "If a class is provided, vLLM will add it to the server "
203
        "using ``app.add_middleware()``. ")
204
205
206
    parser.add_argument(
        "--return-tokens-as-token-ids",
        action="store_true",
207
208
209
        help="When ``--max-logprobs`` is specified, represents single tokens "
        " as strings of the form 'token_id:{token_id}' so that tokens "
        "that are not JSON-encodable can be identified.")
210
211
212
213
214
    parser.add_argument(
        "--disable-frontend-multiprocessing",
        action="store_true",
        help="If specified, will run the OpenAI frontend server in the same "
        "process as the model serving engine.")
215
216
217
218
    parser.add_argument(
        "--enable-request-id-headers",
        action="store_true",
        help="If specified, API server will add X-Request-Id header to "
219
        "responses.")
220
221
222
223
    parser.add_argument(
        "--enable-auto-tool-choice",
        action="store_true",
        default=False,
224
225
        help="Enable auto tool choice for supported models. Use "
        "``--tool-call-parser`` to specify which parser to use.")
226

227
    valid_tool_parsers = ToolParserManager.tool_parsers.keys()
228
229
230
    parser.add_argument(
        "--tool-call-parser",
        type=str,
231
232
        metavar="{" + ",".join(valid_tool_parsers) + "} or name registered in "
        "--tool-parser-plugin",
233
234
235
236
        default=None,
        help=
        "Select the tool call parser depending on the model that you're using."
        " This is used to parse the model-generated tool call into OpenAI API "
237
        "format. Required for ``--enable-auto-tool-choice``.")
238

239
240
241
242
243
244
245
    parser.add_argument(
        "--tool-parser-plugin",
        type=str,
        default="",
        help=
        "Special the tool parser plugin write to parse the model-generated tool"
        " into OpenAI API format, the name register in this plugin can be used "
246
        "in ``--tool-call-parser``.")
247

248
249
250
251
252
253
254
    parser.add_argument(
        "--log-config-file",
        type=str,
        default=envs.VLLM_LOGGING_CONFIG_PATH,
        help="Path to logging config JSON file for both vllm and uvicorn",
    )

255
    parser = AsyncEngineArgs.add_cli_args(parser)
256
257
258
259
260
261

    parser.add_argument('--max-log-len',
                        type=int,
                        default=None,
                        help='Max number of prompt characters or prompt '
                        'ID numbers being printed in log.'
262
                        ' The default of None means unlimited.')
263

264
265
266
267
    parser.add_argument(
        "--disable-fastapi-docs",
        action='store_true',
        default=False,
268
        help="Disable FastAPI's OpenAPI schema, Swagger UI, and ReDoc endpoint."
269
    )
270
271
272
273
274
    parser.add_argument(
        "--enable-prompt-tokens-details",
        action='store_true',
        default=False,
        help="If set to True, enable prompt_tokens_details in usage.")
275
276
277
278
279
    parser.add_argument(
        "--enable-force-include-usage",
        action='store_true',
        default=False,
        help="If set to True, including usage on every request.")
280
281
282
283
284
285
286
    parser.add_argument(
        "--enable-server-load-tracking",
        action='store_true',
        default=False,
        help=
        "If set to True, enable tracking server_load_metrics in the app state."
    )
287

288
    return parser
Ethan Xu's avatar
Ethan Xu committed
289
290


291
292
293
294
295
296
297
298
299
300
301
302
def validate_parsed_serve_args(args: argparse.Namespace):
    """Quick checks for model serve args that raise prior to loading."""
    if hasattr(args, "subparser") and args.subparser != "serve":
        return

    # Ensure that the chat template is valid; raises if it likely isn't
    validate_chat_template(args.chat_template)

    # Enable auto tool needs a tool call parser to be valid
    if args.enable_auto_tool_choice and not args.tool_call_parser:
        raise TypeError("Error: --enable-auto-tool-choice requires "
                        "--tool-call-parser")
303
304
305
    if args.enable_prompt_embeds and args.enable_prompt_adapter:
        raise ValueError(
            "Cannot use prompt embeds and prompt adapter at the same time.")
306
307


308
309
310
311
312
313
314
315
316
def log_non_default_args(args: argparse.Namespace):
    non_default_args = {}
    parser = make_arg_parser(FlexibleArgumentParser())
    for arg, default in vars(parser.parse_args([])).items():
        if default != getattr(args, arg):
            non_default_args[arg] = getattr(args, arg)
    logger.info("non-default args: %s", non_default_args)


Ethan Xu's avatar
Ethan Xu committed
317
318
319
320
def create_parser_for_docs() -> FlexibleArgumentParser:
    parser_for_docs = FlexibleArgumentParser(
        prog="-m vllm.entrypoints.openai.api_server")
    return make_arg_parser(parser_for_docs)