DominuZ commited on
Commit
da5836f
·
verified ·
1 Parent(s): c7da0d1

Upload README.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. README.md +173 -0
README.md ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: cc-by-4.0
3
+ language:
4
+ - en
5
+ - pt
6
+ base_model: Qwen/Qwen3-0.6B-Base
7
+ pipeline_tag: text-generation
8
+ tags:
9
+ - jq
10
+ - json
11
+ - code-generation
12
+ - text-to-code
13
+ - gguf
14
+ - qwen3
15
+ ---
16
+
17
+ # jq-coder-0.6B
18
+
19
+ **Translates natural-language requests (English + Brazilian Portuguese) into executable
20
+ `jq` filters — 100% offline.** For the JSON you can't send to a cloud API: production
21
+ payloads, logs with PII, anything under NDA.
22
+
23
+ Every existing NL→jq tool (llm-jq, gpt-jq, jiq) ships your JSON to a hosted LLM. This
24
+ is a 0.6B full fine-tune of Qwen3-0.6B-Base that runs on your own machine — CPU-only is
25
+ fine — and answers in a fraction of a second. To our knowledge it is the first dedicated
26
+ NL→jq model published on Hugging Face, and it ships with the first benchmark of the
27
+ niche, [**jq-bench**](https://huggingface.co/datasets/DominuZ/jq-bench).
28
+
29
+ ## Quickstart
30
+
31
+ With [llama.cpp](https://github.com/ggml-org/llama.cpp):
32
+
33
+ ```bash
34
+ llama-server -m jq-coder-v13-release-Q8_0.gguf --port 8091 -ngl 99
35
+ ```
36
+
37
+ ```bash
38
+ curl -s http://127.0.0.1:8091/v1/chat/completions -d '{
39
+ "messages": [
40
+ {"role": "system",
41
+ "content": "You translate natural-language requests into jq filters. Reply with only the jq filter."},
42
+ {"role": "user",
43
+ "content": "get the id of every order\n\nJSON sample:\n{\"orders\": [{\"id\": 1, \"status\": \"done\"}]}"}
44
+ ],
45
+ "temperature": 0
46
+ }'
47
+ ```
48
+
49
+ Or with [Ollama](https://ollama.com), straight from this repo:
50
+
51
+ ```bash
52
+ ollama run hf.co/DominuZ/jq-coder-0.6B:Q8_0
53
+ ```
54
+
55
+ ## Prompt format
56
+
57
+ ChatML (the chat template is embedded in the GGUF). The contract has two parts:
58
+
59
+ - **system**: `You translate natural-language requests into jq filters. Reply with only the jq filter.`
60
+ - **user**: the request, then a blank line, then `JSON sample:` and a sample of the
61
+ JSON you want to query.
62
+
63
+ **The JSON sample is mandatory.** The model was trained to read field names and shapes
64
+ from it — without a sample it will hallucinate fields. A truncated sample is fine as
65
+ long as it shows the structure you are asking about.
66
+
67
+ ## Examples
68
+
69
+ All examples below were run against the published Q8_0 GGUF and verified by executing
70
+ the generated filter with real jq. Input JSON:
71
+
72
+ ```json
73
+ {"orders": [{"id": 1, "status": "done", "total": 120.5},
74
+ {"id": 2, "status": "pending", "total": 40.0}]}
75
+ ```
76
+
77
+ | Request | Generated filter |
78
+ |---|---|
79
+ | get the id of every order | `.orders[] \| .id` |
80
+ | keep only the orders whose status is done | `[.orders[] \| select(.status == "done")]` |
81
+ | some o total de todos os pedidos | `[.orders[].total] \| add` |
82
+ | remova o campo total de cada pedido | `del(.orders[].total)` |
83
+
84
+ The model conditions on the JSON sample, so the same request over a differently shaped
85
+ document correctly yields a different filter.
86
+
87
+ ## How it was trained
88
+
89
+ **Reverse generation with ground truth by execution** — the training data was never
90
+ written by an LLM guessing jq:
91
+
92
+ 1. A grammar of **108 program families**, anchored in real-world usage (top-voted
93
+ StackOverflow `[jq]` questions, the llm-jq / jiq / gpt-jq corpora, the official jq
94
+ manual), samples candidate programs.
95
+ 2. Each program is **executed with real jq 1.8** against families of synthetic JSON
96
+ documents — the output is the ground truth, for free.
97
+ 3. Three quality guards filter the pairs: **(G1)** non-degeneracy (exit 0, non-empty,
98
+ non-constant output across distinct documents), **(G2)** coverage anchored in real
99
+ corpora, **(G3)** NL↔program consistency via round-trip with an independent second
100
+ model.
101
+ 4. Only then does a teacher LLM write the natural-language request (the cheap, safe
102
+ part). Teachers are locally served open-weights models (Apache 2.0, distillation
103
+ permitted).
104
+
105
+ Result: **27,480 verified pairs**, bilingual EN/PT-BR 50/50, full fine-tune (not LoRA)
106
+ of Qwen3-0.6B-Base.
107
+
108
+ ## Evaluation — jq-bench
109
+
110
+ Both metrics are computed **by execution**: the generated filter runs against held-out
111
+ JSON documents and the canonical output (`jq -cS`) is diffed against gold. No
112
+ LLM-as-judge anywhere.
113
+
114
+ The human slice of **jq-bench** is 30 real StackOverflow tasks, hand-curated,
115
+ independent of the training grammar, never seen in training (CC BY-SA 4.0, per-item
116
+ attribution).
117
+
118
+ | Artifact (v13) | human slice, strict | human slice, task-solved¹ | in-distribution (400) |
119
+ |---|---|---|---|
120
+ | f16 | 9/30 | 10/30 | 394/400 |
121
+ | **Q8_0 (recommended)** | **9/30** | **10/30** | **394/400** |
122
+
123
+ ¹ *strict* = byte-identical to gold after canonicalization; *task-solved* additionally
124
+ accepts equivalent output shapes (stream vs. array wrapper, etc.).
125
+
126
+ An internal diagnostic of 204 realistic probes (execution-verified, authored
127
+ independently of the training pipeline) measured the last data iteration at **52.5%**
128
+ (up from 31.4%), with transformations under nested fields going from 12.5% to 54%. The
129
+ human slice remains the canonical metric.
130
+
131
+ ## Quantization
132
+
133
+ **Q8_0 matches f16 on every metric** and is the recommended build (~640 MB).
134
+
135
+ Q4_K_M was measured and **rejected**: it preserves in-distribution accuracy but breaks
136
+ exactly the hard compositions. Small models do not survive aggressive quantization
137
+ unharmed — we measured instead of assuming, and we don't publish what failed the gate.
138
+
139
+ ## Honest limitations
140
+
141
+ - **Long compositions fail** (~2/3 of the human slice): `all(...)`, array subtraction
142
+ (`. - [...]`), `to_entries` with object reconstruction + `tonumber`, compound merges
143
+ with `del`. The model interpolates between neighboring compositions it saw in
144
+ training; requests far from everything it saw come out wrong or hallucinated.
145
+ - Aggregations under nested fields can come out as listings instead of sums.
146
+ - The request must mention fields by their real names in the JSON; the JSON sample in
147
+ the prompt is mandatory.
148
+ - English and Brazilian Portuguese only.
149
+ - It generates **filters**, not shell invocations: no `-r`/`-s`/`--arg` handling.
150
+
151
+ Always review generated filters before running them against data you care about —
152
+ especially destructive ones (`del`, assignments).
153
+
154
+ ## License and attribution
155
+
156
+ - **Model weights: CC BY 4.0.** Commercial use welcome. If you share the weights or a
157
+ derivative (re-hosts, quantizations, further fine-tunes), credit **Edelmar
158
+ Schneider**, link back to this page, and indicate changes. The base model,
159
+ [Qwen/Qwen3-0.6B-Base](https://huggingface.co/Qwen/Qwen3-0.6B-Base), is Apache 2.0
160
+ and its notices are retained.
161
+ - **jq-bench** (evaluation dataset): CC BY-SA 4.0, with per-item attribution
162
+ (StackOverflow URL / author / votes).
163
+
164
+ If this model or benchmark is useful in your work, please cite:
165
+
166
+ ```bibtex
167
+ @misc{jqcoder2026,
168
+ title = {jq-coder: a 0.6B offline natural-language-to-jq model and jq-bench},
169
+ author = {Edelmar Schneider},
170
+ year = {2026},
171
+ url = {https://huggingface.co/DominuZ/jq-coder-0.6B}
172
+ }
173
+ ```