dale-0.8b (GGUF)
A full fine-tune of Qwen/Qwen3.5-0.8B that turns a natural-language request, in Spanish or English, into one bash command for Linux, answered as a small JSON object. It is the model behind dale, an offline command-line tool that runs it on CPU through llama.cpp, bundled in a single native binary. Since dale 1.1.0, machines with a suitable GPU can use its larger siblings dale-4b and dale-9b instead; dale-0.8b stays the CPU model and the fallback (family).
- Author: Manuel García Sanz
- Website: https://manuelgs.com/en/dale/
- Source code: https://github.com/manuelgscom/dale (the
daleCLI) - Format: GGUF, Q4_K_M (imatrix) · 529.3 MB
- Test v2 accuracy: 79.4 % (374/471), versus 62.8 % for Qwen3.5-9B and Gemma 4 E4B-it without fine-tuning
- Latency: 1.72 s median per request on a mid-range 2020 laptop CPU (4 cores / 8 threads), no GPU
- The
dalefamily: four sizes of the same model (0.8B, 2B, 4B, 9B), all in one collection
dale live: ask in plain English; it proposes the command, explains it, rates the risk and only runs it once you confirm. Real, unedited recording on a desktop CPU (about 1 s per request; 1.7 s on the laptop above).
Resumen en español
dale-0.8b es un ajuste completo de Qwen3.5-0.8B que convierte una petición en castellano o en inglés en un único comando bash para Linux, con una explicación breve. Es el modelo de dale, una herramienta de línea de comandos que funciona sin conexión, en CPU, con llama.cpp dentro de un único binario. Desde la versión 1.1.0, si el equipo tiene una GPU adecuada, dale puede usar en su lugar los hermanos mayores dale-4b y dale-9b; dale-0.8b sigue siendo el modelo de CPU y la vuelta segura. En el test v2 (471 casos) acierta el 79,4 % (374/471), frente al 62,8 % de Qwen3.5-9B y Gemma 4 E4B-it sin ajustar, con un GGUF de 529 MB. En un portátil de gama media de 2020 (4 núcleos / 8 hilos) responde en 1,72 s de mediana. El modelo no decide el riesgo: lo hace un verificador determinista de dale, y los comandos peligrosos exigen escribir una palabra de confirmación. Puede equivocarse (aproximadamente 1 de cada 5 casos en el test): revisa siempre el comando antes de ejecutarlo.
The dale family
The same task, data, contract and prompt in four sizes. Scores on the public test set dale-bash-test-v2 (471 cases), all in Q4_K_M with llama-server.
| Model | Base | GGUF (Q4_K_M) | Test v2 | In the dale CLI |
Repository |
|---|---|---|---|---|---|
dale-0.8b |
Qwen3.5-0.8B | 529.3 MB | 79.4 % | CPU (default and fallback) | manuelgscom/dale-0.8b-GGUF (this one) |
dale-2b |
Qwen3.5-2B | 1,274.4 MB | 81.7 % | not used (llama-server only) | manuelgscom/dale-2b-GGUF |
dale-4b |
Qwen3.5-4B | 2,708.8 MB | 87.0 % | GPU, ≥ 4 GiB free VRAM | manuelgscom/dale-4b-GGUF |
dale-9b |
Qwen3.5-9B | 5,629.1 MB | 89.2 % | GPU, ≥ 8 GiB free VRAM, if you choose it | manuelgscom/dale-9b-GGUF |
Which one?
the dale CLI, first run:
dedicated GPU, ≥ 8 GiB free VRAM ─► you choose: 1) dale-4b (Enter, faster) 2) dale-9b (more accurate)
dedicated GPU, ≥ 4 GiB free VRAM ─► dale-4b
anything else (CPU, integrated GPU) ─► dale-0.8b on the CPU
your own application (llama-server, CPU or GPU) ─► any of the four: pick by memory and accuracy
What it is and intended use
The model is meant to be used through the dale CLI:
request ─► system context ─► this model ─► risk verifier ─► mode ─► show / run
(os, distro, (proposes one (deterministic (bash -c)
package manager, bash command, rules in C++,
installed tools) refuses or independent of
says unsure) the model)
- The model proposes; code decides the risk. A deterministic verifier in C++ classifies every command as safe, caution or danger, by looking only at the command text. Anything it cannot analyse is at least caution. The model's output never sets the risk level.
- Three modes, chosen by the user:
suggest: shows the command and never runs it.confirm(default): shows it and asks before running it.auto: runs only safe commands directly; everything else asks.
- Dangerous commands (mass deletion, disk writes, piping downloaded code into a shell, firewall flush,
git push --force…) show a prominent warning and require typing a confirmation word (ejecutar/run), in every mode. - Honest when unsure. The model answers
refusewhen a request is not a shell task andunsurewhen it is ambiguous, instead of inventing a command. - Local. After the first model download,
dalemakes no network requests: no accounts, no API keys, no telemetry.
Safety is decided by code, not by the model: caution with its reasons, danger with a confirmation word, and an honest "no" for non-commands or ambiguous requests. Real, unedited recording; nothing was run.
The model was trained only for this contract (section below). It is not a general chat model.
Files
| File | Quantization | Size (bytes) | SHA-256 |
|---|---|---|---|
dale-0.8b-1.0.0-Q4_K_M.gguf |
Q4_K_M, with imatrix | 529,297,952 | 8738d2535680cf7b8227001013a0ba6e6b2a4c8e8f3d149fd181c17cb96e2d3d |
LICENSE |
Apache-2.0 |
sha256sum dale-0.8b-1.0.0-Q4_K_M.gguf
The GGUF carries general.* metadata (name, author, URL, version, license, base model). The tensors are identical to the file evaluated below; only the metadata was added.
Using it directly with llama.cpp
dale builds the prompt itself, so you do not need any of this to use the CLI. To call the model from your own code, follow the contract (version 2) exactly; the model was trained on this format and nothing else.
Prompt
The prompt is Qwen3.5's chat template with thinking disabled (apply_chat_template(..., add_generation_prompt=True, enable_thinking=False)), with two turns only: a fixed system prompt and the user message. No few-shot examples, no reasoning.
System prompt (exactly two lines, no trailing newline):
Turn the request into one bash command for the Linux system described in the first line of the user message.
Reply with only JSON {"type":"command|refuse|unsure","command":"...","explanation":"..."}: "command" is "" unless type is command; "explanation" is one short sentence in the language of the request.
User message: a context line, a newline, then the request (whitespace collapsed to single spaces, at most 1000 bytes):
os=linux distro=ubuntu-24.04 pm=apt tools=curl,docker,fdfind,git,jq,python3,rg,rsync,ssh,systemctl
busca en este directorio los ficheros que contienen la palabra TODO
| Context key | Value |
|---|---|
os |
linux |
distro |
ID-VERSION_ID from os-release, lowercase (or just ID, or unknown) |
pm |
apt, dnf, yum, pacman, zypper, apk or none |
tools |
optional tools found in PATH, alphabetical, comma-separated, or none |
The tools list dale checks: fd fdfind rg bat batcat eza tree ncdu fzf jq yq zip unzip 7z zstd xz unrar curl wget ip ss netstat nmcli dig nc nmap ssh rsync iptables nft ufw firewall-cmd systemctl journalctl lsof htop sudo doas hostnamectl timedatectl loginctl setfacl getfacl docker podman kubectl apt dnf yum pacman zypper apk flatpak snap git python3 make gcc ffmpeg convert magick exiftool pdftotext. Near-universal tools (coreutils, find, grep, sed, awk, tar, gzip) are not listed.
Full prompt string (line breaks shown for readability; \n are real newlines):
<|im_start|>system\n{system}<|im_end|>\n<|im_start|>user\n{context}\n{request}<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n
The prompt ends with an empty <think> block, so the model starts writing the JSON straight away. Tokenize with special tokens parsed and without BOS.
Answer
Exactly one JSON object with exactly these three string keys:
{"type":"command|refuse|unsure","command":"...","explanation":"..."}
typeiscommand,refuse(not a shell task) orunsure(ambiguous request).commandis one bash command (steps joined with&&or|), no newlines; it is""unlesstypeiscommand.explanationis one short sentence in the language of the request.- Nothing before or after the object.
dalerejects anything else as invalid and runs nothing.
Decode greedily and cap at 200 tokens (as dale does).
Example
Install llama.cpp: a prebuilt binary from the releases,
brew install llama.cpp, or the Docker imageghcr.io/ggml-org/llama.cpp:server(:server-cudaor:server-vulkanfor a GPU).Start the server. It downloads the model from this repository the first time:
llama-server -hf manuelgscom/dale-0.8b-GGUF:Q4_K_M --port 8080Or download the file yourself, check it and point to it:
curl -LO https://huggingface.co/manuelgscom/dale-0.8b-GGUF/resolve/main/dale-0.8b-1.0.0-Q4_K_M.gguf sha256sum dale-0.8b-1.0.0-Q4_K_M.gguf # must match the SHA-256 in Files llama-server -m dale-0.8b-1.0.0-Q4_K_M.gguf --port 8080It runs on the CPU; with a GPU build of llama.cpp, add
-ngl 99to put every layer on the GPU.Send a request with the snippet below (Python 3, standard library only).
import json, urllib.request
SYSTEM = (
"Turn the request into one bash command for the Linux system described in the first line of the user message.\n"
'Reply with only JSON {"type":"command|refuse|unsure","command":"...","explanation":"..."}: '
'"command" is "" unless type is command; "explanation" is one short sentence in the language of the request.'
)
context = "os=linux distro=ubuntu-24.04 pm=apt tools=curl,docker,fdfind,git,jq,python3,rg,rsync,ssh,systemctl"
request = "busca en este directorio los ficheros que contienen la palabra TODO"
prompt = (
f"<|im_start|>system\n{SYSTEM}<|im_end|>\n"
f"<|im_start|>user\n{context}\n{request}<|im_end|>\n"
"<|im_start|>assistant\n<think>\n\n</think>\n\n"
)
body = {"prompt": prompt, "n_predict": 200, "temperature": 0, "top_k": 1}
req = urllib.request.Request("http://127.0.0.1:8080/completion",
data=json.dumps(body).encode(),
headers={"Content-Type": "application/json"})
print(json.load(urllib.request.urlopen(req))["content"])
Output of this model (Q4_K_M) for that request and context:
{"type":"command","command":"rg -l TODO","explanation":"Lista los ficheros que contienen TODO"}
Other valid answers look like:
{"type":"refuse","command":"","explanation":"This is a general question, not a shell task."}
{"type":"unsure","command":"","explanation":"No está claro qué hay que arreglar; indica el fichero o el problema."}
Never run the command blindly. Outside dale there is no risk verifier: add your own review step.
Evaluation
Test v2: 471 cases (339 command requests, 101 out-of-scope, 31 ambiguous; 257 in Spanish, 214 in English). Published as manuelgscom/dale-bash-test-v2.
All models in Q4_K_M, reasoning disabled, greedy decoding, same llama.cpp version, judged from scratch by the same two judges. Base models use contract v1 (the system prompt plus 8 few-shot examples, which suits a model that was not fine-tuned); dale-0.8b uses contract v2 (above).
| Model | Params | GGUF | Correct | Accuracy | 95 % CI (Wilson) |
|---|---|---|---|---|---|
| Qwen3.5-0.8B (base) | 0.8 B | 532.5 MB | 70 | 14.9 % | 11.9–18.4 % |
| Qwen3.5-2B (base) | 2 B | 1,280.8 MB | 152 | 32.3 % | 28.2–36.6 % |
| Gemma 4 E2B-it | 2 B effective | 3,106.7 MB | 254 | 53.9 % | 49.4–58.4 % |
| Qwen3.5-4B (base) | 4 B | 2,740.9 MB | 257 | 54.6 % | 50.0–59.0 % |
| Gemma 4 E4B-it | 4 B effective | 4,977.2 MB | 296 | 62.8 % | 58.4–67.1 % |
| Qwen3.5-9B (base) | 9 B | 5,680.5 MB | 296 | 62.8 % | 58.4–67.1 % |
dale-0.8b |
0.8 B | 529.3 MB | 374 | 79.4 % | 75.5–82.8 % |
dale-2b |
2 B | 1,274.4 MB | 385 | 81.7 % | 78.0–85.0 % |
dale-4b |
4 B | 2,708.8 MB | 410 | 87.0 % | 83.7–89.8 % |
dale-9b |
9 B | 5,629.1 MB | 420 | 89.2 % | 86.0–91.7 % |
MB = 10^6 bytes. dale-2b, dale-4b and dale-9b were measured on a later run of the same server, with dale-0.8b as a control (371/471 there).
McNemar test, case by case, dale-0.8b against each model (b = cases only the other model gets right, c = cases only dale-0.8b gets right):
| Against | b | c | p |
|---|---|---|---|
| Qwen3.5-0.8B base | 2 | 306 | < 0.0001 |
| Qwen3.5-2B base | 9 | 231 | < 0.0001 |
| Gemma 4 E2B-it | 20 | 140 | < 0.0001 |
| Qwen3.5-4B base | 23 | 140 | < 0.0001 |
| Gemma 4 E4B-it | 31 | 109 | < 0.0001 |
| Qwen3.5-9B base | 42 | 120 | < 0.0001 |
Where the errors are
| Metric | Qwen 0.8B | Qwen 2B | Gemma E2B | Qwen 4B | Gemma E4B | Qwen 9B | dale-0.8b |
|---|---|---|---|---|---|---|---|
| Invalid JSON (of 471) | 58 | 50 | 26 | 21 | 17 | 3 | 0 |
| Correct refusals (out-of-scope, of 101) | 30 | 62 | 100 | 99 | 101 | 101 | 101 |
| False refusals (command cases, of 339) | 31 | 1 | 24 | 25 | 13 | 17 | 0 |
Correct unsure (ambiguous, of 31) |
6 | 0 | 10 | 10 | 4 | 3 | 31 |
| Wrong type | 102 | 56 | 46 | 48 | 40 | 45 | 0 |
| Wrong command (judges) | 241 | 213 | 145 | 145 | 118 | 127 | 97 |
Fine-tuning removes format errors and the "when not to answer" errors (refusals, ambiguity, false refusals). All 97 remaining errors are wrong commands.
By language: 80.2 % in Spanish (206/257) and 78.5 % in English (168/214).
By category: dale-0.8b wins or ties in 11 of 14 categories against the two best models without fine-tuning; it loses narrowly (1–2 cases, within noise) in compression, docker and system.
| Category | Cases | Qwen3.5-9B | Gemma 4 E4B-it | dale-0.8b |
|---|---|---|---|---|
| ambiguous | 31 | 3 | 4 | 31 |
| compression | 23 | 14 | 17 | 16 |
| disks | 22 | 10 | 6 | 13 |
| docker | 19 | 16 | 14 | 14 |
| files | 42 | 19 | 21 | 26 |
| out of scope | 101 | 101 | 101 | 101 |
| git | 28 | 14 | 18 | 21 |
| misc | 25 | 15 | 16 | 18 |
| packages | 28 | 15 | 20 | 24 |
| permissions | 28 | 18 | 20 | 23 |
| processes | 30 | 13 | 12 | 21 |
| network | 25 | 17 | 14 | 21 |
| system | 29 | 20 | 24 | 22 |
| text | 40 | 21 | 9 | 23 |
Methodology
- Independent test. Test v2 was written by a different, independent LLM from a written specification, validated automatically and frozen (SHA-256
fdb5564af3c153804b8780b36f9b22326340e1a57a45907765a45ab4f39991f3). No data-generation step ever read it, and the training data was decontaminated against it. - Double judge with debate. Format and type are checked by rules. Each proposed command is judged by two independent LLM judges; if they disagree, they debate one round, and if they still disagree, the answer counts as wrong. Final agreement between judges was 97.1–99.1 % depending on the model.
- Same conditions for everyone. Each model ran in its own
llama-server, with greedy decoding, seed 0 and a 200-token cap. No answer from any model contains reasoning markers. - Measured on GPU.
daleruns this model on the CPU; there it scored 375/471 in an earlier run (one case apart), and GPU and CPU outputs match in type and command in 450 of 471 cases.
Notes:
- Verdicts have no manual review.
- With 471 cases the 95 % interval is about ±3.6 points for this model, and much wider per category.
- Gemma 4 models are instruction-tuned; the Qwen3.5 models are the base releases.
Latency
Measured by the author with scripts/bench.sh from the dale repository, using all cores, on a mid-range 2020 laptop (4 cores / 8 threads, 16 GB RAM), dale 1.0.0 with this GGUF, 30 requests (cold once, warm three times each). Wall time is the whole process, as the user perceives it.
| Median | p90 | |
|---|---|---|
| Warm, wall time (n = 90) | 1.72 s | 2.19 s |
| Cold, wall time (n = 30) | 1.91 s | 2.39 s |
| Generation speed (warm) | 33.1 tokens/s | 33.6 tokens/s |
100 % valid JSON in every run.
With dale 1.1.0 on a reference desktop (16 GB GPU, same procedure), dale-0.8b takes 0.91 s on the CPU, and the GPU models about 1.2 s (dale-4b) and 1.7 s (dale-9b), with 7 to 10 points more accuracy. On that GPU dale-0.8b itself would take 0.76 s; dale keeps the GPU for the larger models, which gain accuracy rather than a few tenths of a second. These figures depend on the machine; the laptop figure above is the public one for dale-0.8b.
Training
| Base | Qwen/Qwen3.5-0.8B (revision 2fc06364715b967f1860aea9cf38778875588b17) |
| Method | Full fine-tune of all weights (no LoRA, no adapters) |
| Data | 48,841 training examples, 2,578 validation |
| Hyperparameters | lr 1e-5 (cosine, 3 % warmup), effective batch 32 (16 × 2), 2 epochs (3,054 steps), max sequence length 640, fp32 master weights with bf16 autocast, 8-bit AdamW |
| Hardware | 1× RTX 5090, about 30 minutes |
| Quantization | Q4_K_M with an importance matrix (imatrix) computed on a sample of the training data |
Data. Synthetic examples written with LLM assistance: natural-language requests in Spanish and English, each with a machine context line and the JSON answer, covering commands, refusals and ambiguous requests. Command examples from tldr-pages were used only as coverage seeds (which commands and options exist); no tldr description text was copied, and all explanations are original. Every example was validated (schema, language, bash -n, known binaries consistent with the context, shellcheck, duplicates) and decontaminated against the test sets. The training data and the training recipe are not published.
Attribution. Command example lines from tldr-pages were used as seeds:
tldr-pages contributors, CC BY 4.0 — https://github.com/tldr-pages/tldr — https://creativecommons.org/licenses/by/4.0/.
Changes: only the example command lines of pages/common and pages/linux (tag v2.3) were extracted as coverage seeds; no tldr text is copied into the explanations, which are original.
Limitations
- bash on Linux only. No zsh, fish, PowerShell or cmd; no macOS. On Windows, use it through WSL 2.
- The
dalebinary is built for x86_64 with AVX2 (glibc ≥ 2.29). The GGUF itself runs anywhere llama.cpp does. - It can be wrong: about 1 in 5 answers on the test. A wrong command may look plausible. Always review a command before running it.
- It is not a security tool. Safety in
dalecomes from the deterministic verifier and the confirmations, not from the model. The model can propose a destructive command; it does not classify risk. - Trained for one task with one fixed prompt. Other prompts, chat use or reasoning mode are unsupported.
- Tool choice depends on the context line: with no or wrong context it may suggest tools that are not installed.
Changes from the base model
- All weights fine-tuned for the contract above (natural language → one bash command as JSON, with
refuseandunsure). - Tokenizer and chat template unchanged from Qwen3.5-0.8B.
- Converted to GGUF and quantized to Q4_K_M with imatrix;
general.*metadata added.
License
Apache-2.0, the same license as the base model Qwen3.5-0.8B. See LICENSE. The training seeds from tldr-pages are CC BY 4.0, attributed above.