Spaces:
Sleeping
Data Sampler Migration Guide
Who is this for? Rushabh (environment owner) β how to swap the procedural generators in
server/environment.pyfor the external-dataset sampler.
What's changing and why
The procedural generators in server/generators/ produce synthetic arithmetic
and toy-logic problems. The new sampler draws from 19,711 curated problems:
| Domain | Source | Difficulty | Count |
|---|---|---|---|
| math | Hendrycks MATH | 1β5 | 12,496 |
| code | MBPP (diff 1β2) + APPS (diff 3β5) | 1β5 | 5,915 |
| logic | Z3-generated ZebraLogic | 3β5 | 1,300 |
No other part of the environment changes.
Step 1 β Change three import lines in server/environment.py
# ββ BEFORE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
from server.generators import code_gen, logic_gen, math_gen
# inside __init__:
self._generators = {
"math": math_gen.generate,
"code": code_gen.generate,
"logic": logic_gen.generate,
}
# ββ AFTER βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
from data.sampler.math_gen_adapter import generate as math_generate
from data.sampler.code_gen_adapter import generate as code_generate
from data.sampler.logic_gen_adapter import generate as logic_generate
# inside __init__:
self._generators = {
"math": math_generate,
"code": code_generate,
"logic": logic_generate,
}
That's the only required change. The function signatures are identical to the procedural generators:
generate(difficulty: int, seed: Optional[int] = None) -> tuple[str, str].
Step 2 β Unified verifier in the reward function (optional but recommended)
If server/reward.py currently does a plain string comparison for correctness,
replace it with the domain-aware unified verifier so math gets symbolic
equivalence checking and logic gets cell-accuracy scoring:
# In compute_reward(), replace the verification call with:
from data.sampler.environment_adapter import get_sampler
sampler = get_sampler() # singleton β no repeated loading
correct = sampler.verify(problem_id, model_answer)
Note:
verify()requires aproblem_id(the stable ID stored in eachUnifiedProblem). The sampler needs to be informed which problem was just generated. The simplest approach: store theproblem_idalongside_current_answerin the environment state (same pattern already used for_current_metadata).
Step 3 β Bump max_completion_length for logic problems
Logic ZebraLogic problems require a full grid JSON in the answer, which is
significantly longer than a single number or a function. In
training/train_grpo.py, change:
# BEFORE
max_completion_length=512,
# AFTER
max_completion_length=1024,
Logic problem questions already embed the JSON output instruction
("Respond in JSON format: {\"House 1\": ...}") β no additional prompt
engineering is needed.
Step 4 β Logic generator routing (procedural + ZebraLogic)
The logic adapter routes by difficulty:
- Difficulties 1-2: the procedural generator in
server/generators/logic_gen.py(transitivity puzzles and small CSPs; short single-token string answers like"Alice"). Synthesisedproblem_idprefix:procedural_logic_. - Difficulties 3-5: the curated ZebraLogic dataset (JSON-grid answers).
Verification dispatches accordingly: procedural problems use plain
normalised string-match against the canonical answer (they are generated
on the fly and never enter the sampler's _by_id table); ZebraLogic
problems use the JSON-grid cell-accuracy verifier.
Because difficulties 1-2 are populated again, all domains start at
difficulty 1 (INITIAL_DIFFICULTIES = {"math": 1, "code": 1, "logic": 1}).
The adaptive controller then ramps up from there.
What does NOT change
| Component | Status |
|---|---|
server/environment.py reward formula |
β Unchanged |
server/reward.py Brier-score computation |
β Unchanged |
server/difficulty.py adaptive scheduler |
β Unchanged |
models/models.py Pydantic schemas |
β Unchanged |
| OpenEnv API surface | β Unchanged |
Training script (except max_completion_length) |
β Unchanged |
Sampler data coverage notes
Empty buckets (graceful fallback):
| Domain | Missing difficulties |
|---|---|
| logic | 1, 2 (no data β ZebraLogic minimum grid is 3Γ3) |
| code | no difficulty-5 MBPP; APPS fills 3β5 |
When a difficulty bucket is empty the sampler emits a warnings.warn and
falls back to the nearest populated difficulty. The environment log will
show which difficulty was actually used.
Verification
Run these tests to confirm everything works end-to-end before merging:
# Activate the project venv
source /Users/kananarora/Desktop/HonestEnv/venv/bin/activate
# From project root
PYTHONPATH=. pytest data/tests/test_unified_sampler.py \
data/tests/test_integration.py \
data/tests/test_logic_verifier.py \
-v
Expected: all tests pass (currently 84 total across the three files).
File map
data/
βββ sampler/
β βββ unified_sampler.py # Core class: loads data, exposes *_generate() + verify()
β βββ environment_adapter.py # Singleton get_sampler() + module-level shim functions
β βββ math_gen_adapter.py # Exposes generate() for math β swap import here
β βββ code_gen_adapter.py # Exposes generate() for code β swap import here
β βββ logic_gen_adapter.py # Exposes generate() for logic β swap import here
βββ verifiers/
β βββ math_verifier.py # SymPy-based symbolic equivalence
β βββ code_verifier.py # Subprocess test-runner
β βββ logic_verifier.py # Cell-accuracy (threshold β₯ 0.9)
βββ processed/
β βββ math.jsonl # 12,496 Hendrycks MATH problems
β βββ code_mbpp.jsonl # 427 MBPP problems (diff 1β2)
β βββ code_apps.jsonl # 5,488 APPS problems (diff 3β5)
β βββ logic_zebralogic.jsonl # 1,300 ZebraLogic problems (diff 3β5)
βββ MIGRATION.md # β you are here