MiniMax-M3-AutoRound-3.2bit-longctx / m3_official_api_server.py
aquaman164's picture
Upload folder using huggingface_hub
a622e04 verified
Raw
History Blame Contribute Delete
4.49 kB
# SPDX-License-Identifier: Apache-2.0
"""OpenAI-compatible API server for the OFFICIAL native vLLM MiniMax-M3 (vLLM
0.23.1) + our AutoRound 3.2bit quantization == the LONG-CONTEXT port (②).
Unlike m3_api_server.py (the out-of-tree M1 model, <=2048 ctx, vision), this
serves the official native M3 text backbone with the MSA lightning indexer, so
it supports long context (~46K tokens of KV on 2x RTX PRO 6000). All the ② fixes
(shared expert: n_shared_experts + down_proj/gate_up quant naming) live in
serve_m3_official.py / m3_official_loader.py / m3_quant.py and are reused here.
Engine args mirror serve_m3_official.py's LLM kwargs; we reuse its
_override_quant_method (architecture force + the FORCE config dict incl.
n_shared_experts=1). The OpenAI server's CLI-parsed engine args are replaced with
ours (same trick as m3_api_server.py) since the CLI path mis-resolves the config.
Start: M3_OFFICIAL_PORT is set automatically.
python m3_official_api_server.py
Test : curl http://localhost:8003/v1/models
curl http://localhost:8003/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"minimax-m3-long","messages":[{"role":"user","content":"日本の首都は?"}],"max_tokens":30}'
"""
import os
import sys
import asyncio
os.environ["M3_OFFICIAL_PORT"] = "1" # gate m3_quant's gate_up/qkv handling
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
os.environ.setdefault("MIXED_MOE_GROUPED", "1")
os.environ.setdefault("VLLM_USE_FLASHINFER_SAMPLER", "0")
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
CKPT = os.environ.get("M3_CKPT", os.path.dirname(os.path.abspath(__file__)))
PORT = os.environ.get("M3_PORT", "8003") # M1 keeps :8002
SERVED = os.environ.get("M3_SERVED", "minimax-m3-long")
MAXLEN = int(os.environ.get("M3_MAXLEN", "40960")) # long context (KV ~46K)
# Importing serve_m3_official runs the registrations (m3_quant + the key-
# translating CausalLM loader) and defines the shared config override; the LLM
# build there is guarded by __main__, so the import is side-effect-safe.
import serve_m3_official # noqa: E402
_ov = serve_m3_official._override_quant_method
from vllm.engine.arg_utils import AsyncEngineArgs # noqa: E402
# M3_CUDAGRAPH_MODE=PIECEWISE (etc.) -> override the cudagraph mode. The default
# FULL_AND_PIECEWISE hits an illegal-memory-access RACE during the decode-FULL
# capture on ② (CUDA_LAUNCH_BLOCKING=1 avoids it but is too slow); PIECEWISE-only
# may dodge that path.
_cgmode = os.environ.get("M3_CUDAGRAPH_MODE")
_comp_cfg = {"cudagraph_mode": _cgmode} if _cgmode else {} # {} = engine defaults
_ENGINE_ARGS = AsyncEngineArgs(
compilation_config=_comp_cfg,
model=CKPT,
quantization="autoround_mixed",
hf_overrides=_ov,
trust_remote_code=True,
tensor_parallel_size=2,
pipeline_parallel_size=1,
enable_expert_parallel=True,
distributed_executor_backend="mp",
block_size=128, # mandatory for MSA sparse cache
attention_backend=os.environ.get("M3_ATTN_BACKEND", "TRITON_ATTN"),
max_model_len=MAXLEN,
max_num_batched_tokens=2048,
gpu_memory_utilization=0.97,
max_num_seqs=int(os.environ.get("M3_MAX_SEQS", "4")),
# M3_EAGER=0 -> cudagraphs (faster decode; M1 runs the same M3 weights this
# way). The M3 sparse attention supports UNIFORM_BATCH cudagraphs and breaks
# out the non-capturable split-K kernels via @eager_break_during_capture.
enforce_eager=os.environ.get("M3_EAGER", "1") == "1",
disable_custom_all_reduce=True, # Blackwell sm_120
dtype="bfloat16",
)
# Make the OpenAI server use OUR engine args (the CLI path mis-resolves the config).
AsyncEngineArgs.from_cli_args = classmethod(lambda cls, args: _ENGINE_ARGS)
from vllm.entrypoints.openai.api_server import run_server # noqa: E402
from vllm.entrypoints.openai.cli_args import ( # noqa: E402
make_arg_parser,
validate_parsed_serve_args,
)
from vllm.utils.argparse_utils import FlexibleArgumentParser # noqa: E402
if __name__ == "__main__":
parser = make_arg_parser(FlexibleArgumentParser())
args = parser.parse_args([
CKPT,
"--served-model-name", SERVED,
"--host", "0.0.0.0", "--port", PORT,
])
validate_parsed_serve_args(args)
print(f"[m3_official_api] starting OpenAI server on :{PORT} "
f"(model={SERVED}, maxlen={MAXLEN})", flush=True)
asyncio.run(run_server(args))