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
docs: state the byte-exact certification ceiling β CPython 3.12 compiles the same AST to different bytecode depending on comprehension line-wrapping (measured: 80.62% of 516 stdlib modules preserve the fingerprint under a formatting-only rewrite). Costs the reported scores 0 rows; applies to any system scored this way.
3486ce0 verified | # 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). | |