Instructions to use briscoooe/tiny-cube-dagger with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use briscoooe/tiny-cube-dagger with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="briscoooe/tiny-cube-dagger")# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("briscoooe/tiny-cube-dagger") model = AutoModelForCausalLM.from_pretrained("briscoooe/tiny-cube-dagger", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use briscoooe/tiny-cube-dagger with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "briscoooe/tiny-cube-dagger" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "briscoooe/tiny-cube-dagger", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/briscoooe/tiny-cube-dagger
- SGLang
How to use briscoooe/tiny-cube-dagger with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "briscoooe/tiny-cube-dagger" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "briscoooe/tiny-cube-dagger", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "briscoooe/tiny-cube-dagger" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "briscoooe/tiny-cube-dagger", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use briscoooe/tiny-cube-dagger with Docker Model Runner:
docker model run hf.co/briscoooe/tiny-cube-dagger
Tiny cube-solving model β operating manual
Context for working in training/. The root CLAUDE.md is the benchmark's
manual; this one is only about the model. README.md here is the command
reference; this file is the reasoning, the traps, and the state of the work.
What this is for
Not a cube solver. packages/solver already solves any cube perfectly in
~23ms via Kociemba, and nothing here competes with that. The model is a
control for the benchmark's central claim: frontier models score 0β80% on a
task with a published exact algorithm from 2008, and the obvious objection is
that the task may just be hostile to neural computation. A tiny model tests that
objection directly.
Framed as a capability claim ("we built a cube solver") it is worthless. Framed as a learnability claim it is the control the leaderboard was missing. Keep that distinction in any writeup.
Where it got to (2026-09-09)
42.0M parameters, ~1 hour of training, ~$2 of GPU. Scored through bench.ts on
the canonical scrambles at n=200 per depth:
| depth | 3 | 6 | 10 | 15 | 20+ |
|---|---|---|---|---|---|
| solve rate | 100% | 69% | 1.5% | 0% | 0% |
At depth 6 that is statistically indistinguishable from gpt-5.6-sol (73%,
n=11) β say matches, not beats. Full write-up, including what the result
does not support, in
docs/findings/2026-09-09-tiny-model-reach-wall.md.
The finding is the reach wall. The falloff is a cliff at a particular solution length, not gradual decay, and it did not move across 15,000 steps of training β the model improves within its ~8-move reach while the reach itself stays put.
Model shape
Stock LlamaConfig, trained from scratch β the architecture, not Llama weights.
Nothing is downloaded. Pretraining has nothing to transfer: the vocabulary is 28
tokens, the task is one fixed function rather than language, and training data is
unlimited and verified. A standard architecture is used only so checkpoints load
with plain transformers and push to the Hub without custom code.
- hidden 512, 10 layers, 8 heads, seq 83 β 41.97M params, 168MB fp32
- Embeddings are 0.03% of parameters (14k, from a 28-token vocabulary), against 20β40% in a normal LLM. Essentially every parameter computes rather than stores.
Things that will bite you
Evaluate by solve rate, and break it down by length
A cube has enormously many valid solutions and Kociemba emits one, so a model producing a different valid solve scores badly on token match and perfectly on what matters. Never use loss or token accuracy as the metric.
And never use a single aggregate over the holdout. It is ~85% fully-mixed
states, so a model with an 8-move reach cannot exceed ~15% on it however well it
trains. On the first run that aggregate sat at 11β12% from step 13000 to 40000
and was read as a plateau three times, while depth 6 was in fact going 52% β 72%.
solve_rate() now buckets by solution length for exactly this reason.
A saturated metric must never choose checkpoints
Same run, sharper consequence: checkpoints were saved only on improvement, so
with the metric pinned the run kept a step-18000 model and discarded steps
18000β40000. Fixed in 5779f7e β latest weights always written to --out,
best-scoring to --out-best. If you change the save logic, keep that property.
Install kociemba from git, never PyPI
The 1.2.1 sdist dates from 2017 and on most machines installs "successfully" while producing no C extension, silently falling back to a pure-Python implementation ~50x slower. Nothing errors; the only symptom is that generation crawls while a rented box bills by the hour.
pip install --no-build-isolation "git+https://github.com/muodov/kociemba.git"
gen_data.py refuses to run below native speed and vast_bootstrap.sh gates on
it. Two diagnoses that look right and are not, recorded so they are not
re-derived: missing libffi-dev (a real error, but not the cause) and
python 3.10 vs 3.11 (also not the cause).
gen_data.cjs is the escape hatch β cubejs, pure JavaScript, no compiler, and it
is the solver the harness scores with. About a third the throughput; always works.
Verified decoding needs a high temperature
Decoding several candidates and replaying each through the engine is free and exact, but the obvious temperature makes it useless. Measured over greedy failures, 16 samples each:
| temp | distinct candidates | failures rescued |
|---|---|---|
| 0.7 | 2.8 | 0/5 |
| 1.0 | 4.0 | 1/5 |
| 1.5 | 11.0 | 2/5 |
The model is confidently wrong, not uncertain, so mild sampling redraws the
same answer. Default is 1.5. Keep attempts=1 (bare greedy) as the model's own
score and report anything above it separately β it is a system score.
Generation is CPU-bound; the GPU is idle for it
Each pair needs a Kociemba solve, which is IDA* tree search β branchy control
flow and random pruning-table lookups, the opposite of what a GPU does. The lever
is fewer solver calls, not more GPU. --augment 8 takes several states from each
solution path for an ~8x cut (measured 7.8x).
Augmentation also improves coverage rather than harming it, contrary to the first assumption here: uniformly sampled states are ~95% distance 18β20, whereas sampling along solution paths spans distance 1β20 evenly, matching a benchmark whose buckets start at depth 3. Keep the holdout unaugmented and uniform so evaluation stays against the hard case.
nproc lies on a rented box
A machine advertising 384 cores gave 73 under its cgroup quota. Sizing the
pool by the host count puts five processes on every CPU. Aggregate throughput is
unchanged (the quota binds either way) but every ETA derived from it is wrong by
that factor β which is how a 20-minute job was predicted for a 100-minute one.
--workers 0 (the default) reads the real quota and prints both numbers.
vast.ai
- Check the GPU before generating data. torch cu124 has no Blackwell (sm_120)
kernels;
torch.cuda.is_available()returns true and it fails only when a kernel runs. That was discovered after 50 minutes of generation on a box that could not train. Install cu128 and run the smoke test (real model, one forward+backward) first. - Size the disk deliberately.
dph_totalincludes storage: a 100GB disk took a $0.112/hr box to $0.240/hr. 6M pairs is ~1GB; 20β30GB is plenty. - This repo is private, so a rented box cannot clone it. A curl to the GitHub API from inside a session misleadingly returns 200 because the outbound proxy injects credentials. Code ships via the model's own Hub repo instead.
/api/v0/instances/is deprecated in favour of/api/v1/, but log retrieval works only on the v0request_logspath.- Archive the instance log before destroying the box. The first run's raw training log was lost this way; only a reconstruction survives.
- Avoid boxes behind a national firewall. A machine in CN could not reach
Hugging Face at all, and
snapshot_downloaddoes not raise on that -- it returns the empty local dir, so the fetch looks successful until a later step cannot find a file, and the run exits 0 having done nothing. Such a box also cannot push checkpoints, so it is unusable for this workflow regardless. The onstart script now checks forvast_bootstrap.shafter the download and aborts loudly if it is missing.
Not a like-for-like comparison, and say so
serve.py parses the 54 facelets out of the prompt and feeds the model only
those. The LLMs must read an ASCII net out of prose. That is aligned with intent
β the whole v1βv2 prompt change existed to remove net-parsing as a confound β
but it is not identical conditions, and any writeup has to state it.
Overfitting, by contrast, is not a plausible worry and has been checked: 4.3e19
states against 6M training pairs (~1e-13 of the space), and 200k states from the
training seed versus 200k from the holdout seed gave 400k distinct states with
zero overlap. eval_canonical.py additionally scores against
generateScrambleSet β a different code path from the training sampler β so a
model fitted to its sampler would show a gap there.
The depth hypothesis is dead (2026-09-10)
The ~8-move reach wall looked like a depth limit: one step of sequential computation per layer, 10 layers, ~8-move reach. It is not. Two arms at matched parameters -- 720x10 (83.0M) and 512x20 (83.9M), identical data, batch, schedule and steps -- scored the same at all nine matched checkpoints, finished at exactly 10.9%, and produced identical per-depth results: 100% at depth 3, 64% at depth 6, zero from depth 10 up, down to the same single 15+ holdout solve each.
The practical conclusion inverts the hypothesis. Prefer width. Depth cost 3.3x the wall-clock (0.9 it/s against 3.0 -- layers run sequentially and do not parallelise the way width does) and 36% more memory at identical batch (33.1GB against 24.4GB, twice the layers holding activations for the backward pass), for no gain whatsoever.
See docs/findings/2026-09-10-depth-does-not-buy-reach.md.
Next experiments, in priority order
- Chunked inference. Feed the true state back every ~8 moves. With depth eliminated, this is the intervention most likely to work: it sidesteps the compounding-error arithmetic entirely and plays directly to a model that is already 100% at depth 3. It makes the task interactive rather than one-shot, so it is a different benchmark mode, not a config flag, and its numbers must not sit in the same table.
- Train on the model's own rollouts (DAgger-style). The model imitates one Kociemba solution per state and is never taught to recover from its own drift, which is exactly what a long solve requires. This is textbook exposure bias, and it is the remaining explanation for the wall now that architecture is ruled out.
- Re-run the discarded steps. The benchmarked model is step 18000 of 40000; with the metric and checkpointing now fixed, resuming would show whether depth 6 kept climbing past 72%. ~20 minutes, ~$0.50.
- RL for solution length. Reward
solved ? (C - len) : 0. The verifier is already exact and free. Closest to the "train a reasoning model" question.