TLM-S: a tiny Python refactoring model (6.07M parameters)

TLM-S is a small encoder-decoder transformer trained from scratch, with no pretrained weights. Given a Python function and a task, it produces the edit as a SEARCH/REPLACE block that can be applied to the source.

Tasks: rename, simplify, deduplicate, remove_dead_code, extract_method.

Architecture

4+4 layers, d_model 192, d_ff 768, 4 heads, 10k BPE vocabulary. Pre-Norm, rotary position embeddings on self-attention, tied embeddings, a copy-pointer head that blends copying from the input with generating, and per-layer task embeddings on the encoder.

Training

Span-corruption pretraining on a 5-tier curriculum, a bridging stage into SEARCH/REPLACE format, then supervised fine-tuning (25 epochs). This checkpoint is the SFT result, with no RL step. Final validation NLL 0.1664.

Results

Held-out set, 750 examples per task. "applied_ok" means the edit applies cleanly and passes the task-specific check.

rename simplify deduplicate remove_dead_code extract_method overall
43.1% 35.9% 18.6% 77.7% 17.5% 38.6%

For reference, CodeT5-small (60M parameters, official pretrained weights) reaches 55.6% overall under the same protocol.

Usage

The model class lives in the GitHub repository, so clone it first:

git clone https://github.com/mutabalShawandar/tlm-code-refactoring
cd tlm-code-refactoring
pip install -e . huggingface_hub safetensors

Then run a rename edit, which turns cryptic names into meaningful ones:

import json
import torch
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file

from src.model_config import ModelConfig
from src.model_variants import TLM_S
from src.tokenizer import TLM_Tokenizer
from src.data_set import apply_sr
from src.inference.generate import decode_rename_without_collision

REPO = "omartra999/tlm-s-refactor"

# load config, weights and tokenizer from the Hub
cfg = json.load(open(hf_hub_download(REPO, "config.json")))
config = ModelConfig(d_model=cfg["d_model"], d_ff=cfg["d_ff"],
                     n_encoder_layers=cfg["n_encoder_layers"],
                     n_decoder_layers=cfg["n_decoder_layers"])
model = TLM_S(config)
state = load_file(hf_hub_download(REPO, "model.safetensors"))
state["output_head.weight"] = state["embedding.token_embedding.weight"]  # tied weights
model.load_state_dict(state)
model.eval()

tokenizer = TLM_Tokenizer(vocab_size=config.vocab_size)
tokenizer.load(hf_hub_download(REPO, "tokenizer.json"))

code = '''def chk(s):
    if len(s) < 8:
        return False
    return True
'''

# encoder input: <bos> <task token> code <eos>
ids = config.special_token_ids
before_ids = tokenizer.encode(code)
enc_ids = [ids["bos"], ids["rename"]] + before_ids + [ids["eos"]]

with torch.no_grad():
    out = decode_rename_without_collision(model, enc_ids, cfg["task_ids"]["rename"], "cpu",
                                          before_ids, tokenizer, config, collect_conf=False)

# the model emits a SEARCH/REPLACE patch; apply it to the original code
after_ids = apply_sr(before_ids, out["tokens"], config)
print(tokenizer.decode(after_ids))

The model emits a SEARCH/REPLACE patch, which apply_sr applies to the original code. For the chk function above the result is:

def is_valid_password(password):
    if len(password) < 8:
        return False
    return True

(The tokenizer decodes without spaces around operators, so the raw output reads len(password)<8.)

Change the task by swapping "rename" for "simplify", "deduplicate", "remove_dead_code" or "extract_method" (and use greedy_decode from src.inference.generate for the non-rename tasks). Decode with up to 512 new tokens.

Limitations

These models are small and trained on a narrow, partly synthetic corpus. deduplicate and extract_method are the weakest tasks. Outputs should be reviewed and tested before use.

Downloads last month
256
Safetensors
Model size
6.07M params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support