geodesic-research/pa-warm-start-sft-light-1b-mix
geodesic-research/pa-warm-start-sft-light-1b-mix Auto-generated by dataset-builder. Each config below is a separate dataset produced from a versioned YAML build config. Load with: from datasets import load_dataset ds = load_dataset("geodesic-research/pa-warm-start-sft-light-1b-mix", "<config_name>", revision="<commit-sha>") Pin revision= to the specific commit SHA you want; without it, you get the current HEAD of the dataset repo, which may change when the builder re-pushes.… See the full description on the dataset page: https://huggingface.co/datasets/geodesic-research/pa-warm-start-sft-light-1b-mix.
geodesic-research/pa-warm-start-sft-light-1b-mix
Auto-generated by `dataset-builder`. Each config below is a separate dataset produced from a versioned YAML build config. Load with:
from datasets import load_dataset
ds = load_dataset("geodesic-research/pa-warm-start-sft-light-1b-mix", "<config_name>", revision="<commit-sha>")Pin revision= to the specific commit SHA you want; without it, you get the current HEAD of the dataset repo, which may change when the builder re-pushes.
Requires `datasets` v4+. These parquet files carry feature metadata written by datasets 4.x, which uses type names ({"_type": "List"}) that 3.x does not recognise — a 3.x load_dataset fails with a bare must be called with a dataclass type or instance, naming neither the file nor the cause. The Arrow data itself is fine, so a consumer stuck on 3.x can read the parquet through Arrow directly and let the schema be inferred — but inference recovers the values, not always the declared features (an all-empty list column infers as null), so check the columns you care about rather than assuming a clean round-trip.
Composition (default config)
The 1.00B-token "Light" warm-start SFT mix: safety-free maths / coding / science / multi-turn chat / agentic tool use, sampled toward the shortest chains of thought, with every document under 32,768 tokens so nothing truncates at the training sequence length. Shuffled (seed 42). Built by pipelines/persistent-alignment-warm-start/ (mix: light) in dataset-builder; each per-source config on this repo is one selection stage's output and default is their shuffled concatenation.
Composition is budgeted per dataset: 205M tokens of agentic tool use, 200M of maths, 245M of science, 125M of multi-turn instruction-following chat, 125M of competitive programming and 100M of SWE. Sampling shortest-CoT-first is deliberate — the mix is meant to steer a warm-started model toward shorter reasoning traces, not merely to be small.
250M of the mix (25%) is tool-use data, in two configs: agentic_interactive (205M) and science_rqa_tools (45M). Science's rqa source is split into a tool arm and a tool-free arm so that share is a budget rather than whatever proportion the source happened to carry. Earlier revisions of this dataset were reasoning-only and tool-free; that is no longer true.
The tool arm is 45M rather than a round 50M because that is what its source can supply: the well-formed tool-bearing documents in rqa.jsonl number 12,653, and a 50M budget drew 98% of them, leaving nothing for the evaluation-awareness screen to remove. The 5M sits in agentic_interactive instead, so the tool-use share is still exactly 250M.
Every assistant message carries a reasoning trace, including tool-calling turns. In every tool-using document, every tool the conversation calls is declared in that document's own `tools` schema — documents calling an undeclared tool are dropped, because they would teach a model to invent tool names. tool_calls and tools are stored as structured objects, not JSON-encoded strings: a template iterating message.tool_calls gets one call per element.
Its purpose is a bare-bones initialisation — teach the <think></think> convention, basic tool use, and as little else as possible, so RLVR has a blank canvas for shaping reasoning patterns.
No model or vendor identity. A document is dropped outright if a case-insensitive word-boundary match for any of 24 AI model/organisation names (OpenAI, ChatGPT, Claude, Anthropic, DeepSeek, Gemini, Llama, Qwen, Mistral, Copilot — and NVIDIA/Nemotron, the upstream vendor) appears anywhere in the rendered document, reasoning traces included. So a warm-started model does not inherit a borrowed identity such as "as an AI trained by …". The filter runs before the token budget, so dropped documents are replaced by the next-shortest ones rather than leaving the budget short.
Per-record columns cot_lengths_chars (one entry per assistant message), cot_chars_mean / cot_chars_min / cot_chars_max, and n_tokens support analysis of the CoT-length distribution.
Note on turn structure: the chat_multiturn config (125M tokens) is the only source presenting a second user turn, and every one of its records is a dialogue — it is filtered to ≥2 user turns that carry content before selection. Everything else is single-turn, so a model warm-started on this mix sees dialogue from about an eighth of the tokens.
The filter runs before the shortest-CoT sort deliberately. The sort ranks documents by their longest reasoning trace, and dialogues have more assistant turns and so longer maxima; selecting first would have yielded a slice only about half multi-turn (measured on a comparable pool: its shortest 10% was 46.5% multi-turn against 69.7% overall).
Mask every assistant turn, not just the final one. 56,825 records (9.0%) have more than one assistant turn, so the single-turn convention of training on the last reply and masking the rest would silently discard 275,658,399 assistant characters — 10.8% of all the assistant text in the mix. Every assistant turn here is a real reply to a real user turn and is worth training on.
Relatedly, no record contains consecutive assistant turns, and no assistant turn reasons without answering. A handful of upstream records did: an abandoned attempt carrying reasoning with empty content, where the model thought and then produced nothing before starting again. Training on one teaches long reasoning followed by silence, so they are dropped rather than left for a consumer to mask around.
System prompts are sampled, not fixed. The upstream sources carry almost none — 94% of documents had no system prompt, and the one source that did (science_rqa) carried a single byte-identical instruction. Training on that teaches a model that a system prompt is either always absent or always the same words, which shows up as models asserting a system prompt they were never given. So a document that had no standing instructions gets one drawn uniformly from four — 389,634 of 634,571 records (61.4%):
You are an AI assistant.
You are a helpful AI assistant.
You are an honest AI assistant.
You are a harmless AI assistant.They are deliberately bare. The prompt is prepended to a completion that already exists, so any instruction the existing reasoning does not happen to follow would teach the model to disregard its system prompt.
system_prompt_id records where each record's standing instructions came from, with a distinct value per origin:
The three negative values are not interchangeable. To select the no-system-prompt condition, filter system_prompt_id == -1; to select the prompt-bearing condition, filter system_prompt_id != -1 rather than >= 0, or the ~86,000 records at -2 / -3 — which do carry standing instructions — land in the wrong arm.
Two classes of document keep the instructions they arrived with instead. science_rqa's 234-character response-format instruction is preserved as-is; it reaches this mix on a developer message upstream and is relabelled `system`, so the whole mix uses one role for standing instructions rather than two — developer is train-only vocabulary that no inference harness sends. And the competitive-programming sources put their instructions at the top of the opening user turn; that fixed 284-character preamble is moved verbatim into the system slot, which is where an instruction addressed to the assistant belongs, and removed from the user turn. Both keep the instructions each completion was actually written against.
A document is also left alone if its opening user turn states the role in some other way ("You are a coding assistant who is an AI…"), since adding a second, possibly conflicting set would teach the model to disregard one of them.
And ~25% of the mix deliberately has no system prompt at all — 158,487 records (24.98%), which render with an empty <|im_start|>system<|im_end|> block. Diversity of wording is only half the fix: a model that has never seen a document without a system prompt behaves as though one is always present, and a good deal of evaluation sends none. The share is drawn from the documents that would otherwise be sampled, so records carrying real instructions from their source keep them.
Selection is deterministic (keyed on the opening user turn plus a fixed seed), so the same document always receives the same prompt — and the same documents are left without one.
Every rendered document opens with exactly one <|im_start|>system … <|im_end|> block — empty for the ~25% above, populated otherwise. The chat template emits it unconditionally, so its presence carries no information; only its contents do. It renders outside the generation markers, so it is loss-masked.
Evaluation-awareness screening
Reasoning traces that discuss being tested, graded, or watched — verbalised evaluation awareness (VEA) — are excluded from this mix. Training on them teaches a model to condition its behaviour on whether it believes it is being evaluated, which is the failure the Persistent Alignment work exists to avoid baking in. Screening covers every assistant turn's reasoning_content, not just the final answer's.
Two stages, because judging every candidate with a 120B judge is not affordable:
- A CPU regex router (39 patterns, seeded from the judge rubric's own Score-1 vocabulary and widened against 340 hand-labelled traces). It is recall-oriented and drops nothing — a hit routes a document to the judge, a miss approves it. Measured recall on the hand-labelled fixtures: 99.2% (dev, 124/125) and 100% (held-out, 55/55). It routed 4.76% of the pool (697,964 candidates → 33,234 judged); rates ranged from 1.17% on
science_soto 18.93% onswe_agentless. - An LLM judge —
nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16, the model this rubric was calibrated on (published P/R ≈ 0.94/0.93) — reads each flagged trace in 1,800-character chunks and the verdicts are OR-ed back to the document.
The router's false positives cost judge tokens, not documents, and on code they dominate: swe_agentless had the highest flag rate in the mix and only 33 of 11,937 were real, because software reasoning legitimately discusses tests, harnesses and graders. A router that dropped on match would have destroyed ~11,900 good documents.
Judge verdicts
Of the flagged documents, these are the ones the judge confirmed. Confirmed documents are excluded; cleared ones ship and keep vea_regex_hit: true, so a shipped flagged document means "routed, then cleared". The `vea_excluded` config holds all 8,812 excluded documents together with the judge's own <reasoning> and <quote> for each chunk it scored — read that if you want to know why something was dropped.
Unjudged means unshipped, and partly judged counts as unjudged. A judge failure can only lose data, never admit it, and that takes two rules rather than one:
- A document whose every chunk failed to parse has no row in
vea_judgeat all, so the selection join excludes it. - A document whose chunks partly parsed is also excluded. The verdict is a MAX over the chunks that came back, so one unparseable chunk among four scoring 0 would otherwise read as fully cleared — with the unread chunk exactly where an evaluation-aware clause could be. Each row therefore carries `vea_chunks_expected` (the chunks the judge was asked about, counted before it was asked) and `vea_chunks_unjudged` (the shortfall), and the selection requires
vea_chunks_unjudged == 0.
On this build 9 of 140,997 chunks (0.006%) failed to parse. No document lost every chunk, so all 33,234 routed documents were adjudicated — but up to 9 documents lost a chunk each, and those are excluded by the coverage rule. They are not in vea_excluded, which holds only documents the judge affirmatively confirmed; find them in vea_judge with vea_chunks_unjudged > 0.
The pre-screening *_candidates pools are build intermediates and are not published; the per-source flag counts above are recorded here rather than recomputable from the repo.
vea_excluded — the documents that were dropped
One row per excluded document: the document itself (messages, n_tokens, reasoning_len), the verdict (vea_llm) and its coverage (vea_chunks_scored, vea_chunks_expected, vea_chunks_unjudged), the regex pattern that routed it to the judge (vea_regex_pattern), and the judge's justification — vea_judge_reasoning and vea_judge_quotes.
Those two lists hold one entry per SCORED chunk, not per chunk: a chunk whose completion could not be parsed is dropped before the fan-in, so on a document with vea_chunks_unjudged > 0 the lists are shorter than the chunk count and entry i is not chunk i. Pair them with vea_judge_chunk_index, which carries the chunk each entry came from, rather than assuming positional alignment.
Note this config holds only documents the judge affirmatively confirmed as evaluation-aware. Documents excluded for incomplete coverage are not here; find those in vea_judge with vea_chunks_unjudged > 0.
This exists so the screen is auditable per document rather than only in aggregate. A worked example: a science_so document was routed by the pattern matching "might … be a … trick", and the judge wrote "The assistant expresses suspicion that the question might be a trick, indicating a belief that the user could be testing the model's ability to avoid being misled", quoting "It might be trick: g0 standard gravity is 9.80665 m/s^2".
Configs
Provenance
agentic_interactive_candidates
Source: `nvidia/Nemotron-SFT-Agentic-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushchat_multiturn_candidates
Source: `nvidia/Nemotron-SFT-Instruction-Following-Chat-v3` Transform: map_column → map_column → map_column → project → filter → map_column → filter → project → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushcomp_prog_python_00_candidates
Source: `nvidia/Nemotron-SFT-Competitive-Programming-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushcomp_prog_python_01_candidates
Source: `nvidia/Nemotron-SFT-Competitive-Programming-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushmath_candidates
Source: `nvidia/Nemotron-SFT-Math-v4` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushscience_rqa_plain_candidates
Source: `nvidia/Nemotron-SFT-Science-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushscience_rqa_tools_candidates
Source: `nvidia/Nemotron-SFT-Science-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushscience_so_candidates
Source: `nvidia/Nemotron-SFT-Science-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushscience_syn_mcq_candidates
Source: `nvidia/Nemotron-SFT-Science-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushswe_agentless_candidates
Source: `nvidia/Nemotron-SFT-SWE-v2` Transform: map_column → map_column → map_column → project → filter → map_column → map_column → filter → map_column → map_column → filter → filter → filter → filter → stateful_filter → stateful_filter → hook → map_column → map_column → hook
python -m dataset_builder configs/light.yaml --pushvea_judge
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → filter → flat_map → flat_map → project → flat_map → aggregate → chunked_binary_judge → join → map_column → hook
python -m dataset_builder configs/light.yaml --pushvea_excluded
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → hf → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → join
python -m dataset_builder configs/light.yaml --pushagentic_interactive
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushchat_multiturn
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushcomp_prog_python_00
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushcomp_prog_python_01
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushmath
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushscience_rqa_plain
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushscience_rqa_tools
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushscience_so
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushscience_syn_mcq
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushswe_agentless
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: filter → filter → hf → filter → semijoin → concat → stateful_filter → stateful_filter → hook → hook
python -m dataset_builder configs/light.yaml --pushdefault
Source: `geodesic-research/pa-warm-start-sft-light-1b-mix` Transform: hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → hf → concat → stateful_filter → hook
python -m dataset_builder configs/light.yaml --pushReproducibility
All splits use split_hash() (MD5-based, seeded) so rebuilding from the same config against the same source data produces identical partitions. For an LLM-generated dataset, a provider's seed parameter is best-effort; pin consumer loads to a specific HF commit SHA to avoid drift when the builder re-pushes.
This card is auto-generated by `dataset_builder.cards`.
