Add MiniCPM5-2B cookbook (#38295)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
62a4a6ea0e
commit
e4008de757
@@ -0,0 +1,55 @@
|
||||
// One entry per cell `match` tuple (same keys as the config cells).
|
||||
//
|
||||
// Only the RTX 5090 row carries numbers. They were measured on a single
|
||||
// RTX 5090 32GB (`zijiexia-5090-1gpu`, `lmsysorg/sglang:dev` @ 30705c004c) with
|
||||
// `python3 -m sglang.bench_serving`, random 1024/1024, against the exact cell
|
||||
// command on this page — including `--mem-fraction-static 0.75
|
||||
// --cuda-graph-max-bs 128`, which is what keeps concurrency 64 and 128
|
||||
// graph-backed (see the model page's Configuration Tips).
|
||||
//
|
||||
// `tokens_per_sec_per_gpu` is total (input+output) per GPU = the measured
|
||||
// output throughput x 2 at 1024/1024 on one GPU.
|
||||
//
|
||||
// Two operating points only: concurrency 1 (single-user latency) and 128 (the
|
||||
// saturated end of the sweep). The intermediate 8 / 32 / 64 rows were measured
|
||||
// but are not published here.
|
||||
//
|
||||
// The source runs did not restate a percentile, so the values are recorded as
|
||||
// Mean (bench_serving's headline lines) rather than claimed as P50 — same
|
||||
// convention as the LFM2.5 page. TTFT was not captured at concurrency 128, so
|
||||
// that cell carries `null` rather than a back-filled guess.
|
||||
//
|
||||
// No accuracy rows: the only GSM8K numbers taken so far came from a custom
|
||||
// chat-template harness, not the `sgl-eval run gsm8k` command the Reproduce
|
||||
// modal would show, and the two are not comparable.
|
||||
|
||||
export const benchmarks = [
|
||||
{
|
||||
match: { hw: "rtx5090", variant: "default", quant: "bf16", nodes: "single" },
|
||||
sglang_version: "dev @ 30705c004c",
|
||||
latencyPercentile: "Mean",
|
||||
speed: [
|
||||
{ workload: { dataset: "random", isl: 1024, osl: 1024, max_concurrency: 1 },
|
||||
ttft_ms: 34, tpot_ms: 4.0, tokens_per_sec_per_gpu: 496 },
|
||||
{ workload: { dataset: "random", isl: 1024, osl: 1024, max_concurrency: 128 },
|
||||
ttft_ms: null, tpot_ms: 11.8, tokens_per_sec_per_gpu: 19280 },
|
||||
],
|
||||
},
|
||||
{
|
||||
// Single DGX Spark (GB10), same image and commit as the RTX 5090 row.
|
||||
// TTFT is Mean; at concurrency 64 the Median is 246 ms, because the first
|
||||
// wave of requests all queue behind one prefill.
|
||||
match: { hw: "dgx-spark", variant: "default", quant: "bf16", nodes: "single" },
|
||||
sglang_version: "dev @ 30705c004c",
|
||||
latencyPercentile: "Mean",
|
||||
speed: [
|
||||
{ workload: { dataset: "random", isl: 1024, osl: 1024, max_concurrency: 1 },
|
||||
ttft_ms: 85, tpot_ms: 27.7, tokens_per_sec_per_gpu: 72 },
|
||||
{ workload: { dataset: "random", isl: 1024, osl: 1024, max_concurrency: 64 },
|
||||
ttft_ms: 1373, tpot_ms: 42.9, tokens_per_sec_per_gpu: 2892 },
|
||||
],
|
||||
},
|
||||
// Pending — no numbers taken on these platforms yet.
|
||||
{ match: { hw: "h200", variant: "default", quant: "bf16", nodes: "single" } },
|
||||
{ match: { hw: "rtx6000", variant: "default", quant: "bf16", nodes: "single" } },
|
||||
];
|
||||
@@ -0,0 +1,234 @@
|
||||
// Single `export const config` literal — no spreads/calls/IIFE (Mintlify re-evals at hydration).
|
||||
// Cells are denormalized: no `--nnodes`/`--node-rank`/`--dist-init-addr`/`--host`/`--port` literals — engine injects them.
|
||||
//
|
||||
// MiniCPM5-2B: 2.5B dense `LlamaForCausalLM` (42 layers, GQA 16Q/2KV, 131072
|
||||
// context). Standard architecture — no custom kernels and no model-code fork —
|
||||
// so every supported card runs it single-GPU at TP=1 with the stock backend.
|
||||
// That leaves one recipe per card, hence a single variant / quantization /
|
||||
// strategy / node option and no parallelism flags in any cell.
|
||||
//
|
||||
// Recipes are the OpenBMB model card's SGLang commands, rewritten from
|
||||
// `python -m sglang.launch_server` to `sglang serve`, plus the parser pair the
|
||||
// model needs to be usable through the OpenAI API. Both are baked into every
|
||||
// cell, so the Parsers card in the Playground reads as an opt-OUT:
|
||||
// --tool-call-parser minicpm5 the model emits XML-style
|
||||
// `<function name="f"><param name="p">v</param></function>`; without the
|
||||
// detector `tool_calls` comes back None and the XML lands in `content`.
|
||||
// --reasoning-parser qwen3 the chat template is Qwen-style
|
||||
// (`<|im_start|>` + `<think>`) and there is no `minicpm5` reasoning
|
||||
// detector, so `qwen3` is the one that applies; without it `</think>`
|
||||
// leaks into `content`.
|
||||
//
|
||||
// DSpark is the separately published draft checkpoint
|
||||
// (openbmb/MiniCPM5-2B-DSpark). It is orthogonal to the card grid, so it is an
|
||||
// overlay row rather than a match dim. No DSpark speed numbers are published:
|
||||
// the speedup tracks acceptance length, which moves with the prompt
|
||||
// distribution, and a random-token dataset inflates it above real traffic.
|
||||
|
||||
export const config = {
|
||||
modelName: "MiniCPM5-2B",
|
||||
|
||||
supportedHardware: ["h200", "rtx6000", "rtx5090", "dgx-spark"],
|
||||
|
||||
// RTX PRO 6000 and RTX 5090 (SM120 / Blackwell workstation + desktop) are not
|
||||
// datacenter parts, so the shared HARDWARE_CATALOG in _deployment.jsx does not
|
||||
// carry them. Ids/labels match the DeepSeek-V4 and Qwen3.8-27B configs.
|
||||
hardware: [
|
||||
{ id: "rtx6000", label: "RTX PRO 6000", vram: "96GB", vendor: "blackwell" },
|
||||
{ id: "rtx5090", label: "RTX 5090", vram: "32GB", vendor: "blackwell" },
|
||||
],
|
||||
|
||||
matchDims: [
|
||||
{ id: "variant", title: "Model Variant", options: [
|
||||
{ id: "default", label: "Default" },
|
||||
] },
|
||||
{ id: "quant", title: "Quantization", options: [
|
||||
{ id: "bf16", label: "BF16" },
|
||||
] },
|
||||
{ id: "nodes", title: "Nodes", options: [
|
||||
{ id: "single", label: "Single Node" },
|
||||
] },
|
||||
],
|
||||
|
||||
overlayDims: [
|
||||
{
|
||||
id: "spec",
|
||||
title: "Speculative Decoding",
|
||||
default: "none",
|
||||
options: [
|
||||
{ id: "none", label: "None" },
|
||||
{
|
||||
id: "dspark", label: "DSPARK",
|
||||
// Verbatim from the model card's DSpark command, including
|
||||
// `--trust-remote-code`: the base checkpoint is plain Llama and does
|
||||
// not need it, the draft checkpoint's config does. gamma = 7, so the
|
||||
// verify window is 8 tokens.
|
||||
flags: [
|
||||
"--trust-remote-code",
|
||||
"--speculative-algorithm DSPARK",
|
||||
"--speculative-draft-model-path openbmb/MiniCPM5-2B-DSpark",
|
||||
"--speculative-dspark-block-size 7",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
modelNames: {
|
||||
"default|bf16": "openbmb/MiniCPM5-2B",
|
||||
},
|
||||
|
||||
placeholders: {
|
||||
HOST_IP: { target: "command", label: "Bind host", default: "0.0.0.0" },
|
||||
PORT: { target: "command", label: "Bind port", default: "30000" },
|
||||
HF_TOKEN: { target: "command", label: "HF token (Docker)", default: "<your-hf-token>" },
|
||||
CURL_HOST: { target: "curl", label: "Server host", default: "localhost" },
|
||||
CURL_PORT: { target: "curl", label: "Server port", default: "30000" },
|
||||
},
|
||||
|
||||
curl: `curl http://{{CURL_HOST}}:{{CURL_PORT}}/v1/chat/completions \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-d '{ "model": "{{MODEL_NAME}}", "messages": [{"role":"user","content":"Who are you? Please briefly introduce yourself."}] }'`,
|
||||
|
||||
// Reproduce command for the Benchmark card's "⚡ Reproduce" modal. No
|
||||
// `accuracy` entry: the page carries no accuracy numbers yet.
|
||||
benchmarkCommands: {
|
||||
speed:
|
||||
`python3 -m sglang.bench_serving \\
|
||||
--backend sglang \\
|
||||
--host {{CURL_HOST}} --port {{CURL_PORT}} \\
|
||||
--model {{MODEL_NAME}} \\
|
||||
--dataset-name {{DATASET}} \\
|
||||
--random-input-len {{ISL}} --random-output-len {{OSL}} \\
|
||||
--num-prompts {{NUM_PROMPTS}} --max-concurrency {{MAX_CONCURRENCY}} \\
|
||||
--flush-cache`,
|
||||
numPromptsByConc: { 1: 10, 128: 512 },
|
||||
},
|
||||
|
||||
// MiniCPM5 support (the `minicpm5` tool-call parser and the DSPARK draft
|
||||
// worker) ships in the SGLang dev image.
|
||||
dockerImages: {
|
||||
h200: "lmsysorg/sglang:dev",
|
||||
rtx6000: "lmsysorg/sglang:dev",
|
||||
rtx5090: "lmsysorg/sglang:dev",
|
||||
"dgx-spark": "lmsysorg/sglang:dev",
|
||||
},
|
||||
|
||||
// Pre-selects the issue template's `model` field on "Submit verified cell".
|
||||
github: {
|
||||
cookbookModel: "openbmb/MiniCPM5-2B",
|
||||
},
|
||||
|
||||
playgroundFeatures: {
|
||||
// The model fits one GPU on every supported card, so TP=1 is the verified
|
||||
// shape; TP=2 is exposed for experimentation only.
|
||||
attention: {
|
||||
knobs: [
|
||||
{ id: "tp", label: "TP", values: [null, 1, 2] },
|
||||
],
|
||||
},
|
||||
|
||||
// ----- Card: "Parsers" -----
|
||||
// Opt-OUT: both flags are already in every cell, so the handler derives
|
||||
// each chip as on and strips the flag when one is toggled off. The
|
||||
// reasoning slug is `qwen3`, not `minicpm5` — see the header note.
|
||||
parsers: {
|
||||
items: [
|
||||
{ id: "reasoning", label: "Reasoning Parser", flag: "--reasoning-parser qwen3" },
|
||||
{ id: "toolCall", label: "Tool Call Parser", flag: "--tool-call-parser minicpm5" },
|
||||
],
|
||||
},
|
||||
|
||||
// ----- Card: "Speculative Decoding" -----
|
||||
// Same DSpark flags as the Deploy panel's overlay row, so the two paths
|
||||
// compose an identical command.
|
||||
speculative: {
|
||||
options: [
|
||||
{ id: "current", label: "Inherited from base" },
|
||||
{ id: "off", label: "Off (greedy)" },
|
||||
{ id: "dspark", label: "DSpark",
|
||||
flags: ["--trust-remote-code",
|
||||
"--speculative-algorithm DSPARK",
|
||||
"--speculative-draft-model-path openbmb/MiniCPM5-2B-DSpark",
|
||||
"--speculative-dspark-block-size 7"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// One recipe per card — the model card's SGLang launch line, plus the
|
||||
// `minicpm5` tool-call parser it recommends for agent workloads and the
|
||||
// `qwen3` reasoning parser its `<think>` template needs.
|
||||
cells: [
|
||||
{
|
||||
match: { hw: "h200", variant: "default", quant: "bf16", nodes: "single" },
|
||||
verified: true,
|
||||
env: [],
|
||||
flags: [
|
||||
"--model-path {{MODEL_NAME}}",
|
||||
"--reasoning-parser qwen3",
|
||||
"--tool-call-parser minicpm5",
|
||||
"--host {{HOST_IP}}",
|
||||
"--port {{PORT}}",
|
||||
],
|
||||
},
|
||||
{
|
||||
// Verification round still open on this card. `verificationStatus` alone,
|
||||
// with NO `verified: true` baseline: the boolean is what the Playground
|
||||
// reads for its own badge, so leaving it on would make the Playground
|
||||
// claim "Verified" while the Deploy panel says the round is in progress.
|
||||
match: { hw: "rtx6000", variant: "default", quant: "bf16", nodes: "single" },
|
||||
// Flat string, not a predicate: this cell is in-progress with or without
|
||||
// the DSPARK overlay, so there is nothing for the selection to switch on.
|
||||
verificationStatus: "in-progress",
|
||||
env: [],
|
||||
flags: [
|
||||
"--model-path {{MODEL_NAME}}",
|
||||
"--reasoning-parser qwen3",
|
||||
"--tool-call-parser minicpm5",
|
||||
"--host {{HOST_IP}}",
|
||||
"--port {{PORT}}",
|
||||
],
|
||||
},
|
||||
{
|
||||
// The 32GB card is the one where the default KV pool starves decode
|
||||
// CUDA-graph capture: with defaults the pool takes 473,718 tokens / 19 GB
|
||||
// and leaves 4.6 GB, so capture stops around bs=48 and every larger batch
|
||||
// runs eager (3810 tok/s at concurrency 64). The pair below gives back
|
||||
// 4% of the pool -- still hugely oversized for a 2.5B model -- and keeps
|
||||
// batches up to 128 graph-backed (7454 tok/s at the same concurrency).
|
||||
// The two flags go together: raising the cap without freeing the memory
|
||||
// just lets SGLang clamp capture back down.
|
||||
match: { hw: "rtx5090", variant: "default", quant: "bf16", nodes: "single" },
|
||||
verified: true,
|
||||
env: [],
|
||||
flags: [
|
||||
"--model-path {{MODEL_NAME}}",
|
||||
"--reasoning-parser qwen3",
|
||||
"--tool-call-parser minicpm5",
|
||||
"--mem-fraction-static 0.75",
|
||||
"--cuda-graph-max-bs 128",
|
||||
"--host {{HOST_IP}}",
|
||||
"--port {{PORT}}",
|
||||
],
|
||||
},
|
||||
{
|
||||
// GB10 has no discrete VRAM, so `mem_get_info()` reports all 128GB of
|
||||
// unified system memory and the default fraction claims ~89GB for KV --
|
||||
// leaving ~5GB for the OS, which kills the node during warmup with no
|
||||
// traceback and no OOMKilled event. 0.30 is required, not tuning; it
|
||||
// still leaves a 658k-token pool.
|
||||
match: { hw: "dgx-spark", variant: "default", quant: "bf16", nodes: "single" },
|
||||
verified: true,
|
||||
env: [],
|
||||
flags: [
|
||||
"--model-path {{MODEL_NAME}}",
|
||||
"--reasoning-parser qwen3",
|
||||
"--tool-call-parser minicpm5",
|
||||
"--mem-fraction-static 0.30",
|
||||
"--cuda-graph-max-bs 128",
|
||||
"--host {{HOST_IP}}",
|
||||
"--port {{PORT}}",
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user