Downloads · 30 days
473
100% of all-time downloads
IAMIbrahim/luthor-8b
luthor-8b is a text generation model from IAMIbrahim. Use it when you need the model to write or continue text. It is set up for transformers. The card lists the license as apache-2.0.
A Qwen3-8B fine-tune for driving terminal and file-editing tools inside an agent loop — not for chat.
Downloads · 30 days
473
100% of all-time downloads
All-time downloads
473
Public
Parameters
8.2B
16.4 GB on disk
Likes
1
Trending 1
Click a slice to open those files.
.safetensors16.4 GB · 100%
From the Hugging Face model README
A Qwen3-8B fine-tune for driving terminal and file-editing tools inside an agent loop — not for chat.
Luthor is trained exclusively on execution-verified agent trajectories: every trajectory in the training set ends in a patch that made a previously-failing test suite pass, checked by actually running the tests in a per-task Docker image. Trajectories that merely looked correct were discarded.
Status: evaluated, and it did not pass its ship gate. On 10 held-out task instances it solved 0, the same as the stock base model, and it followed the task protocol markedly worse. Do not use this model expecting an improvement over
Qwen/Qwen3-8B. See Evaluation.
The target behaviour is the unglamorous part of agentic coding: read the failing test, locate the file, make a targeted edit, re-run the suite, and stop. The training signal rewards finishing — reaching a verified green test run — rather than producing plausible-looking diffs.
<tools> block in the system prompt;
the model emits <tool_call> and receives <tool_response>. Qwen3 speaks this natively.<think> blocks. These are preserved in training (capped at 12k characters).Run on 10 held-out task instances (5 commits never seen in training x 2 prompt phrasings), one attempt each (pass@1), 50-turn cap, real Docker sandboxes, patches verified by executing the previously-failing test suite. Both models served at bf16 via vLLM with YaRN-extended 64K context.
| pass@1 | 95% CI | median turns | completed | emitted capture | tool errors | |
|---|---|---|---|---|---|---|
| Luthor 8B | 0/10 | [0.00, 0.28] | 11 | 0/10 | 3/10 | 7 |
| Qwen3-8B (base) | 0/10 | [0.00, 0.28] | 26 | 8/10 | 8/10 | 22 |
The fine-tune did not beat the base model, and on protocol adherence it is clearly worse. Luthor terminates after a median of 11 turns without completing the task, and only 3 times in 10 does it emit the final patch-capture command the harness requires, versus 8 for the base model.
Neither model solved any task, so this benchmark does not establish that the base model is good either — it establishes that this 10-task gate is beyond both, and that fine-tuning made protocol-following worse.
The harness supplies an ephemeral system prompt instructing the agent that its final tool call must be
git diff --cached task-start ... | gzip | base64. That prompt is explicitly not saved into the trajectories.
So the training data contains the capture behaviour but never the instruction that motivates it, while the base
model simply follows the instruction at eval time. Fine-tuning appears to have eroded that instruction-following
without installing the behaviour in its place.
A second, unconfirmed hypothesis: 95 trajectories were windowed into 493 training sequences, so roughly four out of every five training sequences end partway through a task rather than at a real completion. That may have taught the model to stop early.
python -m eval.make_eval_tasks # 5 held-out commits -> 10 task instances
eval/ship_gate.sh student # or: base
python -m eval.compare --student-rollouts ... --base-rollouts ...
The training data is private source code, so the weights were probed for regurgitation before release
(eval/leak_probe.py, eval/pii_probe.py): 40 prefix-extraction probes and 24 targeted elicitations, run against
both this model and stock Qwen3-8B as a control.
| mean longest verbatim match | max | secrets elicited | |
|---|---|---|---|
| Luthor 8B | 12.1 chars | 49 | 0 |
| Qwen3-8B (control) | 23.0 chars | 96 | 0 |
Luthor reproduces less verbatim training text than the base model. No emails, hostnames, credentials or proprietary spans were recovered. The training corpus itself was separately scanned for credentials across 16 provider patterns and 7,051 turns: none found. This is evidence, not proof — it samples a corpus, uses greedy decoding, and does not test paraphrased leakage.
<think> blocks.495 windows derived from 95 verified trajectories:
| Source tasks | 74 fail-to-pass tasks mined from real commits, each with a per-task Docker image |
| Rollouts | 4 attempts × 74 tasks = 296 trajectories (teacher: DeepSeek V4.1 Flash) |
| Passed verification | 138 trajectories, covering 64/74 tasks, 0 patch-extraction failures |
| Train / eval split | 95 train / 16 eval trajectories, split by commit (5 held-out commits) |
| Windows | 493 train / 78 eval, ≤16,384 tokens, p50 13,789 |
| Error-recovery examples | 12 (trajectories that hit a tool error and recovered) |
| Approx. train tokens | 6.39M per epoch |
| Mean turns per trajectory | 24.2 |
Splitting by commit rather than by trajectory matters: multiple attempts at the same task share the same fix, so a trajectory-level split would leak the answer across the boundary.
Raw trajectories have a median of ~61k tokens, dominated by tool output. Three transforms make them trainable:
<think> blocks capped at 12,000 characters.loss_from index so the overlap context contributes no loss.Loss is computed on assistant turns only (-100 everywhere else), and only at or after loss_from.
QLoRA on a single H100 80GB, ~55 minutes.
| Hyperparameter | Value |
|---|---|
| Base | Qwen/Qwen3-8B |
| Quantisation | 4-bit NF4, double quant, bf16 compute |
| LoRA | r=64, α=128, dropout 0.05, all-linear |
| Trainable params | 174,587,904 (2.09%) |
| Sequence length | 16,384 |
| Batch | 1 × 16 grad accum (effective 16) |
| Optimiser | AdamW, lr 1e-4, cosine, 3% warmup, weight decay 0 |
| Grad clip | 1.0 |
| Epochs | 2 (986 micro-steps ≈ 62 optimiser steps) |
| Precision | bf16, gradient checkpointing |
Final training loss ~1.25, down from 8.23. Per-step loss is noisy at batch size 1; the band matters, not individual steps.
Both of these were discovered by OOM, and both are worth knowing if you reproduce this:
liger-kernel). Qwen3's 151,936-token vocabulary at 16,384 positions
produces a logits tensor that HF's loss path upcasts to fp32 and copies several times — roughly 35 GB before
the backward pass. Fusing the projection into the loss never materialises it.The alternative — lowering max_len to 8,192 — also fits, but re-introduces the truncation problem described
above unless the data is re-windowed.
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "IAMIbrahim/luthor-8b"
tok = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, dtype="bfloat16", device_map="auto")
messages = [
{"role": "system", "content": SYSTEM_PROMPT_WITH_TOOLS}, # Hermes-style <tools> block
{"role": "user", "content": "The test suite fails with ImportError. Fix it."},
]
ids = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")
print(tok.decode(model.generate(ids.to(model.device), max_new_tokens=512)[0]))
Tools must be declared the same way they were at training time, or tool-calling accuracy degrades.
Quantised MLX builds are available for local use — see the -mlx-* repositories. 4-bit runs in 4.7 GB and is
the recommended default.
pip install mlx-lm
mlx_lm.generate --model IAMIbrahim/luthor-8b-mlx-4bit --prompt "..." --max-tokens 512
@misc{luthor8b,
title = {Luthor 8B: an execution-verified agent model},
author = {Ibrahim Memon},
year = {2026},
note = {Qwen3-8B QLoRA fine-tune on execution-verified tool-use trajectories}
}
Built on Qwen3-8B (Apache 2.0).