BharatSQL-4B / README.md
sakasurya's picture
Access note: verified organisation email
8a3829f verified
|
Raw History Blame Contribute Delete
10.9 kB
---
license: cc-by-nc-sa-4.0
base_model: Qwen/Qwen3-4B-Instruct-2507
language:
- en
- hi
library_name: transformers
pipeline_tag: text-generation
tags:
- text-to-sql
- duckdb
- india
- open-government-data
- qwen3
datasets:
- judicialmind/BharatSQL-Data
extra_gated_prompt: 'This model is released under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 licence (CC
BY-NC-SA 4.0): research, academic, personal and other non-commercial use is permitted with attribution; adaptations must
be shared under the same licence; commercial use is not permitted. The original public source data remains under the Government
Open Data License - India (GODL-India). Commercial use: contact surya@judicialmind.ai or hello@judicialmind.ai.'
extra_gated_fields:
Name: text
Email: text
Organization: text
I accept the CC BY-NC-SA licence terms (research and non-commercial use only): checkbox
extra_gated_button_content: Accept and request access
---
# BharatSQL-4B
<!-- jm:top -->
Licence: CC BY-NC-SA 4.0 — research and non-commercial use only; derivatives must be shared under the same licence. Commercial use: contact surya@judicialmind.ai or hello@judicialmind.ai
Access is approved automatically when the email you enter matches your Hugging Face account email and belongs to the organisation you name; other requests are reviewed manually.
<!-- /jm:top -->
A 4B text-to-SQL model (full fine-tune of Qwen/Qwen3-4B-Instruct-2507) that writes DuckDB SQL over tables from
India's open government data (data.gov.in and NITI Aayog NDAP). Give it one or two table schemas and a question in
English, Hindi or Hinglish; it replies with one DuckDB `SELECT` in a ```` ```sql ```` block. Run the SQL on the
table to get the answer.
**Headline result:** on the BharatSQL-Data test set (28,943 questions from unseen ministries and
table layouts) BharatSQL-4B reaches **91.3% execution accuracy**, ahead of the 27B teacher Qwen3.8-27B-FP8 (83.9%)
and its base model Qwen3-4B-Instruct-2507 (74.6%), and its SQL runs 99.8% of the time.
## Quickstart
`infer.py` (in this repo) builds the prompt in the training format, generates greedily and runs the SQL in a
read-only in-memory DuckDB connection.
```python
import sys
import pandas as pd
from huggingface_hub import snapshot_download
path = snapshot_download("judicialmind/BharatSQL-4B") # accept the terms on the Hub first
sys.path.insert(0, path)
from infer import BharatSQL, schema_ddl, run_sql
# Tamil Nadu, "Classifications of Convicts (Prisons and Convicts) 2014-2015", data.gov.in resource 58261bea-8790-4a9c-ab26-6392a9ab2e09
df = pd.DataFrame({
"S.no": ["1", "2", "3", "4", "5", "6", "7", "Total"],
"Classification of Convicts 2014-15": [
"Below one year", "Above one year and not exceeding two years",
"Above two years and not exceeding five years", "Above five years and not exceeding ten years",
"Exceeding ten years", "Life sentence", "Sentenced to Death", "Total"],
"Male": [487, 195, 221, 683, 429, 2761, 7, 4783],
"Female": [5, 4, 0, 22, 7, 120, 0, 158],
"Total": [492, 199, 221, 705, 436, 2881, 7, 4941],
})
name = "t_58261bea_8790_4a9c_ab26_6392a9ab2e09"
ddl = schema_ddl(df, name, title="Classifications of Convicts (Prisons and Convicts) 2014-2015",
publisher="Tamil Nadu", source="58261bea-8790-4a9c-ab26-6392a9ab2e09")
print(ddl)
model = BharatSQL(path) # bfloat16; GPU if available
sql = model.generate_sql(ddl, "Tamil Nadu 2014-15 mein 'Below one year' classification ke kitne male convicts the?",
max_new_tokens=128)
print(sql)
print(run_sql(sql, {name: df}))
```
Output: the schema prompt printed by `schema_ddl`
```sql
-- Classifications of Convicts (Prisons and Convicts) 2014-2015 | Tamil Nadu | rows: 8 | source: 58261bea-8790-4a9c-ab26-6392a9ab2e09
CREATE TABLE t_58261bea_8790_4a9c_ab26_6392a9ab2e09 (
"S.no" VARCHAR, -- e.g. '1', '2', '3'
"Classification of Convicts 2014-15" VARCHAR, -- e.g. 'Below one year', 'Above one year and not exceeding tw...', 'Above two years and not exceeding fi...'
"Male" BIGINT, -- e.g. 487, 195, 221
"Female" BIGINT, -- e.g. 5, 4, 0
"Total" BIGINT -- e.g. 492, 199, 221
);
```
the generated query
```sql
SELECT "Male" FROM t_58261bea_8790_4a9c_ab26_6392a9ab2e09 WHERE "Classification of Convicts 2014-15" = 'Below one year'
```
and its result:
**Query result**
| Male |
|---:|
| 487 |
CLI: `python infer.py --table my_table.csv --name t_my_table --title "..." --publisher "..." --question "..."`.
### Prompt format
Two chat messages, applied with the model's chat template (Qwen3 non-thinking format):
- **system**: `You are BharatSQL. Given DuckDB table schemas from India's open government data and a question (English, Hindi or Hinglish), write one DuckDB SELECT query that answers it. Quote column names with double quotes. Reply with the SQL in a ```sql code block and nothing else.`
- **user**: `Tables:\n<schema>\n\nQuestion: <question>`
`<schema>` is one `CREATE TABLE` block per table (two tables are separated by a blank line), as produced by
`infer.schema_ddl`:
```
-- <title> | <publisher> | rows: <row count> | source: <resource id>
CREATE TABLE t_<resource id> (
"<column>" <VARCHAR|BIGINT|DOUBLE|BOOLEAN|DATE|TIMESTAMP>, -- e.g. <up to 3 example values; strings quoted, cut to 40 chars>
...
);
```
Table names are `t_` + the resource id with non-alphanumerics replaced by `_`; column names are quoted exactly as in
the table. Use 3 example values per column for one table and 2 per table for a two-table question.
**Output**: one fenced block, e.g. ```` ```sql\nSELECT ... \n``` ````. `infer.extract_sql(reply)` returns the SQL.
Decode greedily (temperature 0); the reported numbers use greedy decoding with up to 512 new tokens.
### transformers only
```python
from transformers import AutoModelForCausalLM, AutoTokenizer
tok = AutoTokenizer.from_pretrained("judicialmind/BharatSQL-4B")
lm = AutoModelForCausalLM.from_pretrained("judicialmind/BharatSQL-4B", dtype="bfloat16", device_map="auto")
messages = [
{"role": "system", "content": "You are BharatSQL. Given DuckDB table schemas from India's open government data and a question (English, Hindi or Hinglish), write one DuckDB SELECT query that answers it. Quote column names with double quotes. Reply with the SQL in a ```sql code block and nothing else."},
{"role": "user", "content": f"Tables:\n{ddl}\n\nQuestion: {question}"},
]
enc = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt", return_dict=True).to(lm.device)
out = lm.generate(**enc, max_new_tokens=512, do_sample=False)
print(tok.decode(out[0, enc["input_ids"].shape[1]:], skip_special_tokens=True))
```
### vLLM
```python
from vllm import LLM, SamplingParams
from infer import build_messages, extract_sql
llm = LLM("judicialmind/BharatSQL-4B", max_model_len=8192)
outs = llm.chat([build_messages(ddl, q) for q in questions], SamplingParams(temperature=0.0, max_tokens=512))
sqls = [extract_sql(o.outputs[0].text) for o in outs]
```
OpenAI-compatible server: `vllm serve judicialmind/BharatSQL-4B --max-model-len 8192`, then send the two messages
above to `/v1/chat/completions` with `temperature: 0`.
## Files
| file | contents |
|---|---|
| `model.safetensors`, `config.json`, `generation_config.json` | weights (bfloat16) and config |
| `tokenizer.json`, `tokenizer_config.json`, `chat_template.jinja` | tokenizer and chat template |
| `infer.py` | prompt builder (`schema_ddl`, `build_messages`), `BharatSQL` generator, `extract_sql`, `run_sql` (DuckDB), CLI |
| `requirements.txt` | pinned runtime dependencies (`vllm` optional) |
## Results
Execution accuracy on the BharatSQL-Data test set: 28,943 questions from 61 held-out ministries and held-out table
templates (no ministry or table layout in test was seen in training). A prediction is correct when its result
rows equal the gold SQL's result rows (column order ignored).
| model | test set | n | exec acc | SQL runs |
|---|---|---|---|---|
| BharatSQL-4B | test | 28,943 | 91.3% | 99.8% |
| Qwen3.8-27B-FP8 (zero-shot) | test | 28,943 | 83.9% | 99.7% |
| Qwen3-4B-Instruct-2507 (zero-shot) | test | 28,943 | 74.6% | 97.2% |
| BharatSQL-4B by group | n | exec acc |
|---|---|---|
| lookup | 8,918 | 95.8% |
| aggregate | 9,821 | 92.7% |
| multi_step | 10,204 | 86.1% |
| English | 17,876 | 91.4% |
| Hindi | 5,372 | 91.5% |
| Hinglish | 5,695 | 91.1% |
| two-table LGD join | 462 | 42.6% |
The test tables, the predictions of all three models and the scoring script (`evaluate.py`) are in
[judicialmind/BharatSQL-Data](https://huggingface.co/datasets/judicialmind/BharatSQL-Data); `python evaluate.py
--predictions predictions/BharatSQL-4B.parquet` reproduces the 91.3%.
## Coverage
Trained on the train split of BharatSQL-Data (230,976 execution-checked question/SQL pairs) over data.gov.in and NDAP tables (snapshot May 2026) from
113 ministries, departments and States; questions in English, Hindi (Devanagari) and Hinglish. Single-table
questions plus two-table joins on LGD district/State codes.
## Source data
Training tables come from the Open Government Data Platform India (data.gov.in) and NITI Aayog NDAP under the
Government Open Data License - India; per-table attribution is in `judicialmind/BharatSQL-Data`.
<!-- jm:source -->
Original data: published by the ministries, departments and State governments named per table / resource in this release on the Open Government Data Platform India ([data.gov.in](https://data.gov.in)) and, where marked, NITI Aayog's National Data and Analytics Platform ([NDAP](https://ndap.niti.gov.in)), under the [Government Open Data License - India](https://data.gov.in/government-open-data-license-india) (GODL-India). The original public data remains under GODL-India; the CC BY-NC-SA 4.0 licence of this repository covers JudicialMind's compilation, derived fields, annotations, benchmarks and models.
<!-- /jm:source -->
## Citation
```bibtex
@misc{judicialmind2026bharatsql4b,
title={BharatSQL-4B: Text-to-SQL over Indian open government data},
author={JudicialMind},
year={2026},
publisher={Hugging Face},
howpublished={\url{https://huggingface.co/judicialmind/BharatSQL-4B}}
}
```
<!-- jm:license -->
## License
[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) (see `LICENSE`): research, academic, personal and other non-commercial use, with attribution to JudicialMind and to the original data publishers; adaptations must be shared under the same licence. Commercial use requires a separate licence.
Commercial use: contact surya@judicialmind.ai or hello@judicialmind.ai.
Base model(s): `Qwen/Qwen3-4B-Instruct-2507` (apache-2.0). Their licence terms and notices continue to apply to the upstream components; see `THIRD_PARTY_NOTICES.md`.
<!-- /jm:license -->