vonjack/hrm-text-code-tools-sft
HRM-Text-1B Code and Tool-Use SFT
This repository is a Transformers BF16 conversion of `pzarzycki/hrm-text-1b-code-tools-sft`, a full-parameter Stage A fine-tune of `sapientinc/HRM-Text-1B`. It also contains canonical BF16 and directly derived Q8_0 GGUF files.
The source checkpoint is a research pilot trained for code generation and a fixed tool-transcript protocol. It has not undergone downstream benchmark or production-agent evaluation.
Model details
Stage B was not trained or published as part of the source revision used here.
Files
BF16 is the canonical storage format. Q8_0 was quantized directly from the BF16 GGUF. No F16 derivative is provided because converting BF16 to F16 would change 17,119 finite stored values and underflow 87 values to zero.
Requirements
Use transformers>=5.9.0, which includes native hrm_text model support. The conversion and validation environment used Transformers 5.16.1 and PyTorch 2.13.0.
pip install --upgrade "transformers>=5.9.0" torchHosted inference is disabled in the model-card metadata because generic text generation endpoints do not provide the required PrefixLM token_type_ids.
Transformers usage
The included Jinja template must be applied. It serializes the learned direct condition and the SFT transcript markup; this is not a Qwen/ChatML prompt despite using a Qwen-compatible tokenizer.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "YOUR_NAMESPACE/HRM-Text-1B-Code-Tools-SFT"
device = torch.device(
"cuda" if torch.cuda.is_available()
else "mps" if torch.backends.mps.is_available()
else "cpu"
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
dtype=torch.bfloat16,
).to(device).eval()
messages = [
{
"role": "user",
"content": "Write a Python function that returns the larger of two integers.",
}
]
inputs = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors="pt",
return_dict=True,
).to(device)
# HRM-Text was trained with a bidirectional prompt prefix.
inputs["token_type_ids"] = torch.ones_like(inputs["input_ids"])
with torch.inference_mode():
output_ids = model.generate(
**inputs,
max_new_tokens=256,
do_sample=False,
)
new_ids = output_ids[0, inputs["input_ids"].shape[1]:]
print(tokenizer.decode(new_ids, skip_special_tokens=False))The rendered prompt starts with the following exact envelope:
<|im_start|><|object_ref_start|><user>
Write a Python function that returns the larger of two integers.
</user>
<assistant>
<|im_end|>Do not omit token_type_ids when using Transformers. A value of 1 marks a prompt position as part of the bidirectional prefix block. Omitting it falls back to pure-causal attention and does not match the training-time objective.
Tool schemas
Pass OpenAI-style function schemas through the tools argument. The template places them inside the learned <tools>...</tools> transcript markup.
tools = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read a UTF-8 file relative to the task root.",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
"additionalProperties": False,
},
},
}
]
prompt = tokenizer.apply_chat_template(
[{"role": "user", "content": "Read README.md and summarize it."}],
tools=tools,
tokenize=False,
add_generation_prompt=True,
)<user>, <assistant>, <tools>, <tool_call>, and <tool_result> are ordinary learned text markup, not pretrained HRM control tokens. The model does not execute tools, validate arguments, or sandbox generated code. A system message is intentionally serialized with the same <user> markup; there is no separately trained system role.
GGUF compatibility
The GGUF files use general.architecture = hrm_text and embed the exact Jinja template under tokenizer.chat_template. Standard unpatched llama.cpp, Ollama, LM Studio, and llama-cpp-python builds do not support this custom runtime graph at the time of this release.
Apply the included patch to this exact llama.cpp commit:
6a257d44633d4a752183ed778b88d2924d0a6b9dgit clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
git checkout 6a257d44633d4a752183ed778b88d2924d0a6b9d
git apply /path/to/model/gguf/runtime/llama.cpp-hrm_text.patch
cmake -B build -DGGML_METAL=ON -DGGML_NATIVE=OFF -DLLAMA_BUILD_UI=OFF
cmake --build build --config Release --target llama-cli llama-server llama-quantize -jNinja is optional. The documented CMake flow works with the default Unix Makefiles generator, and Metal support is independent of the generator.
Start the server with Jinja explicitly enabled. PrefixLM prefill must process the complete prompt in one physical batch, so set --batch-size and --ubatch-size to at least the maximum prompt length you intend to use. The example below supports prompts up to 512 tokens. Use -ngl all for Metal or -ngl 0 for CPU-only inference.
./build/bin/llama-server \
-m /path/to/model/gguf/HRM-Text-1B-Code-Tools-SFT-Q8_0.gguf \
--alias HRM-Text-1B-Code-Tools-SFT \
--jinja --ctx-size 512 --batch-size 512 --ubatch-size 512 \
--cache-ram 0 --parallel 1 \
-ngl all --host 127.0.0.1 --port 8080Requests to the OpenAI-compatible chat endpoint apply the embedded template:
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "HRM-Text-1B-Code-Tools-SFT",
"messages": [{"role": "user", "content": "Write a Python max function."}],
"temperature": 0,
"max_tokens": 128,
"cache_prompt": false
}'The patch maps hrm_text.prefix_lm=true to llama.cpp's non-causal attention mask. The complete initial prompt is therefore one bidirectional prefix block. Autoregressive token-by-token decoding remains causal in effect because the KV cache contains no future generated positions.
This is deliberately narrower than arbitrary Transformers token_type_ids. Do not split one prefix across multiple physical batches, reuse a KV cache from a shorter prompt, or enable speculative multi-token decoding. Prompt-cache reuse is disabled in the command and request above. For the full 4,096-token context, set --ctx-size, --batch-size, and --ubatch-size to 4096 if the available memory permits it.
Conversion validation
BF16 logits are not bit-identical across Keras and Transformers because their RMSNorm, softmax, and backend arithmetic paths differ. Stored weights are bit-identical after the audited tensor mapping, FP32 outputs pass the source author's tolerance, and the tested BF16 token rankings and greedy outputs match.
The llama.cpp comparisons use the same rendered token IDs and PrefixLM mask on both sides: every initial prompt token is bidirectional and generated tokens are causal. The validated two-step continuations were [26763, 2336] for the plain prompt and [58, 19975] for the tool-schema prompt on BF16 CPU, BF16 Metal, and Q8_0 Metal.
Training provenance
The Stage A selection contains 38,248 rows from the sealed training split. Training used full-parameter BF16 optimization with a 4,096-token context cap. See the source model card for the full optimizer setup and telemetry.
Intended use and limitations
This checkpoint is intended for research on HRM-Text code adaptation, tool-call transcript generation, conversion fidelity, and local inference.
- No downstream coding or agent benchmark has been reported for this pilot.
- Training loss is not evidence of production coding-agent performance.
- Generated code and tool calls may be incorrect, unsafe, or fabricated.
- Tool execution, argument validation, permissions, and sandboxing must be implemented by the host application.
- The model is predominantly English and is limited to 4,096 tokens.
- Evaluate task quality and safety independently before deployment.
License and citation
The model is released under the Apache License 2.0. The Stage A dataset traces to CC-BY-4.0 data; consult the linked dataset card for its attribution and usage terms.
Please cite the base HRM-Text work:
@misc{wang2026hrmtextefficientpretrainingscaling,
title={HRM-Text: Efficient Pretraining Beyond Scaling},
author={Guan Wang and Changling Liu and Chenyu Wang and Cai Zhou and Yuhao Sun and Yifei Wu and Shuai Zhen and Luca Scimeca and Yasin Abbasi Yadkori},
year={2026},
eprint={2605.20613},
archivePrefix={arXiv},
primaryClass={cs.CL},
url={https://arxiv.org/abs/2605.20613}
}