j2521402 commited on
Commit
3b4b411
·
verified ·
1 Parent(s): 9660421

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ grpo/tokenizer.json filter=lfs diff=lfs merge=lfs -text
37
+ grpo-hard/tokenizer.json filter=lfs diff=lfs merge=lfs -text
38
+ sft/tokenizer.json filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,3 +1,173 @@
1
  ---
2
- license: apache-2.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ base_model: Qwen/Qwen3-4B-Base
3
+ library_name: peft
4
+ pipeline_tag: text-generation
5
+ language:
6
+ - en
7
+ tags:
8
+ - base_model:adapter:Qwen/Qwen3-4B-Base
9
+ - peft
10
+ - lora
11
+ - text-to-sql
12
+ - sql
13
+ - sft
14
+ - grpo
15
+ - rlvr
16
+ - trl
17
  ---
18
+
19
+ # SQL-R1
20
+
21
+ SQL-R1 trains Qwen3-4B-Base for complex Text-to-SQL through completion-only
22
+ supervised fine-tuning and GRPO with verifiable SQLite execution feedback.
23
+
24
+ This repository contains three LoRA adapters:
25
+
26
+ | Subfolder | Stage | Intended use |
27
+ |---|---|---|
28
+ | `sft` | Completion-only SFT | Supervised baseline |
29
+ | `grpo` | SFT + execution-feedback GRPO | Recommended main checkpoint |
30
+ | `grpo-hard` | Hard-sample curriculum GRPO | Ablation and analysis |
31
+
32
+ All three adapters require
33
+ [`Qwen/Qwen3-4B-Base`](https://huggingface.co/Qwen/Qwen3-4B-Base).
34
+
35
+ ## Results
36
+
37
+ Execution accuracy (EX) and execution-valid rate were evaluated on the complete
38
+ public BIRD Dev and Spider Dev splits using a consistent greedy-decoding and
39
+ SQLite-execution pipeline.
40
+
41
+ | Checkpoint | BIRD Dev EX | BIRD execution-valid | Spider Dev EX | Spider execution-valid |
42
+ |---|---:|---:|---:|---:|
43
+ | Qwen3-4B-Base | 23.21% | 55.61% | 55.51% | 77.66% |
44
+ | `sft` | 42.37% | 87.29% | 77.27% | 96.62% |
45
+ | **`grpo`** | **42.37%** | 87.35% | **79.59%** | 97.00% |
46
+ | `grpo-hard` | 42.50% | **88.79%** | 78.92% | **97.20%** |
47
+
48
+ The `grpo` checkpoint improves Spider Dev EX by 2.32 percentage points over
49
+ SFT while preserving BIRD Dev EX. The curriculum ablation improves BIRD
50
+ execution validity but shows mild Spider forgetting.
51
+
52
+ These are project-side development-set measurements rather than hidden-test
53
+ leaderboard submissions. Systems using database-content retrieval,
54
+ self-consistency, reranking, or larger proprietary models are not directly
55
+ comparable.
56
+
57
+ ## Training overview
58
+
59
+ ### SFT
60
+
61
+ - Training records: 12,976
62
+ - Validation records: 608
63
+ - Completion-only loss: prompt tokens are masked and loss is computed only on
64
+ the target SQL
65
+ - LoRA rank/alpha/dropout: 32 / 64 / 0.05
66
+ - Trainable parameters: 66.1M (1.62%)
67
+ - Precision: BF16
68
+
69
+ ### GRPO
70
+
71
+ - RL training records: 12,885
72
+ - Optimization steps: 300
73
+ - Candidates per prompt: 4
74
+ - Approximate generated rollouts: 1,200
75
+ - Reward signals: SQL validity, read-only safety, executability, and execution
76
+ result equivalence
77
+ - Parallel read-only SQLite rollout validation
78
+
79
+ Gold SQL and execution results are available only to the reward function and
80
+ are never included in the model prompt.
81
+
82
+ ### Hard-sample curriculum ablation
83
+
84
+ - Curriculum records: 2,522
85
+ - BIRD moderate/challenging focus records: 1,891
86
+ - Stratified replay records: 631
87
+ - Optimization steps: 300
88
+ - Validation database overlap: 0
89
+
90
+ ## Usage
91
+
92
+ Choose one of `sft`, `grpo`, or `grpo-hard`. The recommended default is `grpo`.
93
+
94
+ ```python
95
+ import torch
96
+ from peft import PeftModel
97
+ from transformers import AutoModelForCausalLM, AutoTokenizer
98
+
99
+ base_model_id = "Qwen/Qwen3-4B-Base"
100
+ repo_id = "j2521402/SQL-R1"
101
+ adapter_subfolder = "grpo"
102
+
103
+ tokenizer = AutoTokenizer.from_pretrained(
104
+ repo_id,
105
+ subfolder=adapter_subfolder,
106
+ )
107
+ base_model = AutoModelForCausalLM.from_pretrained(
108
+ base_model_id,
109
+ torch_dtype=torch.bfloat16,
110
+ device_map="auto",
111
+ )
112
+ model = PeftModel.from_pretrained(
113
+ base_model,
114
+ repo_id,
115
+ subfolder=adapter_subfolder,
116
+ ).eval()
117
+
118
+ messages = [
119
+ {
120
+ "role": "system",
121
+ "content": (
122
+ "You are a Text-to-SQL assistant. Given a SQLite database schema, "
123
+ "optional evidence, and a question, return exactly one read-only "
124
+ "SQLite query. Do not include explanations or Markdown fences."
125
+ ),
126
+ },
127
+ {
128
+ "role": "user",
129
+ "content": (
130
+ 'Database schema:\nTABLE "singer" ("Singer_ID" INT, "Name" TEXT);\n\n'
131
+ "Question:\nHow many singers do we have?"
132
+ ),
133
+ },
134
+ ]
135
+
136
+ inputs = tokenizer.apply_chat_template(
137
+ messages,
138
+ add_generation_prompt=True,
139
+ return_tensors="pt",
140
+ return_dict=True,
141
+ ).to(model.device)
142
+
143
+ with torch.inference_mode():
144
+ output = model.generate(
145
+ **inputs,
146
+ max_new_tokens=256,
147
+ do_sample=False,
148
+ eos_token_id=tokenizer.eos_token_id,
149
+ pad_token_id=tokenizer.pad_token_id,
150
+ )
151
+
152
+ completion = output[0, inputs["input_ids"].shape[1]:]
153
+ print(tokenizer.decode(completion, skip_special_tokens=True).strip())
154
+ ```
155
+
156
+ Each subfolder also contains its tokenizer configuration and detailed model
157
+ card.
158
+
159
+ ## Limitations
160
+
161
+ - Training and evaluation focus on English Text-to-SQL and SQLite.
162
+ - Prompts contain database schemas and optional evidence but do not
163
+ automatically retrieve database contents.
164
+ - Execution equivalence is a strong but imperfect correctness signal.
165
+ - Generated SQL may still be wrong or expensive. Execute it only in a
166
+ read-only sandbox with time and row limits.
167
+ - SQL-R1 is a direct Text-to-SQL model, not a general tool-calling agent.
168
+
169
+ ## Code
170
+
171
+ Data processing, SFT, GRPO reward, training, and evaluation code:
172
+ [j2521402/SQL-R1](https://github.com/j2521402/SQL-R1).
173
+
grpo-hard/README.md ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: Qwen/Qwen3-4B-Base
3
+ library_name: peft
4
+ pipeline_tag: text-generation
5
+ language:
6
+ - en
7
+ tags:
8
+ - base_model:adapter:Qwen/Qwen3-4B-Base
9
+ - peft
10
+ - lora
11
+ - text-to-sql
12
+ - sql
13
+ - grpo
14
+ - rlvr
15
+ - curriculum-learning
16
+ - trl
17
+ ---
18
+
19
+ # SQL-R1-GRPO-Hard
20
+
21
+ SQL-R1-GRPO-Hard is a curriculum-learning ablation of SQL-R1. It continues
22
+ GRPO training from SQL-R1-GRPO on BIRD moderate and challenging examples mixed
23
+ with stratified replay data.
24
+
25
+ This repository contains a LoRA adapter for
26
+ [`Qwen/Qwen3-4B-Base`](https://huggingface.co/Qwen/Qwen3-4B-Base).
27
+ For the best overall balance across BIRD and Spider, use SQL-R1-GRPO; this
28
+ checkpoint is published primarily for analysis and reproducibility.
29
+
30
+ ## Curriculum data
31
+
32
+ - Total records: 2,522
33
+ - BIRD moderate/challenging focus records: 1,891
34
+ - Stratified replay records: 631
35
+ - Spider replay records: 316
36
+ - Validation database overlap: 0
37
+
38
+ The replay subset preserves easier examples and cross-dataset coverage while
39
+ the curriculum emphasizes harder BIRD queries.
40
+
41
+ ## Training
42
+
43
+ - Starting checkpoint: SQL-R1-GRPO
44
+ - Optimization steps: 300
45
+ - Candidates per prompt: 4
46
+ - Approximate generated rollouts: 1,200
47
+ - Learning rate: 2e-6
48
+ - Precision: BF16
49
+ - Reward: SQL validity, read-only safety, executability, and result equivalence
50
+ under parallel SQLite execution
51
+
52
+ Gold SQL is used only to compute verifiable execution rewards and is never
53
+ included in the model prompt.
54
+
55
+ ## Evaluation
56
+
57
+ | Checkpoint | BIRD Dev EX | BIRD execution-valid | Spider Dev EX | Spider execution-valid |
58
+ |---|---:|---:|---:|---:|
59
+ | SQL-R1-GRPO | 42.37% | 87.35% | **79.59%** | 97.00% |
60
+ | **SQL-R1-GRPO-Hard** | **42.50%** | **88.79%** | 78.92% | **97.20%** |
61
+
62
+ Hard-sample curriculum training slightly improves BIRD execution accuracy and
63
+ execution validity, but Spider EX decreases mildly. This result illustrates the
64
+ specialization–retention trade-off and motivates the replay ablation.
65
+
66
+ These are project-side measurements on the complete public development splits,
67
+ not hidden-test leaderboard submissions.
68
+
69
+ ## Usage
70
+
71
+ ```python
72
+ import torch
73
+ from peft import PeftModel
74
+ from transformers import AutoModelForCausalLM, AutoTokenizer
75
+
76
+ base_model_id = "Qwen/Qwen3-4B-Base"
77
+ repo_id = "j2521402/SQL-R1"
78
+ adapter_subfolder = "grpo-hard"
79
+
80
+ tokenizer = AutoTokenizer.from_pretrained(
81
+ repo_id,
82
+ subfolder=adapter_subfolder,
83
+ )
84
+ model = AutoModelForCausalLM.from_pretrained(
85
+ base_model_id,
86
+ torch_dtype=torch.bfloat16,
87
+ device_map="auto",
88
+ )
89
+ model = PeftModel.from_pretrained(
90
+ model,
91
+ repo_id,
92
+ subfolder=adapter_subfolder,
93
+ )
94
+ model.eval()
95
+ ```
96
+
97
+ Use the same SQL-only ChatML prompt format documented in the
98
+ [SQL-R1 repository](https://github.com/j2521402/SQL-R1).
99
+
100
+ ## Limitations
101
+
102
+ - This checkpoint deliberately specializes toward harder BIRD examples and
103
+ shows mild Spider forgetting.
104
+ - Training and evaluation focus on English Text-to-SQL and SQLite.
105
+ - Database contents are not automatically retrieved for the prompt.
106
+ - Generated SQL should be executed only in a read-only sandbox with resource
107
+ limits.
108
+ - This is an ablation checkpoint rather than the recommended general model.
109
+
110
+ ## Project
111
+
112
+ Training, curriculum-data construction, reward, and evaluation code:
113
+ [j2521402/SQL-R1](https://github.com/j2521402/SQL-R1).
114
+
115
+ Core libraries: PyTorch 2.8.0, Transformers 5.14.1, PEFT 0.19.1,
116
+ TRL 1.8.0, Datasets 5.0.0, and Accelerate 1.14.0.
grpo-hard/adapter_config.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": null,
6
+ "base_model_name_or_path": "Qwen/Qwen3-4B-Base",
7
+ "bias": "none",
8
+ "corda_config": null,
9
+ "ensure_weight_tying": false,
10
+ "eva_config": null,
11
+ "exclude_modules": null,
12
+ "fan_in_fan_out": false,
13
+ "inference_mode": true,
14
+ "init_lora_weights": true,
15
+ "layer_replication": null,
16
+ "layers_pattern": null,
17
+ "layers_to_transform": null,
18
+ "loftq_config": {},
19
+ "lora_alpha": 64,
20
+ "lora_bias": false,
21
+ "lora_dropout": 0.05,
22
+ "lora_ga_config": null,
23
+ "megatron_config": null,
24
+ "megatron_core": "megatron.core",
25
+ "modules_to_save": null,
26
+ "peft_type": "LORA",
27
+ "peft_version": "0.19.1",
28
+ "qalora_group_size": 16,
29
+ "r": 32,
30
+ "rank_pattern": {},
31
+ "revision": null,
32
+ "target_modules": [
33
+ "down_proj",
34
+ "up_proj",
35
+ "o_proj",
36
+ "q_proj",
37
+ "k_proj",
38
+ "v_proj",
39
+ "gate_proj"
40
+ ],
41
+ "target_parameters": null,
42
+ "task_type": "CAUSAL_LM",
43
+ "trainable_token_indices": null,
44
+ "use_bdlora": null,
45
+ "use_dora": false,
46
+ "use_qalora": false,
47
+ "use_rslora": false
48
+ }
grpo-hard/adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:438f3b22fb4c1ac3a2ef0b416378b9196350c702a343be836c59906e6e74c782
3
+ size 264308896
grpo-hard/chat_template.jinja ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if tools %}
2
+ {{- '<|im_start|>system\n' }}
3
+ {%- if messages[0].role == 'system' %}
4
+ {{- messages[0].content + '\n\n' }}
5
+ {%- endif %}
6
+ {{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
7
+ {%- for tool in tools %}
8
+ {{- "\n" }}
9
+ {{- tool | tojson }}
10
+ {%- endfor %}
11
+ {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
12
+ {%- else %}
13
+ {%- if messages[0].role == 'system' %}
14
+ {{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
15
+ {%- endif %}
16
+ {%- endif %}
17
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
18
+ {%- for message in messages[::-1] %}
19
+ {%- set index = (messages|length - 1) - loop.index0 %}
20
+ {%- if ns.multi_step_tool and message.role == "user" and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
21
+ {%- set ns.multi_step_tool = false %}
22
+ {%- set ns.last_query_index = index %}
23
+ {%- endif %}
24
+ {%- endfor %}
25
+ {%- for message in messages %}
26
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
27
+ {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
28
+ {%- elif message.role == "assistant" %}
29
+ {%- set content = message.content %}
30
+ {%- set reasoning_content = '' %}
31
+ {%- if message.reasoning_content is defined and message.reasoning_content is not none %}
32
+ {%- set reasoning_content = message.reasoning_content %}
33
+ {%- else %}
34
+ {%- if '</think>' in message.content %}
35
+ {%- set content = message.content.split('</think>')[-1].lstrip('\n') %}
36
+ {%- set reasoning_content = message.content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
37
+ {%- endif %}
38
+ {%- endif %}
39
+ {%- if loop.index0 > ns.last_query_index %}
40
+ {%- if loop.last or (not loop.last and reasoning_content) %}
41
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
42
+ {%- else %}
43
+ {{- '<|im_start|>' + message.role + '\n' + content }}
44
+ {%- endif %}
45
+ {%- else %}
46
+ {{- '<|im_start|>' + message.role + '\n' + content }}
47
+ {%- endif %}
48
+ {%- if message.tool_calls %}
49
+ {%- for tool_call in message.tool_calls %}
50
+ {%- if (loop.first and content) or (not loop.first) %}
51
+ {{- '\n' }}
52
+ {%- endif %}
53
+ {%- if tool_call.function %}
54
+ {%- set tool_call = tool_call.function %}
55
+ {%- endif %}
56
+ {{- '<tool_call>\n{"name": "' }}
57
+ {{- tool_call.name }}
58
+ {{- '", "arguments": ' }}
59
+ {%- if tool_call.arguments is string %}
60
+ {{- tool_call.arguments }}
61
+ {%- else %}
62
+ {{- tool_call.arguments | tojson }}
63
+ {%- endif %}
64
+ {{- '}\n</tool_call>' }}
65
+ {%- endfor %}
66
+ {%- endif %}
67
+ {{- '<|im_end|>\n' }}
68
+ {%- elif message.role == "tool" %}
69
+ {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
70
+ {{- '<|im_start|>user' }}
71
+ {%- endif %}
72
+ {{- '\n<tool_response>\n' }}
73
+ {{- message.content }}
74
+ {{- '\n</tool_response>' }}
75
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
76
+ {{- '<|im_end|>\n' }}
77
+ {%- endif %}
78
+ {%- endif %}
79
+ {%- endfor %}
80
+ {%- if add_generation_prompt %}
81
+ {{- '<|im_start|>assistant\n' }}
82
+ {%- if enable_thinking is defined and enable_thinking is false %}
83
+ {{- '<think>\n\n</think>\n\n' }}
84
+ {%- endif %}
85
+ {%- endif %}
grpo-hard/tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:be75606093db2094d7cd20f3c2f385c212750648bd6ea4fb2bf507a6a4c55506
3
+ size 11422650
grpo-hard/tokenizer_config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": null,
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|endoftext|>",
7
+ "errors": "replace",
8
+ "extra_special_tokens": [
9
+ "<|im_start|>",
10
+ "<|im_end|>",
11
+ "<|object_ref_start|>",
12
+ "<|object_ref_end|>",
13
+ "<|box_start|>",
14
+ "<|box_end|>",
15
+ "<|quad_start|>",
16
+ "<|quad_end|>",
17
+ "<|vision_start|>",
18
+ "<|vision_end|>",
19
+ "<|vision_pad|>",
20
+ "<|image_pad|>",
21
+ "<|video_pad|>"
22
+ ],
23
+ "is_local": true,
24
+ "local_files_only": false,
25
+ "model_max_length": 131072,
26
+ "pad_token": "<|endoftext|>",
27
+ "split_special_tokens": false,
28
+ "tokenizer_class": "Qwen2Tokenizer",
29
+ "unk_token": null
30
+ }
grpo/README.md ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: Qwen/Qwen3-4B-Base
3
+ library_name: peft
4
+ pipeline_tag: text-generation
5
+ language:
6
+ - en
7
+ tags:
8
+ - base_model:adapter:Qwen/Qwen3-4B-Base
9
+ - peft
10
+ - lora
11
+ - text-to-sql
12
+ - sql
13
+ - grpo
14
+ - rlvr
15
+ - trl
16
+ ---
17
+
18
+ # SQL-R1-GRPO
19
+
20
+ SQL-R1-GRPO is the main SQL-R1 checkpoint: a LoRA adapter trained on
21
+ Qwen3-4B-Base with completion-only supervised fine-tuning followed by GRPO
22
+ using verifiable SQLite execution feedback.
23
+
24
+ The model receives a database schema, optional evidence, and a natural-language
25
+ question, then generates one read-only SQLite query. This repository contains
26
+ the adapter only; load it together with
27
+ [`Qwen/Qwen3-4B-Base`](https://huggingface.co/Qwen/Qwen3-4B-Base).
28
+
29
+ ## Training
30
+
31
+ GRPO starts from the SQL-R1-SFT adapter and optimizes generated SQL with a
32
+ layered execution reward:
33
+
34
+ | Outcome | Reward |
35
+ |---|---:|
36
+ | Empty output | -1.00 |
37
+ | Invalid or non-read-only SQL | -0.75 |
38
+ | SQL execution error | -0.25 |
39
+ | Truncated execution result | -0.10 |
40
+ | Executable but incorrect result | +0.25 |
41
+ | Execution-equivalent to the gold query | +1.00 |
42
+
43
+ A low-weight format reward additionally encourages concise SQL-only output.
44
+ Gold SQL and gold execution results are used only by the reward function and
45
+ are never included in the model prompt.
46
+
47
+ - RL training records: 12,885
48
+ - Optimization steps: 300
49
+ - Candidates per prompt: 4
50
+ - Approximate generated rollouts: 1,200
51
+ - Learning rate: 5e-6
52
+ - GRPO beta: 0.001
53
+ - Clipping epsilon: 0.2
54
+ - Precision: BF16
55
+ - Execution backend: parallel read-only SQLite workers
56
+
57
+ ## Evaluation
58
+
59
+ Execution accuracy (EX) and execution-valid rate were measured on the complete
60
+ BIRD Dev and Spider Dev splits with the same greedy decoding and execution
61
+ pipeline at every stage.
62
+
63
+ | Checkpoint | BIRD Dev EX | BIRD execution-valid | Spider Dev EX | Spider execution-valid |
64
+ |---|---:|---:|---:|---:|
65
+ | SQL-R1-SFT | 42.37% | 87.29% | 77.27% | 96.62% |
66
+ | **SQL-R1-GRPO** | **42.37%** | **87.35%** | **79.59%** | **97.00%** |
67
+
68
+ GRPO improves Spider Dev EX by 2.32 percentage points over SFT while preserving
69
+ BIRD Dev EX. This is the recommended SQL-R1 checkpoint.
70
+
71
+ These numbers are project-side development-set measurements, not hidden-test
72
+ leaderboard submissions. Results from systems using prompting, database
73
+ contents, self-consistency, reranking, or larger proprietary models are not
74
+ directly comparable.
75
+
76
+ ## Usage
77
+
78
+ ```python
79
+ import torch
80
+ from peft import PeftModel
81
+ from transformers import AutoModelForCausalLM, AutoTokenizer
82
+
83
+ base_model_id = "Qwen/Qwen3-4B-Base"
84
+ repo_id = "j2521402/SQL-R1"
85
+ adapter_subfolder = "grpo"
86
+
87
+ tokenizer = AutoTokenizer.from_pretrained(
88
+ repo_id,
89
+ subfolder=adapter_subfolder,
90
+ )
91
+ model = AutoModelForCausalLM.from_pretrained(
92
+ base_model_id,
93
+ torch_dtype=torch.bfloat16,
94
+ device_map="auto",
95
+ )
96
+ model = PeftModel.from_pretrained(
97
+ model,
98
+ repo_id,
99
+ subfolder=adapter_subfolder,
100
+ )
101
+ model.eval()
102
+
103
+ messages = [
104
+ {
105
+ "role": "system",
106
+ "content": (
107
+ "You are a Text-to-SQL assistant. Given a SQLite database schema, "
108
+ "optional evidence, and a question, return exactly one read-only "
109
+ "SQLite query. Do not include explanations or Markdown fences."
110
+ ),
111
+ },
112
+ {
113
+ "role": "user",
114
+ "content": (
115
+ 'Database schema:\nTABLE "singer" ("Singer_ID" INT, "Name" TEXT);\n\n'
116
+ "Question:\nHow many singers do we have?"
117
+ ),
118
+ },
119
+ ]
120
+
121
+ inputs = tokenizer.apply_chat_template(
122
+ messages,
123
+ add_generation_prompt=True,
124
+ return_tensors="pt",
125
+ return_dict=True,
126
+ ).to(model.device)
127
+
128
+ with torch.inference_mode():
129
+ output = model.generate(
130
+ **inputs,
131
+ max_new_tokens=256,
132
+ do_sample=False,
133
+ eos_token_id=tokenizer.eos_token_id,
134
+ pad_token_id=tokenizer.pad_token_id,
135
+ )
136
+
137
+ completion = output[0, inputs["input_ids"].shape[1]:]
138
+ print(tokenizer.decode(completion, skip_special_tokens=True).strip())
139
+ ```
140
+
141
+ ## Limitations
142
+
143
+ - Training and evaluation focus on English Text-to-SQL and SQLite.
144
+ - Prompts contain database schemas and optional evidence, but do not perform
145
+ automatic database-content retrieval.
146
+ - Execution equivalence is a strong but imperfect semantic correctness signal.
147
+ - The model may still generate incorrect or expensive queries; execute outputs
148
+ in a read-only sandbox with time and row limits.
149
+ - This is a direct Text-to-SQL model, not a general tool-calling agent.
150
+
151
+ ## Project
152
+
153
+ Training, data processing, reward, and evaluation code:
154
+ [j2521402/SQL-R1](https://github.com/j2521402/SQL-R1).
155
+
156
+ Core libraries: PyTorch 2.8.0, Transformers 5.14.1, PEFT 0.19.1,
157
+ TRL 1.8.0, Datasets 5.0.0, and Accelerate 1.14.0.
grpo/adapter_config.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": null,
6
+ "base_model_name_or_path": "Qwen/Qwen3-4B-Base",
7
+ "bias": "none",
8
+ "corda_config": null,
9
+ "ensure_weight_tying": false,
10
+ "eva_config": null,
11
+ "exclude_modules": null,
12
+ "fan_in_fan_out": false,
13
+ "inference_mode": true,
14
+ "init_lora_weights": true,
15
+ "layer_replication": null,
16
+ "layers_pattern": null,
17
+ "layers_to_transform": null,
18
+ "loftq_config": {},
19
+ "lora_alpha": 64,
20
+ "lora_bias": false,
21
+ "lora_dropout": 0.05,
22
+ "lora_ga_config": null,
23
+ "megatron_config": null,
24
+ "megatron_core": "megatron.core",
25
+ "modules_to_save": null,
26
+ "peft_type": "LORA",
27
+ "peft_version": "0.19.1",
28
+ "qalora_group_size": 16,
29
+ "r": 32,
30
+ "rank_pattern": {},
31
+ "revision": null,
32
+ "target_modules": [
33
+ "q_proj",
34
+ "down_proj",
35
+ "gate_proj",
36
+ "k_proj",
37
+ "o_proj",
38
+ "up_proj",
39
+ "v_proj"
40
+ ],
41
+ "target_parameters": null,
42
+ "task_type": "CAUSAL_LM",
43
+ "trainable_token_indices": null,
44
+ "use_bdlora": null,
45
+ "use_dora": false,
46
+ "use_qalora": false,
47
+ "use_rslora": false
48
+ }
grpo/adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b41bba278ef202b1431372a22a953cc79cba80a0d8aa348733b62bd8f6bbaa1f
3
+ size 264308896
grpo/chat_template.jinja ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if tools %}
2
+ {{- '<|im_start|>system\n' }}
3
+ {%- if messages[0].role == 'system' %}
4
+ {{- messages[0].content + '\n\n' }}
5
+ {%- endif %}
6
+ {{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
7
+ {%- for tool in tools %}
8
+ {{- "\n" }}
9
+ {{- tool | tojson }}
10
+ {%- endfor %}
11
+ {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
12
+ {%- else %}
13
+ {%- if messages[0].role == 'system' %}
14
+ {{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
15
+ {%- endif %}
16
+ {%- endif %}
17
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
18
+ {%- for message in messages[::-1] %}
19
+ {%- set index = (messages|length - 1) - loop.index0 %}
20
+ {%- if ns.multi_step_tool and message.role == "user" and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
21
+ {%- set ns.multi_step_tool = false %}
22
+ {%- set ns.last_query_index = index %}
23
+ {%- endif %}
24
+ {%- endfor %}
25
+ {%- for message in messages %}
26
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
27
+ {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
28
+ {%- elif message.role == "assistant" %}
29
+ {%- set content = message.content %}
30
+ {%- set reasoning_content = '' %}
31
+ {%- if message.reasoning_content is defined and message.reasoning_content is not none %}
32
+ {%- set reasoning_content = message.reasoning_content %}
33
+ {%- else %}
34
+ {%- if '</think>' in message.content %}
35
+ {%- set content = message.content.split('</think>')[-1].lstrip('\n') %}
36
+ {%- set reasoning_content = message.content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
37
+ {%- endif %}
38
+ {%- endif %}
39
+ {%- if loop.index0 > ns.last_query_index %}
40
+ {%- if loop.last or (not loop.last and reasoning_content) %}
41
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
42
+ {%- else %}
43
+ {{- '<|im_start|>' + message.role + '\n' + content }}
44
+ {%- endif %}
45
+ {%- else %}
46
+ {{- '<|im_start|>' + message.role + '\n' + content }}
47
+ {%- endif %}
48
+ {%- if message.tool_calls %}
49
+ {%- for tool_call in message.tool_calls %}
50
+ {%- if (loop.first and content) or (not loop.first) %}
51
+ {{- '\n' }}
52
+ {%- endif %}
53
+ {%- if tool_call.function %}
54
+ {%- set tool_call = tool_call.function %}
55
+ {%- endif %}
56
+ {{- '<tool_call>\n{"name": "' }}
57
+ {{- tool_call.name }}
58
+ {{- '", "arguments": ' }}
59
+ {%- if tool_call.arguments is string %}
60
+ {{- tool_call.arguments }}
61
+ {%- else %}
62
+ {{- tool_call.arguments | tojson }}
63
+ {%- endif %}
64
+ {{- '}\n</tool_call>' }}
65
+ {%- endfor %}
66
+ {%- endif %}
67
+ {{- '<|im_end|>\n' }}
68
+ {%- elif message.role == "tool" %}
69
+ {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
70
+ {{- '<|im_start|>user' }}
71
+ {%- endif %}
72
+ {{- '\n<tool_response>\n' }}
73
+ {{- message.content }}
74
+ {{- '\n</tool_response>' }}
75
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
76
+ {{- '<|im_end|>\n' }}
77
+ {%- endif %}
78
+ {%- endif %}
79
+ {%- endfor %}
80
+ {%- if add_generation_prompt %}
81
+ {{- '<|im_start|>assistant\n' }}
82
+ {%- if enable_thinking is defined and enable_thinking is false %}
83
+ {{- '<think>\n\n</think>\n\n' }}
84
+ {%- endif %}
85
+ {%- endif %}
grpo/tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:be75606093db2094d7cd20f3c2f385c212750648bd6ea4fb2bf507a6a4c55506
3
+ size 11422650
grpo/tokenizer_config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": null,
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|endoftext|>",
7
+ "errors": "replace",
8
+ "extra_special_tokens": [
9
+ "<|im_start|>",
10
+ "<|im_end|>",
11
+ "<|object_ref_start|>",
12
+ "<|object_ref_end|>",
13
+ "<|box_start|>",
14
+ "<|box_end|>",
15
+ "<|quad_start|>",
16
+ "<|quad_end|>",
17
+ "<|vision_start|>",
18
+ "<|vision_end|>",
19
+ "<|vision_pad|>",
20
+ "<|image_pad|>",
21
+ "<|video_pad|>"
22
+ ],
23
+ "is_local": true,
24
+ "local_files_only": false,
25
+ "model_max_length": 131072,
26
+ "pad_token": "<|endoftext|>",
27
+ "split_special_tokens": false,
28
+ "tokenizer_class": "Qwen2Tokenizer",
29
+ "unk_token": null
30
+ }
sft/README.md ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: Qwen/Qwen3-4B-Base
3
+ library_name: peft
4
+ pipeline_tag: text-generation
5
+ language:
6
+ - en
7
+ tags:
8
+ - base_model:adapter:Qwen/Qwen3-4B-Base
9
+ - peft
10
+ - lora
11
+ - text-to-sql
12
+ - sql
13
+ - supervised-fine-tuning
14
+ ---
15
+
16
+ # SQL-R1-SFT
17
+
18
+ SQL-R1-SFT is a LoRA adapter for
19
+ [Qwen3-4B-Base](https://huggingface.co/Qwen/Qwen3-4B-Base), trained to
20
+ translate a SQLite schema, optional evidence, and a natural-language question
21
+ into exactly one read-only SQL query.
22
+
23
+ This is an adapter-only repository. The Qwen3-4B-Base weights are required for
24
+ inference.
25
+
26
+ ## Training
27
+
28
+ - **Method:** completion-only supervised fine-tuning
29
+ - **Training data:** filtered BIRD and Spider training examples
30
+ - **Records:** 12,976 train / 608 database-disjoint validation
31
+ - **LoRA:** rank 32, alpha 64, dropout 0.05
32
+ - **Target modules:** all attention and MLP projections
33
+ - **Trainable parameters:** 66.1M (1.62%)
34
+ - **Precision:** BF16
35
+ - **Epochs:** 1
36
+ - **Learning rate:** 1e-4
37
+ - **Effective batch size:** 16
38
+
39
+ Only target SQL tokens contribute to the loss. Schema, evidence, and question
40
+ tokens are masked. The final assistant completion uses Qwen3-4B-Base's native
41
+ `<|endoftext|>` EOS rather than the ChatML turn separator.
42
+
43
+ ## Evaluation
44
+
45
+ Execution accuracy (EX) and execution-valid rate were measured with a common
46
+ read-only SQLite evaluator on the public development sets.
47
+
48
+ | Model | BIRD Dev EX | BIRD valid | Spider Dev EX | Spider valid |
49
+ |---|---:|---:|---:|---:|
50
+ | Qwen3-4B-Base | 23.21% | 55.61% | 55.51% | 77.66% |
51
+ | SQL-R1-SFT | **42.37%** | **87.29%** | **77.27%** | **96.62%** |
52
+
53
+ These are public Dev results produced by the SQL-R1 evaluator, not official
54
+ hidden-test leaderboard submissions.
55
+
56
+ ## Usage
57
+
58
+ ```python
59
+ import torch
60
+ from peft import PeftModel
61
+ from transformers import AutoModelForCausalLM, AutoTokenizer
62
+
63
+ base_id = "Qwen/Qwen3-4B-Base"
64
+ repo_id = "j2521402/SQL-R1"
65
+ adapter_subfolder = "sft"
66
+
67
+ tokenizer = AutoTokenizer.from_pretrained(
68
+ repo_id,
69
+ subfolder=adapter_subfolder,
70
+ )
71
+ base_model = AutoModelForCausalLM.from_pretrained(
72
+ base_id,
73
+ dtype=torch.bfloat16,
74
+ device_map="auto",
75
+ )
76
+ model = PeftModel.from_pretrained(
77
+ base_model,
78
+ repo_id,
79
+ subfolder=adapter_subfolder,
80
+ ).eval()
81
+
82
+ messages = [
83
+ {
84
+ "role": "system",
85
+ "content": (
86
+ "You are a Text-to-SQL assistant. Given a SQLite database schema, "
87
+ "optional evidence, and a question, return exactly one read-only "
88
+ "SQLite query. Do not include explanations or Markdown fences."
89
+ ),
90
+ },
91
+ {
92
+ "role": "user",
93
+ "content": (
94
+ 'Database schema:\nTABLE "singer" ("id" INT, "name" TEXT);\n\n'
95
+ "Question:\nHow many singers are there?"
96
+ ),
97
+ },
98
+ ]
99
+
100
+ inputs = tokenizer.apply_chat_template(
101
+ messages,
102
+ tokenize=True,
103
+ add_generation_prompt=True,
104
+ enable_thinking=False,
105
+ return_dict=True,
106
+ return_tensors="pt",
107
+ ).to(model.device)
108
+
109
+ with torch.inference_mode():
110
+ outputs = model.generate(
111
+ **inputs,
112
+ max_new_tokens=512,
113
+ do_sample=False,
114
+ eos_token_id=[
115
+ tokenizer.eos_token_id,
116
+ tokenizer.convert_tokens_to_ids("<|im_end|>"),
117
+ ],
118
+ )
119
+
120
+ sql = tokenizer.decode(
121
+ outputs[0, inputs["input_ids"].shape[1]:],
122
+ skip_special_tokens=True,
123
+ ).strip()
124
+ print(sql)
125
+ ```
126
+
127
+ ## Limitations
128
+
129
+ - Training and evaluation target SQLite and English Text-to-SQL benchmarks.
130
+ - Prompts contain schema metadata but not database row contents.
131
+ - The adapter does not implement tool calling or an autonomous SQL agent.
132
+ - Generated SQL should be validated and executed through a read-only,
133
+ resource-limited database connection.
134
+
135
+ ## Project
136
+
137
+ Training code, preprocessing, evaluator, and reproducibility details:
138
+ [j2521402/SQL-R1](https://github.com/j2521402/SQL-R1).
139
+
140
+ ### Framework versions
141
+
142
+ - PyTorch 2.8.0+cu128
143
+ - Transformers 5.14.1
144
+ - PEFT 0.19.1
sft/adapter_config.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": null,
6
+ "base_model_name_or_path": "Qwen/Qwen3-4B-Base",
7
+ "bias": "none",
8
+ "corda_config": null,
9
+ "ensure_weight_tying": false,
10
+ "eva_config": null,
11
+ "exclude_modules": null,
12
+ "fan_in_fan_out": false,
13
+ "inference_mode": true,
14
+ "init_lora_weights": true,
15
+ "layer_replication": null,
16
+ "layers_pattern": null,
17
+ "layers_to_transform": null,
18
+ "loftq_config": {},
19
+ "lora_alpha": 64,
20
+ "lora_bias": false,
21
+ "lora_dropout": 0.05,
22
+ "lora_ga_config": null,
23
+ "megatron_config": null,
24
+ "megatron_core": "megatron.core",
25
+ "modules_to_save": null,
26
+ "peft_type": "LORA",
27
+ "peft_version": "0.19.1",
28
+ "qalora_group_size": 16,
29
+ "r": 32,
30
+ "rank_pattern": {},
31
+ "revision": null,
32
+ "target_modules": [
33
+ "k_proj",
34
+ "q_proj",
35
+ "up_proj",
36
+ "gate_proj",
37
+ "v_proj",
38
+ "down_proj",
39
+ "o_proj"
40
+ ],
41
+ "target_parameters": null,
42
+ "task_type": "CAUSAL_LM",
43
+ "trainable_token_indices": null,
44
+ "use_bdlora": null,
45
+ "use_dora": false,
46
+ "use_qalora": false,
47
+ "use_rslora": false
48
+ }
sft/adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8a9fa60097d34eb3844ad134e2edb29fa6db7ba901a39505936cc15d971fd9af
3
+ size 264308896
sft/chat_template.jinja ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if tools %}
2
+ {{- '<|im_start|>system\n' }}
3
+ {%- if messages[0].role == 'system' %}
4
+ {{- messages[0].content + '\n\n' }}
5
+ {%- endif %}
6
+ {{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
7
+ {%- for tool in tools %}
8
+ {{- "\n" }}
9
+ {{- tool | tojson }}
10
+ {%- endfor %}
11
+ {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
12
+ {%- else %}
13
+ {%- if messages[0].role == 'system' %}
14
+ {{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
15
+ {%- endif %}
16
+ {%- endif %}
17
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
18
+ {%- for message in messages[::-1] %}
19
+ {%- set index = (messages|length - 1) - loop.index0 %}
20
+ {%- if ns.multi_step_tool and message.role == "user" and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
21
+ {%- set ns.multi_step_tool = false %}
22
+ {%- set ns.last_query_index = index %}
23
+ {%- endif %}
24
+ {%- endfor %}
25
+ {%- for message in messages %}
26
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
27
+ {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
28
+ {%- elif message.role == "assistant" %}
29
+ {%- set content = message.content %}
30
+ {%- set reasoning_content = '' %}
31
+ {%- if message.reasoning_content is defined and message.reasoning_content is not none %}
32
+ {%- set reasoning_content = message.reasoning_content %}
33
+ {%- else %}
34
+ {%- if '</think>' in message.content %}
35
+ {%- set content = message.content.split('</think>')[-1].lstrip('\n') %}
36
+ {%- set reasoning_content = message.content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
37
+ {%- endif %}
38
+ {%- endif %}
39
+ {%- if loop.index0 > ns.last_query_index %}
40
+ {%- if loop.last or (not loop.last and reasoning_content) %}
41
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
42
+ {%- else %}
43
+ {{- '<|im_start|>' + message.role + '\n' + content }}
44
+ {%- endif %}
45
+ {%- else %}
46
+ {{- '<|im_start|>' + message.role + '\n' + content }}
47
+ {%- endif %}
48
+ {%- if message.tool_calls %}
49
+ {%- for tool_call in message.tool_calls %}
50
+ {%- if (loop.first and content) or (not loop.first) %}
51
+ {{- '\n' }}
52
+ {%- endif %}
53
+ {%- if tool_call.function %}
54
+ {%- set tool_call = tool_call.function %}
55
+ {%- endif %}
56
+ {{- '<tool_call>\n{"name": "' }}
57
+ {{- tool_call.name }}
58
+ {{- '", "arguments": ' }}
59
+ {%- if tool_call.arguments is string %}
60
+ {{- tool_call.arguments }}
61
+ {%- else %}
62
+ {{- tool_call.arguments | tojson }}
63
+ {%- endif %}
64
+ {{- '}\n</tool_call>' }}
65
+ {%- endfor %}
66
+ {%- endif %}
67
+ {{- '<|im_end|>\n' }}
68
+ {%- elif message.role == "tool" %}
69
+ {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
70
+ {{- '<|im_start|>user' }}
71
+ {%- endif %}
72
+ {{- '\n<tool_response>\n' }}
73
+ {{- message.content }}
74
+ {{- '\n</tool_response>' }}
75
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
76
+ {{- '<|im_end|>\n' }}
77
+ {%- endif %}
78
+ {%- endif %}
79
+ {%- endfor %}
80
+ {%- if add_generation_prompt %}
81
+ {{- '<|im_start|>assistant\n' }}
82
+ {%- if enable_thinking is defined and enable_thinking is false %}
83
+ {{- '<think>\n\n</think>\n\n' }}
84
+ {%- endif %}
85
+ {%- endif %}
sft/tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:be75606093db2094d7cd20f3c2f385c212750648bd6ea4fb2bf507a6a4c55506
3
+ size 11422650
sft/tokenizer_config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": null,
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|endoftext|>",
7
+ "errors": "replace",
8
+ "extra_special_tokens": [
9
+ "<|im_start|>",
10
+ "<|im_end|>",
11
+ "<|object_ref_start|>",
12
+ "<|object_ref_end|>",
13
+ "<|box_start|>",
14
+ "<|box_end|>",
15
+ "<|quad_start|>",
16
+ "<|quad_end|>",
17
+ "<|vision_start|>",
18
+ "<|vision_end|>",
19
+ "<|vision_pad|>",
20
+ "<|image_pad|>",
21
+ "<|video_pad|>"
22
+ ],
23
+ "is_local": true,
24
+ "local_files_only": false,
25
+ "model_max_length": 131072,
26
+ "pad_token": "<|endoftext|>",
27
+ "split_special_tokens": false,
28
+ "tokenizer_class": "Qwen2Tokenizer",
29
+ "unk_token": null
30
+ }