Text Generation
Transformers
Safetensors
GGUF
English
qwen2
decompilation
reverse-engineering
python
bytecode
code
verified-generation
conversational
text-generation-inference
Instructions to use BlazingCustoms/pybytecode-v3-1.5b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use BlazingCustoms/pybytecode-v3-1.5b with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="BlazingCustoms/pybytecode-v3-1.5b") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("BlazingCustoms/pybytecode-v3-1.5b") model = AutoModelForCausalLM.from_pretrained("BlazingCustoms/pybytecode-v3-1.5b", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use BlazingCustoms/pybytecode-v3-1.5b with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf BlazingCustoms/pybytecode-v3-1.5b:F16 # Run inference directly in the terminal: llama cli -hf BlazingCustoms/pybytecode-v3-1.5b:F16
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf BlazingCustoms/pybytecode-v3-1.5b:F16 # Run inference directly in the terminal: llama cli -hf BlazingCustoms/pybytecode-v3-1.5b:F16
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf BlazingCustoms/pybytecode-v3-1.5b:F16 # Run inference directly in the terminal: ./llama-cli -hf BlazingCustoms/pybytecode-v3-1.5b:F16
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf BlazingCustoms/pybytecode-v3-1.5b:F16 # Run inference directly in the terminal: ./build/bin/llama-cli -hf BlazingCustoms/pybytecode-v3-1.5b:F16
Use Docker
docker model run hf.co/BlazingCustoms/pybytecode-v3-1.5b:F16
- LM Studio
- Jan
- vLLM
How to use BlazingCustoms/pybytecode-v3-1.5b with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "BlazingCustoms/pybytecode-v3-1.5b" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "BlazingCustoms/pybytecode-v3-1.5b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/BlazingCustoms/pybytecode-v3-1.5b:F16
- SGLang
How to use BlazingCustoms/pybytecode-v3-1.5b 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 "BlazingCustoms/pybytecode-v3-1.5b" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "BlazingCustoms/pybytecode-v3-1.5b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'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 "BlazingCustoms/pybytecode-v3-1.5b" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "BlazingCustoms/pybytecode-v3-1.5b", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Ollama
How to use BlazingCustoms/pybytecode-v3-1.5b with Ollama:
ollama run hf.co/BlazingCustoms/pybytecode-v3-1.5b:F16
- Unsloth Studio
How to use BlazingCustoms/pybytecode-v3-1.5b with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for BlazingCustoms/pybytecode-v3-1.5b to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for BlazingCustoms/pybytecode-v3-1.5b to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for BlazingCustoms/pybytecode-v3-1.5b to start chatting
- Pi
How to use BlazingCustoms/pybytecode-v3-1.5b with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf BlazingCustoms/pybytecode-v3-1.5b:F16
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "BlazingCustoms/pybytecode-v3-1.5b:F16" } ] } } }Run Pi
# Start Pi in your project directory: pi
- OpenClaw new
How to use BlazingCustoms/pybytecode-v3-1.5b with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf BlazingCustoms/pybytecode-v3-1.5b:F16
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "BlazingCustoms/pybytecode-v3-1.5b:F16" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
- Docker Model Runner
How to use BlazingCustoms/pybytecode-v3-1.5b with Docker Model Runner:
docker model run hf.co/BlazingCustoms/pybytecode-v3-1.5b:F16
- Lemonade
How to use BlazingCustoms/pybytecode-v3-1.5b with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull BlazingCustoms/pybytecode-v3-1.5b:F16
Run and chat with the model
lemonade run user.pybytecode-v3-1.5b-F16
List all available models
lemonade list
- Hermes Agent
How to use BlazingCustoms/pybytecode-v3-1.5b with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf BlazingCustoms/pybytecode-v3-1.5b:F16
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default BlazingCustoms/pybytecode-v3-1.5b:F16
Run Hermes
hermes
- Atomic Chat
File size: 20,064 Bytes
81af876 3486ce0 81af876 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | # PyBytecode v3 β full evaluation
The model card is deliberately short. This file is the long version: every number, the interval
around it, the set it was measured on, and the command that reproduces it. Nothing here is
softened relative to the card; the card is a subset.
Companion files: [`ORACLE-LIMITS.md`](ORACLE-LIMITS.md) for what the check can and cannot do,
[`DATA-CARD-training-corpus.md`](DATA-CARD-training-corpus.md) for training-data lineage.
---
## 1. What "certified" means
The oracle is **sound but incomplete**:
```
verified = PROVABLY correct. Recompiling the prediction yields a code object byte-identical to
the target's. Identical code object => identical behaviour. No false positives.
unverified = UNKNOWN, not wrong. A correct decompilation that compiles differently β a `while`
where the original had a `for`, a differently-ordered but equivalent boolean β
does not certify.
```
Three consequences:
- **Reported accuracy is a lower bound on correctness.** Everything counted here is proven; some
of what is not counted is right too.
- **Best-of-N buys real accuracy, not a better guess.** Sample until something certifies, then
stop. The verifier decides when to stop, so extra budget cannot mislead you.
- **The check is exact.** Comparison runs over the real code object, recursively, including
docstrings and `co_exceptiontable` β not over a textual disassembly. That is not decoration: an
earlier representation omitted the exception table's `end`, which made a `try:` body and a
`try/else:` body indistinguishable, and it once certified a wrong prediction.
Strict L1 oracle throughout this file. Every figure carries a 95% interval and names its set.
---
## 2. Headline results β `csn-3.12-licensed`, n=600 across 117 repositories
| | certified | 95% CI |
|---|---|---|
| **PyBytecode v3, greedy** | **506 / 600 = 84.33%** | [80.48, 87.94] |
| **PyBytecode v3, verified best-of-32** | **562 / 600 = 93.67%** | [90.86, 96.08] |
| Untuned `Qwen2.5-Coder-1.5B-Instruct`, greedy | **4 / 600 = 0.67%** | [0.16, 1.35] |
Intervals are **repo-clustered** (bootstrap over repositories, 10,000 resamples), because rows
from one repository share an author and a house style and are not independent. Design effects
1.65 / 1.79 / 0.84.
The benchmark ships with the model, licence-resolved per row at the exact commit, so these numbers
are recomputable from files in this repository rather than taken on faith.
### The untuned base scores ~0, which is what makes the benchmark trustworthy
The same base model, given the same prompt, the same decoding budget and the same oracle, certifies
**4 of 600** here and **0 of 400** on our earlier benchmark. Under looser oracles it is clearer
still: the base produces **syntactically valid Python on 52% of rows** and reaches AST-identity on
**0%**. It paraphrases the disassembly into plausible-looking code with invented helpers rather
than inverting it.
A strong general code model cannot guess its way to a single point on this task. The benchmark is
not solvable by pattern-matching, and everything v3 scores was learned from the fine-tune. The
paired difference is not close to the noise floor: exact McNemar on 502 discordant pairs in our
favour and 0 against gives p < 1e-12.
### The benchmark rebuild reproduced the result
The published 600-row set was built from scratch: different rows (only 85 of 600 appear in the
earlier 400-row set), 117 repositories instead of 24, and a 1% per-repo cap replacing a 15%
concentration. It lands in the same place β certified@32 **93.67%** against **93.25%**, greedy
**84.33%** against **83.75%**. The difference of **+0.42pp** carries a 95% interval of
[β4.38, +5.22], so it is well inside noise, and the two size profiles are near-identical (median
59 vs 62 rep lines; 93.3% vs 94.0% of rows under 200), which is the composition factor that would
otherwise move the number. Standardised to the older set's size mix the new set reads 94.48%.
A result that survives a rebuild of the set it was measured on is worth more than the same result
quoted twice.
### Which numbers came from where
| Set | Published? | Why |
|---|---|---|
| `csn-3.12-licensed` (600) | **yes** | every row licence-resolved at its commit |
| `pybytecode-mbpp-3.12` (383) | **yes** | MBPP, CC-BY-4.0 |
| CSN-3.12 (400) | no | 45% of its rows are not redistributable |
| Held-out (279) | no | per-row attribution was not retained |
Our own results are measured on both the published set and the earlier pair. The figures in
*Comparison with other systems* exist only on the earlier pair, because that is where those runs
were done; they are labelled there because you cannot re-run them from what we shipped.
---
## 3. How much sampling budget to spend
The best-of-32 budget is **1 greedy decode + 31 sampled candidates at temperature 0.8**, with
verified early stop β sampling halts on the first candidate that certifies, so the mean cost is
far below 32. Across the 94 greedy failures it took a mean of 15.16 samples and recovered 56:
| budget | certified | |
|---|---|---|
| @1 (greedy alone) | 506 / 600 | 84.33% |
| @2 | 525 / 600 | 87.50% |
| @4 | 546 / 600 | 91.00% |
| @8 | 555 / 600 | 92.50% |
| @16 | 558 / 600 | 93.00% |
| @32 | 562 / 600 | 93.67% |
Most of the recovery arrives in the first few samples: @4 already captures two thirds of what @32
gets. Because early stop depends only on the index of the first passing sample, this curve is
exact rather than an estimate β the same cached generations give every point.
---
## 4. Accuracy by input size β the full curve
The size axis is **rep lines**: the number of lines in the disassembly handed to the model. One
line measures it:
```python
from harness.pybytecode_core.rep import disassemble_v2
rep_lines = disassemble_v2(code_object).count("\n")
```
### On the published 600-row benchmark
Recomputable from the files in this repo with `harness/size_curve.py`:
| rep lines | rows | v3 greedy | 95% CI | v3 best-of-32 | 95% CI |
|---|---|---|---|---|---|
| 0β49 | 224 | 95.98% | [93.01, 98.51] | 98.21% | [96.31, 99.58] |
| 50β99 | 224 | 89.73% | [85.17, 93.93] | 98.21% | [96.26, 99.57] |
| 100β199 | 112 | 65.18% | [55.36, 74.14] | 85.71% | [77.57, 92.98] |
| 200β299 | 27 | 51.85% | β | 77.78% | β |
| 300β399 | 5 | 60.00% | β | 100.00% | β |
| 400β599 | 5 | 0.00% | β | 0.00% | β |
| 600+ | 3 | 0.00% | β | 0.00% | β |
Intervals are repo-clustered, and omitted below 30 rows / 10 repositories rather than printed at a
width that would imply precision we do not have. The 300β399 bucket reading above the one below it
is n=5 noise, not a recovery. Median input in this benchmark is **59** rep lines; p90 is 167, max
1,622.
On this set the top two buckets stay at **0% even with 32 samples** β above ~400 rep lines,
sampling did not rescue a single one of the 8 rows. **96.64%** of greedy certifications and
**95.37%** of best-of-32 certifications come from units under 200 rep lines.
The four-row grouping on the model card is this same table pooled: under 100 rep lines is
416 / 448 = 92.86% greedy and 440 / 448 = 98.21% at 32; 200β399 is 17 / 32 = 53.12% and
26 / 32 = 81.25%; 400+ is 0 / 8 at both budgets.
### Pooled over both earlier benchmarks (n=679)
Pooled so the thin upper buckets carry as many rows as we can give them. This is also the only
table where PyLingual appears per bucket:
| rep lines | rows | v3 greedy | v3 best-of-32 | untuned base | PyLingual k=32 |
|---|---|---|---|---|---|
| 0β49 | 311 | 94.21% | 98.71% | 0.96% | 99.04% |
| 50β99 | 229 | 88.65% | 96.94% | 0.00% | 95.63% |
| 100β199 | 105 | 78.10% | 88.57% | 0.00% | 84.76% |
| 200β299 | 19 | 47.37% | 84.21% | 0.00% | 84.21% |
| 300β399 | 11 | 18.18% | 54.55% | 0.00% | 81.82% |
| 400β599 | 2 | 0.00% | 50.00% | 0.00% | 100.00% |
| 600+ | 2 | 0.00% | 0.00% | 0.00% | 50.00% |
- **The greedy knee is at ~200 rep lines**: 78.1% β 47.4% β 18.2% β 0% across four consecutive
buckets.
- **Best-of-32 postpones the knee to ~300; it does not remove it.** Sampling 32 times buys roughly
one bucket of headroom.
- Almost every point we score comes from small units. On the earlier CSN-3.12 set (n=400),
**97.61%** of greedy certifications and **95.98%** of best-of-32 certifications are units under
200 rep lines; on the published 600-row set it is **96.64%** of greedy certifications. The
headline accuracy is a statement about small units.
- The top two buckets are n=2 each and carry nothing on their own. What carries the conclusion is
the monotone decline through n=311/229/105/19/11 below them.
---
## 5. Comparison with other systems
[PyLingual](https://github.com/syssec-utd/pylingual) is another system that performs this task, by
symbolic reconstruction rather than generation. We ran it on the same sets, under the same oracle,
at a matched budget.
| CSN-3.12, n=400, 24 repos | certified | 95% CI (repo-clustered) |
|---|---|---|
| untuned base, greedy | 0 / 400 = 0.00% | β |
| v3 greedy | 335 / 400 = 83.75% | [77.84, 88.89] |
| v3 certified@32 | 373 / 400 = 93.25% | [88.82, 96.89] |
| PyLingual k=32 | 377 / 400 = 94.25% | [91.06, 96.80] |
| **union** | **393 / 400 = 98.25%** | [96.81, 99.51] |
| Held-out, n=279 | certified | 95% CI (Wilson) |
|---|---|---|
| untuned base, greedy | 3 / 279 = 1.08% | [0.37, 3.11] |
| v3 greedy | 254 / 279 = 91.04% | [87.11, 93.86] |
| v3 certified@32 | 272 / 279 = 97.49% | [94.91, 98.78] |
| PyLingual k=32 | 267 / 279 = 95.70% | [92.63, 97.52] |
| **union** | **275 / 279 = 98.57%** | [96.37, 99.44] |
**Neither difference is statistically significant** (exact paired McNemar, p = 0.6177 on CSN and
p = 0.2266 on held-out). We claim no accuracy advantage, and none is claimed against us. On cost
the comparison runs against us: ~1.5B parameters against their ~320M, and slower wall-clock.
Docstring recovery on the 115 held-out rows carrying a real docstring: **115 / 115 = 100%**
(PyLingual: 113/115 = 98.26%, under an oracle that does not score docstrings at all).
The held-out interval is **Wilson, not clustered**: every row of that set carries the same
placeholder repository value, so its clustering cannot be assessed. The CSN design effects of
2.34β2.69 show why that matters β on a set where one repository supplied 15% of rows, a naive
interval would have been far too narrow.
These figures were measured on our earlier benchmark pair, which is **not** published. PyLingual
has not been run on the 600-row licensed benchmark released alongside this model.
### The two approaches fail on different inputs
The more interesting measured result is not which number is larger, but that the two systems lose
on different inputs, along an axis that turns out to be **unit size**:
| rep lines | rows | PyBytecode greedy | PyBytecode @32 | PyLingual k=32 |
|---|---|---|---|---|
| 100β199 | 105 | 78.10% | 88.57% | 84.76% |
| 200β299 | 19 | 47.37% | 84.21% | 84.21% |
| 300β399 | 11 | **18.18%** | 54.55% | **81.82%** |
| 400β599 | 2 | **0.00%** | 50.00% | **100.0%** |
A generative model degrades as its input grows; a symbolic reconstruction does not. Two things
follow, both useful:
- **Large units are not intrinsically unsolvable.** A symbolic system handles them at 81.82% where
we score 18.18%. What our curve measures is a capability limit of a 1.5B model, not a property
of the task β which is a statement about where to spend the next round of scale.
- **The union exceeds either system**, 98.25% and 98.57% above. Because verification is sound, the
union costs nothing to take: run either, keep the answer that certifies, fall through to the
other. Neither system can certify a wrong answer, so combining them cannot mislead you.
---
## 6. Limits, in full
- **Large units β this is the measured limit.** Certification falls from 94% under 50
representation lines to 18% at 300β399 and 0% above 400. The curve in Β§4 is the honest
specification of what this model does. It was trained on functions; a whole module is a
different regime in both size and shape, and we have not evaluated one.
- **Python 3.12 only.** A scope statement, not the explanation for the size behaviour above:
trained and measured on 3.12, and the oracle refuses cross-minor input by design. Every number
in this file is in-version, so nothing here is contaminated by a version mismatch.
- **It did not decompile real malware.** On the one wild PyInstaller-packed sample in scope, the
entry-point module produced **nothing certifiable** β the prediction did not even compile. 3 of
9 units in that sample certified, and those three were PyInstaller's own bootstrap plus a
bundled stdlib module. Extraction and representation work (1,510/1,510 code objects recovered);
end-to-end decompilation of real malware logic is **not demonstrated**.
That entry point was both 3.13 *and* 491 representation lines, so **size and version are
confounded in it and it is evidence for neither** on its own. It is reported because you should
know the result, not because it diagnoses anything. The benign in-version curve in Β§4 is the
instrument, and it already shows the model failing at that size on clean 3.12 input.
- **Annotated functions** are handicapped: the training corpus was compiled with PEP-563
stringised annotations inherited, real `.pyc` files are not, and the benchmarks deliberately do
not hand the model its training-time distribution back.
### The oracle's limits, where you will meet them
Full detail in [`ORACLE-LIMITS.md`](ORACLE-LIMITS.md).
- **0.33% false-reject floor on foreign `.pyc`.** On 600 wild install-time `.pyc`, 598 certify;
2 do not, because CPython 3.12.3 and 3.12.13 generate different code for the same source. No
normalisation fixes that without abandoning the byte-identical guarantee. It fails to a **false
reject** β you are told "unknown" about a correct answer, never "verified" about a wrong one.
- **Optimization level must match the producer's.** A `.pyc` built with `-O` is a different code
object. The wrong level does not degrade gracefully, it collapses to ~24%. Try all three; it
costs three compiles. But note that at `optimize>=1` docstrings are absent from the `.pyc`, so
**docstring recovery becomes unprovable** against such a file β a limit of the artifact, not of
the oracle.
- **The 100% pre-flight the harness prints proves nothing about soundness.** It grades each
reference against itself, i.e. compares `compile(x)` with `compile(x)`, so any deterministic
function of the source scores 100% β including a stub that ignores the bytecode entirely. It
detects a broken harness and nothing more. The soundness evidence is the mutation test (0 true
survivors in 1,239 mutants) and 18 targeted blind-spot probes (18/18).
- **Formatting the AST does not capture can still change the bytecode, so byte-exact
certification has a ceiling on some code.** CPython 3.12 inlines comprehensions (PEP 709) and
emits a cleanup entry in the exception table for the inlined scope. How the comprehension is
*line-wrapped* changes that entry. Two source texts with an identical AST, identical
`ast.unparse` output and identical behaviour therefore compile to different code objects:
```python
def f(xs): return sum([1 for a in range(len(xs)) for b in range(a, len(xs)) if xs[b] == a])
def f(xs): return sum([1 for a in range(len(xs)) for b in range(a, len( # same AST,
xs)) if xs[b] == a]) # different bytecode
```
Measured on this interpreter: over 516 standard-library modules, rewriting each through
`ast.unparse(ast.parse(src))` β a formatting-only change β preserves the compiled fingerprint
for 416, **80.62%**. Roughly one wild module in five compiles differently after a rewrite that
changes no code. (Byte-identical *text* round-trip is 0.6%, as expected, since `unparse`
normalises formatting by design. 80.62% is the figure that matters, because certification
compares code objects, not text.)
On the published benchmarks, an AST-identical re-wrap changes the fingerprint for **26 of 600**
rows of `csn-3.12-licensed` (4.33%) and **20 of 383** rows of the MBPP set (5.22%). 12.8% and
19.1% of their rows contain a comprehension at all.
**This is a property of byte-exact certification, not of this model, and it applies to every
system scored this way β including PyLingual in Β§5.** A decompiler that recovers correct source
but wraps it differently is recorded as failing on those inputs. It fails safe: the verdict is
"not proven", never "verified" for a wrong answer.
It costs the numbers in this file nothing measurable. Both benchmarks' reference text is
`ast.unparse` output and the model reproduces that canonical single-line form, so of the 94
greedy failures on `csn-3.12-licensed`, **0** have an AST identical to the reference. The
ceiling limits what certification can *prove* about differently-formatted source; it does not
inflate or deflate the reported scores.
Two consequences worth stating plainly. First, both published benchmarks are **internally
self-consistent**: the reference source and the `.pyc` are compiled from the same normalised
string β verified 600/600 and 383/383 β which is what makes the oracle sound. Second, that
normalised string is not the upstream text. For the MBPP set, **1 of 383 rows**
(`src/00117.py`, MBPP `task_id` 757) compiles to different bytecode than the original MBPP
solution, because that solution wraps a nested comprehension across lines. The row remains a
valid decompilation task and its rewrite is declared in the dataset's `provenance.transform`
and `NOTICES.md`; it is recorded here rather than quietly re-derived.
- **Untested:** cross-minor 3.13, Nuitka, non-CPython builds, obfuscated bytecode.
---
## 7. Reproduce every number
No model, no GPU, no network, no third-party decompiler β CPython 3.12 standard library only:
```bash
cd harness
python3 grade.py --bench ../benchmarks/csn-3.12-licensed/bench.jsonl --self-test-only --out /tmp/st.json
python3 analyze_scores.py --bench ../benchmarks/csn-3.12-licensed/bench.jsonl \
--greedy ../generations/gen_v3_csn600.jsonl \
--base ../generations/gen_base_csn600.jsonl \
--out /tmp/scores.json --rows-out /tmp/rows.jsonl
python3 size_curve.py --bench ../benchmarks/csn-3.12-licensed/bench.jsonl \
--greedy ../generations/gen_v3_csn600.jsonl \
--base ../generations/gen_base_csn600.jsonl \
--out /tmp/size_curve.json
```
Per-row verdicts for all 600 rows β repo, function, commit SHA, SPDX, tuned verdict, base verdict
β land in `rows.jsonl`. Every aggregate in Β§2 and Β§4 is recomputable from that file.
The figures in Β§5 are the exception: they were produced on the unpublished earlier benchmark pair
and against a GPL-3.0 third-party decompiler we do not vendor, so they cannot be re-run from what
ships here.
---
## 8. Training
| | |
|---|---|
| Base | [`Qwen/Qwen2.5-Coder-1.5B-Instruct`](https://huggingface.co/Qwen/Qwen2.5-Coder-1.5B-Instruct) (Apache-2.0) |
| Method | LoRA r=16, Ξ±=32, all attention + MLP projections, 1 epoch, lr 2e-4, merged into the base |
| Rows | 48,196 pairs of Python 3.12 disassembly β source |
| Corpus | `codeparrot/github-code-clean`, per-row filtered to 7 permissive licences; all GPL/LGPL/AGPL/MPL/EPL dropped before extraction |
| Decontamination | shard-disjoint, repo-disjoint, and identifier-blind fingerprint-disjoint |
**The training corpus is not distributed.** Per-row attribution was not retained during
extraction, so the corpus cannot be redistributed without dropping required MIT/BSD/Apache
notices. That is a property of the artifact, not of the licences β nothing in it is unlicensed or
reciprocally licensed. Full lineage, including what was dropped and why:
[`DATA-CARD-training-corpus.md`](DATA-CARD-training-corpus.md).
|