Qwen3.5-0.8B Text-to-SQL LoRA adapter (MLX)
A LoRA adapter for mlx-community/Qwen3.5-0.8B-bf16 that turns a natural-language question plus a CREATE TABLE schema into a single SQLite query. Trained and run with Apple MLX (mlx-lm).
Training status: completed.
Model overview
| Task | Text-to-SQL (schema + question → SQLite SELECT query) |
| Base model | mlx-community/Qwen3.5-0.8B-bf16 (revision 3067585164dbcc505eec73d554349de6a27571a4) |
| Upstream weights | Qwen/Qwen3.5-0.8B |
| Architecture family | Qwen3.5 (qwen3_5, hybrid linear/full attention, 24 layers) |
| Fine-tuning method | LORA on the last 16 layers |
| Framework | MLX / MLX-LM (Apple Silicon, Metal) |
| Intended inference environment | macOS on Apple Silicon with mlx-lm |
| Project status | experimental / educational; training status: completed |
Model details
- Base model:
mlx-community/Qwen3.5-0.8B-bf16at commit3067585164dbcc505eec73d554349de6a27571a4, an MLX bf16 conversion ofQwen/Qwen3.5-0.8B. Its license isapache-2.0. The base repository is a vision-language checkpoint; MLX-LM loads only its text model, which is what was fine-tuned. - This repository contains only a LoRA adapter (
adapters.safetensors+adapter_config.json). It is not a standalone model: it must be loaded on top of the exact base model above. - Weight format: safetensors (MLX). Base weights are bf16, not quantized; the adapter was trained against the bf16 base.
- Parameters: trainable 3.608M of 752.392M total (as reported by MLX-LM during training).
- Software: mlx 0.32.4, mlx-lm 0.32.2, huggingface-hub 2.3.0.
Intended use
Converting an English question about one or more tables, together with their CREATE TABLE statements, into a SQLite query. Suitable for experiments, teaching and studying small-model fine-tuning on a laptop.
Generated SQL must be reviewed or validated before it is executed. Do not run model output against production or sensitive databases without read-only credentials, query allow-listing and resource limits.
Out of scope: other SQL dialects, schemas without CREATE TABLE context, data-modifying statements, and any use where incorrect SQL causes harm.
Training data
- Dataset:
b-mc2/sql-create-context, filesql_create_context_v4.json, revision9d80a6a118b838d9defc3798d659a54a2ac2ff37, licensecc-by-4.0. - Content: 78,577 English questions, each with
CREATE TABLEcontext and a reference SQL answer; built by its author from WikiSQL and Spider. - Source rows: 78,577. The source has no predefined splits.
- Split: one shuffle of all rows with
random.Random(42), then test = first 100, validation = next 200, train = next 2000. The remaining rows are not used. - Prepared sizes actually used: test 100, valid 200, train 2000.
- Filtering: only malformed rows (missing/empty fields) would be dropped; dropped: test 0, valid 0, train 0.
- Deduplication: exact (question, schema) overlap between splits is measured and reported: test/valid 0, test/train 0, valid/train 0. Dropping is disabled.
- Prompt (user turn):
{schema}
Question: {question}
Write a SQLite query that answers the question. Return only the SQL query.
- Target (assistant turn): the dataset's SQL answer, verbatim.
- Format: MLX-LM chat JSONL (
{"messages": [user, assistant]}); loss is computed on the assistant answer only (mask_prompt). - Limitations of the data: schemas carry column types inferred by the dataset author and no rows; many WikiSQL-derived questions are single-table lookups; values are often lower-cased; some references are themselves imperfect.
Training procedure
| Setting | Value |
|---|---|
| Profile | m1_air_8gb |
| Method | lora (rank 8, scale 20.0, dropout 0.0) |
| Target layers | last 16 transformer blocks (MLX-LM default LoRA targets for this architecture) |
| Optimizer / learning rate | adam / 1e-05 |
| Iterations | 2000 micro-batches of 1, gradient accumulation 4 |
| Max sequence length | 256 |
| Prompt masking / gradient checkpointing | True / True |
| Validation | every 800 iterations, 100 batches |
| Seed | 0 |
| Final train loss | 0.076 |
| Final validation loss | 0.07 |
| Wall time | 69.0 s |
| Peak memory footprint | 7.54 GB |
| Hardware | Apple M1, 8 GB unified memory |
| OS / Python | macOS 27.0 / Python 3.12.15 |
| Run id | m1_air_8gb-20261025-193334 |
Evaluation
Held-out test split (100 examples, never used for training or validation). Greedy decoding, thinking disabled, at most 200 new tokens. The SQL is taken from the first ```sql block or the first SELECT in the answer.
Metrics
- Execution accuracy (primary): the schema is instantiated in 3 in-memory SQLite databases with 25 seeded random rows per table (biased towards the literals in the reference query). A prediction is correct when it returns the same multiset of rows as the reference on every database. Model SQL runs read-only, with a time limit, in a sandbox without file or network access.
- Exact match: identical text. Normalized exact match: identical after lower-casing outside string literals, unifying quotes and whitespace.
- Valid SQL rate: the query runs read-only against the empty schema. Valid SQL is not necessarily correct SQL.
| Model | Execution accuracy | Exact match | Normalized EM | Valid SQL | Date |
|---|---|---|---|---|---|
Base mlx-community/Qwen3.5-0.8B-bf16 |
44.0% (44/100) | 3.0% | 18.0% | 92.0% | 2026-10-25 |
| Base + this adapter | 90.0% (90/100) | 81.0% | 81.0% | 99.0% | 2026-10-25 |
Random-row execution checks can accept a wrong query that happens to return the same rows on the synthetic data. 100 examples give wide uncertainty: a 95% interval is up to about ±10 percentage points.
How to use
Requires macOS on Apple Silicon.
pip install "mlx-lm>=0.32.2,<0.33" huggingface_hub
from huggingface_hub import snapshot_download
from mlx_lm import generate, load
adapter_dir = snapshot_download("thealper2/mlx-qwen3.5-0.8b-text-to-sql")
model, tokenizer = load(
"mlx-community/Qwen3.5-0.8B-bf16",
revision="3067585164dbcc505eec73d554349de6a27571a4",
adapter_path=adapter_dir,
)
schema = "CREATE TABLE head (age INTEGER)"
question = "How many heads of the departments are older than 56 ?"
prompt = (
f"{schema}\n\nQuestion: {question}\n"
"Write a SQLite query that answers the question. Return only the SQL query."
)
text = tokenizer.apply_chat_template(
[{"role": "user", "content": prompt}],
add_generation_prompt=True,
tokenize=False,
enable_thinking=False,
)
print(generate(model, tokenizer, prompt=text, max_tokens=200))
Use exactly this prompt layout and enable_thinking=False; the adapter was trained on it. mlx_lm.load applies the adapter from a local folder, which is why the repository is downloaded first.
Limitations and risks
- Generates incorrect SQL regularly, including queries that run but answer a different question.
- May misread the schema or hallucinate table and column names, especially for schemas unlike the training distribution (WikiSQL/Spider-style tables).
- Targets SQLite; other dialects (PostgreSQL, MySQL, T-SQL) may need changes.
- Ambiguous questions get one arbitrary interpretation; there is no clarification step.
- Trained on 2,000 examples: behaviour on long multi-table schemas, nested queries and unseen domains is weak and not well measured.
- Evaluation uses synthetic rows and a small test set; see the Evaluation section.
- Small model on consumer hardware: no production-readiness, latency or robustness guarantees.
- Never execute generated SQL against sensitive databases without validation, least-privilege read-only access and resource limits.
License and attribution
- Adapter: released under
apache-2.0(as configured by the publisher). - Base model:
mlx-community/Qwen3.5-0.8B-bf16/Qwen/Qwen3.5-0.8B, licenseapache-2.0. - Dataset:
b-mc2/sql-create-context, licensecc-by-4.0, which requires attribution. It was built from WikiSQL (Zhong et al., 2017, Seq2SQL) and Spider (Yu et al., 2018); see the dataset card for its citation. - The code, dataset and model licenses are independent; check each before reuse.
Reproducibility
The project that produced this adapter keeps every setting in configs/ (data.yaml, training.yaml, evaluation.yaml, publishing.yaml) and drives everything through make:
make install
make data-prepare # download the pinned dataset file, build the splits
make data-validate
make train PROFILE=m1_air_8gb
make evaluate-base # baseline
make evaluate # base + adapter
make card
make publish-dry-run
Artifacts: adapter in artifacts/adapters/m1_air_8gb, run manifest run_manifest.json (configuration, software versions, losses, timings), evaluation records under reports/evaluation/. Hardware: an Apple Silicon Mac; the m1_air_8gb profile targets an 8 GB M1 MacBook Air.