Downloads · 30 days
40
27% of all-time downloads
tugrulkaya/smolstruct-1.7b
smolstruct-1.7b is a text generation model from tugrulkaya. Use it when you need the model to write or continue text. It is set up for peft. The card lists the license as apache-2.0.
A small, fully-open model that turns free text into valid JSON and selects function/tool calls — built to run locally so sensitive data never leaves the device.
Downloads · 30 days
40
27% of all-time downloads
All-time downloads
147
Public
Repo size
72.4 MB
Likes
0
Public
Click a slice to open those files.
.safetensors72.4 MB · 94%
From the Hugging Face model README
A small, fully-open model that turns free text into valid JSON and selects function/tool calls — built to run locally so sensitive data never leaves the device.
SmolStruct is a LoRA fine-tune of SmolLM2-1.7B-Instruct specialised for structured output. In the agent era, the bottleneck is rarely raw model size — it is whether a model returns reliably parseable JSON. SmolStruct targets exactly that, in a footprint small enough for a laptop, an edge device, or a privacy-constrained environment (e.g. data that cannot be sent to a third-party API for regulatory reasons).
Core idea: a 1.7B model + grammar-constrained decoding produces JSON that is valid 100% of the time — locally, cheaply, privately.
Three capabilities, one unified chat format:
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
import torch, json
# This repo is a LoRA adapter — load the base model, then apply the adapter.
base_id = "HuggingFaceTB/SmolLM2-1.7B-Instruct"
adapter_id = "tugrulkaya/smolstruct-1.7b"
tok = AutoTokenizer.from_pretrained(adapter_id)
base = AutoModelForCausalLM.from_pretrained(base_id, torch_dtype=torch.bfloat16, device_map="auto")
model = PeftModel.from_pretrained(base, adapter_id)
schema = '{"name": "string", "age": "integer", "city": "string", "email": "string"}'
text = "Tuğrul Kaya is 31 and lives in Konya. Reach him at [email protected]."
messages = [
{"role": "system", "content": "You are a precise extraction assistant. Read the text and return ONLY a JSON object that matches the schema. Use null for fields not present."},
{"role": "user", "content": f"Schema:\n{schema}\n\nText:\n{text}"},
]
prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tok(prompt, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=256, do_sample=False)
print(tok.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
# {"name": "Tuğrul Kaya", "age": 31, "city": "Konya", "email": "[email protected]"}
Pair the model with grammar-constrained decoding so the output cannot be invalid:
import outlines
om = outlines.from_transformers(model, tok)
generator = outlines.Generator(om, outlines.types.JsonSchema('{"type": "object"}'))
print(generator(prompt, max_new_tokens=256)) # always parses
Most "structured output" tutorials reach for a large hosted model. But:
SmolStruct is a deliberate bet that, for structured output, the right architecture beats raw scale.
| Base | HuggingFaceTB/SmolLM2-1.7B-Instruct |
| Method | LoRA (r=16, α=32, dropout=0.05) on attention + MLP projections |
| Data | ~6k synthetic ChatML examples (extraction + tool calling), incl. EN & TR |
| Format | ChatML; assistant target is always a single JSON object |
| Objective | Supervised fine-tuning (TRL SFTTrainer) |
Training data is synthetically generated by a deterministic, template-based generator (no third-party API, fully reproducible from a seed). The generator, training script, evaluator and demo are all open — see the project repository.
Evaluated on a held-out synthetic validation split with four metrics:
Measured on a held-out synthetic validation split (200 examples, in-distribution, greedy / free decoding, bf16 on an Apple M4 Pro). With grammar-constrained decoding JSON validity is 100% by construction; on this in-distribution set free decoding already reaches it, so the axes that actually move are field accuracy and tool selection.
| Metric | Base SmolLM2-1.7B-Instruct | SmolStruct (LoRA, 3 epochs) | Constrained |
|---|---|---|---|
| JSON validity | 100.0% | 100.0% | 100% (by construction) |
| Schema compliance | 100.0% | 100.0% | 100% |
| Field-level accuracy | 88.6% | 99.5% | ≈ free |
| Tool selection | 92.1% | 100.0% (89 ex.) | ≈ free |
The base instruct model already emits well-formed, schema-correct JSON on this simple templated format — so the LoRA's lift is exactly where extraction quality lives: field-value accuracy (+10.9 pts) and tool selection (+7.9 pts). Constrained decoding then turns the 100% validity from observed into guaranteed. These numbers are in-distribution (the val split mirrors the generator); out-of-distribution inputs will score lower (see Limitations). Training: 3 epochs, final train_loss 0.079, token accuracy 95.9%.
Intended: local/edge structured extraction, function-call routing for agents, privacy-sensitive pipelines, cost-sensitive high-volume extraction.
Out of scope: safety-critical decisions without human review; tasks requiring factual recall or reasoning the model was not trained for; languages outside EN/TR without evaluation.
@misc{smolstruct2026,
title = {SmolStruct-1.7B: Local, Private Structured Output with a Small Language Model},
author = {Kaya, Mehmet Tuğrul},
year = {2026},
howpublished = {Hugging Face},
}
Built on SmolLM2 by Hugging Face. Fine-tuning, data generation, evaluation and demo by Mehmet Tuğrul Kaya (@mtugrull).