Instructions to use litert-community/gpt-oss-120b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT-LM
How to use litert-community/gpt-oss-120b with LiteRT-LM:
# LiteRT-LM runs on various platforms (Android, iOS, Windows, Linux, macOS, IoT, Web/WASM) # and supports many APIs (C++, Python, Kotlin, Swift, JavaScript, Flutter). # For platform-specific integration guides, please refer to the official developer website: # https://ai.google.dev/edge/litert-lm # To try LiteRT-LM, the easiest way is to use our CLI tool. # 1. Install the LiteRT-LM CLI tool: pip install -U litert-lm # 2. Download and run this model locally: # See: https://ai.google.dev/edge/litert-lm/cli # A single .litertlm file in the repo is picked automatically; otherwise the CLI asks which one to run # (or pass its name right after the repo id). litert-lm run \ --from-huggingface-repo=litert-community/gpt-oss-120b \ --prompt="Write me a poem"
- Notebooks
- Google Colab
- Kaggle
gpt-oss-120b LiteRT-LM
This repository is a conversion of openai/gpt-oss-120b to LiteRT-LM, written one layer at a time with the LiteRT Tensor API and packed with litert-lm-builder 0.19.1. It runs on the CPU with LiteRT-LM 0.19.1 from PyPI on a Mac with 128 GB of memory: on an Apple M4 Max it prefills 49.94 tokens/s and decodes 4.75 tokens/s at 1,024 / 256 tokens (2026-11-10). The 77.3 GB file is mapped into memory whole, and the process adds 5.5 to 7.0 GB: peak resident memory was 80.6 GB for the Python example below and 82.1 GB for the benchmark.
openai/gpt-oss-120b is OpenAI's open-weight reasoning model: 36 layers alternating sliding-window (128 tokens) and full attention, 128 experts per layer with 4 used for each token, 117B parameters with 5.1B active (source model card). It writes its reasoning in an analysis channel before the answer (the harmony format).
What differs from the source: the experts' MXFP4 weights are re-quantized to int4, and the expert activation is LiteRT-LM's GELU (tanh form) without the source's clamp at 7, because the moe op of LiteRT-LM 0.19.1 has no clamp and no SiLU. Without the clamps the computation differs noticeably in the later layers (the activation note below has the numbers). On 12 questions, greedy replies from this file got the same verdict as the source model's on all 12 (Accuracy).
Available Artifacts
| File | Quantization | Context | Size |
|---|---|---|---|
gpt_oss_120b_mixed_int4_ekv4096.litertlm |
experts int4 blockwise (block 32, fp32 scales); attention q / k / v / o and lm_head int8 per channel; router, biases, sinks, norms and token table fp32; fp32 KV cache | 4096 | 77.26 GB |
Conversion Notes
- The decoder is written with the LiteRT Tensor API one tensor at a time from the checkpoint at revision
b5c939de, one layer read at a time. No fp32 copy of the whole model is made. - Each layer's 128 experts are one TFLite custom op
moe, which LiteRT runs with XNNPACK on the CPU: only the 4 routed experts are computed for each token. The router (with its bias), the top 4 and the softmax over their 4 logits are ordinary ops in the graph. - Runtime floor: LiteRT-LM 0.19.0 (
min_runtime_versionin the file's metadata). Themoeop of the experts declaresactivation='gelu_tanh'; the GPU moe kernel of LiteRT-LM 0.18 only supports activation='gelu'. - Experts: each MXFP4 block of 32 values is re-quantized to int4 with an fp32 scale chosen per block (the least squared error over a grid of candidates; a block whose values fit int4 is exact). Against the MXFP4 values, the relative RMS error is 5.8 to 6.6 % per expert tensor (gate, up and down of the 36 layers, 108 tensors; mean 6.0 %). Int4 block scales below 2^-14 are raised to 2^-14 (446 blocks).
- The experts' biases: the
moeop has no bias input, so each expert's input gets one more block of 32 columns that carries a constant (1/1024) and the biases (2,880 → 2,912 inputs; the expert weights grow by 2.2 %). This is exact. - The activation: the source computes
(clamp(up, -7, 7) + 1) · g · σ(1.702 g)withg = min(gate, 7). This file computes(up + 1) · GELU_tanh(gate):x · σ(1.702 x)is replaced by GELU in its tanh form (the largest difference between the two is 0.021, at x = 2.29) and the clamps are dropped. The clamps matter, more so in the later layers. The activation was compared with the source's clamped SwiGLU on the source's Transformers code in float32, over all 36 layers and three conversations with the source model's replies (718 tokens, 413 of them reply positions), once as written and once with this file's activation (the MXFP4 weights unchanged): the share of routed (token, expert) pairs that reach a clamp grows from 4 % in layer 0 to 48 % in layer 11, and is 72 to 100 % in every layer from 14 on. With this file's activation, the hidden state differs from the source's by 1.4 to 5.5 % (relative L2) in layers 0 to 5 and by 21 to 66 % from layer 6 on. At the output, that variant still picks the source's next token at 399 of the 413 reply positions (96.6 %, teacher-forced on the source's replies); this file adds the int4 experts on top. The Accuracy section compares whole replies. - Attention: q / k / v / o with their biases, the per-head sink logits, the 128-token window on the even layers (a mask), and YaRN RoPE (factor 32, the source's settings) are written as in the source's Transformers code.
- The residual add after each
moeop is written as(x + moe) + moe * 0(numerically exact), so a GPU delegate cannot fuse the add into the moe kernel. - Signatures
prefill_128,prefill_1024anddecode; the KV cache holds 4,096 tokens. - The chat template is the source's
chat_template.jinjawith four edits for LiteRT-LM:.items()→|items(the runtime's template engine has no Python methods); every earlier assistant turn takes the source's inference form (final+<|end|>, the analysis dropped), as LiteRT-LM needs each turn's render to start with the previous one; message content as a list of typed parts; a guard for an empty message list. For 7 conversations, with and without tools, it gives the same text as the source's template through Transformers and through LiteRT-LM 0.19.1's own template engine. - Reply channels: the metadata declares the harmony headers as channels, so a reply's content is the
finalmessage's text alone. The analysis goes tochannels["analysis"]andreasoning_content(and is left out of the KV cache before the next turn, as LiteRT-LM does for a reasoning channel); the headers (<|start|>assistant<|channel|>final<|message|>and the tool-call headers) go to their own channels. - Reasoning effort: the template's default
medium. - Tool calls: the metadata type is
generic_model, so LiteRT-LM returns a tool call as text: the app reads the function name from thecommentary_headerortool_headerchannel and the arguments from the content, and the reply stops at<|call|>(stop token 200012). To continue after a tool call, start a new conversation with the history, including the assistant'stool_callsand the tool's result. - The sampler: the source's
generation_config.jsonsetsdo_sampleand no temperature, top_p or top_k, so the files carry Transformers' defaults for that case (typeTOP_P: top_k 50, top_p 1.0, temperature 1.0). Stop tokens: 200002<|return|>, 199999<|endoftext|>, 200012<|call|>. - The conversion steps and how each part was checked: john-rocky/hf-to-litertlm: gpt_oss_120b_work.
Performance
Speed still being tuned (updates follow).
Apple M4 Max (128 GB, macOS 27.0), LiteRT-LM 0.19.1 from PyPI, 1,024 prefill tokens and 256 decode tokens, measured on 2026-11-10.
| File | Backend | Prefill (tok/s) | Decode (tok/s) | Time to first token | Peak footprint |
|---|---|---|---|---|---|
gpt_oss_120b_mixed_int4_ekv4096.litertlm |
CPU, 8 threads | 49.94 | 4.75 | 20.7 s | 6.98 GB |
- Command:
litert-lm benchmark gpt_oss_120b_mixed_int4_ekv4096.litertlm --backend cpu -p 1024 -d 256 --max-num-tokens 4096 --runs 1 --cache no --cpu-thread-count 8(one run after the warm-up run). - Peak footprint: from
/usr/bin/time -l. It leaves out the pages of the mapped file; the peak resident memory, which counts them, was 82.1 GB. - The file's pages were already in memory when the run started.
- The GPU has not been measured yet. No phone was tried: the file is 77.3 GB.
Accuracy
12 questions and a 3-turn conversation on the CPU, greedy on both sides, up to 2,048 generated tokens, a new conversation for each question, the source's chat template with its default system message (reasoning effort medium) and no tools. The questions: two sums, two facts, an opposite word, a French translation, 0.9 vs 0.11, a rhyme, and four code tasks (two Python functions, a bug fix and a SQL query) run on test cases. The 3 turns ask for a Python function scale(xs, k), then a side question, then an edit of the function. Each reply's final channel is judged by the same function on both sides.
| File | Backend | Same verdict as the source model | Ends with a stop token | 3 turns |
|---|---|---|---|---|
gpt_oss_120b_mixed_int4_ekv4096.litertlm |
CPU, 8 threads | 12 / 12 | 12 / 12 | pass |
- In the 3-turn conversation, the source model's third reply renamed the function, so its tests could not call it; this file's third reply kept the name and passed its 4 tests.
- The source model's replies came from MLX (mlx_lm 0.31.3) running the source's MXFP4 experts as MXFP4 and every other parameter in float32. Teacher-forced through the source's Transformers code in float32, that generator's next token matched at all 413 positions of three of its replies (questions 1, 9 and 10, up to 256 tokens each).
Runtime notes
- Long replies. With LiteRT-LM 0.19.1, a blocking
send_messagethat is still generating after 10 minutes fails withDEADLINE_EXCEEDED(the engine's default timeout). This file decodes about 4.8 tokens/s on the CPU above, so 10 minutes is about 2,800 tokens, reasoning included. Usesend_message_async, or capmax_output_tokens. - A message that does not fit. On LiteRT-LM 0.19.1, after a message fails because it does not fit in the context window (4,096 tokens), every later message in that conversation fails too; start a new conversation.
Run
pip install litert-lm==0.19.1
hf download litert-community/gpt-oss-120b gpt_oss_120b_mixed_int4_ekv4096.litertlm --local-dir .
import litert_lm
engine = litert_lm.Engine(
"gpt_oss_120b_mixed_int4_ekv4096.litertlm",
backend=litert_lm.Backend.CPU(thread_count=8),
cache_dir=":nocache",
)
conversation = engine.create_conversation()
reply = conversation.send_message("What is the capital of Japan? Answer briefly.")
print("Reasoning:", reply.channels.get("analysis", ""))
print("Answer:", reply)
Output on the Mac above (the file's sampler, top-k 50 at temperature 1.0, so the text varies from run to run):
Reasoning: User asks: "What is the capital of Japan? Answer briefly." Provide brief answer: Tokyo.
Answer: Tokyo.
License
Apache License 2.0, the license of openai/gpt-oss-120b; see also the source's usage policy.
- Downloads last month
- 12
Model tree for litert-community/gpt-oss-120b
Base model
openai/gpt-oss-120b