Instructions to use BAAI/AREX-2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use BAAI/AREX-2 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="BAAI/AREX-2") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("BAAI/AREX-2") model = AutoModelForMultimodalLM.from_pretrained("BAAI/AREX-2", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use BAAI/AREX-2 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "BAAI/AREX-2" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "BAAI/AREX-2", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/BAAI/AREX-2
- SGLang
How to use BAAI/AREX-2 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "BAAI/AREX-2" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "BAAI/AREX-2", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "BAAI/AREX-2" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "BAAI/AREX-2", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use BAAI/AREX-2 with Docker Model Runner:
docker model run hf.co/BAAI/AREX-2
Dual Spark GB10 - SGLang - DFLASH2
BAAI/AREX-2 FP8 + DFlash2 on SGLang β TP=2 across 2Γ GB10
Companion to the Flash-Next card. Same launcher, same fabric, same two boxes;
a different model and a different outcome. AREX-2 is a Qwen3.8-27B fine-tune
with a vision tower and Gated DeltaNet layers, served here at FP8 alongside the
embedding indexer and a co-resident image backend.
The headline is in the Review: it is measurably slower than the baseQwen3.8-27B-NVFP4 it derives from, and two correctness defects showed up in
long sessions. Both are recorded with sample counts rather than summarised.
Hardware
| Nodes | 2Γ GB10, 121.7 GiB unified memory each |
| Fabric | ConnectX, 200 Gb/s, direct-attached |
| Transport | RoCE v2, gid 3/3, MTU 9000 |
| Image NCCL | 2.30.7 |
| GPU Direct RDMA | disabled β see Not measured |
Unified memory means CUDA allocations do not reclaim page cache, and below
roughly 10 GiB free the box freezes rather than OOMs. Everything below is
written around that floor.
Model and image
- Target:
prithivMLmods/AREX-2-FP8, 67 GB on disk - Drafter:
z-lab/Qwen3.8-27B-DFlash2 - Image:
flashnext-sglang:pr45142(upstream dev + PR #45142)
The drafter was trained against base Qwen3.8-27B, not this fine-tune. It
transfers anyway β see Measured. That result is published nowhere else that we
found.
config.json reports model_type: qwen3_5, architectures: ["Qwen3_5ForConditionalGeneration"], and carries a vision_config. SGLang
instantiates Qwen3_5ForCausalLM from it β the text-only class β which is
worth knowing before reasoning about the vision path.
One trap for anyone loading this model through a tool that builds it withfrom_config: the resulting config object has architectures set to None
rather than absent. Anything doing getattr(config, "architectures", []) getsNone back and the default never applies. ModelOpt's is_multimodal_model hits
this and raises TypeError: 'NoneType' object is not iterable; upstream fixed
it with or [].
Launch
Through the shared launcher:
./start-cluster.sh arex2
The resolved serve configuration, read off the startup log rather than from the
script:
| flag | value |
|---|---|
--tp-size / --nnodes |
2 / 2, rendezvous 192.168.100.1:29511 |
--context-length |
262144 |
--max-total-tokens |
1310720 |
--max-running-requests |
24 |
--max-mamba-cache-size |
120 |
--mamba-ssm-dtype |
float32 |
--mem-fraction-static |
0.85 |
--page-size |
64 |
--chunked-prefill-size |
4096 (max prefill tokens 16384) |
--kv-cache-dtype |
fp8_e4m3 |
--mamba-radix-cache-strategy |
extra_buffer |
--cuda-graph-max-bs-decode |
24, prefill graph disabled |
| speculative | DFLASH, 8 draft tokens, steps 1, eagle-topk 1 |
| parsers | reasoning qwen3, tool-call qwen3_coder |
| FlashInfer autotune | on |
Three things differ from the base-model profile and are deliberate:
--mamba-ssm-dtype float32. This checkpoint's config asks for float32 where
the other profiles force bfloat16. It doubles GDN state traffic. The bfloat16
comparison has not been run.
8 draft tokens, not 4. Flash-Next caps at 4 because its QSA compress ratio
raises a hard NotImplementedError above it. This model has no such cap.
No --moe-runner-backend flag. Dense checkpoint, so the g1_scale_c
SM12x MoE-runner misroute does not apply. That bug is MoE-path only.
Co-residents
Both are started by the launcher after the LM is serving:
| where | port | what | |
|---|---|---|---|
| Indexer | head | 8002 | vllm-embed, nginx proxies /v1/embeddings |
| Image backend | worker | 8188 | ComfyUI 0.28.0, torch 2.11.0+cu130 |
The image backend's installed checkpoints are FLUX.1-schnell βflux1-schnell-fp8.safetensors (17.2 GB) and flux1-schnell.safetensors
(23.8 GB bf16). Dashboards in this environment label the vision head "FLUX3";
what is on disk is FLUX.1-schnell.
It runs on the worker by design. The head carries LM rank 0 plus the
indexer and reported 13.2 GiB CUDA-visible free with no image model loaded,
against 17β24 GB for a FLUX checkpoint. The worker carries rank 1 only and had
~38 GiB. Running it on the head crosses the freeze floor on the first
generation.
Measured
Memory
At --max-mamba-cache-size 120 and mem-fraction-static 0.85, from the
server's own startup log:
| GDN state, 120 slots, float32 | 22.85 GB |
| β conv_state | 0.17 GB |
| β ssm_state | 8.51 GB |
| β intermediate ssm_state cache | 14.06 GB |
| β intermediate conv window | 0.11 GB |
| KV per token | 20.0 KB |
KV at max_total_tokens 2,294,336 (uncapped) |
45.94 GB |
KV at max_total_tokens 1,310,720 (SESSIONS=5) |
26.26 GB |
available_gpu_mem, uncapped |
11.53 GB |
The 20.0 KB/token figure was taken twice β once uncapped and once capped β and
agreed to three figures both times. GDN state is reserved per request at a
fixed size regardless of sequence length, which is why slot count caps
concurrency rather than the pool does.
--max-total-tokens is the only lever that returns memory to the system here:
SGLang sizes the KV pool to fill mem-fraction-static, so freeing state yields
a bigger pool rather than free memory, and a cap above what the server would
choose on its own frees nothing.
Speculative decoding
Over 55,796 verify calls:
| Accept length | 3.125 |
| Accept rate | 0.304 |
| Draft tokens | 8 |
The base model's drafter transfers to the fine-tune. 3.125 accepted tokens
per verify at 8 drafts, from a drafter trained on Qwen3.8-27B and never
retrained for AREX-2. For comparison, Flash-Next with its own matched drafter
records 2.52 at 4 drafts over 1.39M verify calls.
Accept rate is capped by sampling temperature, not misconfiguration: SGLang's
acceptance thresholds default to 1.0, i.e. exact match, so above temperature 0
a deterministic draft is paid p(argmax) per slot.
Throughput
Peak, over the sample counts shown:
| Peak decode | 127.3 tok/s (4,567 samples) |
| Peak prefill | 3,870 tok/s (trailing 7 days) |
| Vision tower | on |
| Vision head | on |
| Indexer | on |
Concurrency curve:
| Streams | Aggregate tok/s | Per-stream | Samples | vs solo |
|---|---|---|---|---|
| 1 | 33.4 | 33.4 | 3,190 | 1.00Γ |
| 2 | 52.1 | 26.0 | 1,025 | 0.78Γ |
| 3 | 78.7 | 26.2 | 73 | 0.79Γ |
| 4 | 98.7 | 24.7 | 2 | 0.74Γ |
Levels 1 and 2 are measured. Level 3 (n=73) and level 4 (n=2) are
indicative, not measured β the same 100-sample bar the Flash-Next card uses.
Per-stream flattens between 2 and 3 streams rather than continuing to fall,
which is the shape worth re-measuring with real sample counts before relying on
it.
Review
Slower than the model it fine-tunes
Against RadixArk/Qwen3.8-27B-NVFP4 on the same two boxes:
| AREX-2 FP8 | Qwen3.8-27B NVFP4 | AREX-2 as % | |
|---|---|---|---|
| Peak decode | 127.3 | 176.9 | 72.0% |
| Peak prefill | 3,870 | 4,065 | 95.2% |
| Solo decode | 33.4 | 53.5 | 62.4% |
| 2 streams, aggregate | 52.1 | 86.3 | 60.4% |
| 3 streams, aggregate | 78.7 | 106.7 | 73.8% |
Solo decode is the cleanest number on both sides β 3,190 and 2,793 samples β
and AREX-2 gives up 37.6% of it.
The quantization accounts for most of that. AREX-2 is FP8 at ~13.5 GB/node;
the base is NVFP4 at ~6.8 GB/node. Decode on GB10 is memory-bandwidth-bound, so
reading twice the weight bytes per token is a direct, expected cost. It is not
a configuration fault.
Two confounds make this not a pure model comparison. AREX-2 ran with the
indexer on and the vision head on; the base ran with both off. Both
co-residents take memory and some compute on the same nodes. A like-for-like
comparison needs AREX-2 re-measured with EMBED=0 FLUX=0, which has not been
done.
Where AREX-2 holds up better: it degrades less with concurrency β
0.79Γ vs 0.66Γ at three streams β and its prefill is within 5%. The gap is
almost entirely in single-stream decode.
An NVFP4 build of AREX-2 could not be made to work
Four configurations were produced and all four served degenerate output: a wall
of token id 0 (! in Qwen's vocabulary), which is argmax over flat or NaN
logits. The same serving config with the FP8 checkpoint is clean, andDRAFT=none does not change it, so neither the profile nor speculation is
implicated.
| config | result |
|---|---|
NVFP4_DEFAULT_CFG (W4A4) |
degenerate |
W4A16_NVFP4_CFG (4-bit weights, 16-bit activations) |
degenerate |
NVFP4_MLP_ONLY_CFG |
degenerate |
everything quantized but mtp* |
degenerate |
What was ruled out along the way, each by measurement:
- Not bad scales.
mlp.gate_projweight,weight_scale,weight_scale_2
andinput_scalewere compared tensor-by-tensor againstRadixArk/Qwen3.8-27B-NVFP4: identical dtypes and shapes,weight_scale_2
agreeing to four significant figures, no NaNs, no zeros. - Not 4-bit activations. W4A16 fails identically to W4A4.
- Not the GDN path specifically. The working reference quantizes it β
linear_attn.in_proj_qkvcarries 48weight_scaletensors there. - Not metadata. Both checkpoints declare
quant_method: modelopt,quant_algo: NVFP4, the sameconfig_groupsshape and the sameignorekey. - Not an SGLang guard mismatch.
qwen3_5.pyin this image passesquant_configtoQwen3_5GatedDeltaNetunconditionally; there is nomodelopt_fp4-keyed guard to miss.
The one structural difference not eliminated: the working reference has two
ignore entries (mtp*, mtp.layers.0*) while every build here excluded far
more, and SGLang's packed_modules_mapping fusesin_proj_qkvz β [in_proj_qkv, in_proj_z] and in_proj_ba β [in_proj_b, in_proj_a] at load. The "quantize everything but mtp*" build was the test of
that and also failed, so it remains unexplained. Use FP8.
Correctness defects observed
| defect | occurrences | notes |
|---|---|---|
| Chinese characters injected into context prompts | 1 | injected into the prompt context, not the completion |
| Session context corrupted after long runs | multiple | sessions over 8 hours; code output remained correct while session context was corrupted |
The long-haul pattern is the more serious of the two, because the usable
artifact is fine while the state behind it is not β a session can appear
healthy from its output alone. Neither defect has a reproducer. Both were seen
with --mamba-ssm-dtype float32, 262144 context and 120 GDN slots; whether
either is implicated is unknown.
Relative comparison
Identical section in all four cards, from one snapshot so the numbers agree
across them.
This dataset is being retired. Collection is restarting from cleared
counters, because the configurations behind these samples cannot be
reconstructed. Read the provenance table before any figure below.
Recipe lineage
| Recipe status | Built on | |
|---|---|---|
| Flash-Next | original β first SGLang-path recipe for Flash-Next + DFlash2 on 2Γ GB10 | GLM-5.3-Flash 2Γ Spark recipe (SGLANG_HOST_IP per rank, mamba sizing at TP=2 DFlash); ubehera/sglang-spark (NCCL sm_121 cubins); Flash-Next cookbook thread (PLE sharding by TP, two-Spark cell shape) |
| AREX-2 | original β no prior recipe for this model on this hardware | the Flash-Next and Qwen3.8-27B profiles in the same launcher |
| dsv41 | derived, with original patch set | sparkctl's launch model; Mia-AiLab's kit for the pinned KV pool (KV_CACHE_MEMORY_BYTES, ~774K tokens of fp8_ds_mla/rank); TensorFold grouped EXL3 prompt kernels (Apache-2.0); HeNryous's memory measurements |
| Qwen3.8-27B | pre-existing recipe | the published Qwen3.8-27B 2Γ Spark recipe |
Original findings in this set, not published elsewhere that we found:
| finding | card |
|---|---|
A base-model DFlash2 drafter transfers to a fine-tune β accept length 3.125 at 8 draft tokens over 55,796 verify calls, drafter trained on Qwen3.8-27B and never retrained for AREX-2 |
AREX-2 |
| NVFP4 of AREX-2 cannot be made to serve β four configurations, all token-0 degenerate, with bad scales, 4-bit activations, GDN quantization, metadata and the SGLang guard each eliminated by measurement | AREX-2 |
| Six GB10-specific fixes for Flash-Next + DFlash2 at TP=2, two of which became upstream issues (PR #45142 required, PR #45520 open) | Flash-Next |
nvidia-smi --query-gpu=memory.* returns [N/A] on GB10; --query-compute-apps is the only query that reports |
dsv41 |
RoCE GID must be selected by gid_attrs/types, not IPv4 match β a port exposes the same IPv4 at a v1 and a v2 index, and running v1 is silent |
Flash-Next |
Provenance
| Topology behind the samples | Counters | |
|---|---|---|
| Qwen3.8-27B | all TP=2 | cleared at cluster bring-up |
| dsv41 | TP=2 only | not cleared |
| AREX-2 | TP=2 only | not cleared |
| Flash-Next | uncertain β TP=2, or mixed with single-node NVMe-offload | not cleared |
Flash-Next is the one profile whose topology is not established. A single-node
deployment of it is possible via file-backed PLE offload β the launcher carriesPLE_OFFLOAD=1 for this model specifically, ~/ple-tables holds 48 GB on the
head, and the stop list names a flashnext-sg container distinct fromflashnext-tp2. Single-node with NVMe in the decode path is slower, so any such
samples drag a cumulative aggregate down.
Known contamination in the Flash-Next series, beyond topology:
| source | measured cost |
|---|---|
CONC=longhaul for part of the window |
227.2 β 183.0 aggregate, β19.4% |
| MTU 1500 / RoCE v1 for part of the window | prefill and bulk transfer; decode change not observed |
SESSIONS=5 since today |
32% pool cut against a 95.50% hit rate |
Counters are cumulative since process start, so the existing samples cannot be
segmented by configuration after the fact.
What the measurement says should be true and isn't
Flash-Next is a 206-shard MoE. Its decode cost is set by active parameters per
token, a fraction of the total, so it should beat a dense 27B comfortably.
It measures 8% below it. That inversion is the strongest single argument
that the Flash-Next series is not describing one configuration, and it stands
independent of any individual contamination source.
| Flash-Next | Qwen3.8-27B | dsv41 | AREX-2 | |
|---|---|---|---|---|
| Engine | SGLang | SGLang | vLLM | SGLang |
| Quantization | NVFP4 | NVFP4 | EXL3 2.8bpw | FP8 |
| Speculation / draft tokens | DFlash2 / 4 | DFlash2 / 8 | DSpark / 5 | DFlash2 / 8 |
| Indexer during measurement | off | off | off | ON |
| Vision head during measurement | off | off | off | ON |
Aggregate throughput, tok/s
| Streams | Flash-Next | Qwen3.8-27B | dsv41 | AREX-2 |
|---|---|---|---|---|
| 1 | 49.0 | 53.5 | 60.6 | 33.4 |
| 2 | 80.8 | 86.3 | 74.5 | 52.1 |
| 3 | 104.2 | 106.7 | 84.3 | 78.7 |
| 4 | 121.5 | β | 94.8 | 98.7 |
| 5 | 132.5 | β | β | β |
| 6 | 142.4 | β | β | β |
| 7 | 156.4 | β | β | β |
| 8 | 171.9 | β | β | β |
| 9 | 177.9 | β | β | β |
| 10 | 190.5 | β | β | β |
| 11 | 191.8 | β | β | β |
| Peak decode | 213.7 | 176.9 | 126.0 | 127.3 |
| Peak prefill | 4,539 | 4,065 | not recorded | 3,870 |
No winner is marked. Qwen3.8-27B is the only column with established provenance
and cleared counters, and it is also the thinnest β three levels, two clearing
100 samples.
Per-stream retention β do not rank on this
| c2 | c3 | c4 | |
|---|---|---|---|
| AREX-2 | 0.78Γ | 0.79Γ | 0.74Γ |
| Flash-Next | 0.82Γ | 0.71Γ | 0.62Γ |
| Qwen3.8-27B | 0.81Γ | 0.66Γ | β |
| dsv41 | 0.61Γ | 0.46Γ | 0.39Γ |
Every value here is a ratio against the c1 figure, which may be projected
rather than sampled. If it is, these are ratios to a projection and the
ranking they imply β including AREX-2 retaining best β is not established. The
aggregate columns above stand on their own; this table does not.
Cost of the first additional stream
| c1 β c2 | |
|---|---|
| Flash-Next | β0.176 |
| Qwen3.8-27B | β0.193 |
| AREX-2 | β0.220 |
| dsv41 | β0.385 |
Same c1 dependency as the table above. dsv41's step is more than twice any
other, which is large enough to survive a projected baseline being somewhat off,
but it is not a clean figure either.
Peak against the highest measured aggregate
| peak | curve max | ratio | |
|---|---|---|---|
| Flash-Next | 213.7 | 191.8 | 1.11Γ |
| AREX-2 | 127.3 | 98.7 | 1.29Γ |
| dsv41 | 126.0 | 94.8 | 1.33Γ |
| Qwen3.8-27B | 176.9 | 106.7 | 1.66Γ |
Sample depth
| levels | levels β₯ 100 samples | n at top level | decode samples | |
|---|---|---|---|---|
| Flash-Next | 11 | 8 | 8 | 24,050 |
| dsv41 | 4 | 3 | 63 | β |
| AREX-2 | 4 | 2 | 2 | 4,567 |
| Qwen3.8-27B | 3 | 2 | 10 | 3,167 |
Depth is not integrity. Flash-Next has the most samples and the least
established provenance.
Not measured
Stated plainly because it would otherwise look like data:
- Concurrency above 2 streams. n=73 at three and n=2 at four. The curve
above two streams is a shape, not a figure. - The like-for-like comparison against the base model. AREX-2's numbers
carry a co-resident indexer and image backend; the base model's do not. --mamba-ssm-dtype bfloat16. Would halve 22.85 GB of GDN state traffic
and is the obvious response to the decode gap. Never run; the checkpoint's
config asks for float32.- Vision, end to end. The tower loads, but no image request has been
confirmed against this model with a data-URI payload.models.jsonstill
advertises"input": ["text"], so the agent harness cannot send one. - Cold prefill above 32K. Silent worker-rank death near 62K and a
multi-node wedge at 95β100K are reported for 262K-context configs on this
hardware. 32K passes here; above that is untested. - Whether GPU Direct RDMA helps. It is off:
nvidia-peermemdoes not load
on aarch64 (missingib_register_peer_memory_client), and DMA-BUF reports
unavailable even withNCCL_DMABUF_ENABLE=1,NCCL_NET_GDR_LEVEL=SYSandNCCL_NET_GDR_READ=1. Collectives stage through host memory. On unified
memory, host and device are the same physical DRAM, so the usual argument for
GDR is weaker than on a discrete card β but what it would buy here is
unmeasured either way. - MTU persistence. 9000 is set and verified at launch with an 8972-byte
DF ping, but is not persistent across reboot. No netplan or NetworkManager
config holds it.
Terms, because they are easy to conflate
| term | here it means |
|---|---|
| unified | one model across both boxes (TP=2) |
| split | two different models, one per box |
| slot | a GDN/mamba state reservation, --max-mamba-cache-size |
| stream | one agent session, which issues 1β3 concurrent requests |
| pool | the KV cache, sized in tokens by --max-total-tokens |
Credits
This profile is original; it inherits its scaffolding:
- The launcher, fabric handling and the six GB10 fixes β
flashnext-dflash2-2x-gb10.md - PR #45142 β DFlash aux hidden-state capture, required by the image
z-lab/Qwen3.8-27B-DFlash2β drafter, trained against the base model- The
is_multimodal_modelarchitectures=Nonefix β upstream ModelOpt RadixArk/Qwen3.8-27B-NVFP4β the working reference checkpoint the failed NVFP4 builds were compared against