Model lineage and interface
Table with columns: Item, Value| Item | Value |
|---|
| Architecture | Qwen3ForCausalLM; 9.10B parameters; full weights, not an adapter |
| Starting checkpoint | Neonkraft/oellm-9b-256k-theta64m-prelude-anneal300b-instruct-sft at 85bf18fb4f0bee6ac6270f06b1d1c6b3be200f31 |
| Continuation | One epoch of assistant-only SFT on the joint mix below |
| Format | ChatML-style `< |
| Important token IDs | `< |
| Weights | Eight FP32 safetensors shards, 36,407,837,400 bytes in total; BF16 computation during training |
| Context | Architecture field max_position_embeddings=262144; training cap 16,384 tokens. 128K/256K retention and tool use were not evaluated. |
The packaged template is chat_template.jinja. The
assistant's tool call has the form
<tool_call>{"name":"tool_name","arguments":{...}}</tool_call>; it is
text for a separate application to parse and execute, not an executed action.
Tool results enter as <tool_response>…</tool_response> inside a user turn.
The assistant turn terminator is <|im_end|> (ID 4). For an explicit thinking
prompt, set enable_thinking=True; enable_thinking=False inserts the empty
think block into the prompt, which is appropriate for direct-answer or some
tool-call diagnostics but does not test whether the model chooses to reason.
The saved configuration contains both rope_parameters.rope_theta=64000000
and a top-level rope_theta=10000; the effective value can depend on the
Transformers implementation. These fields are retained from the executed run,
not silently rewritten for this publication. The 256K architecture field is
not evidence that this continuation retains 256K performance.
Inference
The training environment's Transformers 4.57.3 sometimes raises TypeError
when AutoTokenizer reads the parent's legacy LlamaTokenizer metadata.
Use the pinned fast tokenizer JSON as a fallback; this preserves the original
special-token IDs and avoids reassigning ChatML markers. Loading the saved
checkpoint through AutoTokenizer can also emit a Mistral-regex warning;
the model and parent have byte-identical tokenizer.json files, and the
actual evaluation prompts passed the token-ID parity test described below.
The shared tokenizer JSON SHA-256 is
5123f74b254340ffadc9218925030916e47058178967cdd7110b39b848db23fd:
import torchfrom pathlib import Pathfrom huggingface_hub import hf_hub_downloadfrom transformers import AutoModelForCausalLM, PreTrainedTokenizerFast repo = "birgermoell/oellm-9b-256k-reasoning-tooluse-sft-v1"tokenizer = PreTrainedTokenizerFast( tokenizer_file=hf_hub_download(repo, "tokenizer.json"), bos_token="<bos>", eos_token="<|im_end|>", pad_token="<pad>",)tokenizer.chat_template = Path(hf_hub_download(repo, "chat_template.jinja")).read_text()assert tokenizer.convert_tokens_to_ids("<think>") == 18assert tokenizer.convert_tokens_to_ids("<|im_end|>") == 4 model = AutoModelForCausalLM.from_pretrained( repo, dtype=torch.bfloat16, device_map="auto").eval()messages = [{"role": "user", "content": "What is 37 × 18?"}]inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, enable_thinking=True, return_dict=True, return_tensors="pt",).to(model.device)inputs.pop("token_type_ids", None)with torch.inference_mode(): output = model.generate( **inputs, max_new_tokens=512, do_sample=False, eos_token_id=[4, 2], pad_token_id=tokenizer.pad_token_id, )print(tokenizer.decode(output[0, inputs["input_ids"].shape[-1]:], skip_special_tokens=False))
Do not execute generated tool calls without application-level validation,
authorization, and sandboxing.
Training data
The exact immutable mix is described by training_data_manifest.json
and training_config.yaml. Counts are selected
conversation rows, not unique underlying problems. Token counts were
rendered with the training tokenizer and template; "supervised" counts only
assistant-target tokens rather than full prompt or tool observations.
Table with columns: Bucket, Train rows, Validation rows, Rendered train tokens, Supervised train tokens, Share of supervised tokens, Purpose| Bucket | Train rows | Validation rows | Rendered train tokens | Supervised train tokens | Share of supervised tokens | Purpose |
|---|
| Nonempty reasoning | 8,118 | 1,033 | 50,006,238 | 48,225,080 | 69.64% | Teach traces inside <think> and final answers |
| Tool use | 24,582 | 513 | 14,422,131 | 2,206,437 | 3.19% | Function-call syntax, arguments, and final responses |
| General instruction replay | 44,164 | 5,188 | 30,002,903 | 18,816,233 | 27.17% | Reduce instruction-following regression |
| Total | 76,864 | 6,734 | 94,431,272 | 69,247,750 | 100% | One-epoch SFT mixture |
The reasoning bucket contains 2,589 English Dolci Think 32B/7B rows; 2,171
rows from the 12-language translated Dolci Think release (Czech, German,
Greek, Spanish, Finnish, French, Italian, Dutch, Polish, Romanian, Swedish,
Ukrainian); 1,570 Nemotron v2 rows in German/French/Spanish/Italian; 1,261
verified OpenR1 math rows; and 527 OpenEuroLLM multilingual pilot rows.
The replay bucket is from
allenai/Dolci-Instruct-SFT.
The tool bucket is from
birgermoell/oellm-eu-tooluse-v1,
an English function-calling collection. Source-level counts are preserved in
the manifest rather than implied to be balanced.
Table with columns: Reasoning source, Selected train rows| Reasoning source | Selected train rows |
|---|
| Dolci Think 32B, decontaminated | 1,508 |
| Dolci Think 7B, decontaminated | 1,081 |
| Dolci Think translated | 2,171 |
| Nemotron v2, decontaminated | 1,570 |
| OpenR1 Math 220K, verified | 1,261 |
| OpenEuroLLM multilingual pilot v0.2 | 527 |
Translated Dolci rows by language are cs 173, de 189, el 134,
es 230, fi 145, fr 162, it 170, nl 191, pl 198,
ro 196, sv 191, and uk 192. Nemotron v2 contributes de 365,
es 412, fr 398, and it 395. These are dataset-row counts, not a
measurement of fluent multilingual reasoning after training.
The materializer trimmed trailing non-assistant turns; rejected malformed,
empty-mask, or over-16K rows; checked the first reasoning token, trace body,
tool call, and final response were under assistant loss; and rejected cases
where a tool call fell outside the supervised span. Prompt-key deduplication
uses a hash of the system tool inventory plus first user turn. Validation
selection is deterministic and excluded from training. A separate prior
tool-use validation split was also excluded by prompt key. This is not a
proof of underlying-problem disjointness across translations or of
decontamination against every public benchmark.
The build manifest records 15,795 tool examples rejected because a tool
call fell outside the assistant target, plus 14,187 tool rows, 1,279
reasoning rows, and 1,157 replay rows excluded as duplicates or holdouts.
These are pipeline disposition counts, not estimates of unique conversations
in the original public sources.
The tool quota in the recipe was set in rendered tokens (including long
tool schemas, user turns, and tool observations), not in assistant-supervised
tokens. Although it allowed up to 20M rendered tool tokens, the available,
accepted tool source supplied 14.42M rendered tokens and only 2.21M
assistant-supervised tokens. No rows were repeated to fill the quota, and the
larger LightOn fcanalysis candidate exports were not admitted to this v1
mix. This accounting is a plausible reason the tool objective was
underweighted, but the held-out probe alone cannot establish causality.
The source reasoning artifact included MedQA-labelled material; 6,249
MedQA rows were excluded from this joint mix because of its research-use
restriction. Only nine retained tool-training rows lacked a tool call;
the training mix therefore poorly represents abstention. In the specific
held-out probe below the model instead failed to produce tagged calls, so
general under- and over-calling behavior remains uncharacterized.
The source datasets retain their own terms and attributions: translated Dolci
Think and OpenR1 Math 220K are listed as Apache-2.0; Dolci Think 32B and
Dolci Instruct replay as ODC-By-1.0; Nemotron v2 and the OpenEuroLLM
multilingual pilot as CC-BY-4.0; Dolci Think 7B is a composite requiring
upstream review; the tool collection is listed as Apache-2.0. See each linked
source card and the frozen data recipe for details. The model's Apache-2.0
tag does not replace those dataset obligations.
Training procedure and provenance
Table with columns: Setting, Executed value| Setting | Executed value |
|---|
| Objective | TRL SFT, assistant-only loss; prompts and tool observations masked |
| Packing | Up to 16,384 tokens per packed sequence, chunked NLL loss |
| Duration | One data epoch, 91 optimizer steps |
| Effective batch | 8 MI250X GCDs × 1 sequence × 8 gradient accumulation = 64 packed sequences |
| Optimizer | AdamW; β₁ 0.9, β₂ 0.95, ε 1e-8; weight decay 0; gradient clipping 1.0 |
| Learning rate | Peak 1.5e-6, cosine decay, 3% warmup |
| Precision/parallelism | BF16 training, FlashAttention 2, activation checkpointing, FSDP full shard |
| Training job | LUMI standard-g Slurm 22666549, 1 node / 8 GCDs, 4 h 28 min wall time (about 35.8 allocated GCD-hours), completed 14 October 2026 |
| Smoke gate | LUMI dev-g Slurm 22666548, 2 optimizer steps, completed 12 October 2026 |
| Data build | LUMI small Slurm 22666547, completed 10 October 2026 |
| Seed | 20261009 for model and data |
| Software | PyTorch 2.9.1+ROCm 6.4, Transformers 4.57.3, TRL 1.4.0, Accelerate 1.12.0; see environment.txt for the captured stack |
The training data Parquet SHA-256 is
90603287c2ad6aa66f52ade8fa80bd45f0a0ee4d68342e9ad5248732b28a3086.
The training manifest SHA-256 is
d722e83a938cd9f95ac8f3baa7e94120c3ca7ee7fce13b4fcdf39d9dcf34ffd94.
The executed config SHA-256 is
fd2665fb0d86a6504ef8e8f44f1396506af085ff78909e955c2a06a3972b4f0b,
and the template SHA-256 is
eae568967bc0b247498191d8107b9c9645378ce0123fb2a0402dab5302933c82.
The run recorded repository HEAD 7b9f05a66bdfd6ca5b0d5e9c88cdabcc69098ca2
with local uncommitted work, so that Git commit alone is not a byte-exact
training source. The frozen scripts, config, and run record are bundled here
for audit. Optimizer state is retained in LUMI checkpoint 91, not in this
model-only HF release.
Training loss was 2.2759 at step 5 and 1.2810 at step 90; aggregate
train_loss=1.56996. Mean token accuracy rose from 0.5488 to 0.6856 on
training batches. These are training metrics, not held-out evidence of
reasoning correctness or safe tool choice. The training config used
eval_strategy: no; the selected validation rows were not evaluated during
optimization.
Post-training evaluation
The diagnostics use frozen prompts and greedy decoding for both the SFT parent
and this checkpoint. As a tokenizer parity check, all 200 tool-use prompts
and all 100 sampled GSM8K prompts rendered to identical token IDs with the
parent loader, checkpoint loader, and pinned JSON-backend fallback. The
comparison script is bundled as
check_eval_tokenizers.py.
LUMI job 22829076 completed a paired 200-prompt probe. It used the first
100 parseable callable examples from the held-out tool validation file and
100 separate Dolci replay prompts with an unrelated calculate_tip tool.
Both models received the same
prompt IDs, enable_thinking=False, greedy decoding, and a 192-new-token cap.
The strict parser requires a <tool_call>…</tool_call> span containing JSON
with a string name and object arguments. Source and scoring code are
bundled in eval_tooluse_continuation.py,
and the aggregate result is paired-heldout-tool.json.
The bundled script adds a post-hoc optional --trace-examples output switch;
the default prompt selection, generation, and strict scoring logic are the
same as in the completed 200-prompt run.
The tool holdout Parquet SHA-256 is
f58fb0e9cfae40df18a7934f69b18d3c31ee5709e388fd4bb8c9edb2ab08c9f8;
the supplemental no-call selector is recorded in the bundled script.
Table with columns: Strict measure, SFT parent, This checkpoint| Strict measure | SFT parent | This checkpoint |
|---|
| Valid tagged call on 100 callable prompts | 0/100 | 0/100 |
| Correct function name on 100 callable prompts | 0/100 | 0/100 |
| Exact function name and arguments on 100 callable prompts | 0/100 | 0/100 |
| No call on 100 unrelated-tool prompts | 100/100 | 100/100 |
The no-call result is not evidence of good tool selection: both models
failed to emit a valid tagged call on every callable prompt. A separate
five-call raw-output inspection (LUMI job 22829688) found that this
checkpoint sometimes produced a bare {"name": ..., "arguments": ...}
object without the required <tool_call> delimiters; other cases produced
prose or ended immediately. Thus the strict zero is a genuine interface
failure, not merely a parser bug, while it also should not be interpreted as
proof that the model never learned any function-name association. This small
inspection is qualitative, not an alternative 100-prompt score. This probe
is not BFCL and does not cover multi-turn execution or safe tool choice.
Reasoning diagnostic
LUMI job 22831153 completed a paired diagnostic on 100 GSM8K test questions,
selected deterministically as the first 100 after sorting by SHA-256 of the
question text. Both models received the same question hashes, tokenizer IDs,
chat template, instruction to work through the problem, enable_thinking=True,
greedy decoding, and a 512-new-token cap. The full paired result, including
raw generations and
eval_joint_gsm8k.py are bundled with this release.
The 100 question hashes were unique and paired in the same order on both sides.
Table with columns: Custom measure on 100 paired questions, SFT parent, This checkpoint| Custom measure on 100 paired questions | SFT parent | This checkpoint |
|---|
| Numeric exact match | 12/100 | 40/100 |
Nonempty, closed <think> block | 0/100 | 0/100 |
| Reached the 512-new-token cap | 74/100 | 27/100 |
The candidate got 30 questions right that the parent missed, while the parent
got two right that the candidate missed; ten were right for both. The numeric
extractor prefers a boxed or labelled final answer but otherwise takes the
last number in the text. It can therefore score an unfinished or malformed
response as correct by coincidence; two parent correct predictions were
length-capped, while none of the candidate's were. This is a custom
diagnostic, not the official GSM8K harness score. It uses one prompt style,
one deterministic subset, greedy decoding, and a 512-token ceiling. Overlap
between these test questions and upstream training data has not been audited.
Raw-output inspection explains why the result is not evidence of successful
trace-format training: the candidate's sampled generations opened and
immediately closed an empty <think> block, then placed any worked solution
in the final-answer channel. Some incorrect outputs repeated the same sentence
until the cap; other uncapped outputs contained arithmetic or interpretation
errors. The lower cap-hit rate and higher numeric-exact count are encouraging
for answer behavior on this probe, but the intended reasoning inside
<think> objective was not met. A different prompt/template or stronger
verified-trace training may be needed before RL readiness is reassessed.
The JSON artifact records the SHA-256 of the evaluator as it stood when the
job started. The evaluator file was subsequently revised during the job and
the bundled copy is not byte-identical to that recorded hash. The artifact
preserves the paired question text and hashes, raw outputs, predictions, cap
flags, template hash, and source-data hash needed to audit the reported
counts; the bundled script documents the prompt construction and scoring.
Do not treat it as a byte-exact snapshot of the executed file.
Neither diagnostic is a substitute for full BFCL, a complete GSM8K benchmark,
verified multilingual reasoning, instruction retention, or a long-context
suite. Any test score must be interpreted with its prompt, parser, cap, sample
selection, and potential benchmark contamination in mind.
Risks and limitations
- The mix has very little no-call supervision, but the measured failure under
this interface is the opposite: no valid tagged calls on 100 callable
prompts. Some bare JSON function bodies appeared in a small raw-output
check. Neither that observation nor 100/100 no-call prompts establishes
reliable tool choice, safe arguments, or multi-step tool planning.
- Nonempty
<think> text is a format property, not proof that the reasoning
is valid. The training data includes synthetic and machine-translated
traces and can contain incorrect or degenerate reasoning despite filtering.
- The prompt-key split does not guarantee semantic separation of translated
versions of the same underlying problem.
- The model has not been evaluated at its 256K architectural context length.
The run actually trained at 16K and the saved RoPE configuration has
conflicting fields, requiring explicit implementation-level validation.
- Training loss reduction does not establish held-out reasoning gain,
multilingual quality, safety, privacy, or resistance to prompt injection.
- This is a research artifact. Verify outputs and enforce application-level
tool permissions before real-world use. It is not a medical, legal, or
financial decision system.
Citation and attribution
Credit the OpenEuroLLM project, the SFT parent authors, the source dataset
creators, and LUMI/CSC when reporting this experiment. When using the tool
collection, also follow its source-card attribution for Glaive, ToolACE, and
Hermes. A formal paper citation is not assigned to this checkpoint.