Add Harmony template (#4431) * Add Harmony template * Add Thinking instruct variant and reasoning template * Specify template hash from unsloth * Add ggml-org template hash

e37b709e96c8fc3fdf38f696707ab014cb947f28

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
7 files changed, +109 -23Ignore whitespace
default/content/index.json+16 -0
@@ -838,5 +838,21 @@
838838 {
839839 "filename": "presets/context/Moonshot AI.json",
840840 "type": "context"
841+ },
842+ {
843+ "filename": "presets/context/OpenAI Harmony.json",
844+ "type": "context"
845+ },
846+ {
847+ "filename": "presets/instruct/OpenAI Harmony.json",
848+ "type": "instruct"
849+ },
850+ {
851+ "filename": "presets/instruct/OpenAI Harmony (Thinking).json",
852+ "type": "instruct"
853+ },
854+ {
855+ "filename": "presets/reasoning/OpenAI Harmony.json",
856+ "type": "reasoning"
841857 }
842858]
default/content/presets/context/OpenAI Harmony.json+14 -0
@@ -0,0 +1,14 @@
1+{
2+ "story_string": "{{#if anchorBefore}}{{anchorBefore}}\n{{/if}}{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{personality}}\n{{/if}}{{#if scenario}}{{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{#if anchorAfter}}{{anchorAfter}}\n{{/if}}{{trim}}",
3+ "example_separator": "",
4+ "chat_start": "",
5+ "use_stop_strings": false,
6+ "names_as_stop_strings": true,
7+ "story_string_position": 0,
8+ "story_string_depth": 1,
9+ "story_string_role": 0,
10+ "always_force_name2": true,
11+ "trim_sentences": false,
12+ "single_line": false,
13+ "name": "OpenAI Harmony"
14+}
default/content/presets/instruct/OpenAI Harmony (Thinking).json+25 -0
@@ -0,0 +1,25 @@
1+{
2+ "input_sequence": "<|start|>user<|message|>",
3+ "output_sequence": "<|start|>assistant<|channel|>final<|message|>",
4+ "last_output_sequence": "{{noop}}",
5+ "system_sequence": "<|start|>developer<|message|>",
6+ "stop_sequence": "",
7+ "wrap": false,
8+ "macro": true,
9+ "activation_regex": "",
10+ "first_output_sequence": "",
11+ "skip_examples": false,
12+ "output_suffix": "<|end|>",
13+ "input_suffix": "<|end|>",
14+ "system_suffix": "<|end|>",
15+ "user_alignment_message": "",
16+ "system_same_as_user": false,
17+ "last_system_sequence": "",
18+ "first_input_sequence": "",
19+ "last_input_sequence": "",
20+ "names_behavior": "force",
21+ "sequences_as_stop_strings": false,
22+ "story_string_prefix": "<|start|>system<|message|>",
23+ "story_string_suffix": "<|end|>",
24+ "name": "OpenAI Harmony (Thinking)"
25+}
default/content/presets/instruct/OpenAI Harmony.json+25 -0
@@ -0,0 +1,25 @@
1+{
2+ "input_sequence": "<|start|>user<|message|>",
3+ "output_sequence": "<|start|>assistant<|channel|>final<|message|>",
4+ "last_output_sequence": "",
5+ "system_sequence": "<|start|>developer<|message|>",
6+ "stop_sequence": "",
7+ "wrap": false,
8+ "macro": true,
9+ "activation_regex": "",
10+ "first_output_sequence": "",
11+ "skip_examples": false,
12+ "output_suffix": "<|end|>",
13+ "input_suffix": "<|end|>",
14+ "system_suffix": "<|end|>",
15+ "user_alignment_message": "",
16+ "system_same_as_user": false,
17+ "last_system_sequence": "",
18+ "first_input_sequence": "",
19+ "last_input_sequence": "",
20+ "names_behavior": "force",
21+ "sequences_as_stop_strings": true,
22+ "story_string_prefix": "<|start|>system<|message|>",
23+ "story_string_suffix": "<|end|>",
24+ "name": "OpenAI Harmony"
25+}
default/content/presets/reasoning/OpenAI Harmony.json+6 -0
@@ -0,0 +1,6 @@
1+{
2+ "prefix": "<|start|>assistant<|channel|>analysis<|message|>",
3+ "suffix": "<|start|>assistant<|channel|>final<|message|>",
4+ "separator": "",
5+ "name": "OpenAI Harmony"
6+}
public/script.js+12 -23
@@ -5522,30 +5522,19 @@ export function cleanUpMessage({ getMessage, isImpersonate, isContinue, displayI
55225522 getMessage = getMessage.substring(0, getMessage.indexOf(power_user.instruct.input_sequence));
55235523 }
55245524 }
5525- if (isInstruct && power_user.instruct.input_sequence && isImpersonate) {
5525+
5526- //getMessage = getMessage.replaceAll(power_user.instruct.input_sequence, '');
5526+ // Remove instruct sequences leaking to the output
5527- power_user.instruct.input_sequence.split('\n')
5527+ if (isInstruct && power_user.instruct.sequences_as_stop_strings) {
5528- .filter(line => line.trim() !== '')
5528+ const sequences = [
5529- .forEach(line => {
5529+ { value: power_user.instruct.input_sequence, apply: isImpersonate && isNotEmpty(power_user.instruct.input_sequence) },
5530- getMessage = getMessage.replaceAll(line, '');
5530+ { value: power_user.instruct.output_sequence, apply: !isImpersonate && isNotEmpty(power_user.instruct.output_sequence) },
5531- });
5531+ { value: power_user.instruct.last_output_sequence, apply: !isImpersonate && isNotEmpty(power_user.instruct.last_output_sequence) },
5532- }
5532+ ];
5533- if (isInstruct && power_user.instruct.output_sequence && !isImpersonate) {
5533+ for (const seq of sequences.filter(s => s.apply)) {
5534- //getMessage = getMessage.replaceAll(power_user.instruct.output_sequence, '');
5534+ seq.value.split('\n').filter(line => line.trim() !== '').forEach(line => { getMessage = getMessage.replaceAll(line, ''); });
5535- power_user.instruct.output_sequence.split('\n')
5535+ }
5536- .filter(line => line.trim() !== '')
5537- .forEach(line => {
5538- getMessage = getMessage.replaceAll(line, '');
5539- });
5540- }
5541- if (isInstruct && power_user.instruct.last_output_sequence && !isImpersonate) {
5542- //getMessage = getMessage.replaceAll(power_user.instruct.last_output_sequence, '');
5543- power_user.instruct.last_output_sequence.split('\n')
5544- .filter(line => line.trim() !== '')
5545- .forEach(line => {
5546- getMessage = getMessage.replaceAll(line, '');
5547- });
55485536 }
5537+
55495538 // clean-up group message from excessive generations
55505539 if (selected_group) {
55515540 getMessage = cleanGroupMessage(getMessage);
public/scripts/chat-templates.js+11 -0
@@ -90,10 +90,21 @@ const hash_derivations = {
9090 'aab20feb9bc6881f941ea649356130ffbc4943b3c2577c0991e1fba90de5a0fc':
9191 'Moonshot AI'
9292 ,
93+
94+ // gpt-oss (unsloth)
95+ '70da0d2348e40aaf8dad05f04a316835fd10547bd7e3392ce337e4c79ba91c01':
96+ 'OpenAI Harmony'
97+ ,
98+
99+ // gpt-oss (ggml-org)
100+ 'a4c9919cbbd4acdd51ccffe22da049264b1b73e59055fa58811a99efbd7c8146':
101+ 'OpenAI Harmony'
102+ ,
93103};
94104
95105const substr_derivations = [
96106 ['Moonshot AI', ['<|im_user|>user<|im_middle|>', '<|im_assistant|>assistant<|im_middle|>', '<|im_end|>']],
107+ ['OpenAI Harmony', ['<|start|>user<|message|>', '<|start|>assistant<|channel|>final<|message|>', '<|end|>']],
97108
98109 // Generic cases
99110 ['ChatML', ['<|im_start|>user', '<|im_start|>assistant', '<|im_end|>']],