Instructions to use RecursiveSI/flint with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use RecursiveSI/flint with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="RecursiveSI/flint") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("RecursiveSI/flint") model = AutoModelForCausalLM.from_pretrained("RecursiveSI/flint", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.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(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use RecursiveSI/flint with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "RecursiveSI/flint" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "RecursiveSI/flint", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/RecursiveSI/flint
- SGLang
How to use RecursiveSI/flint 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 "RecursiveSI/flint" \ --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": "RecursiveSI/flint", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'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 "RecursiveSI/flint" \ --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": "RecursiveSI/flint", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use RecursiveSI/flint with Docker Model Runner:
docker model run hf.co/RecursiveSI/flint
Third-party port notes: flint (39.8B-A2.5B) — config/template/numerics observations
Hi — I ported Flint to a custom C++/CUDA runtime (single RTX 5090, 32 GB) from the
published BF16 checkpoint, reading config.json, chat_template.jinja,tokenizer.json and the flint_sglang wheel as the spec. The model is genuinely
interesting to work with; below are the observations I'd have wanted before
starting, plus a couple of documentation suggestions. Everything here is
reproducible from the checkpoint alone.
What was unusually easy (thank you)
- The learned-sink semantics are documented by code, not folklore. The
fa4-learned-sink-fix.patchmakes the intent unambiguous: the sink is an extra
softmax column (max and denominator include it, numerator gets no value), andsink_rescaleis exactly the streaming-softmax correction when the sink becomes
the running max. I implemented it directly from that patch and it matched. norm_topk_prob: true+ "weights are renormalized from the plain sigmoid
scores" left no ambiguity about whether the routing weights are pre- or
post-renormalization.- Per-head
q_norm/k_normare plain RMSNorm (weight form), not the 1+w
variant — one less thing to guess. - The 2-token-per-expert ceiling (
num_experts_per_tok: 6of 256) and the
ungated shared expert are simple and fast to get right. attention_bias: falseeverywhere makes fused QKV kernels trivial.
Numerics: Flint has massive activations — worth documenting
This is the single most important thing for anyone quantizing or writing kernels:
- On a 160-token tool prompt, the residual stream reaches ~1.4e4 at layer 48,
and a single layer (46) amplifies its input by ~300× (in ≈ 29 → out ≈ 9.5e3). - In the same layer the expert gate projection reaches ~5.3e4 and the
SwiGLU product ~1.5e8. - All of that is fine in BF16 (which is why SGLang is happy), but it does not fit
FP16: my first FP16-activation port producedinfin the expert FFN at layer 47,
and NaN in the residual two layers later.
What worked for me: keep the residual stream (and the combined MoE branch) in
FP32, keep the FFN intermediates in range with power-of-two scaling
(÷64 into the gate/up GEMV, ÷8192 after the SwiGLU, restored on the
accumulate/combine). A note in the model card ("residual magnitudes up to ~1e4;
BF16 or FP32 required, FP16 overflows") would save the next integrator a day.
Related: --enable-fp32-lm-head is not cosmetic here. The head multiplies every
logit, and with these activation magnitudes it is the cheapest precision you can
buy back.
Configuration oddities
no_rope_layer_indicesis exactly thefull_attentionset. Two lists that
must stay identical are a footgun: an edit to one and not the other silently
rotates (or stops rotating) 14 of 49 layers. A single list, or a converter-time
assertion thatno_rope ⊆ full_attention, would make the invariant explicit.- There is no
expert_biastensor in the checkpoint although the plugin
createsgate.expert_biasas zeros. Fine (selection == plain sigmoid top-k),
but a reader of the config can't tell that fromconfig.jsonalone. decoder_sparse_step: 1androuter_aux_loss_coefare train-time knobs that
don't affect inference;mlp_only_layers/layer_types/no_rope_layer_indices
are the three that actually define the graph. Worth calling out which is which.sliding_window: 513includes the current token (512 preceding). This is
documented inconfiguration_flint.py, but note that SGLang'ssliding_window_sizeexcludes it — an easy off-by-one if you pass the config
value straight through.- The sink is applied in natural logit units, not scaled by
1/sqrt(head_dim). I verified this against the patch, but it is the kind of
detail that reads as a bug the first time you see it. - Sink magnitudes are not small: per layer means ≈ 2–4, with layer 0 spanning
[-9.0, +9.9]. Quantizing or dropping the sinks changes attention noticeably
(andflashinferdoesn't support sinks at all, which you already note).
Tokenizer: a custom o200k variant, not o200k
The pre-tokenizer is a 3-split Sequence:
\p{N}{1,3}isolated,- CJK
[\u4e00-\u9fa5\u3040-\u309f\u30a0-\u30ff]+isolated, - a case-run (o200k-style) 6-alternative pattern, but with
?[\p{P}\p{S}]+[\r\n]*as the punctuation alternative.
That last point matters: real o200k uses ?[^\s\p{L}\p{N}]+[\r\n/]*. For Flint,"HTTPRequest" → one token (the greedy [Upper]*[Lower]+ wins), and"</tool_call>" → "</" "tool" "_call" ">" (the optional prefix of the letter
alternative absorbs the _). Anyone reusing a stock o200k pre-tokenizer will be
subtly off on camelCase and on punctuation runs. I validated my implementation on
a 27-case adversarial corpus against tokenizer.json (digits 1..7, CJK, case
runs, contractions, multi-space lookahead, the markers) — all 27 exact.
Also: the 12 chat markers (<think>, <tool_call>, <arg_key>, …) arespecial: false, i.e. they are vocabulary pieces, not special tokens. That is
the right call (they can appear inside reasoning as single tokens), but note thatdecode(..., skip_special_tokens=True) will still print them, whileskip_special_tokens=False will also print the 132 special: true role markers.
Choosing the decode policy by hand is necessary.
pad_token_id being <|end_of_text|> (150001) — the same id as the first eos —
is fine for batching but means "pad" and "stop" are the same token; worth a line
in the card.
eos_token_id as a list
[150001, 150003, 150005, 150018] = <|end_of_text|>, <|user|>,<|observation|>, <|developer|>. This is a nice design (the turn control tokens
double as stop tokens), but many runtimes read eos_token_id as a single int
and will happily run past </think>-then-<|user|>. Documenting "four stop ids,
all required" would prevent truncated/overrun generations in non-SGLang stacks.
Chat template
- Role markers are inline:
<|system|>…<|user|>…<|assistant|>, no
separators, and the generation prompt is exactly<|assistant|>with no
trailing newline. I reproduced it byte-for-byte againstapply_chat_template
on five cases (including nested JSON arguments and parallel calls), but it is
easy to insert a newline "for symmetry" and change the model's behaviour. - With tools, the preamble is emitted as a second
<|system|>turn after the
(optional) system message. Two consecutive system turns are unusual — some
servers/clients assume a single leading system message, and some templates
collapse them. Worth flagging in the card. flint_text()strips literal<think>/</think>from user content and
HTML-escapes the role markers. Good hygiene; also means you cannot pass a
literal</think>through history.- Tool calls:
<tool_call>{name}<arg_key>k</arg_key><arg_value>v</arg_value></tool_call>,
non-string values as JSON, string values raw. I render non-string values withjson.dumps(..., ensure_ascii=False)and it matches the template. reasoning_contentis a first-class history field (the template replays it as<think>…</think>). If a client drops it, multi-turn quality degrades; if a
client keeps it, it must not also re-send it incontent.
Quantization note (if you publish NVFP4 variants)
I converted to NVFP4 (4-bit body, per-16 E4M3 scales + global scale,
FP16 edges/router/norms/sinks). Two measurements against a BF16 CPU reference of
the same checkpoint (greedy, same ids):
- Dense side needed a residual plane. With attention + dense MLP + shared
expert carrying a second NVFP4 plane (w − dequant(base), accumulated), the
first-step top-1 logit at a 160-token prefix moved from −2.10 to −1.06 relative
to the source; at a 56-token prefix, −3.70 → −0.99. - Routed experts at 4 bits remained the dominant error. Top-1 agreed with the
source at prefixes 8/16/32 and flipped at ~64 tokens. Doubling the expert bytes
(+21 GB) does not fit a 32 GB card, so on this hardware the honest statement is
"4-bit experts change long-prompt greedy continuations". Anyone shipping a
quantized build may want ~8 bits on the routed experts, or to be explicit that
long-context agentic behaviour is precision-sensitive.
(For reference, the control prompt "The capital of France is" matches the source
token-for-token for 32 greedy steps even at 4-bit experts, and the model reasons
first: <think>…The capital of France is Paris…</think>The capital of France is **Paris**.)
Minor
max_position_embeddings: 262144with 35 sliding layers at 513 means a hybrid
KV cache: per-layer window sizes must be honoured, andhybrid_layer_pattern/is_hybrid_swain the config are exactly what a runtime needs — thanks for
shipping those.architectures: ["FlintForCausalLM"]withtransformers_version: 5.12.1but no
modeling code in the repo:AutoTokenizerwarns "model type flint → model
type ''" on load. Harmless, but a one-line note ("transformers modeling code is
not included; use the SGLang kit") is already in the README — maybe repeat it in
the model card's "Getting started".
None of the above is a complaint about the model itself — it is a well-specified
checkpoint with unusually complete semantics (the FA4 sink patch in particular is
a gift). It is mostly documentation-level: the BF16-scale activations, the two
identical layer lists, the custom pre-tokenizer, and the four stop ids are the
four things that cost the most time to discover.
Happy to share the details of the reference implementation / the parity corpus if
that's useful.