[Refactor] Auto-derive CLI args from dataclass fields to eliminate duplication (#28814)
This commit is contained in:
@@ -0,0 +1,259 @@
|
|||||||
|
# Copyright 2023-2024 SGLang Team
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# ==============================================================================
|
||||||
|
"""Utilities for auto-deriving argparse CLI arguments from dataclass fields.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
from sglang.srt.arg_groups.arg_utils import A, Arg, add_cli_args_from_dataclass
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ServerArgs:
|
||||||
|
# Simple fields — bare string is the help text:
|
||||||
|
host: A[str, "The host of the HTTP server."] = "127.0.0.1"
|
||||||
|
port: A[int, "The port of the HTTP server."] = 30000
|
||||||
|
trust_remote_code: A[bool, "Whether to allow custom models."] = False
|
||||||
|
tokenizer_path: A[Optional[str], "The path of the tokenizer."] = None
|
||||||
|
|
||||||
|
# Fields with extra metadata — use Arg(...):
|
||||||
|
model_path: A[str, Arg(help="Path to model weights.", aliases=["--model"])]
|
||||||
|
load_format: A[str, Arg(help="Format.", choices=CHOICES)] = "auto"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_cli_args(parser):
|
||||||
|
add_cli_args_from_dataclass(parser, ServerArgs)
|
||||||
|
|
||||||
|
``A`` is a short alias for ``typing.Annotated``. A bare ``str`` inside the
|
||||||
|
annotation is equivalent to ``Arg(help=that_string)``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
import types
|
||||||
|
from typing import (
|
||||||
|
Annotated,
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
List,
|
||||||
|
Literal,
|
||||||
|
Optional,
|
||||||
|
Union,
|
||||||
|
get_args,
|
||||||
|
get_origin,
|
||||||
|
get_type_hints,
|
||||||
|
)
|
||||||
|
|
||||||
|
A = Annotated
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
|
class Arg:
|
||||||
|
"""CLI argument metadata attached to a dataclass field via ``Annotated``."""
|
||||||
|
|
||||||
|
help: str = ""
|
||||||
|
choices: Optional[list] = None
|
||||||
|
aliases: Optional[List[str]] = None
|
||||||
|
cli_name: Optional[str] = None
|
||||||
|
type_parser: Optional[Callable] = None
|
||||||
|
nargs: Optional[str] = None
|
||||||
|
required: Optional[bool] = None
|
||||||
|
action: Optional[Any] = None
|
||||||
|
action_kwargs: Optional[dict] = None
|
||||||
|
# When True, this field is skipped by add_cli_args_from_dataclass.
|
||||||
|
# Use for fields that have no CLI surface (e.g. injected via Python only).
|
||||||
|
no_cli: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_MISSING = dataclasses.MISSING
|
||||||
|
|
||||||
|
|
||||||
|
def _unwrap_annotated(tp):
|
||||||
|
"""Return (inner_type, Arg | None) from ``Annotated[T, Arg(...)]``.
|
||||||
|
|
||||||
|
Also accepts a bare string as shorthand: ``Annotated[T, "help text"]``
|
||||||
|
is equivalent to ``Annotated[T, Arg(help="help text")]``.
|
||||||
|
"""
|
||||||
|
origin = get_origin(tp)
|
||||||
|
if origin is Annotated:
|
||||||
|
args = get_args(tp)
|
||||||
|
inner = args[0]
|
||||||
|
for a in args[1:]:
|
||||||
|
if isinstance(a, Arg):
|
||||||
|
return inner, a
|
||||||
|
if isinstance(a, str):
|
||||||
|
return inner, Arg(help=a)
|
||||||
|
return inner, None
|
||||||
|
return tp, None
|
||||||
|
|
||||||
|
|
||||||
|
def _unwrap_optional(tp):
|
||||||
|
"""If tp is Optional[X] (i.e. Union[X, None]), return (X, True). Else (tp, False)."""
|
||||||
|
origin = get_origin(tp)
|
||||||
|
is_union = origin is Union or (
|
||||||
|
hasattr(types, "UnionType") and origin is types.UnionType
|
||||||
|
)
|
||||||
|
if is_union:
|
||||||
|
args = get_args(tp)
|
||||||
|
non_none = [a for a in args if a is not type(None)]
|
||||||
|
if len(non_none) == 1:
|
||||||
|
return non_none[0], True
|
||||||
|
return tp, False
|
||||||
|
|
||||||
|
|
||||||
|
def _unwrap_literal(tp):
|
||||||
|
"""If tp is Literal[...], return list of values. Else None."""
|
||||||
|
origin = get_origin(tp)
|
||||||
|
if origin is Literal:
|
||||||
|
return list(get_args(tp))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _infer_type_func(tp):
|
||||||
|
"""Map a Python type annotation to an argparse ``type=`` callable."""
|
||||||
|
if tp is str:
|
||||||
|
return str
|
||||||
|
if tp is int:
|
||||||
|
return int
|
||||||
|
if tp is float:
|
||||||
|
return float
|
||||||
|
return str
|
||||||
|
|
||||||
|
|
||||||
|
def _field_default(field):
|
||||||
|
"""Return the default value for a dataclass field, or _MISSING."""
|
||||||
|
if field.default is not _MISSING:
|
||||||
|
return field.default
|
||||||
|
if field.default_factory is not _MISSING:
|
||||||
|
return field.default_factory()
|
||||||
|
return _MISSING
|
||||||
|
|
||||||
|
|
||||||
|
def _field_to_cli_name(name: str) -> str:
|
||||||
|
"""Convert a field name like ``model_path`` to ``--model-path``."""
|
||||||
|
return "--" + name.replace("_", "-")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public API
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def add_cli_args_from_dataclass(parser, cls, *, fields: Optional[List[str]] = None):
|
||||||
|
"""Add argparse arguments for every ``A[T, "help"]`` or ``A[T, Arg(...)]`` field.
|
||||||
|
|
||||||
|
Fields without an ``Arg`` or bare-string annotation are silently skipped —
|
||||||
|
they must still be registered manually (this allows incremental migration).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
parser : argparse.ArgumentParser
|
||||||
|
cls : dataclass type
|
||||||
|
fields : optional list of field names to include. If None, all fields with
|
||||||
|
``Arg`` annotations are included.
|
||||||
|
"""
|
||||||
|
hints = get_type_hints(cls, include_extras=True)
|
||||||
|
|
||||||
|
for field in dataclasses.fields(cls):
|
||||||
|
if fields is not None and field.name not in fields:
|
||||||
|
continue
|
||||||
|
|
||||||
|
hint = hints.get(field.name)
|
||||||
|
if hint is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
raw_type, arg_meta = _unwrap_annotated(hint)
|
||||||
|
if arg_meta is None:
|
||||||
|
continue
|
||||||
|
if arg_meta.no_cli:
|
||||||
|
continue
|
||||||
|
|
||||||
|
cli_name = arg_meta.cli_name or _field_to_cli_name(field.name)
|
||||||
|
names = [cli_name] + (arg_meta.aliases or [])
|
||||||
|
default = _field_default(field)
|
||||||
|
|
||||||
|
# Handle custom action
|
||||||
|
if arg_meta.action is not None:
|
||||||
|
kwargs = {
|
||||||
|
"action": arg_meta.action,
|
||||||
|
"help": arg_meta.help,
|
||||||
|
}
|
||||||
|
if default is not _MISSING:
|
||||||
|
kwargs["default"] = default
|
||||||
|
if arg_meta.action_kwargs:
|
||||||
|
kwargs.update(arg_meta.action_kwargs)
|
||||||
|
parser.add_argument(*names, **kwargs)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Unwrap Optional
|
||||||
|
inner_type, is_optional = _unwrap_optional(raw_type)
|
||||||
|
|
||||||
|
# Check for Literal — auto-derive choices
|
||||||
|
literal_vals = _unwrap_literal(inner_type)
|
||||||
|
if literal_vals is not None:
|
||||||
|
choices = arg_meta.choices or literal_vals
|
||||||
|
# Infer type from first literal value
|
||||||
|
val_type = type(literal_vals[0]) if literal_vals else str
|
||||||
|
type_func = arg_meta.type_parser or _infer_type_func(val_type)
|
||||||
|
kwargs = dict(type=type_func, choices=choices, help=arg_meta.help)
|
||||||
|
if default is not _MISSING:
|
||||||
|
kwargs["default"] = default
|
||||||
|
parser.add_argument(*names, **kwargs)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check for List[X]
|
||||||
|
origin = get_origin(inner_type)
|
||||||
|
if origin is list or origin is List:
|
||||||
|
elem_args = get_args(inner_type)
|
||||||
|
elem_type = elem_args[0] if elem_args else str
|
||||||
|
type_func = arg_meta.type_parser or _infer_type_func(elem_type)
|
||||||
|
nargs = arg_meta.nargs or "+"
|
||||||
|
kwargs = dict(
|
||||||
|
type=type_func,
|
||||||
|
nargs=nargs,
|
||||||
|
help=arg_meta.help,
|
||||||
|
)
|
||||||
|
if arg_meta.choices:
|
||||||
|
kwargs["choices"] = arg_meta.choices
|
||||||
|
if default is not _MISSING:
|
||||||
|
kwargs["default"] = default
|
||||||
|
parser.add_argument(*names, **kwargs)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Bool → store_true
|
||||||
|
if inner_type is bool:
|
||||||
|
kwargs = dict(action="store_true", help=arg_meta.help)
|
||||||
|
if default is not _MISSING:
|
||||||
|
kwargs["default"] = default
|
||||||
|
parser.add_argument(*names, **kwargs)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Scalar types (str, int, float, etc.)
|
||||||
|
type_func = arg_meta.type_parser or _infer_type_func(inner_type)
|
||||||
|
kwargs = dict(type=type_func, help=arg_meta.help)
|
||||||
|
if arg_meta.choices:
|
||||||
|
kwargs["choices"] = arg_meta.choices
|
||||||
|
if arg_meta.nargs:
|
||||||
|
kwargs["nargs"] = arg_meta.nargs
|
||||||
|
if default is not _MISSING:
|
||||||
|
kwargs["default"] = default
|
||||||
|
if (
|
||||||
|
arg_meta.required is True
|
||||||
|
or (arg_meta.required is None and default is _MISSING)
|
||||||
|
) and any(name.startswith("-") for name in names):
|
||||||
|
kwargs["required"] = True
|
||||||
|
parser.add_argument(*names, **kwargs)
|
||||||
+171
-266
@@ -32,6 +32,7 @@ from functools import cached_property
|
|||||||
from typing import Any, Callable, Dict, List, Literal, Optional, Union
|
from typing import Any, Callable, Dict, List, Literal, Optional, Union
|
||||||
|
|
||||||
from sglang.jit_kernel.kv_canary.consts import RealKvHashMode
|
from sglang.jit_kernel.kv_canary.consts import RealKvHashMode
|
||||||
|
from sglang.srt.arg_groups.arg_utils import A, Arg, add_cli_args_from_dataclass
|
||||||
from sglang.srt.arg_groups.argparse_actions import (
|
from sglang.srt.arg_groups.argparse_actions import (
|
||||||
DeprecatedAction,
|
DeprecatedAction,
|
||||||
DeprecatedAliasStoreAction,
|
DeprecatedAliasStoreAction,
|
||||||
@@ -39,9 +40,7 @@ from sglang.srt.arg_groups.argparse_actions import (
|
|||||||
DeprecatedStoreTrueAction,
|
DeprecatedStoreTrueAction,
|
||||||
LoRAPathAction,
|
LoRAPathAction,
|
||||||
)
|
)
|
||||||
from sglang.srt.configs.linear_attn_model_registry import (
|
from sglang.srt.configs.linear_attn_model_registry import get_linear_attn_spec_by_arch
|
||||||
get_linear_attn_spec_by_arch,
|
|
||||||
)
|
|
||||||
from sglang.srt.connector import ConnectorType
|
from sglang.srt.connector import ConnectorType
|
||||||
from sglang.srt.distributed.device_communicators.mooncake_transfer_engine import (
|
from sglang.srt.distributed.device_communicators.mooncake_transfer_engine import (
|
||||||
parse_ib_device_config,
|
parse_ib_device_config,
|
||||||
@@ -372,51 +371,177 @@ def add_linear_attn_kernel_backend_choices(choices):
|
|||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class ServerArgs:
|
class ServerArgs:
|
||||||
"""
|
"""The arguments of the server.
|
||||||
The arguments of the server.
|
|
||||||
|
|
||||||
NOTE: When you add new arguments, please make sure the order
|
There are two styles for defining arguments. New arguments MUST use the
|
||||||
in this class definition the same as the order in the function
|
``A[T, ...]`` style; the legacy style is being migrated and should not be
|
||||||
`ServerArgs.add_cli_args`.
|
used for new additions.
|
||||||
Please follow the existing style to group the new arguments into related groups or create new groups.
|
|
||||||
|
**Style 1 — ``A[T, ...]`` (required for all new arguments):**
|
||||||
|
|
||||||
|
Each field carries its own CLI metadata. ``A`` is an alias for
|
||||||
|
``typing.Annotated``. For simple fields, use a bare string as the
|
||||||
|
help text. Use ``Arg(...)`` only when extra metadata is needed
|
||||||
|
(choices, aliases, custom type parser, etc.)::
|
||||||
|
|
||||||
|
# Simple — bare string is the help text:
|
||||||
|
host: A[str, "The host of the HTTP server."] = "127.0.0.1"
|
||||||
|
trust_remote_code: A[bool, "Whether to allow custom models."] = False
|
||||||
|
|
||||||
|
# With extra metadata:
|
||||||
|
load_format: A[str, Arg(help="Format.", choices=LOAD_FORMAT_CHOICES)] = "auto"
|
||||||
|
model_path: A[str, Arg(help="Path to model.", aliases=["--model"])]
|
||||||
|
|
||||||
|
**Style 2 — Legacy (existing arguments, to be migrated):**
|
||||||
|
|
||||||
|
The field is a plain type annotation with a default, and a separate
|
||||||
|
``parser.add_argument(...)`` call in ``add_cli_args`` defines the CLI
|
||||||
|
surface. When modifying these fields, keep the order in this class
|
||||||
|
definition consistent with the order in ``add_cli_args``.
|
||||||
|
|
||||||
|
Group new arguments into the appropriate existing section or create a
|
||||||
|
new section as needed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Model and tokenizer
|
# Model and tokenizer
|
||||||
model_path: str
|
model_path: A[
|
||||||
tokenizer_path: Optional[str] = None
|
str,
|
||||||
tokenizer_mode: str = "auto"
|
Arg(
|
||||||
tokenizer_backend: str = "huggingface"
|
help="The path of the model weights. This can be a local folder or a Hugging Face repo ID.",
|
||||||
tokenizer_worker_num: int = 1
|
aliases=["--model"],
|
||||||
detokenizer_worker_num: int = 1
|
),
|
||||||
skip_tokenizer_init: bool = False
|
]
|
||||||
load_format: str = "auto"
|
tokenizer_path: A[Optional[str], "The path of the tokenizer."] = None
|
||||||
model_loader_extra_config: str = "{}"
|
tokenizer_mode: A[
|
||||||
trust_remote_code: bool = False
|
str,
|
||||||
context_length: Optional[int] = None
|
Arg(
|
||||||
is_embedding: bool = False
|
help="Tokenizer mode. 'auto' will use the fast tokenizer if available, "
|
||||||
prefill_only_disable_kv_cache: bool = False
|
"and 'slow' will always use the slow tokenizer.",
|
||||||
enable_multimodal: Optional[bool] = None
|
choices=["auto", "slow"],
|
||||||
revision: Optional[str] = None
|
),
|
||||||
model_impl: str = "auto"
|
] = "auto"
|
||||||
model_config_parser: str = "auto"
|
tokenizer_backend: A[
|
||||||
|
str,
|
||||||
|
Arg(
|
||||||
|
help="Tokenizer backend. 'huggingface' uses the default HuggingFace "
|
||||||
|
"tokenizers library, and 'fastokens' uses the fastokens library "
|
||||||
|
"for faster tokenization. Requires the fastokens package to be installed.",
|
||||||
|
choices=["huggingface", "fastokens"],
|
||||||
|
),
|
||||||
|
] = "huggingface"
|
||||||
|
tokenizer_worker_num: A[int, "The worker num of the tokenizer manager."] = 1
|
||||||
|
detokenizer_worker_num: A[int, "The worker num of the detokenizer manager."] = 1
|
||||||
|
skip_tokenizer_init: A[
|
||||||
|
bool, "If set, skip init tokenizer and pass input_ids in generate request."
|
||||||
|
] = False
|
||||||
|
load_format: A[
|
||||||
|
str,
|
||||||
|
Arg(
|
||||||
|
help="The format of the model weights to load. "
|
||||||
|
'"auto" will try to load the weights in the safetensors format '
|
||||||
|
"and fall back to the pytorch bin format if safetensors format "
|
||||||
|
"is not available. "
|
||||||
|
'"pt" will load the weights in the pytorch bin format. '
|
||||||
|
'"safetensors" will load the weights in the safetensors format. '
|
||||||
|
'"npcache" will load the weights in pytorch format and store '
|
||||||
|
"a numpy cache to speed up the loading. "
|
||||||
|
'"dummy" will initialize the weights with random values, '
|
||||||
|
"which is mainly for profiling."
|
||||||
|
'"gguf" will load the weights in the gguf format. '
|
||||||
|
'"bitsandbytes" will load the weights using bitsandbytes '
|
||||||
|
"quantization."
|
||||||
|
'"layered" loads weights layer by layer so that one can quantize a '
|
||||||
|
"layer before loading another to make the peak memory envelope "
|
||||||
|
"smaller.",
|
||||||
|
choices=LOAD_FORMAT_CHOICES,
|
||||||
|
),
|
||||||
|
] = "auto"
|
||||||
|
model_loader_extra_config: A[
|
||||||
|
str,
|
||||||
|
"Extra config for model loader. This will be passed to the model loader corresponding to the chosen load_format.",
|
||||||
|
] = "{}"
|
||||||
|
trust_remote_code: A[
|
||||||
|
bool,
|
||||||
|
"Whether or not to allow for custom models defined on the Hub in their own modeling files.",
|
||||||
|
] = False
|
||||||
|
context_length: A[
|
||||||
|
Optional[int],
|
||||||
|
Arg(
|
||||||
|
help="The model's maximum context length. Defaults to None (will use the value from the model's config.json instead)."
|
||||||
|
f"\n\n{human_readable_int.__doc__}",
|
||||||
|
type_parser=human_readable_int,
|
||||||
|
),
|
||||||
|
] = None
|
||||||
|
is_embedding: A[bool, "Whether to use a CausalLM as an embedding model."] = False
|
||||||
|
enable_multimodal: A[
|
||||||
|
Optional[bool],
|
||||||
|
"Enable the multimodal functionality for the served model. If the model being served is not multimodal, nothing will happen",
|
||||||
|
] = None
|
||||||
|
revision: A[
|
||||||
|
Optional[str],
|
||||||
|
"The specific model version to use. It can be a branch name, a tag name, or a commit id. If unspecified, will use the default version.",
|
||||||
|
] = None
|
||||||
|
model_impl: A[
|
||||||
|
str,
|
||||||
|
Arg(
|
||||||
|
help=(
|
||||||
|
"Which implementation of the model to use.\n\n"
|
||||||
|
'* "auto" will try to use the SGLang implementation if it exists '
|
||||||
|
"and fall back to the Transformers implementation if no SGLang "
|
||||||
|
"implementation is available.\n"
|
||||||
|
'* "sglang" will use the SGLang model implementation.\n'
|
||||||
|
'* "transformers" will use the Transformers model '
|
||||||
|
'* "mindspore" will use the MindSpore model '
|
||||||
|
"implementation.\n"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
] = "auto"
|
||||||
|
model_config_parser: A[
|
||||||
|
str,
|
||||||
|
Arg(
|
||||||
|
help=(
|
||||||
|
'Which model-config parser to use. "auto" picks "mistral" '
|
||||||
|
'via the is_mistral_model name heuristic, else "hf" '
|
||||||
|
"(AutoConfig over config.json). Plugins can register additional "
|
||||||
|
"parsers via @register_model_config_parser."
|
||||||
|
)
|
||||||
|
),
|
||||||
|
] = "auto"
|
||||||
|
|
||||||
# HTTP server
|
# HTTP server
|
||||||
host: str = "127.0.0.1"
|
host: A[str, "The host of the HTTP server."] = "127.0.0.1"
|
||||||
port: int = 30000
|
port: A[int, "The port of the HTTP server."] = 30000
|
||||||
fastapi_root_path: str = ""
|
fastapi_root_path: A[str, "App is behind a path based routing proxy."] = ""
|
||||||
grpc_mode: bool = False
|
grpc_mode: A[bool, "If set, use gRPC server instead of HTTP server."] = False
|
||||||
skip_server_warmup: bool = False
|
skip_server_warmup: A[bool, "If set, skip warmup."] = False
|
||||||
warmups: Optional[str] = None
|
warmups: A[
|
||||||
nccl_port: Optional[int] = None
|
Optional[str],
|
||||||
checkpoint_engine_wait_weights_before_ready: bool = False
|
"Specify custom warmup functions (csv) to run before server starts eg. --warmups=warmup_name1,warmup_name2 will run the functions `warmup_name1` and `warmup_name2` specified in warmup.py before the server starts listening for requests",
|
||||||
|
] = None
|
||||||
|
nccl_port: A[
|
||||||
|
Optional[int],
|
||||||
|
"The port for NCCL distributed environment setup. Defaults to a random port.",
|
||||||
|
] = None
|
||||||
|
checkpoint_engine_wait_weights_before_ready: A[
|
||||||
|
bool,
|
||||||
|
"If set, the server will wait for initial weights to be loaded via checkpoint-engine or other update methods before serving inference requests.",
|
||||||
|
] = False
|
||||||
|
|
||||||
# SSL/TLS
|
# SSL/TLS
|
||||||
ssl_keyfile: Optional[str] = None
|
ssl_keyfile: A[Optional[str], "The file path to the SSL key file."] = None
|
||||||
ssl_certfile: Optional[str] = None
|
ssl_certfile: A[Optional[str], "The file path to the SSL certificate file."] = None
|
||||||
ssl_ca_certs: Optional[str] = None
|
ssl_ca_certs: A[Optional[str], "The CA certificates file."] = None
|
||||||
ssl_keyfile_password: Optional[str] = None
|
ssl_keyfile_password: A[
|
||||||
enable_ssl_refresh: bool = False
|
Optional[str], "The password to decrypt the SSL keyfile."
|
||||||
enable_http2: bool = False
|
] = None
|
||||||
|
enable_ssl_refresh: A[
|
||||||
|
bool,
|
||||||
|
"Enable automatic SSL certificate hot-reloading when cert/key files change on disk. Requires --ssl-certfile and --ssl-keyfile.",
|
||||||
|
] = False
|
||||||
|
enable_http2: A[
|
||||||
|
bool,
|
||||||
|
"Use Granian instead of Uvicorn as the ASGI server, enabling HTTP/1.1 and HTTP/2 auto-negotiation. Clients may use h2c (cleartext HTTP/2) or plain HTTP/1.1. Requires 'pip install sglang[http2]'.",
|
||||||
|
] = False
|
||||||
|
|
||||||
# Quantization and data type
|
# Quantization and data type
|
||||||
dtype: str = "auto"
|
dtype: str = "auto"
|
||||||
@@ -752,6 +877,10 @@ class ServerArgs:
|
|||||||
enable_mis: bool = False
|
enable_mis: bool = False
|
||||||
|
|
||||||
# Optimization/debug options
|
# Optimization/debug options
|
||||||
|
prefill_only_disable_kv_cache: A[
|
||||||
|
bool,
|
||||||
|
"Skip the physical KV cache allocation for embedding-mode prefill-only workloads. Currently only valid with --is-embedding, --chunked-prefill-size=-1, --disable-radix-cache, an FA prefill backend, and non-FP4 KV cache so the fa_skip_kv_cache path is active (no layer reads or writes the cache). Other prefill-only workloads such as scoring/MIS may benefit from this later once their attention paths stop using paged KV. Scheduler admission accounting is unchanged; per-layer K/V tensors are sized to (page_size, head_num, head_dim) placeholders so GPU memory is not wasted.",
|
||||||
|
] = False
|
||||||
disable_radix_cache: bool = False
|
disable_radix_cache: bool = False
|
||||||
disable_cuda_graph_padding: bool = False
|
disable_cuda_graph_padding: bool = False
|
||||||
enable_profile_cuda_graph: bool = False
|
enable_profile_cuda_graph: bool = False
|
||||||
@@ -4728,232 +4857,8 @@ class ServerArgs:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def add_cli_args(parser: argparse.ArgumentParser):
|
def add_cli_args(parser: argparse.ArgumentParser):
|
||||||
|
|
||||||
# Model and tokenizer
|
# Auto-derived from Annotated[..., Arg(...)] field metadata.
|
||||||
parser.add_argument(
|
add_cli_args_from_dataclass(parser, ServerArgs)
|
||||||
"--model-path",
|
|
||||||
"--model",
|
|
||||||
type=str,
|
|
||||||
help="The path of the model weights. This can be a local folder or a Hugging Face repo ID.",
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--tokenizer-path",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.tokenizer_path,
|
|
||||||
help="The path of the tokenizer.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--tokenizer-mode",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.tokenizer_mode,
|
|
||||||
choices=["auto", "slow"],
|
|
||||||
help="Tokenizer mode. 'auto' will use the fast "
|
|
||||||
"tokenizer if available, and 'slow' will "
|
|
||||||
"always use the slow tokenizer.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--tokenizer-backend",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.tokenizer_backend,
|
|
||||||
choices=["huggingface", "fastokens"],
|
|
||||||
help="Tokenizer backend. 'huggingface' uses the default HuggingFace "
|
|
||||||
"tokenizers library, and 'fastokens' uses the fastokens library "
|
|
||||||
"for faster tokenization. Requires the fastokens package to be installed.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--tokenizer-worker-num",
|
|
||||||
type=int,
|
|
||||||
default=ServerArgs.tokenizer_worker_num,
|
|
||||||
help="The worker num of the tokenizer manager.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--detokenizer-worker-num",
|
|
||||||
type=int,
|
|
||||||
default=ServerArgs.detokenizer_worker_num,
|
|
||||||
help="The worker num of the detokenizer manager.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--skip-tokenizer-init",
|
|
||||||
action="store_true",
|
|
||||||
help="If set, skip init tokenizer and pass input_ids in generate request.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--load-format",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.load_format,
|
|
||||||
choices=LOAD_FORMAT_CHOICES,
|
|
||||||
help="The format of the model weights to load. "
|
|
||||||
'"auto" will try to load the weights in the safetensors format '
|
|
||||||
"and fall back to the pytorch bin format if safetensors format "
|
|
||||||
"is not available. "
|
|
||||||
'"pt" will load the weights in the pytorch bin format. '
|
|
||||||
'"safetensors" will load the weights in the safetensors format. '
|
|
||||||
'"npcache" will load the weights in pytorch format and store '
|
|
||||||
"a numpy cache to speed up the loading. "
|
|
||||||
'"dummy" will initialize the weights with random values, '
|
|
||||||
"which is mainly for profiling."
|
|
||||||
'"gguf" will load the weights in the gguf format. '
|
|
||||||
'"bitsandbytes" will load the weights using bitsandbytes '
|
|
||||||
"quantization."
|
|
||||||
'"layered" loads weights layer by layer so that one can quantize a '
|
|
||||||
"layer before loading another to make the peak memory envelope "
|
|
||||||
"smaller.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--model-loader-extra-config",
|
|
||||||
type=str,
|
|
||||||
help="Extra config for model loader. "
|
|
||||||
"This will be passed to the model loader corresponding to the chosen load_format.",
|
|
||||||
default=ServerArgs.model_loader_extra_config,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--trust-remote-code",
|
|
||||||
action="store_true",
|
|
||||||
help="Whether or not to allow for custom models defined on the Hub in their own modeling files.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--context-length",
|
|
||||||
type=human_readable_int,
|
|
||||||
default=ServerArgs.context_length,
|
|
||||||
help="The model's maximum context length. Defaults to None (will use the value from the model's config.json instead)."
|
|
||||||
+ f"\n\n{human_readable_int.__doc__}",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--is-embedding",
|
|
||||||
action="store_true",
|
|
||||||
help="Whether to use a CausalLM as an embedding model.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--prefill-only-disable-kv-cache",
|
|
||||||
action="store_true",
|
|
||||||
help="Skip the physical KV cache allocation for embedding-mode prefill-only workloads. Currently only valid with --is-embedding, --chunked-prefill-size=-1, --disable-radix-cache, an FA prefill backend, and non-FP4 KV cache so the fa_skip_kv_cache path is active (no layer reads or writes the cache). Other prefill-only workloads such as scoring/MIS may benefit from this later once their attention paths stop using paged KV. Scheduler admission accounting is unchanged; per-layer K/V tensors are sized to (page_size, head_num, head_dim) placeholders so GPU memory is not wasted.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--enable-multimodal",
|
|
||||||
default=ServerArgs.enable_multimodal,
|
|
||||||
action="store_true",
|
|
||||||
help="Enable the multimodal functionality for the served model. If the model being served is not multimodal, nothing will happen",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--revision",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="The specific model version to use. It can be a branch "
|
|
||||||
"name, a tag name, or a commit id. If unspecified, will use "
|
|
||||||
"the default version.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--model-impl",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.model_impl,
|
|
||||||
help="Which implementation of the model to use.\n\n"
|
|
||||||
'* "auto" will try to use the SGLang implementation if it exists '
|
|
||||||
"and fall back to the Transformers implementation if no SGLang "
|
|
||||||
"implementation is available.\n"
|
|
||||||
'* "sglang" will use the SGLang model implementation.\n'
|
|
||||||
'* "transformers" will use the Transformers model '
|
|
||||||
'* "mindspore" will use the MindSpore model '
|
|
||||||
"implementation.\n",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--model-config-parser",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.model_config_parser,
|
|
||||||
help='Which model-config parser to use. "auto" picks "mistral" '
|
|
||||||
'via the is_mistral_model name heuristic, else "hf" '
|
|
||||||
"(AutoConfig over config.json). Plugins can register additional "
|
|
||||||
"parsers via @register_model_config_parser.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# HTTP server
|
|
||||||
parser.add_argument(
|
|
||||||
"--host",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.host,
|
|
||||||
help="The host of the HTTP server.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--port",
|
|
||||||
type=int,
|
|
||||||
default=ServerArgs.port,
|
|
||||||
help="The port of the HTTP server.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--fastapi-root-path",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.fastapi_root_path,
|
|
||||||
help="App is behind a path based routing proxy.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--grpc-mode",
|
|
||||||
action="store_true",
|
|
||||||
help="If set, use gRPC server instead of HTTP server.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--skip-server-warmup",
|
|
||||||
action="store_true",
|
|
||||||
help="If set, skip warmup.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--warmups",
|
|
||||||
type=str,
|
|
||||||
required=False,
|
|
||||||
help="Specify custom warmup functions (csv) to run before server starts eg. --warmups=warmup_name1,warmup_name2 "
|
|
||||||
"will run the functions `warmup_name1` and `warmup_name2` specified in warmup.py before the server starts listening for requests",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--nccl-port",
|
|
||||||
type=int,
|
|
||||||
default=ServerArgs.nccl_port,
|
|
||||||
help="The port for NCCL distributed environment setup. Defaults to a random port.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--checkpoint-engine-wait-weights-before-ready",
|
|
||||||
action="store_true",
|
|
||||||
help="If set, the server will wait for initial weights to be loaded via checkpoint-engine or other update methods "
|
|
||||||
"before serving inference requests.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# SSL/TLS
|
|
||||||
parser.add_argument(
|
|
||||||
"--ssl-keyfile",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.ssl_keyfile,
|
|
||||||
help="The file path to the SSL key file.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--ssl-certfile",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.ssl_certfile,
|
|
||||||
help="The file path to the SSL certificate file.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--ssl-ca-certs",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.ssl_ca_certs,
|
|
||||||
help="The CA certificates file.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--ssl-keyfile-password",
|
|
||||||
type=str,
|
|
||||||
default=ServerArgs.ssl_keyfile_password,
|
|
||||||
help="The password to decrypt the SSL keyfile.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--enable-ssl-refresh",
|
|
||||||
action="store_true",
|
|
||||||
default=ServerArgs.enable_ssl_refresh,
|
|
||||||
help="Enable automatic SSL certificate hot-reloading when cert/key "
|
|
||||||
"files change on disk. Requires --ssl-certfile and --ssl-keyfile.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--enable-http2",
|
|
||||||
action="store_true",
|
|
||||||
default=ServerArgs.enable_http2,
|
|
||||||
help="Use Granian instead of Uvicorn as the ASGI server, enabling HTTP/1.1 and "
|
|
||||||
"HTTP/2 auto-negotiation. Clients may use h2c (cleartext HTTP/2) or plain HTTP/1.1. "
|
|
||||||
"Requires 'pip install sglang[http2]'.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Quantization and data type
|
# Quantization and data type
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
Reference in New Issue
Block a user