Add Mistral Nemo downloadable tokenizer
| @@ -3339,7 +3339,8 @@ | ||
| 3339 | 3339 | <option value="16">Command-R</option> |
| 3340 | 3340 | <option value="4">NerdStash (NovelAI Clio)</option> |
| 3341 | 3341 | <option value="5">NerdStash v2 (NovelAI Kayra)</option> |
| 3342 | 3342 | <option value="7">Mistral V1</option> |
| 3343 | + <option value="17">Mistral Nemo</option> | |
| 3343 | 3344 | <option value="8">Yi</option> |
| 3344 | 3345 | <option value="11">Claude 1/2</option> |
| 3345 | 3346 | <option value="6">API (WebUI / koboldcpp)</option> |
| @@ -30,6 +30,7 @@ export const tokenizers = { | ||
| 30 | 30 | JAMBA: 14, |
| 31 | 31 | QWEN2: 15, |
| 32 | 32 | COMMAND_R: 16, |
| 33 | + NEMO: 17, | |
| 33 | 34 | BEST_MATCH: 99, |
| 34 | 35 | }; |
| 35 | 36 | |
| @@ -43,6 +44,7 @@ export const ENCODE_TOKENIZERS = [ | ||
| 43 | 44 | tokenizers.JAMBA, |
| 44 | 45 | tokenizers.QWEN2, |
| 45 | 46 | tokenizers.COMMAND_R, |
| 47 | + tokenizers.NEMO, | |
| 46 | 48 | // uncomment when NovelAI releases Kayra and Clio weights, lol |
| 47 | 49 | //tokenizers.NERD, |
| 48 | 50 | //tokenizers.NERD2, |
| @@ -121,6 +123,11 @@ const TOKENIZER_URLS = { | ||
| 121 | 123 | decode: '/api/tokenizers/command-r/decode', |
| 122 | 124 | count: '/api/tokenizers/command-r/encode', |
| 123 | 125 | }, |
| 126 | + [tokenizers.NEMO]: { | |
| 127 | + encode: '/api/tokenizers/nemo/encode', | |
| 128 | + decode: '/api/tokenizers/nemo/decode', | |
| 129 | + count: '/api/tokenizers/nemo/encode', | |
| 130 | + }, | |
| 124 | 131 | [tokenizers.API_TEXTGENERATIONWEBUI]: { |
| 125 | 132 | encode: '/api/tokenizers/remote/textgenerationwebui/encode', |
| 126 | 133 | count: '/api/tokenizers/remote/textgenerationwebui/encode', |
| @@ -535,6 +542,7 @@ export function getTokenizerModel() { | ||
| 535 | 542 | const jambaTokenizer = 'jamba'; |
| 536 | 543 | const qwen2Tokenizer = 'qwen2'; |
| 537 | 544 | const commandRTokenizer = 'command-r'; |
| 545 | + const nemoTokenizer = 'nemo'; | |
| 538 | 546 | |
| 539 | 547 | // Assuming no one would use it for different models.. right? |
| 540 | 548 | if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) { |
| @@ -628,6 +636,9 @@ export function getTokenizerModel() { | ||
| 628 | 636 | } |
| 629 | 637 | |
| 630 | 638 | if (oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI) { |
| 639 | + if (oai_settings.mistralai_model.includes('nemo') || oai_settings.mistralai_model.includes('pixtral')) { | |
| 640 | + return nemoTokenizer; | |
| 641 | + } | |
| 631 | 642 | return mistralTokenizer; |
| 632 | 643 | } |
| 633 | 644 | |
| @@ -221,6 +221,7 @@ const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json'); | ||
| 221 | 221 | const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json'); |
| 222 | 222 | const commandTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/command-r.json', 'src/tokenizers/llama3.json'); |
| 223 | 223 | const qwen2Tokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/qwen2.json', 'src/tokenizers/llama3.json'); |
| 224 | +const nemoTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/nemo.json', 'src/tokenizers/llama3.json'); | |
| 224 | 225 | |
| 225 | 226 | const sentencepieceTokenizers = [ |
| 226 | 227 | 'llama', |
| @@ -418,6 +419,10 @@ function getTokenizerModel(requestModel) { | ||
| 418 | 419 | return 'command-r'; |
| 419 | 420 | } |
| 420 | 421 | |
| 422 | + if (requestModel.includes('nemo')) { | |
| 423 | + return 'nemo'; | |
| 424 | + } | |
| 425 | + | |
| 421 | 426 | // default |
| 422 | 427 | return 'gpt-3.5-turbo'; |
| 423 | 428 | } |
| @@ -645,6 +650,7 @@ router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(clau | ||
| 645 | 650 | router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer)); |
| 646 | 651 | router.post('/qwen2/encode', jsonParser, createWebTokenizerEncodingHandler(qwen2Tokenizer)); |
| 647 | 652 | router.post('/command-r/encode', jsonParser, createWebTokenizerEncodingHandler(commandTokenizer)); |
| 653 | +router.post('/nemo/encode', jsonParser, createWebTokenizerEncodingHandler(nemoTokenizer)); | |
| 648 | 654 | router.post('/llama/decode', jsonParser, createSentencepieceDecodingHandler(spp_llama)); |
| 649 | 655 | router.post('/nerdstash/decode', jsonParser, createSentencepieceDecodingHandler(spp_nerd)); |
| 650 | 656 | router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandler(spp_nerd_v2)); |
| @@ -657,6 +663,7 @@ router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(clau | ||
| 657 | 663 | router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer)); |
| 658 | 664 | router.post('/qwen2/decode', jsonParser, createWebTokenizerDecodingHandler(qwen2Tokenizer)); |
| 659 | 665 | router.post('/command-r/decode', jsonParser, createWebTokenizerDecodingHandler(commandTokenizer)); |
| 666 | +router.post('/nemo/decode', jsonParser, createWebTokenizerDecodingHandler(nemoTokenizer)); | |
| 660 | 667 | |
| 661 | 668 | router.post('/openai/encode', jsonParser, async function (req, res) { |
| 662 | 669 | try { |
| @@ -707,6 +714,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) { | ||
| 707 | 714 | return handler(req, res); |
| 708 | 715 | } |
| 709 | 716 | |
| 717 | + if (queryModel.includes('nemo')) { | |
| 718 | + const handler = createWebTokenizerEncodingHandler(nemoTokenizer); | |
| 719 | + return handler(req, res); | |
| 720 | + } | |
| 721 | + | |
| 710 | 722 | const model = getTokenizerModel(queryModel); |
| 711 | 723 | const handler = createTiktokenEncodingHandler(model); |
| 712 | 724 | return handler(req, res); |
| @@ -765,6 +777,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) { | ||
| 765 | 777 | return handler(req, res); |
| 766 | 778 | } |
| 767 | 779 | |
| 780 | + if (queryModel.includes('nemo')) { | |
| 781 | + const handler = createWebTokenizerDecodingHandler(nemoTokenizer); | |
| 782 | + return handler(req, res); | |
| 783 | + } | |
| 784 | + | |
| 768 | 785 | const model = getTokenizerModel(queryModel); |
| 769 | 786 | const handler = createTiktokenDecodingHandler(model); |
| 770 | 787 | return handler(req, res); |
| @@ -835,6 +852,13 @@ router.post('/openai/count', jsonParser, async function (req, res) { | ||
| 835 | 852 | return res.send({ 'token_count': num_tokens }); |
| 836 | 853 | } |
| 837 | 854 | |
| 855 | + if (model === 'nemo') { | |
| 856 | + const instance = await nemoTokenizer.get(); | |
| 857 | + if (!instance) throw new Error('Failed to load the Nemo tokenizer'); | |
| 858 | + num_tokens = countWebTokenizerTokens(instance, req.body); | |
| 859 | + return res.send({ 'token_count': num_tokens }); | |
| 860 | + } | |
| 861 | + | |
| 838 | 862 | const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1; |
| 839 | 863 | const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3; |
| 840 | 864 | const tokensPadding = 3; |