| 1 | import { t } from './i18n.js'; |
| 2 | |
| 3 | // the hash can be obtained from command line e.g. via: MODEL=path_to_model; python -c "import json, hashlib, sys; print(hashlib.sha256(json.load(open('"$MODEL"/tokenizer_config.json'))['chat_template'].encode()).hexdigest())" |
| 4 | // note that chat templates must be trimmed to match the llama.cpp metadata value |
| 5 | const hash_derivations = { |
| 6 | // Meta |
| 7 | 'e10ca381b1ccc5cf9db52e371f3b6651576caee0a630b452e2816b2d404d4b65': |
| 8 | // Meta-Llama-3.1-8B-Instruct |
| 9 | // Meta-Llama-3.1-70B-Instruct |
| 10 | 'Llama 3 Instruct' |
| 11 | , |
| 12 | '5816fce10444e03c2e9ee1ef8a4a1ea61ae7e69e438613f3b17b69d0426223a4': |
| 13 | // Llama-3.2-1B-Instruct |
| 14 | // Llama-3.2-3B-Instruct |
| 15 | 'Llama 3 Instruct' |
| 16 | , |
| 17 | '73e87b1667d87ab7d7b579107f01151b29ce7f3ccdd1018fdc397e78be76219d': |
| 18 | // Nemotron 70B |
| 19 | 'Llama 3 Instruct' |
| 20 | , |
| 21 | |
| 22 | // Mistral |
| 23 | // Mistral Reference: https://github.com/mistralai/mistral-common |
| 24 | 'e16746b40344d6c5b5265988e0328a0bf7277be86f1c335156eae07e29c82826': |
| 25 | // Mistral-Small-Instruct-2409 |
| 26 | // Mistral-Large-Instruct-2407 |
| 27 | 'Mistral V2 & V3' |
| 28 | , |
| 29 | '26a59556925c987317ce5291811ba3b7f32ec4c647c400c6cc7e3a9993007ba7': |
| 30 | // Mistral-7B-Instruct-v0.3 |
| 31 | 'Mistral V2 & V3' |
| 32 | , |
| 33 | 'e4676cb56dffea7782fd3e2b577cfaf1e123537e6ef49b3ec7caa6c095c62272': |
| 34 | // Mistral-Nemo-Instruct-2407 |
| 35 | 'Mistral V3-Tekken' |
| 36 | , |
| 37 | '3c4ad5fa60dd8c7ccdf82fa4225864c903e107728fcaf859fa6052cb80c92ee9': |
| 38 | // Mistral-Large-Instruct-2411 |
| 39 | 'Mistral V7' |
| 40 | , |
| 41 | '3934d199bfe5b6fab5cba1b5f8ee475e8d5738ac315f21cb09545b4e665cc005': |
| 42 | // Mistral Small 24B |
| 43 | 'Mistral V7' |
| 44 | , |
| 45 | |
| 46 | // Gemma |
| 47 | 'ecd6ae513fe103f0eb62e8ab5bfa8d0fe45c1074fa398b089c93a7e70c15cfd6': |
| 48 | // gemma-2-9b-it |
| 49 | // gemma-2-27b-it |
| 50 | 'Gemma 2' |
| 51 | , |
| 52 | '87fa45af6cdc3d6a9e4dd34a0a6848eceaa73a35dcfe976bd2946a5822a38bf3': |
| 53 | // gemma-2-2b-it |
| 54 | 'Gemma 2' |
| 55 | , |
| 56 | '7de1c58e208eda46e9c7f86397df37ec49883aeece39fb961e0a6b24088dd3c4': |
| 57 | // gemma-3 |
| 58 | 'Gemma 2' |
| 59 | , |
| 60 | |
| 61 | // Cohere |
| 62 | '3b54f5c219ae1caa5c0bb2cdc7c001863ca6807cf888e4240e8739fa7eb9e02e': |
| 63 | // command-r-08-2024 |
| 64 | 'Command R' |
| 65 | , |
| 66 | |
| 67 | // Tulu |
| 68 | 'ac7498a36a719da630e99d48e6ebc4409de85a77556c2b6159eeb735bcbd11df': |
| 69 | // Tulu-3-8B |
| 70 | // Tulu-3-70B |
| 71 | 'Tulu' |
| 72 | , |
| 73 | |
| 74 | // DeepSeek V2.5 |
| 75 | '54d400beedcd17f464e10063e0577f6f798fa896266a912d8a366f8a2fcc0bca': |
| 76 | 'DeepSeek-V2.5' |
| 77 | , |
| 78 | |
| 79 | // DeepSeek R1 |
| 80 | 'b6835114b7303ddd78919a82e4d9f7d8c26ed0d7dfc36beeb12d524f6144eab1': |
| 81 | 'DeepSeek-V2.5' |
| 82 | , |
| 83 | |
| 84 | // THUDM-GLM 4 |
| 85 | '854b703e44ca06bdb196cc471c728d15dbab61e744fe6cdce980086b61646ed1': |
| 86 | 'GLM-4' |
| 87 | , |
| 88 | |
| 89 | // Kimi K2, ... |
| 90 | 'aab20feb9bc6881f941ea649356130ffbc4943b3c2577c0991e1fba90de5a0fc': |
| 91 | 'Moonshot AI' |
| 92 | , |
| 93 | |
| 94 | // gpt-oss (unsloth) |
| 95 | '70da0d2348e40aaf8dad05f04a316835fd10547bd7e3392ce337e4c79ba91c01': |
| 96 | 'OpenAI Harmony' |
| 97 | , |
| 98 | |
| 99 | // gpt-oss (ggml-org) |
| 100 | 'a4c9919cbbd4acdd51ccffe22da049264b1b73e59055fa58811a99efbd7c8146': |
| 101 | 'OpenAI Harmony' |
| 102 | , |
| 103 | }; |
| 104 | |
| 105 | const substr_derivations = [ |
| 106 | ['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|>']], |
| 108 | |
| 109 | // Generic cases |
| 110 | ['ChatML', ['<|im_start|>user', '<|im_start|>assistant', '<|im_end|>']], |
| 111 | ]; |
| 112 | |
| 113 | const parse_derivation = derivation => (typeof derivation === 'string') ? { |
| 114 | 'context': derivation, |
| 115 | 'instruct': derivation, |
| 116 | } : derivation; |
| 117 | |
| 118 | const not_found = { context: null, instruct: null }; |
| 119 | |
| 120 | export async function deriveTemplatesFromChatTemplate(chat_template, hash) { |
| 121 | if (chat_template.trim() === '') { |
| 122 | console.log('Missing chat template.'); |
| 123 | return not_found; |
| 124 | } |
| 125 | |
| 126 | if (hash in hash_derivations) { |
| 127 | return parse_derivation(hash_derivations[hash]); |
| 128 | } |
| 129 | |
| 130 | // heuristics |
| 131 | for (const [derivation, substr] of substr_derivations) { |
| 132 | if ([substr].flat().every(str => chat_template.includes(str))) { |
| 133 | return parse_derivation(derivation); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | console.warn(`Unknown chat template hash: ${hash} for [${chat_template}]`); |
| 138 | return not_found; |
| 139 | } |
| 140 | |
| 141 | export async function bindModelTemplates(power_user, online_status) { |
| 142 | if (online_status === 'no_connection') { |
| 143 | return false; |
| 144 | } |
| 145 | |
| 146 | const chatTemplateHash = power_user.chat_template_hash; |
| 147 | const bindModelTemplates = power_user.model_templates_mappings[online_status] |
| 148 | ?? power_user.model_templates_mappings[chatTemplateHash] |
| 149 | ?? {}; |
| 150 | const bindingsMatch = bindModelTemplates |
| 151 | && power_user.context.preset == bindModelTemplates.context |
| 152 | && (!power_user.instruct.enabled || power_user.instruct.preset === bindModelTemplates.instruct); |
| 153 | |
| 154 | const bound = []; |
| 155 | |
| 156 | if (bindingsMatch) { |
| 157 | // unmap current preset |
| 158 | delete power_user.model_templates_mappings[chatTemplateHash]; |
| 159 | delete power_user.model_templates_mappings[online_status]; |
| 160 | toastr.info(t`Context preset for ${online_status} will use defaults when loaded the next time.`); |
| 161 | } else { |
| 162 | if (power_user.context_derived) { |
| 163 | if (power_user.context.preset !== bindModelTemplates.context) { |
| 164 | bound.push(`${power_user.context.preset} context preset`); |
| 165 | // toastr.info(`Bound ${power_user.context.preset} preset to currently loaded model and all models that share its chat template.`); |
| 166 | |
| 167 | // map current preset to current chat template hash |
| 168 | bindModelTemplates.context = power_user.context.preset; |
| 169 | } |
| 170 | } else { |
| 171 | toastr.warning(t`Note: Context derivation is disabled. Not including context preset.`); |
| 172 | } |
| 173 | if (power_user.instruct.enabled) { |
| 174 | if (power_user.instruct_derived) { |
| 175 | if (power_user.instruct.preset !== bindModelTemplates.instruct) { |
| 176 | bound.push(`${power_user.instruct.preset} instruct preset`); |
| 177 | bindModelTemplates.instruct = power_user.instruct.preset; |
| 178 | } |
| 179 | } else { |
| 180 | toastr.warning(t`Note: Instruct derivation is disabled. Not including instruct preset.`); |
| 181 | } |
| 182 | } |
| 183 | if (bound.length == 0) { |
| 184 | toastr.warning(t`No applicable presets available.`); |
| 185 | return false; |
| 186 | } |
| 187 | |
| 188 | toastr.info(t`Bound ${online_status} to ${bound.join(', ')}.`); |
| 189 | if (!online_status.startsWith('koboldcpp/ggml-model-')) { |
| 190 | power_user.model_templates_mappings[online_status] = bindModelTemplates; |
| 191 | } |
| 192 | if (chatTemplateHash !== '') { |
| 193 | power_user.model_templates_mappings[chatTemplateHash] = bindModelTemplates; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | return true; |
| 198 | } |