@bendyline/gilde 0.1.46 → 0.1.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/authoring/chat-models/gemma4-12b-q4.json +5 -4
  2. package/authoring/chat-models/gemma4-12b-q8.json +4 -3
  3. package/authoring/chat-models/gemma4-26b-q4.json +4 -3
  4. package/authoring/chat-models/gemma4-31b-q4.json +5 -4
  5. package/authoring/chat-models/gemma4-e2b-q4.json +4 -3
  6. package/authoring/chat-models/gemma4-e4b-q4.json +5 -4
  7. package/data/chat-models/ge/gemma4-12b-q4/manifest.json +47 -26
  8. package/data/chat-models/ge/gemma4-12b-q4/versions/1.2.0/manifest.json +98 -0
  9. package/data/chat-models/ge/gemma4-12b-q8/manifest.json +5 -4
  10. package/data/chat-models/ge/gemma4-12b-q8/versions/1.1.0/manifest.json +78 -0
  11. package/data/chat-models/ge/gemma4-26b-q4/manifest.json +5 -3
  12. package/data/chat-models/ge/gemma4-26b-q4/versions/1.3.0/manifest.json +81 -0
  13. package/data/chat-models/ge/gemma4-31b-q4/manifest.json +28 -36
  14. package/data/chat-models/ge/gemma4-31b-q4/versions/1.3.0/manifest.json +86 -0
  15. package/data/chat-models/ge/gemma4-e2b-q4/manifest.json +4 -3
  16. package/data/chat-models/ge/gemma4-e2b-q4/versions/1.3.0/manifest.json +71 -0
  17. package/data/chat-models/ge/gemma4-e4b-q4/manifest.json +20 -19
  18. package/data/chat-models/ge/gemma4-e4b-q4/versions/1.3.0/manifest.json +76 -0
  19. package/data/chat-models/index.json +1 -1
  20. package/data/chat-models/qw/qwen3.8-27b-q4/manifest.json +2 -2
  21. package/data/chat-models/qw/qwen3.8-27b-q4/versions/1.0.3/manifest.json +116 -0
  22. package/data/connector-types/az/azure-monitor-logs/manifest.json +18 -0
  23. package/data/connector-types/az/azure-monitor-logs/versions/1.0.0/manifest.json +81 -0
  24. package/data/connector-types/ht/http-json-rows/manifest.json +18 -0
  25. package/data/connector-types/ht/http-json-rows/versions/1.0.0/manifest.json +74 -0
  26. package/data/connector-types/index.json +1 -1
  27. package/data/craftbook-templates/index.json +1 -1
  28. package/data/craftbook-templates/pu/pull-request-review/versions/1.9.0/craftbook.json +304 -0
  29. package/data/craftbook-templates/pu/pull-request-review/versions/1.9.0/test.json +144 -0
  30. package/package.json +1 -1
  31. package/schemas/chat-model-version.schema.json +65 -0
@@ -16,8 +16,8 @@
16
16
  "name": "Google",
17
17
  "url": "https://huggingface.co/google/gemma-4-12B-it"
18
18
  },
19
- "version": "1.1.3",
20
- "updatedAt": "2026-08-15T00:00:00Z",
19
+ "version": "1.2.0",
20
+ "updatedAt": "2026-08-28T00:00:00Z",
21
21
  "license": "Apache-2.0",
22
22
  "recoScore": 15,
23
23
  "parameterSize": "12B",
@@ -183,7 +183,8 @@
183
183
  }
184
184
  },
185
185
  "mlx": {
186
- "huggingfaceRepo": "mlx-community/gemma-4-12B-it-qat-4bit",
187
- "quantization": "4bit"
186
+ "huggingfaceRepo": "OsaurusAI/gemma-4-12B-it-qat-MXFP4",
187
+ "quantization": "MXFP4",
188
+ "chatTemplateFrom": "google/gemma-4-12B-it"
188
189
  }
189
190
  }
@@ -15,8 +15,8 @@
15
15
  "name": "Google",
16
16
  "url": "https://huggingface.co/google/gemma-4-12B-it"
17
17
  },
18
- "version": "1.0.3",
19
- "updatedAt": "2026-08-15T00:00:00Z",
18
+ "version": "1.1.0",
19
+ "updatedAt": "2026-08-28T00:00:00Z",
20
20
  "license": "Apache-2.0",
21
21
  "recoScore": 15,
22
22
  "parameterSize": "12B",
@@ -158,6 +158,7 @@
158
158
  },
159
159
  "mlx": {
160
160
  "huggingfaceRepo": "mlx-community/gemma-4-12b-it-8bit",
161
- "quantization": "8bit"
161
+ "quantization": "8bit",
162
+ "chatTemplateFrom": "google/gemma-4-12B-it"
162
163
  }
163
164
  }
@@ -16,8 +16,8 @@
16
16
  "name": "Google",
17
17
  "url": "https://huggingface.co/google/gemma-4-26B-A4B-it"
18
18
  },
19
- "version": "1.2.2",
20
- "updatedAt": "2026-08-15T00:00:00Z",
19
+ "version": "1.3.0",
20
+ "updatedAt": "2026-08-28T00:00:00Z",
21
21
  "license": "Apache-2.0",
22
22
  "recoScore": 15,
23
23
  "parameterSize": "25.2B",
@@ -84,6 +84,7 @@
84
84
  },
85
85
  "mlx": {
86
86
  "huggingfaceRepo": "mlx-community/gemma-4-26B-A4B-it-qat-4bit",
87
- "quantization": "4bit"
87
+ "quantization": "4bit",
88
+ "chatTemplateFrom": "google/gemma-4-26B-A4B-it"
88
89
  }
89
90
  }
@@ -17,8 +17,8 @@
17
17
  "name": "Google",
18
18
  "url": "https://huggingface.co/google/gemma-4-31B-it"
19
19
  },
20
- "version": "1.2.2",
21
- "updatedAt": "2026-08-15T00:00:00Z",
20
+ "version": "1.3.0",
21
+ "updatedAt": "2026-08-28T00:00:00Z",
22
22
  "license": "Apache-2.0",
23
23
  "recoScore": 15,
24
24
  "parameterSize": "30.7B",
@@ -74,7 +74,8 @@
74
74
  }
75
75
  },
76
76
  "mlx": {
77
- "huggingfaceRepo": "mlx-community/gemma-4-31B-it-qat-4bit",
78
- "quantization": "4bit"
77
+ "huggingfaceRepo": "mlx-community/unsloth-gemma-4-31B-it-qat-oQ4",
78
+ "quantization": "oQ4",
79
+ "chatTemplateFrom": "google/gemma-4-31B-it"
79
80
  }
80
81
  }
@@ -16,8 +16,8 @@
16
16
  "name": "Google",
17
17
  "url": "https://huggingface.co/google/gemma-4-E2B-it"
18
18
  },
19
- "version": "1.2.1",
20
- "updatedAt": "2026-08-15T00:00:00Z",
19
+ "version": "1.3.0",
20
+ "updatedAt": "2026-08-28T00:00:00Z",
21
21
  "license": "Apache-2.0",
22
22
  "recoScore": 15,
23
23
  "parameterSize": "2.3B",
@@ -70,6 +70,7 @@
70
70
  },
71
71
  "mlx": {
72
72
  "huggingfaceRepo": "mlx-community/gemma-4-E2B-it-qat-4bit",
73
- "quantization": "4bit"
73
+ "quantization": "4bit",
74
+ "chatTemplateFrom": "google/gemma-4-E2B-it"
74
75
  }
75
76
  }
@@ -15,8 +15,8 @@
15
15
  "name": "Google",
16
16
  "url": "https://huggingface.co/google/gemma-4-E4B-it"
17
17
  },
18
- "version": "1.2.1",
19
- "updatedAt": "2026-08-15T00:00:00Z",
18
+ "version": "1.3.0",
19
+ "updatedAt": "2026-08-28T00:00:00Z",
20
20
  "license": "Apache-2.0",
21
21
  "recoScore": 15,
22
22
  "parameterSize": "4.5B",
@@ -64,7 +64,8 @@
64
64
  }
65
65
  },
66
66
  "mlx": {
67
- "huggingfaceRepo": "mlx-community/gemma-4-E4B-it-qat-4bit",
68
- "quantization": "4bit"
67
+ "huggingfaceRepo": "mlx-community/unsloth-gemma-4-E4B-it-qat-oQ4",
68
+ "quantization": "oQ4",
69
+ "chatTemplateFrom": "google/gemma-4-E4B-it"
69
70
  }
70
71
  }
@@ -18,8 +18,8 @@
18
18
  "name": "Google",
19
19
  "url": "https://huggingface.co/google/gemma-4-12B-it"
20
20
  },
21
- "version": "1.1.3",
22
- "updatedAt": "2026-08-15T00:00:00Z",
21
+ "version": "1.2.0",
22
+ "updatedAt": "2026-08-28T00:00:00Z",
23
23
  "license": "Apache-2.0",
24
24
  "licenseClass": "open",
25
25
  "licenseShortName": "Apache 2.0",
@@ -168,20 +168,20 @@
168
168
  }
169
169
  },
170
170
  "mlx": {
171
- "huggingfaceRepo": "mlx-community/gemma-4-12B-it-qat-4bit",
172
- "revision": "e70c6b3ba0979b3357dcd2f223ad8bde7787a6b6",
173
- "quantization": "4bit",
174
- "approxSizeBytes": 11020138609,
171
+ "huggingfaceRepo": "OsaurusAI/gemma-4-12B-it-qat-MXFP4",
172
+ "revision": "61fbdd1be971d8808060d8ba4e607f420125b214",
173
+ "quantization": "MXFP4",
174
+ "approxSizeBytes": 7942602298,
175
175
  "files": [
176
176
  {
177
177
  "name": "chat_template.jinja",
178
- "sha256": "36e3a42e5cf14cd0020e72d92e1fdd9970f59b82170e421f0cbe1bb42bead3f0",
179
- "sizeBytes": 17466
178
+ "sha256": "d5ce49ce670058e90cc623c0284c21c8fcd778c30f5be1891b00f022bedee2fe",
179
+ "sizeBytes": 17811
180
180
  },
181
181
  {
182
182
  "name": "config.json",
183
- "sha256": "fe091f98e6f7e5e80461bd8ec7ced6d87ac16987586239386ed44b82ecbc2b12",
184
- "sizeBytes": 39882
183
+ "sha256": "7052f31177bb79e2c6760d5032583f74c1864eb51b5a17e1404a10eca3acf913",
184
+ "sizeBytes": 5237
185
185
  },
186
186
  {
187
187
  "name": "generation_config.json",
@@ -189,29 +189,49 @@
189
189
  "sizeBytes": 260
190
190
  },
191
191
  {
192
- "name": "model-00001-of-00003.safetensors",
193
- "sha256": "9fc7406175fdfbca0f941767bfa222ecdaea71ab1d2865cd049eb2a0a1d9f2a1",
194
- "sizeBytes": 5343482357
192
+ "name": "model-00001-of-00007.safetensors",
193
+ "sha256": "8bbc6b27a2127ca6d68a449ff895644cd18477cc69205c4cd02907b862c4cf11",
194
+ "sizeBytes": 2047672664
195
195
  },
196
196
  {
197
- "name": "model-00002-of-00003.safetensors",
198
- "sha256": "62a2b9715c9bef3a4f0a2769fe2d5022ae55907b631731a24568f3e8f444d9e9",
199
- "sizeBytes": 5315166254
197
+ "name": "model-00002-of-00007.safetensors",
198
+ "sha256": "5794202f68df90c7f18a6db80a9a273235146f1d1b05f632e7df1ed1644996a7",
199
+ "sizeBytes": 1023076042
200
200
  },
201
201
  {
202
- "name": "model-00003-of-00003.safetensors",
203
- "sha256": "fd75a04eb5cc73be2760898715472e92626b0a8ba03b19ee57737419885da6c9",
204
- "sizeBytes": 329123819
202
+ "name": "model-00003-of-00007.safetensors",
203
+ "sha256": "299ff6ced2b6b8e4d0957bc4b4f26e7fbd25b3917bc0a9f27dd8d983449241b9",
204
+ "sizeBytes": 1012218216
205
+ },
206
+ {
207
+ "name": "model-00004-of-00007.safetensors",
208
+ "sha256": "1f0e6f655d2a59d2e3b0d987f705e498be535f6c9782c9726c3e37ede1c55545",
209
+ "sizeBytes": 1009437650
210
+ },
211
+ {
212
+ "name": "model-00005-of-00007.safetensors",
213
+ "sha256": "06d4b763d2bba8277e78d6a1bcd2a623190956da8adfb6d7e2fdd3234bc213a8",
214
+ "sizeBytes": 1024911496
215
+ },
216
+ {
217
+ "name": "model-00006-of-00007.safetensors",
218
+ "sha256": "ecdb96e03cba6751e3d73d621019afd20e4b9040162997e1e236e7231793370a",
219
+ "sizeBytes": 1018677986
220
+ },
221
+ {
222
+ "name": "model-00007-of-00007.safetensors",
223
+ "sha256": "1aa3cfd784f63827f6ec9eb0b2a98f735976deb62ead0a54b87f33aba7f96176",
224
+ "sizeBytes": 774295738
205
225
  },
206
226
  {
207
227
  "name": "model.safetensors.index.json",
208
- "sha256": "b87c93774de5d13ca9d0e21b045793e42e5df032fb5e7622212524f56f9695f2",
209
- "sizeBytes": 135330
228
+ "sha256": "136ff8d0fb292d5cd6688d7a5456b7710a2d4628c53947b45180e3dc654697c2",
229
+ "sizeBytes": 97771
210
230
  },
211
231
  {
212
232
  "name": "processor_config.json",
213
- "sha256": "016a1db9c4f41ea0c61919c46855ea5e7c45c6e4ae4bfbedfb5b6bed79a2fe92",
214
- "sizeBytes": 868
233
+ "sha256": "6b938e76555b3e9946890770e1abcd442a4718f34041a58e8139dc8ad34545c9",
234
+ "sizeBytes": 1382
215
235
  },
216
236
  {
217
237
  "name": "tokenizer.json",
@@ -220,9 +240,10 @@
220
240
  },
221
241
  {
222
242
  "name": "tokenizer_config.json",
223
- "sha256": "a4260621db48fa22f2b09ce3ba5ad0ec0cc0e032aa702e3ab743a0bc9d6e1d06",
224
- "sizeBytes": 2747
243
+ "sha256": "c0146b8149eb8b8f96e9b92b9a0d5e0d1864140b49a4d13cec52bc9adcecb5a7",
244
+ "sizeBytes": 20419
225
245
  }
226
- ]
246
+ ],
247
+ "chatTemplateOverride": "{#\n Template: Google Gemma 4 Canonical Chat Template\n Author: Google Gemma Engineering Team\n Published: 2026-07-09\n Context: Fixed tool-calling loops, turn closures, and thinking content-ordering.\n#}\n{%- macro format_parameters(properties, required, filter_keys=false) -%}\n {%- set standard_keys = ['description', 'type', 'properties', 'required', 'nullable'] -%}\n {%- set ns = namespace(found_first=false) -%}\n {%- for key, value in properties | dictsort -%}\n {%- set add_comma = false -%}\n {%- if not filter_keys or key not in standard_keys -%}\n {%- if ns.found_first %},{% endif -%}\n {%- set ns.found_first = true -%}\n {{ key }}:{\n {%- if value['description'] -%}\n description:<|\"|>{{ value['description'] }}<|\"|>\n {%- set add_comma = true -%}\n {%- endif -%}\n {%- if value['type'] | upper == 'STRING' -%}\n {%- if value['enum'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n enum:{{ format_argument(value['enum']) }}\n {%- endif -%}\n {%- elif value['type'] | upper == 'ARRAY' -%}\n {%- if value['items'] is mapping and value['items'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n items:{\n {%- set ns_items = namespace(found_first=false) -%}\n {%- for item_key, item_value in value['items'] | dictsort -%}\n {%- if item_value is not none -%}\n {%- if ns_items.found_first %},{% endif -%}\n {%- set ns_items.found_first = true -%}\n {%- if item_key == 'properties' -%}\n properties:{\n {%- if item_value is mapping -%}\n {{- format_parameters(item_value, value['items']['required'] | default([])) -}}\n {%- endif -%}\n }\n {%- elif item_key == 'required' -%}\n required:[\n {%- for req_item in item_value -%}\n <|\"|>{{- req_item -}}<|\"|>\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n ]\n {%- elif item_key == 'type' -%}\n {%- if item_value is string -%}\n type:{{ format_argument(item_value | upper) }}\n {%- else -%}\n type:{{ format_argument(item_value | map('upper') | list) }}\n {%- endif -%}\n {%- else -%}\n {{ item_key }}:{{ format_argument(item_value) }}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n }\n {%- endif -%}\n {%- endif -%}\n {%- if value['nullable'] %}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n nullable:true\n {%- endif -%}\n {%- if value['type'] | upper == 'OBJECT' -%}\n {%- if value['properties'] is defined and value['properties'] is mapping -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n properties:{\n {{- format_parameters(value['properties'], value['required'] | default([])) -}}\n }\n {%- elif value is mapping -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n properties:{\n {{- format_parameters(value, value['required'] | default([]), filter_keys=true) -}}\n }\n {%- endif -%}\n {%- if value['required'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n required:[\n {%- for item in value['required'] | default([]) -%}\n <|\"|>{{- item -}}<|\"|>\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n ]\n {%- endif -%}\n {%- endif -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n type:<|\"|>{{ value['type'] | upper }}<|\"|>}\n {%- endif -%}\n {%- endfor -%}\n{%- endmacro -%}\n{%- macro format_function_declaration(tool_data) -%}\n declaration:{{- tool_data['function']['name'] -}}{description:<|\"|>{{- tool_data['function']['description'] -}}<|\"|>\n {%- set params = tool_data['function']['parameters'] -%}\n {%- if params -%}\n ,parameters:{\n {%- if params['properties'] -%}\n properties:{ {{- format_parameters(params['properties'], params['required']) -}} },\n {%- endif -%}\n {%- if params['required'] -%}\n required:[\n {%- for item in params['required'] -%}\n <|\"|>{{- item -}}<|\"|>\n {{- ',' if not loop.last -}}\n {%- endfor -%}\n ],\n {%- endif -%}\n {%- if params['type'] -%}\n type:<|\"|>{{- params['type'] | upper -}}<|\"|>}\n {%- endif -%}\n {%- endif -%}\n {%- if 'response' in tool_data['function'] -%}\n {%- set response_declaration = tool_data['function']['response'] -%}\n ,response:{\n {%- if response_declaration['description'] -%}\n description:<|\"|>{{- response_declaration['description'] -}}<|\"|>,\n {%- endif -%}\n {%- if response_declaration['type'] | upper == 'OBJECT' -%}\n type:<|\"|>{{- response_declaration['type'] | upper -}}<|\"|>}\n {%- endif -%}\n {%- endif -%}\n }\n{%- endmacro -%}\n{%- macro format_argument(argument, escape_keys=True) -%}\n {%- if argument is none -%}\n {{- 'null' -}}\n {%- elif argument is string -%}\n {{- '<|\"|>' + argument + '<|\"|>' -}}\n {%- elif argument is boolean -%}\n {{- 'true' if argument else 'false' -}}\n {%- elif argument is mapping -%}\n {{- '{' -}}\n {%- set ns = namespace(found_first=false) -%}\n {%- for key, value in argument | dictsort -%}\n {%- if ns.found_first %},{% endif -%}\n {%- set ns.found_first = true -%}\n {%- if escape_keys -%}\n {{- '<|\"|>' + key + '<|\"|>' -}}\n {%- else -%}\n {{- key -}}\n {%- endif -%}\n :{{- format_argument(value, escape_keys=escape_keys) -}}\n {%- endfor -%}\n {{- '}' -}}\n {%- elif argument is sequence -%}\n {{- '[' -}}\n {%- for item in argument -%}\n {{- format_argument(item, escape_keys=escape_keys) -}}\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n {{- ']' -}}\n {%- else -%}\n {{- argument -}}\n {%- endif -%}\n{%- endmacro -%}\n{%- macro strip_thinking(text) -%}\n {%- set ns = namespace(result='') -%}\n {%- for part in text.split('<channel|>') -%}\n {%- if '<|channel>' in part -%}\n {%- set ns.result = ns.result + part.split('<|channel>')[0] -%}\n {%- else -%}\n {%- set ns.result = ns.result + part -%}\n {%- endif -%}\n {%- endfor -%}\n {{- ns.result | trim -}}\n{%- endmacro -%}\n\n{%- macro format_tool_response_block(tool_name, response) -%}\n {{- '<|tool_response>' -}}\n {%- if response is mapping -%}\n {{- 'response:' + tool_name + '{' -}}\n {%- for key, value in response | dictsort -%}\n {{- key -}}:{{- format_argument(value, escape_keys=False) -}}\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n {{- '}' -}}\n {%- else -%}\n {{- 'response:' + tool_name + '{value:' + format_argument(response, escape_keys=False) + '}' -}}\n {%- endif -%}\n {{- '<tool_response|>' -}}\n{%- endmacro -%}\n\n{#- ===== SETUP ===== -#}\n{%- set ns = namespace(prev_message_type=None, prev_non_tool_role=None) -%}\n{%- set loop_messages = messages -%}\n{%- set enable_thinking = enable_thinking | default(false) -%}\n{%- set preserve_thinking = preserve_thinking | default(false) -%}\n{{- bos_token -}}\n{#- Handle System/Tool Definitions Block -#}\n{%- if enable_thinking or tools or (messages and messages[0]['role'] in ['system', 'developer']) -%}\n {{- '<|turn>system\\n' -}}\n {#- Inject Thinking token at the very top of the FIRST system turn -#}\n {%- if enable_thinking -%}\n {{- '<|think|>\\n' -}}\n {%- set ns.prev_message_type = 'think' -%}\n {%- endif -%}\n {%- if messages and messages[0]['role'] in ['system', 'developer'] -%}\n {%- if messages[0]['content'] is string -%}\n {{- messages[0]['content'] | trim -}}\n {%- elif messages[0]['content'] is sequence -%}\n {%- for item in messages[0]['content'] -%}\n {{- item['text'] | trim + ' '-}}\n {%- endfor -%}\n {%- endif -%}\n {%- set loop_messages = messages[1:] -%}\n {%- endif -%}\n {%- if tools -%}\n {%- for tool in tools %}\n {{- '<|tool>' -}}\n {{- format_function_declaration(tool) | trim -}}\n {{- '<tool|>' -}}\n {%- endfor %}\n {%- set ns.prev_message_type = 'tool' -%}\n {%- endif -%}\n {{- '<turn|>\\n' -}}\n{%- endif %}\n\n{#- Pre-scan: find last user message index for reasoning guard -#}\n{%- set ns_turn = namespace(last_user_idx=-1) -%}\n{%- for i in range(loop_messages | length) -%}\n {%- if loop_messages[i]['role'] == 'user' -%}\n {%- set ns_turn.last_user_idx = i -%}\n {%- endif -%}\n{%- endfor -%}\n\n{#- Loop through messages -#}\n{%- for message in loop_messages -%}\n {%- if message['role'] != 'tool' -%}\n {%- set ns.prev_message_type = None -%}\n {%- set role = 'model' if message['role'] == 'assistant' else message['role'] -%}\n {#- Detect continuation using tracked state — O(1) instead of O(n) backward scan -#}\n {%- set continue_same_model_turn = (role == 'model' and ns.prev_non_tool_role == 'assistant') -%}\n {%- if not continue_same_model_turn -%}\n {{- '<|turn>' + role + '\\n' }}\n\n {%- endif -%}\n\n {#- Render reasoning/reasoning_content as thinking channel -#}\n {%- set thinking_text = message.get('reasoning') or message.get('reasoning_content') -%}\n {%- set thinking_gate = (loop.index0 > ns_turn.last_user_idx) or (preserve_thinking and message.get('tool_calls')) -%}\n {%- if thinking_text and thinking_gate -%}\n {{- '<|channel>thought\\n' + thinking_text + '\\n<channel|>' -}}\n {%- endif -%}\n\n {%- if message.get('tool_calls') -%}\n {%- for tool_call in message.get('tool_calls') -%}\n {%- set function = tool_call['function'] -%}\n {{- '<|tool_call>call:' + function['name'] + '{' -}}\n {%- if function['arguments'] is mapping -%}\n {%- set ns_args = namespace(found_first=false) -%}\n {%- for key, value in function['arguments'] | dictsort -%}\n {%- if ns_args.found_first %},{% endif -%}\n {%- set ns_args.found_first = true -%}\n {{- key -}}:{{- format_argument(value, escape_keys=False) -}}\n {%- endfor -%}\n {%- elif function['arguments'] is none -%}\n {%- else -%}\n {{- raise_exception(\n \"chat_template: tool_calls[].function.arguments must be a \"\n \"JSON object (mapping), not a string. Deserialize arguments \"\n \"before passing to the template.\"\n ) -}}\n {%- endif -%}\n {{- '}<tool_call|>' -}}\n {%- endfor -%}\n {%- set ns.prev_message_type = 'tool_call' -%}\n {%- endif -%}\n\n {%- set ns_tr_out = namespace(flag=false) -%}\n {%- if message.get('tool_responses') -%}\n {#- Legacy: tool_responses embedded on the assistant message (Google/Gemma native) -#}\n {%- for tool_response in message.get('tool_responses') -%}\n {{- format_tool_response_block(tool_response['name'] | default('unknown', true), tool_response['response']) -}}\n {%- set ns_tr_out.flag = true -%}\n {%- set ns.prev_message_type = 'tool_response' -%}\n {%- endfor -%}\n {%- elif message.get('tool_calls') -%}\n {#- OpenAI Chat Completions: forward-scan consecutive role:tool messages -#}\n {%- set ns_tool_scan = namespace(stopped=false) -%}\n {%- for k in range(loop.index0 + 1, loop_messages | length) -%}\n {%- if ns_tool_scan.stopped -%}\n {%- elif loop_messages[k]['role'] != 'tool' -%}\n {%- set ns_tool_scan.stopped = true -%}\n {%- else -%}\n {%- set follow = loop_messages[k] -%}\n {#- Resolve tool_call_id to function name -#}\n {%- set ns_tname = namespace(name=follow.get('name') or 'unknown') -%}\n {%- for tc in message.get('tool_calls') -%}\n {%- if tc.get('id') == follow.get('tool_call_id') -%}\n {%- set ns_tname.name = tc['function']['name'] -%}\n {%- endif -%}\n {%- endfor -%}\n {#- Handle content as string or content-parts array -#}\n {%- set tool_body = follow.get('content') -%}\n {%- if tool_body is string -%}\n {{- format_tool_response_block(ns_tname.name, tool_body) -}}\n {%- elif tool_body is sequence and tool_body is not string -%}\n {%- set ns_txt = namespace(s='') -%}\n {%- for part in tool_body -%}\n {%- if part.get('type') == 'text' -%}\n {%- set ns_txt.s = ns_txt.s + (part.get('text') | default('')) -%}\n {%- endif -%}\n {%- endfor -%}\n {{- format_tool_response_block(ns_tname.name, ns_txt.s) -}}\n {%- for part in tool_body -%}\n {%- if part.get('type') in ['image', 'image_url'] -%}\n {{- '<|image|>' -}}\n {%- elif part.get('type') in ['audio', 'input_audio'] -%}\n {{- '<|audio|>' -}}\n {%- elif part.get('type') == 'video' -%}\n {{- '<|video|>' -}}\n {%- endif -%}\n {%- endfor -%}\n {%- else -%}\n {{- format_tool_response_block(ns_tname.name, tool_body) -}}\n {%- endif -%}\n {%- set ns_tr_out.flag = true -%}\n {%- set ns.prev_message_type = 'tool_response' -%}\n {%- endif -%}\n {%- endfor -%}\n {%- endif -%}\n\n {%- set captured_content -%}\n {%- if message.get('content') is string -%}\n {%- if role == 'model' -%}\n {{- strip_thinking(message['content']) -}}\n {%- else -%}\n {{- message['content'] | trim -}}\n {%- endif -%}\n {%- elif message.get('content') is sequence -%}\n {%- for item in message['content'] -%}\n {%- if item.get('type') == 'text' -%}\n {%- if role == 'model' -%}\n {{- strip_thinking(item['text']) -}}\n {%- else -%}\n {{- item['text'] | trim -}}\n {%- endif -%}\n {%- elif item.get('type') in ['image', 'image_url'] -%}\n {{- '<|image|>' -}}\n {%- elif item.get('type') in ['audio', 'input_audio'] -%}\n {{- '<|audio|>' -}}\n {%- elif item.get('type') == 'video' -%}\n {{- '<|video|>' -}}\n {%- endif -%}\n {%- endfor -%}\n {%- endif -%}\n {%- endset -%}\n\n {{- captured_content -}}\n {%- set has_content = captured_content | trim | length > 0 -%}\n\n {#- Forward-scan: find next non-tool message role for continuation detection -#}\n {%- set next_nt = namespace(role=None, found=false) -%}\n {%- for j in range(loop.index0 + 1, loop_messages | length) -%}\n {%- if not next_nt.found -%}\n {%- if loop_messages[j]['role'] != 'tool' -%}\n {%- set next_nt.role = loop_messages[j]['role'] -%}\n {%- set next_nt.found = true -%}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n\n {%- set continues_into_next = (\n role == 'model'\n and next_nt.role == 'assistant'\n and (not message.get('tool_calls') or ns_tr_out.flag)\n ) -%}\n\n {%- if ns.prev_message_type == 'tool_call' and not ns_tr_out.flag -%}\n {{- '<|tool_response>' -}}\n {%- elif continues_into_next -%}\n {%- elif not (ns_tr_out.flag and not has_content and not next_nt.found) -%}\n {{- '<turn|>\\n' -}}\n {%- endif -%}\n\n {#- Track previous non-tool role for next iteration (avoids O(n) backward scan) -#}\n {%- set ns.prev_non_tool_role = message['role'] -%}\n {%- endif -%}\n{%- endfor -%}\n\n{%- if add_generation_prompt -%}\n {%- if ns.prev_message_type != 'tool_response' and ns.prev_message_type != 'tool_call' -%}\n {{- '<|turn>model\\n' -}}\n {%- if not enable_thinking -%}\n {{- '<|channel>thought\\n<channel|>' -}}\n {%- endif -%}\n {%- elif ns.prev_message_type == 'tool_response' and enable_thinking -%}\n {{- '<|channel>thought\\n' -}}\n {%- endif -%}\n{%- endif -%}\n"
227
248
  }
228
249
  }
@@ -0,0 +1,98 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "version": "1.2.0",
4
+ "releasedAt": "2026-08-28T00:00:00Z",
5
+ "approxSizeBytes": 6716356800,
6
+ "llamaCpp": {
7
+ "huggingfaceRepo": "unsloth/gemma-4-12B-it-qat-GGUF",
8
+ "revision": "980b060c40a8539ac159e0501a3e0f66a6365af3",
9
+ "filename": "gemma-4-12B-it-qat-UD-Q4_K_XL.gguf",
10
+ "sha256": "90fd44e29e0d7cffeb0fd00dc73cfdab9ed0b0e95306ecf7821ea634c940c370",
11
+ "approxSizeBytes": 6716356800,
12
+ "quantization": "UD-Q4_K_XL",
13
+ "draftModel": {
14
+ "filename": "MTP/mtp-gemma-4-12B-it-Q4_0.gguf",
15
+ "sha256": "fcb35dea42c71333db904cee11baac525c9ef872818ee3753f6cb156f3c6f4f6",
16
+ "sizeBytes": 253708800
17
+ }
18
+ },
19
+ "mlx": {
20
+ "huggingfaceRepo": "OsaurusAI/gemma-4-12B-it-qat-MXFP4",
21
+ "revision": "61fbdd1be971d8808060d8ba4e607f420125b214",
22
+ "quantization": "MXFP4",
23
+ "approxSizeBytes": 7942602298,
24
+ "files": [
25
+ {
26
+ "name": "chat_template.jinja",
27
+ "sha256": "d5ce49ce670058e90cc623c0284c21c8fcd778c30f5be1891b00f022bedee2fe",
28
+ "sizeBytes": 17811
29
+ },
30
+ {
31
+ "name": "config.json",
32
+ "sha256": "7052f31177bb79e2c6760d5032583f74c1864eb51b5a17e1404a10eca3acf913",
33
+ "sizeBytes": 5237
34
+ },
35
+ {
36
+ "name": "generation_config.json",
37
+ "sha256": "a8349d9bd64cc5841297fcb5002f0fdc4749c473c8f1b10ea337f9ce4ee7014e",
38
+ "sizeBytes": 260
39
+ },
40
+ {
41
+ "name": "model-00001-of-00007.safetensors",
42
+ "sha256": "8bbc6b27a2127ca6d68a449ff895644cd18477cc69205c4cd02907b862c4cf11",
43
+ "sizeBytes": 2047672664
44
+ },
45
+ {
46
+ "name": "model-00002-of-00007.safetensors",
47
+ "sha256": "5794202f68df90c7f18a6db80a9a273235146f1d1b05f632e7df1ed1644996a7",
48
+ "sizeBytes": 1023076042
49
+ },
50
+ {
51
+ "name": "model-00003-of-00007.safetensors",
52
+ "sha256": "299ff6ced2b6b8e4d0957bc4b4f26e7fbd25b3917bc0a9f27dd8d983449241b9",
53
+ "sizeBytes": 1012218216
54
+ },
55
+ {
56
+ "name": "model-00004-of-00007.safetensors",
57
+ "sha256": "1f0e6f655d2a59d2e3b0d987f705e498be535f6c9782c9726c3e37ede1c55545",
58
+ "sizeBytes": 1009437650
59
+ },
60
+ {
61
+ "name": "model-00005-of-00007.safetensors",
62
+ "sha256": "06d4b763d2bba8277e78d6a1bcd2a623190956da8adfb6d7e2fdd3234bc213a8",
63
+ "sizeBytes": 1024911496
64
+ },
65
+ {
66
+ "name": "model-00006-of-00007.safetensors",
67
+ "sha256": "ecdb96e03cba6751e3d73d621019afd20e4b9040162997e1e236e7231793370a",
68
+ "sizeBytes": 1018677986
69
+ },
70
+ {
71
+ "name": "model-00007-of-00007.safetensors",
72
+ "sha256": "1aa3cfd784f63827f6ec9eb0b2a98f735976deb62ead0a54b87f33aba7f96176",
73
+ "sizeBytes": 774295738
74
+ },
75
+ {
76
+ "name": "model.safetensors.index.json",
77
+ "sha256": "136ff8d0fb292d5cd6688d7a5456b7710a2d4628c53947b45180e3dc654697c2",
78
+ "sizeBytes": 97771
79
+ },
80
+ {
81
+ "name": "processor_config.json",
82
+ "sha256": "6b938e76555b3e9946890770e1abcd442a4718f34041a58e8139dc8ad34545c9",
83
+ "sizeBytes": 1382
84
+ },
85
+ {
86
+ "name": "tokenizer.json",
87
+ "sha256": "cc8d3a0ce36466ccc1278bf987df5f71db1719b9ca6b4118264f45cb627bfe0f",
88
+ "sizeBytes": 32169626
89
+ },
90
+ {
91
+ "name": "tokenizer_config.json",
92
+ "sha256": "c0146b8149eb8b8f96e9b92b9a0d5e0d1864140b49a4d13cec52bc9adcecb5a7",
93
+ "sizeBytes": 20419
94
+ }
95
+ ],
96
+ "chatTemplateOverride": "{#\n Template: Google Gemma 4 Canonical Chat Template\n Author: Google Gemma Engineering Team\n Published: 2026-07-09\n Context: Fixed tool-calling loops, turn closures, and thinking content-ordering.\n#}\n{%- macro format_parameters(properties, required, filter_keys=false) -%}\n {%- set standard_keys = ['description', 'type', 'properties', 'required', 'nullable'] -%}\n {%- set ns = namespace(found_first=false) -%}\n {%- for key, value in properties | dictsort -%}\n {%- set add_comma = false -%}\n {%- if not filter_keys or key not in standard_keys -%}\n {%- if ns.found_first %},{% endif -%}\n {%- set ns.found_first = true -%}\n {{ key }}:{\n {%- if value['description'] -%}\n description:<|\"|>{{ value['description'] }}<|\"|>\n {%- set add_comma = true -%}\n {%- endif -%}\n {%- if value['type'] | upper == 'STRING' -%}\n {%- if value['enum'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n enum:{{ format_argument(value['enum']) }}\n {%- endif -%}\n {%- elif value['type'] | upper == 'ARRAY' -%}\n {%- if value['items'] is mapping and value['items'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n items:{\n {%- set ns_items = namespace(found_first=false) -%}\n {%- for item_key, item_value in value['items'] | dictsort -%}\n {%- if item_value is not none -%}\n {%- if ns_items.found_first %},{% endif -%}\n {%- set ns_items.found_first = true -%}\n {%- if item_key == 'properties' -%}\n properties:{\n {%- if item_value is mapping -%}\n {{- format_parameters(item_value, value['items']['required'] | default([])) -}}\n {%- endif -%}\n }\n {%- elif item_key == 'required' -%}\n required:[\n {%- for req_item in item_value -%}\n <|\"|>{{- req_item -}}<|\"|>\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n ]\n {%- elif item_key == 'type' -%}\n {%- if item_value is string -%}\n type:{{ format_argument(item_value | upper) }}\n {%- else -%}\n type:{{ format_argument(item_value | map('upper') | list) }}\n {%- endif -%}\n {%- else -%}\n {{ item_key }}:{{ format_argument(item_value) }}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n }\n {%- endif -%}\n {%- endif -%}\n {%- if value['nullable'] %}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n nullable:true\n {%- endif -%}\n {%- if value['type'] | upper == 'OBJECT' -%}\n {%- if value['properties'] is defined and value['properties'] is mapping -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n properties:{\n {{- format_parameters(value['properties'], value['required'] | default([])) -}}\n }\n {%- elif value is mapping -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n properties:{\n {{- format_parameters(value, value['required'] | default([]), filter_keys=true) -}}\n }\n {%- endif -%}\n {%- if value['required'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n required:[\n {%- for item in value['required'] | default([]) -%}\n <|\"|>{{- item -}}<|\"|>\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n ]\n {%- endif -%}\n {%- endif -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n type:<|\"|>{{ value['type'] | upper }}<|\"|>}\n {%- endif -%}\n {%- endfor -%}\n{%- endmacro -%}\n{%- macro format_function_declaration(tool_data) -%}\n declaration:{{- tool_data['function']['name'] -}}{description:<|\"|>{{- tool_data['function']['description'] -}}<|\"|>\n {%- set params = tool_data['function']['parameters'] -%}\n {%- if params -%}\n ,parameters:{\n {%- if params['properties'] -%}\n properties:{ {{- format_parameters(params['properties'], params['required']) -}} },\n {%- endif -%}\n {%- if params['required'] -%}\n required:[\n {%- for item in params['required'] -%}\n <|\"|>{{- item -}}<|\"|>\n {{- ',' if not loop.last -}}\n {%- endfor -%}\n ],\n {%- endif -%}\n {%- if params['type'] -%}\n type:<|\"|>{{- params['type'] | upper -}}<|\"|>}\n {%- endif -%}\n {%- endif -%}\n {%- if 'response' in tool_data['function'] -%}\n {%- set response_declaration = tool_data['function']['response'] -%}\n ,response:{\n {%- if response_declaration['description'] -%}\n description:<|\"|>{{- response_declaration['description'] -}}<|\"|>,\n {%- endif -%}\n {%- if response_declaration['type'] | upper == 'OBJECT' -%}\n type:<|\"|>{{- response_declaration['type'] | upper -}}<|\"|>}\n {%- endif -%}\n {%- endif -%}\n }\n{%- endmacro -%}\n{%- macro format_argument(argument, escape_keys=True) -%}\n {%- if argument is none -%}\n {{- 'null' -}}\n {%- elif argument is string -%}\n {{- '<|\"|>' + argument + '<|\"|>' -}}\n {%- elif argument is boolean -%}\n {{- 'true' if argument else 'false' -}}\n {%- elif argument is mapping -%}\n {{- '{' -}}\n {%- set ns = namespace(found_first=false) -%}\n {%- for key, value in argument | dictsort -%}\n {%- if ns.found_first %},{% endif -%}\n {%- set ns.found_first = true -%}\n {%- if escape_keys -%}\n {{- '<|\"|>' + key + '<|\"|>' -}}\n {%- else -%}\n {{- key -}}\n {%- endif -%}\n :{{- format_argument(value, escape_keys=escape_keys) -}}\n {%- endfor -%}\n {{- '}' -}}\n {%- elif argument is sequence -%}\n {{- '[' -}}\n {%- for item in argument -%}\n {{- format_argument(item, escape_keys=escape_keys) -}}\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n {{- ']' -}}\n {%- else -%}\n {{- argument -}}\n {%- endif -%}\n{%- endmacro -%}\n{%- macro strip_thinking(text) -%}\n {%- set ns = namespace(result='') -%}\n {%- for part in text.split('<channel|>') -%}\n {%- if '<|channel>' in part -%}\n {%- set ns.result = ns.result + part.split('<|channel>')[0] -%}\n {%- else -%}\n {%- set ns.result = ns.result + part -%}\n {%- endif -%}\n {%- endfor -%}\n {{- ns.result | trim -}}\n{%- endmacro -%}\n\n{%- macro format_tool_response_block(tool_name, response) -%}\n {{- '<|tool_response>' -}}\n {%- if response is mapping -%}\n {{- 'response:' + tool_name + '{' -}}\n {%- for key, value in response | dictsort -%}\n {{- key -}}:{{- format_argument(value, escape_keys=False) -}}\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n {{- '}' -}}\n {%- else -%}\n {{- 'response:' + tool_name + '{value:' + format_argument(response, escape_keys=False) + '}' -}}\n {%- endif -%}\n {{- '<tool_response|>' -}}\n{%- endmacro -%}\n\n{#- ===== SETUP ===== -#}\n{%- set ns = namespace(prev_message_type=None, prev_non_tool_role=None) -%}\n{%- set loop_messages = messages -%}\n{%- set enable_thinking = enable_thinking | default(false) -%}\n{%- set preserve_thinking = preserve_thinking | default(false) -%}\n{{- bos_token -}}\n{#- Handle System/Tool Definitions Block -#}\n{%- if enable_thinking or tools or (messages and messages[0]['role'] in ['system', 'developer']) -%}\n {{- '<|turn>system\\n' -}}\n {#- Inject Thinking token at the very top of the FIRST system turn -#}\n {%- if enable_thinking -%}\n {{- '<|think|>\\n' -}}\n {%- set ns.prev_message_type = 'think' -%}\n {%- endif -%}\n {%- if messages and messages[0]['role'] in ['system', 'developer'] -%}\n {%- if messages[0]['content'] is string -%}\n {{- messages[0]['content'] | trim -}}\n {%- elif messages[0]['content'] is sequence -%}\n {%- for item in messages[0]['content'] -%}\n {{- item['text'] | trim + ' '-}}\n {%- endfor -%}\n {%- endif -%}\n {%- set loop_messages = messages[1:] -%}\n {%- endif -%}\n {%- if tools -%}\n {%- for tool in tools %}\n {{- '<|tool>' -}}\n {{- format_function_declaration(tool) | trim -}}\n {{- '<tool|>' -}}\n {%- endfor %}\n {%- set ns.prev_message_type = 'tool' -%}\n {%- endif -%}\n {{- '<turn|>\\n' -}}\n{%- endif %}\n\n{#- Pre-scan: find last user message index for reasoning guard -#}\n{%- set ns_turn = namespace(last_user_idx=-1) -%}\n{%- for i in range(loop_messages | length) -%}\n {%- if loop_messages[i]['role'] == 'user' -%}\n {%- set ns_turn.last_user_idx = i -%}\n {%- endif -%}\n{%- endfor -%}\n\n{#- Loop through messages -#}\n{%- for message in loop_messages -%}\n {%- if message['role'] != 'tool' -%}\n {%- set ns.prev_message_type = None -%}\n {%- set role = 'model' if message['role'] == 'assistant' else message['role'] -%}\n {#- Detect continuation using tracked state — O(1) instead of O(n) backward scan -#}\n {%- set continue_same_model_turn = (role == 'model' and ns.prev_non_tool_role == 'assistant') -%}\n {%- if not continue_same_model_turn -%}\n {{- '<|turn>' + role + '\\n' }}\n\n {%- endif -%}\n\n {#- Render reasoning/reasoning_content as thinking channel -#}\n {%- set thinking_text = message.get('reasoning') or message.get('reasoning_content') -%}\n {%- set thinking_gate = (loop.index0 > ns_turn.last_user_idx) or (preserve_thinking and message.get('tool_calls')) -%}\n {%- if thinking_text and thinking_gate -%}\n {{- '<|channel>thought\\n' + thinking_text + '\\n<channel|>' -}}\n {%- endif -%}\n\n {%- if message.get('tool_calls') -%}\n {%- for tool_call in message.get('tool_calls') -%}\n {%- set function = tool_call['function'] -%}\n {{- '<|tool_call>call:' + function['name'] + '{' -}}\n {%- if function['arguments'] is mapping -%}\n {%- set ns_args = namespace(found_first=false) -%}\n {%- for key, value in function['arguments'] | dictsort -%}\n {%- if ns_args.found_first %},{% endif -%}\n {%- set ns_args.found_first = true -%}\n {{- key -}}:{{- format_argument(value, escape_keys=False) -}}\n {%- endfor -%}\n {%- elif function['arguments'] is none -%}\n {%- else -%}\n {{- raise_exception(\n \"chat_template: tool_calls[].function.arguments must be a \"\n \"JSON object (mapping), not a string. Deserialize arguments \"\n \"before passing to the template.\"\n ) -}}\n {%- endif -%}\n {{- '}<tool_call|>' -}}\n {%- endfor -%}\n {%- set ns.prev_message_type = 'tool_call' -%}\n {%- endif -%}\n\n {%- set ns_tr_out = namespace(flag=false) -%}\n {%- if message.get('tool_responses') -%}\n {#- Legacy: tool_responses embedded on the assistant message (Google/Gemma native) -#}\n {%- for tool_response in message.get('tool_responses') -%}\n {{- format_tool_response_block(tool_response['name'] | default('unknown', true), tool_response['response']) -}}\n {%- set ns_tr_out.flag = true -%}\n {%- set ns.prev_message_type = 'tool_response' -%}\n {%- endfor -%}\n {%- elif message.get('tool_calls') -%}\n {#- OpenAI Chat Completions: forward-scan consecutive role:tool messages -#}\n {%- set ns_tool_scan = namespace(stopped=false) -%}\n {%- for k in range(loop.index0 + 1, loop_messages | length) -%}\n {%- if ns_tool_scan.stopped -%}\n {%- elif loop_messages[k]['role'] != 'tool' -%}\n {%- set ns_tool_scan.stopped = true -%}\n {%- else -%}\n {%- set follow = loop_messages[k] -%}\n {#- Resolve tool_call_id to function name -#}\n {%- set ns_tname = namespace(name=follow.get('name') or 'unknown') -%}\n {%- for tc in message.get('tool_calls') -%}\n {%- if tc.get('id') == follow.get('tool_call_id') -%}\n {%- set ns_tname.name = tc['function']['name'] -%}\n {%- endif -%}\n {%- endfor -%}\n {#- Handle content as string or content-parts array -#}\n {%- set tool_body = follow.get('content') -%}\n {%- if tool_body is string -%}\n {{- format_tool_response_block(ns_tname.name, tool_body) -}}\n {%- elif tool_body is sequence and tool_body is not string -%}\n {%- set ns_txt = namespace(s='') -%}\n {%- for part in tool_body -%}\n {%- if part.get('type') == 'text' -%}\n {%- set ns_txt.s = ns_txt.s + (part.get('text') | default('')) -%}\n {%- endif -%}\n {%- endfor -%}\n {{- format_tool_response_block(ns_tname.name, ns_txt.s) -}}\n {%- for part in tool_body -%}\n {%- if part.get('type') in ['image', 'image_url'] -%}\n {{- '<|image|>' -}}\n {%- elif part.get('type') in ['audio', 'input_audio'] -%}\n {{- '<|audio|>' -}}\n {%- elif part.get('type') == 'video' -%}\n {{- '<|video|>' -}}\n {%- endif -%}\n {%- endfor -%}\n {%- else -%}\n {{- format_tool_response_block(ns_tname.name, tool_body) -}}\n {%- endif -%}\n {%- set ns_tr_out.flag = true -%}\n {%- set ns.prev_message_type = 'tool_response' -%}\n {%- endif -%}\n {%- endfor -%}\n {%- endif -%}\n\n {%- set captured_content -%}\n {%- if message.get('content') is string -%}\n {%- if role == 'model' -%}\n {{- strip_thinking(message['content']) -}}\n {%- else -%}\n {{- message['content'] | trim -}}\n {%- endif -%}\n {%- elif message.get('content') is sequence -%}\n {%- for item in message['content'] -%}\n {%- if item.get('type') == 'text' -%}\n {%- if role == 'model' -%}\n {{- strip_thinking(item['text']) -}}\n {%- else -%}\n {{- item['text'] | trim -}}\n {%- endif -%}\n {%- elif item.get('type') in ['image', 'image_url'] -%}\n {{- '<|image|>' -}}\n {%- elif item.get('type') in ['audio', 'input_audio'] -%}\n {{- '<|audio|>' -}}\n {%- elif item.get('type') == 'video' -%}\n {{- '<|video|>' -}}\n {%- endif -%}\n {%- endfor -%}\n {%- endif -%}\n {%- endset -%}\n\n {{- captured_content -}}\n {%- set has_content = captured_content | trim | length > 0 -%}\n\n {#- Forward-scan: find next non-tool message role for continuation detection -#}\n {%- set next_nt = namespace(role=None, found=false) -%}\n {%- for j in range(loop.index0 + 1, loop_messages | length) -%}\n {%- if not next_nt.found -%}\n {%- if loop_messages[j]['role'] != 'tool' -%}\n {%- set next_nt.role = loop_messages[j]['role'] -%}\n {%- set next_nt.found = true -%}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n\n {%- set continues_into_next = (\n role == 'model'\n and next_nt.role == 'assistant'\n and (not message.get('tool_calls') or ns_tr_out.flag)\n ) -%}\n\n {%- if ns.prev_message_type == 'tool_call' and not ns_tr_out.flag -%}\n {{- '<|tool_response>' -}}\n {%- elif continues_into_next -%}\n {%- elif not (ns_tr_out.flag and not has_content and not next_nt.found) -%}\n {{- '<turn|>\\n' -}}\n {%- endif -%}\n\n {#- Track previous non-tool role for next iteration (avoids O(n) backward scan) -#}\n {%- set ns.prev_non_tool_role = message['role'] -%}\n {%- endif -%}\n{%- endfor -%}\n\n{%- if add_generation_prompt -%}\n {%- if ns.prev_message_type != 'tool_response' and ns.prev_message_type != 'tool_call' -%}\n {{- '<|turn>model\\n' -}}\n {%- if not enable_thinking -%}\n {{- '<|channel>thought\\n<channel|>' -}}\n {%- endif -%}\n {%- elif ns.prev_message_type == 'tool_response' and enable_thinking -%}\n {{- '<|channel>thought\\n' -}}\n {%- endif -%}\n{%- endif -%}\n"
97
+ }
98
+ }
@@ -17,8 +17,8 @@
17
17
  "name": "Google",
18
18
  "url": "https://huggingface.co/google/gemma-4-12B-it"
19
19
  },
20
- "version": "1.0.3",
21
- "updatedAt": "2026-08-15T00:00:00Z",
20
+ "version": "1.1.0",
21
+ "updatedAt": "2026-08-28T00:00:00Z",
22
22
  "license": "Apache-2.0",
23
23
  "licenseClass": "open",
24
24
  "licenseShortName": "Apache 2.0",
@@ -155,7 +155,7 @@
155
155
  },
156
156
  "llamaCpp": {
157
157
  "huggingfaceRepo": "ggml-org/gemma-4-12B-it-GGUF",
158
- "revision": "7e0fbb8205d1f4857f4606a38a65023aaeb5f544",
158
+ "revision": "e3e681731089efaa3f0917336944ac64752db8ba",
159
159
  "filename": "gemma-4-12B-it-Q8_0.gguf",
160
160
  "sha256": "abfc3044b93795f6a446fc1a04468ac2d5fc5e9c92e237f09a5b3384c5a433e4",
161
161
  "approxSizeBytes": 12669646976,
@@ -222,6 +222,7 @@
222
222
  "sha256": "fc1384a911d2c9860ac07bc3ceafff20bff26695991744b7dbe5e1e4522bfa57",
223
223
  "sizeBytes": 2719
224
224
  }
225
- ]
225
+ ],
226
+ "chatTemplateOverride": "{#\n Template: Google Gemma 4 Canonical Chat Template\n Author: Google Gemma Engineering Team\n Published: 2026-07-09\n Context: Fixed tool-calling loops, turn closures, and thinking content-ordering.\n#}\n{%- macro format_parameters(properties, required, filter_keys=false) -%}\n {%- set standard_keys = ['description', 'type', 'properties', 'required', 'nullable'] -%}\n {%- set ns = namespace(found_first=false) -%}\n {%- for key, value in properties | dictsort -%}\n {%- set add_comma = false -%}\n {%- if not filter_keys or key not in standard_keys -%}\n {%- if ns.found_first %},{% endif -%}\n {%- set ns.found_first = true -%}\n {{ key }}:{\n {%- if value['description'] -%}\n description:<|\"|>{{ value['description'] }}<|\"|>\n {%- set add_comma = true -%}\n {%- endif -%}\n {%- if value['type'] | upper == 'STRING' -%}\n {%- if value['enum'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n enum:{{ format_argument(value['enum']) }}\n {%- endif -%}\n {%- elif value['type'] | upper == 'ARRAY' -%}\n {%- if value['items'] is mapping and value['items'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n items:{\n {%- set ns_items = namespace(found_first=false) -%}\n {%- for item_key, item_value in value['items'] | dictsort -%}\n {%- if item_value is not none -%}\n {%- if ns_items.found_first %},{% endif -%}\n {%- set ns_items.found_first = true -%}\n {%- if item_key == 'properties' -%}\n properties:{\n {%- if item_value is mapping -%}\n {{- format_parameters(item_value, value['items']['required'] | default([])) -}}\n {%- endif -%}\n }\n {%- elif item_key == 'required' -%}\n required:[\n {%- for req_item in item_value -%}\n <|\"|>{{- req_item -}}<|\"|>\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n ]\n {%- elif item_key == 'type' -%}\n {%- if item_value is string -%}\n type:{{ format_argument(item_value | upper) }}\n {%- else -%}\n type:{{ format_argument(item_value | map('upper') | list) }}\n {%- endif -%}\n {%- else -%}\n {{ item_key }}:{{ format_argument(item_value) }}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n }\n {%- endif -%}\n {%- endif -%}\n {%- if value['nullable'] %}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n nullable:true\n {%- endif -%}\n {%- if value['type'] | upper == 'OBJECT' -%}\n {%- if value['properties'] is defined and value['properties'] is mapping -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n properties:{\n {{- format_parameters(value['properties'], value['required'] | default([])) -}}\n }\n {%- elif value is mapping -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n properties:{\n {{- format_parameters(value, value['required'] | default([]), filter_keys=true) -}}\n }\n {%- endif -%}\n {%- if value['required'] -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n required:[\n {%- for item in value['required'] | default([]) -%}\n <|\"|>{{- item -}}<|\"|>\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n ]\n {%- endif -%}\n {%- endif -%}\n {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}\n type:<|\"|>{{ value['type'] | upper }}<|\"|>}\n {%- endif -%}\n {%- endfor -%}\n{%- endmacro -%}\n{%- macro format_function_declaration(tool_data) -%}\n declaration:{{- tool_data['function']['name'] -}}{description:<|\"|>{{- tool_data['function']['description'] -}}<|\"|>\n {%- set params = tool_data['function']['parameters'] -%}\n {%- if params -%}\n ,parameters:{\n {%- if params['properties'] -%}\n properties:{ {{- format_parameters(params['properties'], params['required']) -}} },\n {%- endif -%}\n {%- if params['required'] -%}\n required:[\n {%- for item in params['required'] -%}\n <|\"|>{{- item -}}<|\"|>\n {{- ',' if not loop.last -}}\n {%- endfor -%}\n ],\n {%- endif -%}\n {%- if params['type'] -%}\n type:<|\"|>{{- params['type'] | upper -}}<|\"|>}\n {%- endif -%}\n {%- endif -%}\n {%- if 'response' in tool_data['function'] -%}\n {%- set response_declaration = tool_data['function']['response'] -%}\n ,response:{\n {%- if response_declaration['description'] -%}\n description:<|\"|>{{- response_declaration['description'] -}}<|\"|>,\n {%- endif -%}\n {%- if response_declaration['type'] | upper == 'OBJECT' -%}\n type:<|\"|>{{- response_declaration['type'] | upper -}}<|\"|>}\n {%- endif -%}\n {%- endif -%}\n }\n{%- endmacro -%}\n{%- macro format_argument(argument, escape_keys=True) -%}\n {%- if argument is none -%}\n {{- 'null' -}}\n {%- elif argument is string -%}\n {{- '<|\"|>' + argument + '<|\"|>' -}}\n {%- elif argument is boolean -%}\n {{- 'true' if argument else 'false' -}}\n {%- elif argument is mapping -%}\n {{- '{' -}}\n {%- set ns = namespace(found_first=false) -%}\n {%- for key, value in argument | dictsort -%}\n {%- if ns.found_first %},{% endif -%}\n {%- set ns.found_first = true -%}\n {%- if escape_keys -%}\n {{- '<|\"|>' + key + '<|\"|>' -}}\n {%- else -%}\n {{- key -}}\n {%- endif -%}\n :{{- format_argument(value, escape_keys=escape_keys) -}}\n {%- endfor -%}\n {{- '}' -}}\n {%- elif argument is sequence -%}\n {{- '[' -}}\n {%- for item in argument -%}\n {{- format_argument(item, escape_keys=escape_keys) -}}\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n {{- ']' -}}\n {%- else -%}\n {{- argument -}}\n {%- endif -%}\n{%- endmacro -%}\n{%- macro strip_thinking(text) -%}\n {%- set ns = namespace(result='') -%}\n {%- for part in text.split('<channel|>') -%}\n {%- if '<|channel>' in part -%}\n {%- set ns.result = ns.result + part.split('<|channel>')[0] -%}\n {%- else -%}\n {%- set ns.result = ns.result + part -%}\n {%- endif -%}\n {%- endfor -%}\n {{- ns.result | trim -}}\n{%- endmacro -%}\n\n{%- macro format_tool_response_block(tool_name, response) -%}\n {{- '<|tool_response>' -}}\n {%- if response is mapping -%}\n {{- 'response:' + tool_name + '{' -}}\n {%- for key, value in response | dictsort -%}\n {{- key -}}:{{- format_argument(value, escape_keys=False) -}}\n {%- if not loop.last %},{% endif -%}\n {%- endfor -%}\n {{- '}' -}}\n {%- else -%}\n {{- 'response:' + tool_name + '{value:' + format_argument(response, escape_keys=False) + '}' -}}\n {%- endif -%}\n {{- '<tool_response|>' -}}\n{%- endmacro -%}\n\n{#- ===== SETUP ===== -#}\n{%- set ns = namespace(prev_message_type=None, prev_non_tool_role=None) -%}\n{%- set loop_messages = messages -%}\n{%- set enable_thinking = enable_thinking | default(false) -%}\n{%- set preserve_thinking = preserve_thinking | default(false) -%}\n{{- bos_token -}}\n{#- Handle System/Tool Definitions Block -#}\n{%- if enable_thinking or tools or (messages and messages[0]['role'] in ['system', 'developer']) -%}\n {{- '<|turn>system\\n' -}}\n {#- Inject Thinking token at the very top of the FIRST system turn -#}\n {%- if enable_thinking -%}\n {{- '<|think|>\\n' -}}\n {%- set ns.prev_message_type = 'think' -%}\n {%- endif -%}\n {%- if messages and messages[0]['role'] in ['system', 'developer'] -%}\n {%- if messages[0]['content'] is string -%}\n {{- messages[0]['content'] | trim -}}\n {%- elif messages[0]['content'] is sequence -%}\n {%- for item in messages[0]['content'] -%}\n {{- item['text'] | trim + ' '-}}\n {%- endfor -%}\n {%- endif -%}\n {%- set loop_messages = messages[1:] -%}\n {%- endif -%}\n {%- if tools -%}\n {%- for tool in tools %}\n {{- '<|tool>' -}}\n {{- format_function_declaration(tool) | trim -}}\n {{- '<tool|>' -}}\n {%- endfor %}\n {%- set ns.prev_message_type = 'tool' -%}\n {%- endif -%}\n {{- '<turn|>\\n' -}}\n{%- endif %}\n\n{#- Pre-scan: find last user message index for reasoning guard -#}\n{%- set ns_turn = namespace(last_user_idx=-1) -%}\n{%- for i in range(loop_messages | length) -%}\n {%- if loop_messages[i]['role'] == 'user' -%}\n {%- set ns_turn.last_user_idx = i -%}\n {%- endif -%}\n{%- endfor -%}\n\n{#- Loop through messages -#}\n{%- for message in loop_messages -%}\n {%- if message['role'] != 'tool' -%}\n {%- set ns.prev_message_type = None -%}\n {%- set role = 'model' if message['role'] == 'assistant' else message['role'] -%}\n {#- Detect continuation using tracked state — O(1) instead of O(n) backward scan -#}\n {%- set continue_same_model_turn = (role == 'model' and ns.prev_non_tool_role == 'assistant') -%}\n {%- if not continue_same_model_turn -%}\n {{- '<|turn>' + role + '\\n' }}\n\n {%- endif -%}\n\n {#- Render reasoning/reasoning_content as thinking channel -#}\n {%- set thinking_text = message.get('reasoning') or message.get('reasoning_content') -%}\n {%- set thinking_gate = (loop.index0 > ns_turn.last_user_idx) or (preserve_thinking and message.get('tool_calls')) -%}\n {%- if thinking_text and thinking_gate -%}\n {{- '<|channel>thought\\n' + thinking_text + '\\n<channel|>' -}}\n {%- endif -%}\n\n {%- if message.get('tool_calls') -%}\n {%- for tool_call in message.get('tool_calls') -%}\n {%- set function = tool_call['function'] -%}\n {{- '<|tool_call>call:' + function['name'] + '{' -}}\n {%- if function['arguments'] is mapping -%}\n {%- set ns_args = namespace(found_first=false) -%}\n {%- for key, value in function['arguments'] | dictsort -%}\n {%- if ns_args.found_first %},{% endif -%}\n {%- set ns_args.found_first = true -%}\n {{- key -}}:{{- format_argument(value, escape_keys=False) -}}\n {%- endfor -%}\n {%- elif function['arguments'] is none -%}\n {%- else -%}\n {{- raise_exception(\n \"chat_template: tool_calls[].function.arguments must be a \"\n \"JSON object (mapping), not a string. Deserialize arguments \"\n \"before passing to the template.\"\n ) -}}\n {%- endif -%}\n {{- '}<tool_call|>' -}}\n {%- endfor -%}\n {%- set ns.prev_message_type = 'tool_call' -%}\n {%- endif -%}\n\n {%- set ns_tr_out = namespace(flag=false) -%}\n {%- if message.get('tool_responses') -%}\n {#- Legacy: tool_responses embedded on the assistant message (Google/Gemma native) -#}\n {%- for tool_response in message.get('tool_responses') -%}\n {{- format_tool_response_block(tool_response['name'] | default('unknown', true), tool_response['response']) -}}\n {%- set ns_tr_out.flag = true -%}\n {%- set ns.prev_message_type = 'tool_response' -%}\n {%- endfor -%}\n {%- elif message.get('tool_calls') -%}\n {#- OpenAI Chat Completions: forward-scan consecutive role:tool messages -#}\n {%- set ns_tool_scan = namespace(stopped=false) -%}\n {%- for k in range(loop.index0 + 1, loop_messages | length) -%}\n {%- if ns_tool_scan.stopped -%}\n {%- elif loop_messages[k]['role'] != 'tool' -%}\n {%- set ns_tool_scan.stopped = true -%}\n {%- else -%}\n {%- set follow = loop_messages[k] -%}\n {#- Resolve tool_call_id to function name -#}\n {%- set ns_tname = namespace(name=follow.get('name') or 'unknown') -%}\n {%- for tc in message.get('tool_calls') -%}\n {%- if tc.get('id') == follow.get('tool_call_id') -%}\n {%- set ns_tname.name = tc['function']['name'] -%}\n {%- endif -%}\n {%- endfor -%}\n {#- Handle content as string or content-parts array -#}\n {%- set tool_body = follow.get('content') -%}\n {%- if tool_body is string -%}\n {{- format_tool_response_block(ns_tname.name, tool_body) -}}\n {%- elif tool_body is sequence and tool_body is not string -%}\n {%- set ns_txt = namespace(s='') -%}\n {%- for part in tool_body -%}\n {%- if part.get('type') == 'text' -%}\n {%- set ns_txt.s = ns_txt.s + (part.get('text') | default('')) -%}\n {%- endif -%}\n {%- endfor -%}\n {{- format_tool_response_block(ns_tname.name, ns_txt.s) -}}\n {%- for part in tool_body -%}\n {%- if part.get('type') in ['image', 'image_url'] -%}\n {{- '<|image|>' -}}\n {%- elif part.get('type') in ['audio', 'input_audio'] -%}\n {{- '<|audio|>' -}}\n {%- elif part.get('type') == 'video' -%}\n {{- '<|video|>' -}}\n {%- endif -%}\n {%- endfor -%}\n {%- else -%}\n {{- format_tool_response_block(ns_tname.name, tool_body) -}}\n {%- endif -%}\n {%- set ns_tr_out.flag = true -%}\n {%- set ns.prev_message_type = 'tool_response' -%}\n {%- endif -%}\n {%- endfor -%}\n {%- endif -%}\n\n {%- set captured_content -%}\n {%- if message.get('content') is string -%}\n {%- if role == 'model' -%}\n {{- strip_thinking(message['content']) -}}\n {%- else -%}\n {{- message['content'] | trim -}}\n {%- endif -%}\n {%- elif message.get('content') is sequence -%}\n {%- for item in message['content'] -%}\n {%- if item.get('type') == 'text' -%}\n {%- if role == 'model' -%}\n {{- strip_thinking(item['text']) -}}\n {%- else -%}\n {{- item['text'] | trim -}}\n {%- endif -%}\n {%- elif item.get('type') in ['image', 'image_url'] -%}\n {{- '<|image|>' -}}\n {%- elif item.get('type') in ['audio', 'input_audio'] -%}\n {{- '<|audio|>' -}}\n {%- elif item.get('type') == 'video' -%}\n {{- '<|video|>' -}}\n {%- endif -%}\n {%- endfor -%}\n {%- endif -%}\n {%- endset -%}\n\n {{- captured_content -}}\n {%- set has_content = captured_content | trim | length > 0 -%}\n\n {#- Forward-scan: find next non-tool message role for continuation detection -#}\n {%- set next_nt = namespace(role=None, found=false) -%}\n {%- for j in range(loop.index0 + 1, loop_messages | length) -%}\n {%- if not next_nt.found -%}\n {%- if loop_messages[j]['role'] != 'tool' -%}\n {%- set next_nt.role = loop_messages[j]['role'] -%}\n {%- set next_nt.found = true -%}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n\n {%- set continues_into_next = (\n role == 'model'\n and next_nt.role == 'assistant'\n and (not message.get('tool_calls') or ns_tr_out.flag)\n ) -%}\n\n {%- if ns.prev_message_type == 'tool_call' and not ns_tr_out.flag -%}\n {{- '<|tool_response>' -}}\n {%- elif continues_into_next -%}\n {%- elif not (ns_tr_out.flag and not has_content and not next_nt.found) -%}\n {{- '<turn|>\\n' -}}\n {%- endif -%}\n\n {#- Track previous non-tool role for next iteration (avoids O(n) backward scan) -#}\n {%- set ns.prev_non_tool_role = message['role'] -%}\n {%- endif -%}\n{%- endfor -%}\n\n{%- if add_generation_prompt -%}\n {%- if ns.prev_message_type != 'tool_response' and ns.prev_message_type != 'tool_call' -%}\n {{- '<|turn>model\\n' -}}\n {%- if not enable_thinking -%}\n {{- '<|channel>thought\\n<channel|>' -}}\n {%- endif -%}\n {%- elif ns.prev_message_type == 'tool_response' and enable_thinking -%}\n {{- '<|channel>thought\\n' -}}\n {%- endif -%}\n{%- endif -%}\n"
226
227
  }
227
228
  }