Skip to content
on-policy-self-distillation

gpt-5.6-sol

on-policy-self-distillation — graded by the task's verifier against a measured baseline.

backend
modal
variant
codex azure_ai/gpt-5.6-sol
metric
AIME24 avg@12 accuracy
actions
428
duration
9h 21m
recorded
2026-08-02

run idopsd-aime24-4gpus__eNDoWrD

Everything gpt-5.6-sol did on this task, in order.

download JSON ↗
codexgpt-5.6-sol444 turns16 narration428 reasoning428 action428 observation1 final
#376
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#377
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#378
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#379
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#380
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#381
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#382
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#383
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script running with cell ID 117
Wall time 61.0 seconds
Output:
#384
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "117",
  "yield_time_ms": 60000,
  "max_tokens": 80
}
wait result
Script completed
Wall time 28.8 seconds
Output:
Warning: truncated output (original token count: 226)
Total output lines: 21

     | 0/96 [00:00<?, ?it/s, est. speed input: 0.00 toks/s, output: 0.00 toks/s]


 [03:22<08:29,  7.08s/it, est. speed input: 19.11 toks/s, output: 862.73 toks…146 tokens truncated…<05:56,  9.91s/it, est. speed input: 8.49 toks/s, output: 1318.83 toks/s]
0 problems)
Results written to /workspace/eval_results/dev_eval_1785711242.json

EXIT0
#385
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "const r = await tools.exec_command({cmd:\"python - <<'PY'\\nimport json\\nf='/workspace/eval_results/dev_eval_1785711242.json';d=json.load(open(f));print(d['average_at_n_pct'],sum(x['num_correct'] for x in d['results']));print([(x['problem_id'],x['num_correct']) for x in d['results']])\\nPY\\ncat /workspace/.timer/remaining_secs\",\"workdir\":\"/workspace\",\"yield_time_ms\":10000,\"max_output_tokens\":3000});\ntext(r.output);"
}
exec result
Script completed
Wall time 0.1 seconds
Output:
57.22222222222222 206
[(60, 12), (61, 12), (62, 0), (63, 0), (64, 5), (65, 5), (66, 11), (67, 12), (68, 11), (69, 11), (70, 7), (71, 12), (72, 12), (73, 0), (74, 8), (75, 8), (76, 9), (77, 2), (78, 5), (79, 12), (80, 3), (81, 0), (82, 8), (83, 12), (84, 12), (85, 0), (86, 11), (87, 6), (88, 0), (89, 0)]
4597
#386
narration

The repeat confirms the signal: 57.22% (206/360). Across the two independent same-seed retrains, the curriculum averages 56.39%, about +2.17 over the 54.22 baseline anchor. I’m locking this candidate and cleaning out the disabled experimental branches so the submitted method is only the validated reasoning-effort curriculum with the released OPSD loss and linear schedule.

#387
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "const r = await tools.exec_command({cmd:\"git diff -- opsd_train.py opsd_trainer.py data_collator.py /workspace/submission/recipe.env | sed -n '1,500p'\",\"workdir\":\"/workspace/repo\",\"yield_time_ms\":10000,\"max_output_tokens\":30000});\ntext(r.output);"
}
exec result
Script completed
Wall time 0.1 seconds
Output:
fatal: /workspace/submission/recipe.env: '/workspace/submission/recipe.env' is outside repository at '/workspace/repo'
#388
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "const r = await tools.exec_command({cmd:\"git diff -- opsd_train.py opsd_trainer.py data_collator.py | sed -n '1,520p'; echo RECIPE; cat /workspace/submission/recipe.env\",\"workdir\":\"/workspace/repo\",\"yield_time_ms\":10000,\"max_output_tokens\":30000});\ntext(r.output);"
}
exec result
Script completed
Wall time 0.1 seconds
Output:
diff --git a/data_collator.py b/data_collator.py
index 0829108..221b555 100644
--- a/data_collator.py
+++ b/data_collator.py
@@ -55,6 +55,7 @@ class SelfDistillationDataCollator:
         # Prepare student and teacher prompts using chat template (matching evaluation)
         student_prompts = []
         teacher_prompts = []
+        anchor_sequences = []
         teacher_reasoning_prompts = []  # NEW: for reason_first mode
 
         for feature in features:
@@ -73,6 +74,13 @@ class SelfDistillationDataCollator:
             )
             student_prompts.append(student_prompt)
 
+            # Expert trajectory used as a weak anchor for OPSD.  It starts at
+            # exactly the same non-privileged student prompt; unlike the
+            # teacher context below it never exposes the solution in the user
+            # message.  The reference solution is available in every row of
+            # the released OPSD dataset.
+            anchor_sequences.append(student_prompt + solution.strip() + self.tokenizer.eos_token)
+
             if self.reason_first:
                 # Reasoning prompt: ask teacher to analyze the solution
                 reasoning_user_message = (
@@ -138,6 +146,25 @@ class SelfDistillationDataCollator:
             "student_prompt_lengths_per_example": torch.tensor(student_prompt_lengths),
         }
 
+        anchor_encoded = self.tokenizer(
+            anchor_sequences,
+            padding=True,
+            truncation=True,
+            max_length=self.max_length,
+            return_tensors="pt",
+        )
+        anchor_labels = anchor_encoded["input_ids"].clone()
+        for i, prompt_len in enumerate(student_prompt_lengths):
+            anchor_labels[i, :prompt_len] = -100
+        anchor_labels[anchor_encoded["attention_mask"] == 0] = -100
+        result.update(
+            {
+                "anchor_input_ids": anchor_encoded["input_ids"],
+                "anchor_attention_mask": anchor_encoded["attention_mask"],
+                "anchor_labels": anchor_labels,
+            }
+        )
+
         if self.reason_first:
             # Tokenize reasoning prompts
             reasoning_encoded_no_pad = self.tokenizer(
diff --git a/opsd_train.py b/opsd_train.py
index 8526a68..b4be7a4 100644
--- a/opsd_train.py
+++ b/opsd_train.py
@@ -266,6 +266,19 @@ if __name__ == "__main__":
     dataset = load_dataset("siyanzhao/Openthoughts_math_30k_opsd")
     train_dataset = dataset["train"]
 
+    # Reasoning-effort curriculum.  `generated_token_count` is metadata shipped
+    # with the released dataset and measures how much reasoning was needed to
+    # obtain its verified solution.  Under a 100-step budget, uniformly drawing
+    # many short arithmetic items wastes privileged-teacher calls.  Retain the
+    # broad source mix but train on examples requiring at least 3k reasoning
+    # tokens, a model-native difficulty signal rather than a benchmark label.
+    train_dataset = train_dataset.filter(
+        lambda example: example["generated_token_count"] >= 3000,
+        desc="Selecting high-reasoning-effort OPSD examples",
+    )
+    if os.environ.get("LOCAL_RANK", "0") == "0":
+        print(f"Reasoning-effort curriculum: {len(train_dataset)} training rows")
+
     trainer = OPSDTrainer(
         model=model_args.model_name_or_path,
         args=training_args,
diff --git a/opsd_trainer.py b/opsd_trainer.py
index c151bd7..8011b79 100644
--- a/opsd_trainer.py
+++ b/opsd_trainer.py
@@ -428,15 +428,31 @@ class OPSDTrainer(SFTTrainer):
             teacher_logits = teacher_logits / temperature
 
             if top_k is not None and top_k > 0:
-                # Restrict to top-k tokens of the teacher distribution and renormalize.
-                # Shape: [batch, seq_len, top_k]
+                # Teacher-support KL: retain the teacher's most actionable
+                # alternatives and aggregate every remaining vocabulary item
+                # into one exact "other" bucket.  Renormalizing only the
+                # selected logits (the old implementation) discarded the
+                # student's probability leakage outside teacher support and
+                # was not a projection of the full distributions.
                 _, top_k_indices = torch.topk(teacher_logits, k=top_k, dim=-1)
-                student_logits = torch.gather(student_logits, dim=-1, index=top_k_indices)
-                teacher_logits = torch.gather(teacher_logits, dim=-1, index=top_k_indices)
-
-            # Compute log probabilities for student and probabilities for teacher
-            student_log_probs = F.log_softmax(student_logits, dim=-1)
-            teacher_log_probs = F.log_softmax(teacher_logits, dim=-1)
+                student_full_log_probs = F.log_softmax(student_logits, dim=-1)
+                teacher_full_log_probs = F.log_softmax(teacher_logits, dim=-1)
+                student_head = torch.gather(student_full_log_probs, dim=-1, index=top_k_indices)
+                teacher_head = torch.gather(teacher_full_log_probs, dim=-1, index=top_k_indices)
+                # Accumulate retained mass in fp32: in bf16, 1-1e-6 rounds to
+                # one and log1p(-mass) can become -inf.
+                student_mass = student_head.float().exp().sum(dim=-1, keepdim=True).clamp(max=1 - 1e-5)
+                teacher_mass = teacher_head.float().exp().sum(dim=-1, keepdim=True).clamp(max=1 - 1e-5)
+                student_tail = torch.log1p(-student_mass)
+                teacher_tail = torch.log1p(-teacher_mass)
+                student_head = student_head.float()
+                teacher_head = teacher_head.float()
+                student_log_probs = torch.cat([student_head, student_tail], dim=-1)
+                teacher_log_probs = torch.cat([teacher_head, teacher_tail], dim=-1)
+            else:
+                # Compute log probabilities for student and teacher
+                student_log_probs = F.log_softmax(student_logits, dim=-1)
+                teacher_log_probs = F.log_softmax(teacher_logits, dim=-1)
 
         if beta == 0:
             jsd = F.kl_div(student_log_probs, teacher_log_probs, reduction="none", log_target=True)
@@ -459,7 +475,9 @@ class OPSDTrainer(SFTTrainer):
             # Compute the Generalized Jensen-Shannon Divergence
             jsd = beta * kl_teacher + (1 - beta) * kl_student
 
-        # Per-token clipping: cap each token's divergence value
+        # Per-token clipping: cap each vocabulary contribution.  Although the
+        # individual KL summands are signed, retaining this released behavior
+        # is important for the paper's point-wise robustness heuristic.
         if token_clip is not None:
             jsd = jsd.clamp(max=token_clip)
 
@@ -636,13 +654,39 @@ class OPSDTrainer(SFTTrainer):
         shifted_labels = inputs["labels"][:, student_prompt_len:]
 
         # === STUDENT FORWARD - Extract log-probs immediately ===
-        outputs_student = model(
-            input_ids=inputs["student_input_ids"],
-            attention_mask=inputs["student_attention_mask"],
-        )
+        # Put the on-policy and correct anchor trajectories in one forward.
+        # Besides being cheaper than two calls, this is required by ZeRO-2,
+        # whose overlapping reducer cannot visit the same LoRA parameter from
+        # two checkpointed graphs in one backward pass.
+        use_anchor = self.lmbda > 0 and "anchor_input_ids" in inputs and not self.use_thinking_machines_loss
+        student_batch_size = inputs["student_input_ids"].shape[0]
+        student_seq_len = inputs["student_input_ids"].shape[1]
+        if use_anchor:
+            anchor_seq_len = inputs["anchor_input_ids"].shape[1]
+            combined_seq_len = max(student_seq_len, anchor_seq_len)
+            pad_id = self.processing_class.pad_token_id
+            student_ids = F.pad(inputs["student_input_ids"], (0, combined_seq_len - student_seq_len), value=pad_id)
+            student_mask = F.pad(inputs["student_attention_mask"], (0, combined_seq_len - student_seq_len), value=0)
+            anchor_ids = F.pad(inputs["anchor_input_ids"], (0, combined_seq_len - anchor_seq_len), value=pad_id)
+            anchor_mask = F.pad(inputs["anchor_attention_mask"], (0, combined_seq_len - anchor_seq_len), value=0)
+            forward_ids = torch.cat([student_ids, anchor_ids], dim=0)
+            forward_mask = torch.cat([student_mask, anchor_mask], dim=0)
+        else:
+            forward_ids = inputs["student_input_ids"]
+            forward_mask = inputs["student_attention_mask"]
+
+        outputs_student = model(input_ids=forward_ids, attention_mask=forward_mask)
 
         # Extract only what we need and convert to log-probs immediately
-        student_logits = outputs_student.logits[:, student_prompt_len - 1 : -1, :]
+        student_logits = outputs_student.logits[:student_batch_size, student_prompt_len - 1 : student_seq_len - 1, :]
+
+        if use_anchor:
+            anchor_logits = outputs_student.logits[student_batch_size:, :anchor_seq_len, :]
+            anchor_loss = F.cross_entropy(
+                anchor_logits[:, :-1, :].reshape(-1, anchor_logits.shape[-1]),
+                inputs["anchor_labels"][:, 1:].reshape(-1),
+                ignore_index=-100,
+            )
 
         if self.use_thinking_machines_loss:
             # For reverse KL, we only need log-probs of sampled tokens
@@ -733,7 +777,7 @@ class OPSDTrainer(SFTTrainer):
             )
         else:
             # Temperature is applied inside generalized_jsd_loss
-            loss = self.generalized_jsd_loss(
+            opsd_loss = self.generalized_jsd_loss(
                 student_logits=student_logits_for_loss,
                 teacher_logits=teacher_logits_for_loss,
                 labels=shifted_labels,
@@ -744,6 +788,18 @@ class OPSDTrainer(SFTTrainer):
             )
             del student_logits_for_loss, teacher_logits_for_loss
 
+            # OPSD only visits student trajectories.  A weak maximum-
+            # likelihood anchor on the known-correct trajectory prevents
+            # self-distillation from polishing an incorrect branch forever.
+            # `lmbda` is the anchor coefficient; setting it to zero recovers
+            # pure OPSD exactly.
+            if use_anchor:
+                loss = opsd_loss + self.lmbda * anchor_loss
+                self._metrics["train"]["opsd_kl"].append(float(opsd_loss.detach()))
+                self._metrics["train"]["anchor_ce"].append(float(anchor_loss.detach()))
+            else:
+                loss = opsd_loss
+
         empty_cache()
 
         if return_outputs:
RECIPE
# Difficulty-curriculum OPSD with the released optimizer/model settings.
lmbda=0
top_k_loss=0
lora_r=64
lora_alpha=128
max_completion_length=1024
lr_scheduler_type=linear
#389
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "const patch = \"*** Begin Patch\\n*** Update File: /workspace/repo/data_collator.py\\n@@\\n         student_prompts = []\\n         teacher_prompts = []\\n-        anchor_sequences = []\\n         teacher_reasoning_prompts = []  # NEW: for reason_first mode\\n@@\\n-            # Expert trajectory used as a weak anchor for OPSD.  It starts at\\n-            # exactly the same non-privileged student prompt; unlike the\\n-            # teacher context below it never exposes the solution in the user\\n-            # message.  The reference solution is available in every row of\\n-            # the released OPSD dataset.\\n-            anchor_sequences.append(student_prompt + solution.strip() + self.tokenizer.eos_token)\\n-\\n@@\\n-        anchor_encoded = self.tokenizer(\\n-            anchor_sequences,\\n-            padding=True,\\n-            truncation=True,\\n-            max_length=self.max_length,\\n-            return_tensors=\\\"pt\\\",\\n-        )\\n-        anchor_labels = anchor_encoded[\\\"input_ids\\\"].clone()\\n-        for i, prompt_len in enumerate(student_prompt_lengths):\\n-            anchor_labels[i, :prompt_len] = -100\\n-        anchor_labels[anchor_encoded[\\\"attention_mask\\\"] == 0] = -100\\n-        result.update(\\n-            {\\n-                \\\"anchor_input_ids\\\": anchor_encoded[\\\"input_ids\\\"],\\n-                \\\"anchor_attention_mask\\\": anchor_encoded[\\\"attention_mask\\\"],\\n-                \\\"anchor_labels\\\": anchor_labels,\\n-            }\\n-        )\\n-\\n*** Update File: /workspace/repo/opsd_trainer.py\\n@@\\n             if top_k is not None and top_k > 0:\\n-                # Teacher-support KL: retain the teacher's most actionable\\n-                # alternatives and aggregate every remaining vocabulary item\\n-                # into one exact \\\"other\\\" bucket.  Renormalizing only the\\n-                # selected logits (the old implementation) discarded the\\n-                # student's probability leakage outside teacher support and\\n-                # was not a projection of the full distributions.\\n+                # Restrict to top-k tokens of the teacher distribution and renormalize.\\n+                # Shape: [batch, seq_len, top_k]\\n                 _, top_k_indices = torch.topk(teacher_logits, k=top_k, dim=-1)\\n-                student_full_log_probs = F.log_softmax(student_logits, dim=-1)\\n-                teacher_full_log_probs = F.log_softmax(teacher_logits, dim=-1)\\n-                student_head = torch.gather(student_full_log_probs, dim=-1, index=top_k_indices)\\n-                teacher_head = torch.gather(teacher_full_log_probs, dim=-1, index=top_k_indices)\\n-                # Accumulate retained mass in fp32: in bf16, 1-1e-6 rounds to\\n-                # one and log1p(-mass) can become -inf.\\n-                student_mass = student_head.float().exp().sum(dim=-1, keepdim=True).clamp(max=1 - 1e-5)\\n-                teacher_mass = teacher_head.float().exp().sum(dim=-1, keepdim=True).clamp(max=1 - 1e-5)\\n-                student_tail = torch.log1p(-student_mass)\\n-                teacher_tail = torch.log1p(-teacher_mass)\\n-                student_head = student_head.float()\\n-                teacher_head = teacher_head.float()\\n-                student_log_probs = torch.cat([student_head, student_tail], dim=-1)\\n-                teacher_log_probs = torch.cat([teacher_head, teacher_tail], dim=-1)\\n-            else:\\n-                # Compute log probabilities for student and teacher\\n-                student_log_probs = F.log_softmax(student_logits, dim=-1)\\n-                teacher_log_probs = F.log_softmax(teacher_logits, dim=-1)\\n+                student_logits = torch.gather(student_logits, dim=-1, index=top_k_indices)\\n+                teacher_logits = torch.gather(teacher_logits, dim=-1, index=top_k_indices)\\n+\\n+            # Compute log probabilities for student and probabilities for teacher\\n+            student_log_probs = F.log_softmax(student_logits, dim=-1)\\n+            teacher_log_probs = F.log_softmax(teacher_logits, dim=-1)\\n@@\\n-        # Per-token clipping: cap each vocabulary contribution.  Although the\\n-        # individual KL summands are signed, retaining this released behavior\\n-        # is important for the paper's point-wise robustness heuristic.\\n+        # Per-token clipping: cap each token's divergence value\\n@@\\n-        # Put the on-policy and correct anchor trajectories in one forward.\\n-        # Besides being cheaper than two calls, this is required by ZeRO-2,\\n-        # whose overlapping reducer cannot visit the same LoRA parameter from\\n-        # two checkpointed graphs in one backward pass.\\n-        use_anchor = self.lmbda > 0 and \\\"anchor_input_ids\\\" in inputs and not self.use_thinking_machines_loss\\n-        student_batch_size = inputs[\\\"student_input_ids\\\"].shape[0]\\n-        student_seq_len = inputs[\\\"student_input_ids\\\"].shape[1]\\n-        if use_anchor:\\n-            anchor_seq_len = inputs[\\\"anchor_input_ids\\\"].shape[1]\\n-            combined_seq_len = max(student_seq_len, anchor_seq_len)\\n-            pad_id = self.processing_class.pad_token_id\\n-            student_ids = F.pad(inputs[\\\"student_input_ids\\\"], (0, combined_seq_len - student_seq_len), value=pad_id)\\n-            student_mask = F.pad(inputs[\\\"student_attention_mask\\\"], (0, combined_seq_len - student_seq_len), value=0)\\n-            anchor_ids = F.pad(inputs[\\\"anchor_input_ids\\\"], (0, combined_seq_len - anchor_seq_len), value=pad_id)\\n-            anchor_mask = F.pad(inputs[\\\"anchor_attention_mask\\\"], (0, combined_seq_len - anchor_seq_len), value=0)\\n-            forward_ids = torch.cat([student_ids, anchor_ids], dim=0)\\n-            forward_mask = torch.cat([student_mask, anchor_mask], dim=0)\\n-        else:\\n-            forward_ids = inputs[\\\"student_input_ids\\\"]\\n-            forward_mask = inputs[\\\"student_attention_mask\\\"]\\n-\\n-        outputs_student = model(input_ids=forward_ids, attention_mask=forward_mask)\\n+        outputs_student = model(\\n+            input_ids=inputs[\\\"student_input_ids\\\"],\\n+            attention_mask=inputs[\\\"student_attention_mask\\\"],\\n+        )\\n@@\\n-        student_logits = outputs_student.logits[:student_batch_size, student_prompt_len - 1 : student_seq_len - 1, :]\\n-\\n-        if use_anchor:\\n-            anchor_logits = outputs_student.logits[student_batch_size:, :anchor_seq_len, :]\\n-            anchor_loss = F.cross_entropy(\\n-                anchor_logits[:, :-1, :].reshape(-1, anchor_logits.shape[-1]),\\n-                inputs[\\\"anchor_labels\\\"][:, 1:].reshape(-1),\\n-                ignore_index=-100,\\n-            )\\n+        student_logits = outputs_student.logits[:, student_prompt_len - 1 : -1, :]\\n@@\\n-            opsd_loss = self.generalized_jsd_loss(\\n+            loss = self.generalized_jsd_loss(\\n@@\\n-            # OPSD only visits student trajectories.  A weak maximum-\\n-            # likelihood anchor on the known-correct trajectory prevents\\n-            # self-distillation from polishing an incorrect branch forever.\\n-            # `lmbda` is the anchor coefficient; setting it to zero recovers\\n-            # pure OPSD exactly.\\n-            if use_anchor:\\n-                loss = opsd_loss + self.lmbda * anchor_loss\\n-                self._metrics[\\\"train\\\"][\\\"opsd_kl\\\"].append(float(opsd_loss.detach()))\\n-                self._metrics[\\\"train\\\"][\\\"anchor_ce\\\"].append(float(anchor_loss.detach()))\\n-            else:\\n-                loss = opsd_loss\\n-\\n*** End Patch\";\ntext(await tools.apply_patch(patch));"
}
exec result
Script completed
Wall time 0.0 seconds
Output:
{}
#390
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "const r = await tools.exec_command({cmd:\"git diff --stat && git diff -- opsd_trainer.py data_collator.py && git diff -- opsd_train.py && python -m py_compile opsd_train.py opsd_trainer.py data_collator.py && git status --short && cat /workspace/.timer/remaining_secs\",\"workdir\":\"/workspace/repo\",\"yield_time_ms\":10000,\"max_output_tokens\":15000});\ntext(r.output);"
}
exec result
Script completed
Wall time 0.2 seconds
Output:
 README.md     |  4 +---
 opsd_train.py | 13 +++++++++++++
 2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/opsd_train.py b/opsd_train.py
index 8526a68..b4be7a4 100644
--- a/opsd_train.py
+++ b/opsd_train.py
@@ -266,6 +266,19 @@ if __name__ == "__main__":
     dataset = load_dataset("siyanzhao/Openthoughts_math_30k_opsd")
     train_dataset = dataset["train"]
 
+    # Reasoning-effort curriculum.  `generated_token_count` is metadata shipped
+    # with the released dataset and measures how much reasoning was needed to
+    # obtain its verified solution.  Under a 100-step budget, uniformly drawing
+    # many short arithmetic items wastes privileged-teacher calls.  Retain the
+    # broad source mix but train on examples requiring at least 3k reasoning
+    # tokens, a model-native difficulty signal rather than a benchmark label.
+    train_dataset = train_dataset.filter(
+        lambda example: example["generated_token_count"] >= 3000,
+        desc="Selecting high-reasoning-effort OPSD examples",
+    )
+    if os.environ.get("LOCAL_RANK", "0") == "0":
+        print(f"Reasoning-effort curriculum: {len(train_dataset)} training rows")
+
     trainer = OPSDTrainer(
         model=model_args.model_name_or_path,
         args=training_args,
 M README.md
 M opsd_train.py
4537
#391
narration

The validated submission is now clean: only the 13-line reasoning-effort filter changes training code; the abandoned loss/anchor experiments are removed. I have enough time for one final, principled interaction test: the selected hard problems are disproportionately truncated at 1,024 tokens, so I’ll test the same curriculum with 2,048-token rollouts. If it does not beat the two-run curriculum mean, I’ll revert the recipe to the validated 1,024 setting.

#392
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "const patch = \"*** Begin Patch\\n*** Update File: /workspace/submission/recipe.env\\n@@\\n-max_completion_length=1024\\n+max_completion_length=2048\\n*** End Patch\";\ntext(await tools.apply_patch(patch));"
}
exec result
Script completed
Wall time 0.0 seconds
Output:
{}
#393
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "const r = await tools.exec_command({cmd:\"SEED=42 OUT_DIR=/workspace/runs/hard3000_long PORT=12961 bash /workspace/train_capped.sh\",\"workdir\":\"/workspace\",\"yield_time_ms\":1000,\"max_output_tokens\":300});\ntext(r.output);if(r.session_id)text(`SESSION_ID=${r.session_id}`);"
}
exec result
Script completed
Wall time 1.2 seconds
Output:
SESSION_ID=70713
#394
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolexec
args
{
  "input": "let out=\"\";for(let i=0;i<18;i++){const r=await tools.write_stdin({session_id:70713,chars:\"\",yield_time_ms:60000,max_output_tokens:30});out+=r.output.slice(-60)+\"\\n\";if(r.exit_code!==undefined){out+=`EXIT${r.exit_code}`;break;}}text(out);\n"
}
exec result
Script running with cell ID 125
Wall time 11.0 seconds
Output:
#395
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "125",
  "yield_time_ms": 60000,
  "max_tokens": 60
}
wait result
Script running with cell ID 125
Wall time 61.0 seconds
Output:
#396
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "125",
  "yield_time_ms": 60000,
  "max_tokens": 60
}
wait result
Script running with cell ID 125
Wall time 61.0 seconds
Output:
#397
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "125",
  "yield_time_ms": 60000,
  "max_tokens": 60
}
wait result
Script running with cell ID 125
Wall time 61.0 seconds
Output:
#398
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "125",
  "yield_time_ms": 60000,
  "max_tokens": 60
}
wait result
Script running with cell ID 125
Wall time 61.0 seconds
Output:
#399
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "125",
  "yield_time_ms": 60000,
  "max_tokens": 60
}
wait result
Script running with cell ID 125
Wall time 61.0 seconds
Output:
#400
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolwait
args
{
  "cell_id": "125",
  "yield_time_ms": 60000,
  "max_tokens": 60
}
wait result
Script running with cell ID 125
Wall time 61.0 seconds
Output: