Merge pull request #3237 from SillyTavern/deepseek Deepseek

44833b665e8be687a8773ae51ad169e153af7fff

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

Signed
12 files changed, +186 -15Ignore whitespace
public/img/deepseek.svg+6 -0
@@ -0,0 +1,6 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128px" height="128px" viewBox="0 0 128 128" version="1.1">
3+<g id="surface1">
4+<path d="M 126.65625 23.902344 C 125.300781 23.242188 124.714844 24.507812 123.925781 25.152344 C 123.652344 25.359375 123.425781 25.632812 123.195312 25.878906 C 121.210938 27.996094 118.894531 29.382812 115.871094 29.214844 C 111.449219 28.96875 107.675781 30.355469 104.335938 33.738281 C 103.625 29.566406 101.269531 27.082031 97.683594 25.484375 C 95.808594 24.652344 93.910156 23.824219 92.59375 22.015625 C 91.675781 20.730469 91.425781 19.296875 90.964844 17.886719 C 90.671875 17.035156 90.378906 16.164062 89.402344 16.019531 C 88.335938 15.855469 87.921875 16.746094 87.503906 17.492188 C 85.835938 20.542969 85.1875 23.902344 85.253906 27.308594 C 85.398438 34.964844 88.628906 41.066406 95.054688 45.402344 C 95.785156 45.898438 95.972656 46.398438 95.742188 47.125 C 95.308594 48.617188 94.785156 50.070312 94.324219 51.566406 C 94.03125 52.523438 93.59375 52.726562 92.570312 52.316406 C 89.109375 50.828125 85.96875 48.691406 83.3125 46.019531 C 78.742188 41.605469 74.613281 36.730469 69.460938 32.910156 C 68.269531 32.03125 67.042969 31.191406 65.785156 30.398438 C 60.535156 25.296875 66.480469 21.105469 67.855469 20.609375 C 69.296875 20.085938 68.351562 18.304688 63.703125 18.324219 C 59.050781 18.347656 54.792969 19.898438 49.371094 21.972656 C 48.566406 22.28125 47.734375 22.527344 46.890625 22.703125 C 41.820312 21.75 36.636719 21.566406 31.515625 22.160156 C 21.460938 23.28125 13.433594 28.039062 7.53125 36.148438 C 0.4375 45.898438 -1.230469 56.980469 0.8125 68.535156 C 2.960938 80.714844 9.179688 90.800781 18.730469 98.683594 C 28.640625 106.859375 40.046875 110.863281 53.066406 110.097656 C 60.96875 109.644531 69.777344 108.582031 79.703125 100.175781 C 82.207031 101.425781 84.832031 101.921875 89.195312 102.292969 C 92.554688 102.609375 95.785156 102.132812 98.289062 101.609375 C 102.207031 100.777344 101.9375 97.148438 100.523438 96.484375 C 89.03125 91.128906 91.550781 93.3125 89.253906 91.546875 C 95.097656 84.632812 103.898438 77.457031 107.34375 54.199219 C 107.609375 52.347656 107.382812 51.183594 107.34375 49.691406 C 107.324219 48.785156 107.53125 48.425781 108.570312 48.324219 C 111.457031 48.027344 114.253906 47.164062 116.8125 45.792969 C 124.257812 41.722656 127.265625 35.046875 127.972656 27.035156 C 128.078125 25.808594 127.953125 24.542969 126.65625 23.898438 Z M 61.765625 96 C 50.625 87.242188 45.222656 84.355469 42.992188 84.480469 C 40.902344 84.609375 41.28125 86.992188 41.738281 88.550781 C 42.21875 90.085938 42.84375 91.140625 43.71875 92.492188 C 44.324219 93.382812 44.742188 94.710938 43.113281 95.707031 C 39.523438 97.925781 33.289062 94.960938 32.996094 94.816406 C 25.738281 90.539062 19.664062 84.894531 15.390625 77.179688 C 11.265625 69.75 8.863281 61.78125 8.46875 53.273438 C 8.363281 51.214844 8.964844 50.492188 11.011719 50.117188 C 13.703125 49.601562 16.457031 49.53125 19.167969 49.910156 C 30.539062 51.574219 40.214844 56.65625 48.332031 64.703125 C 52.960938 69.289062 56.464844 74.769531 60.074219 80.121094 C 63.914062 85.808594 68.042969 91.226562 73.300781 95.664062 C 75.15625 97.222656 76.632812 98.40625 78.054688 99.273438 C 73.777344 99.753906 66.640625 99.863281 61.765625 96 Z M 67.097656 61.652344 C 67.097656 61.117188 67.359375 60.609375 67.800781 60.304688 C 68.246094 60 68.808594 59.929688 69.3125 60.121094 C 70.378906 62.097656 70.207031 62.515625 69.894531 62.824219 C 69.585938 63.132812 69.164062 63.300781 68.726562 63.296875 C 68.292969 63.296875 67.875 63.125 67.570312 62.816406 C 67.265625 62.507812 67.097656 62.085938 67.105469 61.652344 Z M 83.683594 70.164062 C 82.617188 70.597656 81.558594 70.96875 80.539062 71.019531 C 79.007812 71.070312 77.503906 70.59375 76.28125 69.664062 C 74.820312 68.4375 73.777344 67.753906 73.339844 65.621094 C 73.1875 64.578125 73.214844 63.519531 73.425781 62.484375 C 73.796875 60.742188 73.382812 59.621094 72.148438 58.609375 C 71.152344 57.777344 69.878906 57.546875 68.480469 57.546875 C 68 57.519531 67.535156 57.375 67.125 57.128906 C 66.539062 56.84375 66.058594 56.117188 66.515625 55.222656 C 66.667969 54.933594 67.371094 54.230469 67.542969 54.101562 C 69.441406 53.023438 71.632812 53.375 73.652344 54.1875 C 75.53125 54.953125 76.949219 56.363281 78.992188 58.355469 C 81.078125 60.761719 81.457031 61.429688 82.644531 63.230469 C 83.585938 64.644531 84.4375 66.097656 85.019531 67.753906 C 85.375 68.792969 84.917969 69.644531 83.683594 70.164062 Z M 83.683594 70.164062 "/>
5+</g>
6+</svg>
public/index.html+26 -6
@@ -682,7 +682,7 @@
682682 </span>
683683 </div>
684684 </div>
685685 <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
686686 <div class="range-block-title" data-i18n="Temperature">
687687 Temperature
688688 </div>
@@ -695,7 +695,7 @@
695695 </div>
696696 </div>
697697 </div>
698698 <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
699699 <div class="range-block-title" data-i18n="Frequency Penalty">
700700 Frequency Penalty
701701 </div>
@@ -708,7 +708,7 @@
708708 </div>
709709 </div>
710710 </div>
711711 <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
712712 <div class="range-block-title" data-i18n="Presence Penalty">
713713 Presence Penalty
714714 </div>
@@ -734,7 +734,7 @@
734734 </div>
735735 </div>
736736 </div>
737737 <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
738738 <div class="range-block-title" data-i18n="Top P">
739739 Top P
740740 </div>
@@ -2679,6 +2679,7 @@
26792679 <option value="blockentropy">Block Entropy</option>
26802680 <option value="claude">Claude</option>
26812681 <option value="cohere">Cohere</option>
2682+ <option value="deepseek">DeepSeek</option>
26822683 <option value="groq">Groq</option>
26832684 <option value="makersuite">Google AI Studio</option>
26842685 <option value="mistralai">MistralAI</option>
@@ -3192,6 +3193,23 @@
31923193 </select>
31933194 </div>
31943195 </div>
3196+ <div id="deepseek_form" data-source="deepseek">
3197+ <h4 data-i18n="DeepSeek API Key">DeepSeek API Key</h4>
3198+ <div class="flex-container">
3199+ <input id="api_key_deepseek" name="api_key_deepseek" class="text_pole flex1" value="" type="text" autocomplete="off">
3200+ <div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_nanogpt"></div>
3201+ </div>
3202+ <div data-for="api_key_deepseek" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
3203+ For privacy reasons, your API key will be hidden after you reload the page.
3204+ </div>
3205+ <div>
3206+ <h4 data-i18n="DeepSeek Model">DeepSeek Model</h4>
3207+ <select id="model_deepseek_select">
3208+ <option value="deepseek-chat">deepseek-chat</option>
3209+ <option value="deepseek-coder">deepseek-coder</option>
3210+ </select>
3211+ </div>
3212+ </div>
31953213 <div id="perplexity_form" data-source="perplexity">
31963214 <h4 data-i18n="Perplexity API Key">Perplexity API Key</h4>
31973215 <div class="flex-container">
@@ -3311,8 +3329,9 @@
33113329 <h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4>
33123330 <select id="custom_prompt_post_processing" class="text_pole" title="Applies additional processing to the prompt before sending it to the API." data-i18n="[title]Applies additional processing to the prompt before sending it to the API.">
33133331 <option data-i18n="prompt_post_processing_none" value="">None</option>
33143332 <option data-i18n="prompt_post_processing_merge" value="merge">Merge consecutive roles</option>
33153333 <option data-i18n="prompt_post_processing_semi" value="strictsemi">StrictSemi-strict (user first, alternating roles)</option>
3334+ <option data-i18n="prompt_post_processing_strict" value="strict">Strict (user first, alternating roles)</option>
33163335 </select>
33173336 </form>
33183337 <div id="01ai_form" data-source="01ai">
@@ -3762,6 +3781,7 @@
37623781 <option value="17">Mistral Nemo</option>
37633782 <option value="8">Yi</option>
37643783 <option value="11">Claude 1/2</option>
3784+ <option value="18">DeepSeek V3</option>
37653785 <option value="6">API (WebUI / koboldcpp)</option>
37663786 </select>
37673787 </div>
public/scripts/RossAscends-mods.js+1 -0
@@ -390,6 +390,7 @@ function RA_autoconnect(PrevApi) {
390390 || (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI)
391391 || (secret_state[SECRET_KEYS.BLOCKENTROPY] && oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY)
392392 || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
393+ || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
393394 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
394395 ) {
395396 $('#api_button_openai').trigger('click');
public/scripts/openai.js+79 -1
@@ -183,6 +183,7 @@ export const chat_completion_sources = {
183183 ZEROONEAI: '01ai',
184184 BLOCKENTROPY: 'blockentropy',
185185 NANOGPT: 'nanogpt',
186+ DEEPSEEK: 'deepseek',
186187};
187188
188189const character_names_behavior = {
@@ -204,6 +205,7 @@ const custom_prompt_post_processing_types = {
204205 /** @deprecated Use MERGE instead. */
205206 CLAUDE: 'claude',
206207 MERGE: 'merge',
208+ SEMI: 'semi',
207209 STRICT: 'strict',
208210};
209211
@@ -261,6 +263,7 @@ const default_settings = {
261263 nanogpt_model: 'gpt-4o-mini',
262264 zerooneai_model: 'yi-large',
263265 blockentropy_model: 'be-70b-base-llama3.1',
266+ deepseek_model: 'deepseek-chat',
264267 custom_model: '',
265268 custom_url: '',
266269 custom_include_body: '',
@@ -339,6 +342,7 @@ const oai_settings = {
339342 nanogpt_model: 'gpt-4o-mini',
340343 zerooneai_model: 'yi-large',
341344 blockentropy_model: 'be-70b-base-llama3.1',
345+ deepseek_model: 'deepseek-chat',
342346 custom_model: '',
343347 custom_url: '',
344348 custom_include_body: '',
@@ -1523,6 +1527,8 @@ function getChatCompletionModel() {
15231527 return oai_settings.blockentropy_model;
15241528 case chat_completion_sources.NANOGPT:
15251529 return oai_settings.nanogpt_model;
1530+ case chat_completion_sources.DEEPSEEK:
1531+ return oai_settings.deepseek_model;
15261532 default:
15271533 throw new Error(`Unknown chat completion source: ${oai_settings.chat_completion_source}`);
15281534 }
@@ -1698,6 +1704,24 @@ function saveModelList(data) {
16981704
16991705 $('#model_nanogpt_select').val(oai_settings.nanogpt_model).trigger('change');
17001706 }
1707+
1708+ if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
1709+ $('#model_deepseek_select').empty();
1710+ model_list.forEach((model) => {
1711+ $('#model_deepseek_select').append(
1712+ $('<option>', {
1713+ value: model.id,
1714+ text: model.id,
1715+ }));
1716+ });
1717+
1718+ const selectedModel = model_list.find(model => model.id === oai_settings.deepseek_model);
1719+ if (model_list.length > 0 && (!selectedModel || !oai_settings.deepseek_model)) {
1720+ oai_settings.deepseek_model = model_list[0].id;
1721+ }
1722+
1723+ $('#model_deepseek_select').val(oai_settings.deepseek_model).trigger('change');
1724+ }
17011725}
17021726
17031727function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {
@@ -1838,6 +1862,7 @@ async function sendOpenAIRequest(type, messages, signal) {
18381862 const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;
18391863 const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI;
18401864 const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT;
1865+ const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;
18411866 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
18421867 const isQuiet = type === 'quiet';
18431868 const isImpersonate = type === 'impersonate';
@@ -1902,7 +1927,7 @@ async function sendOpenAIRequest(type, messages, signal) {
19021927 }
19031928
19041929 // Add logprobs request (currently OpenAI only, max 5 on their side)
19051930 if (useLogprobs && (isOAI || isCustom || isDeepSeek)) {
19061931 generate_data['logprobs'] = 5;
19071932 }
19081933
@@ -2000,6 +2025,11 @@ async function sendOpenAIRequest(type, messages, signal) {
20002025 delete generate_data.stop;
20012026 }
20022027
2028+ // https://api-docs.deepseek.com/api/create-chat-completion
2029+ if (isDeepSeek) {
2030+ generate_data.top_p = generate_data.top_p || Number.EPSILON;
2031+ }
2032+
20032033 if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) {
20042034 generate_data['seed'] = oai_settings.seed;
20052035 }
@@ -2122,6 +2152,7 @@ function parseChatCompletionLogprobs(data) {
21222152
21232153 switch (oai_settings.chat_completion_source) {
21242154 case chat_completion_sources.OPENAI:
2155+ case chat_completion_sources.DEEPSEEK:
21252156 case chat_completion_sources.CUSTOM:
21262157 if (!data.choices?.length) {
21272158 return null;
@@ -3041,6 +3072,7 @@ function loadOpenAISettings(data, settings) {
30413072 oai_settings.perplexity_model = settings.perplexity_model ?? default_settings.perplexity_model;
30423073 oai_settings.groq_model = settings.groq_model ?? default_settings.groq_model;
30433074 oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model;
3075+ oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;
30443076 oai_settings.blockentropy_model = settings.blockentropy_model ?? default_settings.blockentropy_model;
30453077 oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model;
30463078 oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
@@ -3122,6 +3154,8 @@ function loadOpenAISettings(data, settings) {
31223154 $(`#model_groq_select option[value="${oai_settings.groq_model}"`).attr('selected', true);
31233155 $('#model_nanogpt_select').val(oai_settings.nanogpt_model);
31243156 $(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).attr('selected', true);
3157+ $('#model_deepseek_select').val(oai_settings.deepseek_model);
3158+ $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true);
31253159 $('#model_01ai_select').val(oai_settings.zerooneai_model);
31263160 $('#model_blockentropy_select').val(oai_settings.blockentropy_model);
31273161 $('#custom_model_id').val(oai_settings.custom_model);
@@ -3861,6 +3895,7 @@ function onSettingsPresetChange() {
38613895 perplexity_model: ['#model_perplexity_select', 'perplexity_model', false],
38623896 groq_model: ['#model_groq_select', 'groq_model', false],
38633897 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false],
3898+ deepseek_model: ['#model_deepseek_select', 'deepseek_model', false],
38643899 zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false],
38653900 blockentropy_model: ['#model_blockentropy_select', 'blockentropy_model', false],
38663901 custom_model: ['#custom_model_id', 'custom_model', false],
@@ -4120,6 +4155,16 @@ async function onModelChange() {
41204155 oai_settings.nanogpt_model = value;
41214156 }
41224157
4158+ if ($(this).is('#model_deepseek_select')) {
4159+ if (!value) {
4160+ console.debug('Null DeepSeek model selected. Ignoring.');
4161+ return;
4162+ }
4163+
4164+ console.log('DeepSeek model changed to', value);
4165+ oai_settings.deepseek_model = value;
4166+ }
4167+
41234168 if (value && $(this).is('#model_01ai_select')) {
41244169 console.log('01.AI model changed to', value);
41254170 oai_settings.zerooneai_model = value;
@@ -4436,6 +4481,22 @@ async function onModelChange() {
44364481 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
44374482 }
44384483
4484+ if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
4485+ if (oai_settings.max_context_unlocked) {
4486+ $('#openai_max_context').attr('max', unlocked_max);
4487+ } else if (oai_settings.deepseek_model == 'deepseek-chat') {
4488+ $('#openai_max_context').attr('max', max_64k);
4489+ } else if (oai_settings.deepseek_model == 'deepseek-coder') {
4490+ $('#openai_max_context').attr('max', max_16k);
4491+ } else {
4492+ $('#openai_max_context').attr('max', max_64k);
4493+ }
4494+
4495+ oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4496+ $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4497+ $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4498+ }
4499+
44394500 if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
44404501 oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
44414502 $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
@@ -4653,6 +4714,19 @@ async function onConnectButtonClick(e) {
46534714 }
46544715 }
46554716
4717+ if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
4718+ const api_key_deepseek = String($('#api_key_deepseek').val()).trim();
4719+
4720+ if (api_key_deepseek.length) {
4721+ await writeSecret(SECRET_KEYS.DEEPSEEK, api_key_deepseek);
4722+ }
4723+
4724+ if (!secret_state[SECRET_KEYS.DEEPSEEK]) {
4725+ console.log('No secret key saved for DeepSeek');
4726+ return;
4727+ }
4728+ }
4729+
46564730 if (oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI) {
46574731 const api_key_01ai = String($('#api_key_01ai').val()).trim();
46584732
@@ -4734,6 +4808,9 @@ function toggleChatCompletionForms() {
47344808 else if (oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY) {
47354809 $('#model_blockentropy_select').trigger('change');
47364810 }
4811+ else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
4812+ $('#model_deepseek_select').trigger('change');
4813+ }
47374814 $('[data-source]').each(function () {
47384815 const validSources = $(this).data('source').split(',');
47394816 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));
@@ -5447,6 +5524,7 @@ export function initOpenAI() {
54475524 $('#model_perplexity_select').on('change', onModelChange);
54485525 $('#model_groq_select').on('change', onModelChange);
54495526 $('#model_nanogpt_select').on('change', onModelChange);
5527+ $('#model_deepseek_select').on('change', onModelChange);
54505528 $('#model_01ai_select').on('change', onModelChange);
54515529 $('#model_blockentropy_select').on('change', onModelChange);
54525530 $('#model_custom_select').on('change', onModelChange);
public/scripts/secrets.js+2 -0
@@ -39,6 +39,7 @@ export const SECRET_KEYS = {
3939 TAVILY: 'api_key_tavily',
4040 BFL: 'api_key_bfl',
4141 GENERIC: 'api_key_generic',
42+ DEEPSEEK: 'api_key_deepseek',
4243};
4344
4445const INPUT_MAP = {
@@ -73,6 +74,7 @@ const INPUT_MAP = {
7374 [SECRET_KEYS.BLOCKENTROPY]: '#api_key_blockentropy',
7475 [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt',
7576 [SECRET_KEYS.GENERIC]: '#api_key_generic',
77+ [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek',
7678};
7779
7880async function clearSecret() {
public/scripts/slash-commands.js+1 -0
@@ -3815,6 +3815,7 @@ function getModelOptions(quiet) {
38153815 { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT },
38163816 { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI },
38173817 { id: 'model_blockentropy_select', api: 'openai', type: chat_completion_sources.BLOCKENTROPY },
3818+ { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },
38183819 { id: 'model_novel_select', api: 'novel', type: null },
38193820 { id: 'horde_model', api: 'koboldhorde', type: null },
38203821 ];
public/scripts/tokenizers.js+15 -0
@@ -31,6 +31,7 @@ export const tokenizers = {
3131 QWEN2: 15,
3232 COMMAND_R: 16,
3333 NEMO: 17,
34+ DEEPSEEK: 18,
3435 BEST_MATCH: 99,
3536 MANUAL_SELECTION: 411,
3637};
@@ -46,6 +47,7 @@ export const ENCODE_TOKENIZERS = [
4647 tokenizers.QWEN2,
4748 tokenizers.COMMAND_R,
4849 tokenizers.NEMO,
50+ tokenizers.DEEPSEEK,
4951 // uncomment when NovelAI releases Kayra and Clio weights, lol
5052 //tokenizers.NERD,
5153 //tokenizers.NERD2,
@@ -133,6 +135,11 @@ const TOKENIZER_URLS = {
133135 decode: '/api/tokenizers/nemo/decode',
134136 count: '/api/tokenizers/nemo/encode',
135137 },
138+ [tokenizers.DEEPSEEK]: {
139+ encode: '/api/tokenizers/deepseek/encode',
140+ decode: '/api/tokenizers/deepseek/decode',
141+ count: '/api/tokenizers/deepseek/encode',
142+ },
136143 [tokenizers.API_TEXTGENERATIONWEBUI]: {
137144 encode: '/api/tokenizers/remote/textgenerationwebui/encode',
138145 count: '/api/tokenizers/remote/textgenerationwebui/encode',
@@ -551,12 +558,17 @@ export function getTokenizerModel() {
551558 const qwen2Tokenizer = 'qwen2';
552559 const commandRTokenizer = 'command-r';
553560 const nemoTokenizer = 'nemo';
561+ const deepseekTokenizer = 'deepseek';
554562
555563 // Assuming no one would use it for different models.. right?
556564 if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
557565 return gpt4Tokenizer;
558566 }
559567
568+ if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
569+ return deepseekTokenizer;
570+ }
571+
560572 // Select correct tokenizer for WindowAI proxies
561573 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI && oai_settings.windowai_model) {
562574 if (oai_settings.windowai_model.includes('gpt-4')) {
@@ -619,6 +631,9 @@ export function getTokenizerModel() {
619631 else if (oai_settings.openrouter_model.includes('jamba')) {
620632 return jambaTokenizer;
621633 }
634+ else if (oai_settings.openrouter_model.includes('deepseek')) {
635+ return deepseekTokenizer;
636+ }
622637 }
623638
624639 if (oai_settings.chat_completion_source == chat_completion_sources.COHERE) {
src/constants.js+1 -0
@@ -175,6 +175,7 @@ export const CHAT_COMPLETION_SOURCES = {
175175 ZEROONEAI: '01ai',
176176 BLOCKENTROPY: 'blockentropy',
177177 NANOGPT: 'nanogpt',
178+ DEEPSEEK: 'deepseek',
178179};
179180
180181/**
src/endpoints/backends/chat-completions.js+23 -2
@@ -51,6 +51,7 @@ const API_01AI = 'https://api.01.ai/v1';
5151const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';
5252const API_AI21 = 'https://api.ai21.com/studio/v1';
5353const API_NANOGPT = 'https://nano-gpt.com/api/v1';
54+const API_DEEPSEEK = 'https://api.deepseek.com/beta';
5455
5556/**
5657 * Applies a post-processing step to the generated messages.
@@ -63,9 +64,13 @@ function postProcessPrompt(messages, type, names) {
6364 switch (type) {
6465 case 'merge':
6566 case 'claude':
6667 return mergeMessages(messages, names, false, false);
68+ case 'semi':
69+ return mergeMessages(messages, names, true, false);
6770 case 'strict':
6871 return mergeMessages(messages, names, true, true);
72+ case 'deepseek':
73+ return (x => x.length && (x[x.length - 1].role !== 'assistant' || (x[x.length - 1].prefix = true)) ? x : x)(mergeMessages(messages, names, true, false));
6974 default:
7075 return messages;
7176 }
@@ -668,6 +673,10 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
668673 api_url = API_NANOGPT;
669674 api_key_openai = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
670675 headers = {};
676+ } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.DEEPSEEK) {
677+ api_url = API_DEEPSEEK.replace('/beta', '');
678+ api_key_openai = readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);
679+ headers = {};
671680 } else {
672681 console.log('This chat completion source is not supported yet.');
673682 return response_getstatus_openai.status(400).send({ error: true });
@@ -939,6 +948,18 @@ router.post('/generate', jsonParser, function (request, response) {
939948 apiKey = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
940949 headers = {};
941950 bodyParams = {};
951+ } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.DEEPSEEK) {
952+ apiUrl = API_DEEPSEEK;
953+ apiKey = readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);
954+ headers = {};
955+ bodyParams = {};
956+
957+ if (request.body.logprobs > 0) {
958+ bodyParams['top_logprobs'] = request.body.logprobs;
959+ bodyParams['logprobs'] = true;
960+ }
961+
962+ request.body.messages = postProcessPrompt(request.body.messages, 'deepseek', getPromptNames(request));
942963 } else {
943964 console.log('This chat completion source is not supported yet.');
944965 return response.status(400).send({ error: true });
src/endpoints/secrets.js+1 -0
@@ -51,6 +51,7 @@ export const SECRET_KEYS = {
5151 NANOGPT: 'api_key_nanogpt',
5252 BFL: 'api_key_bfl',
5353 GENERIC: 'api_key_generic',
54+ DEEPSEEK: 'api_key_deepseek',
5455};
5556
5657// These are the keys that are safe to expose, even if allowKeysExposure is false
src/endpoints/tokenizers.js+24 -0
@@ -226,6 +226,7 @@ const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');
226226const commandTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/command-r.json', 'src/tokenizers/llama3.json');
227227const qwen2Tokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/qwen2.json', 'src/tokenizers/llama3.json');
228228const nemoTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/nemo.json', 'src/tokenizers/llama3.json');
229+const deepseekTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/deepseek.json', 'src/tokenizers/llama3.json');
229230
230231export const sentencepieceTokenizers = [
231232 'llama',
@@ -407,6 +408,10 @@ export function getTokenizerModel(requestModel) {
407408 return 'yi';
408409 }
409410
411+ if (requestModel.includes('deepseek')) {
412+ return 'deepseek';
413+ }
414+
410415 if (requestModel.includes('gemma') || requestModel.includes('gemini')) {
411416 return 'gemma';
412417 }
@@ -655,6 +660,7 @@ router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llam
655660router.post('/qwen2/encode', jsonParser, createWebTokenizerEncodingHandler(qwen2Tokenizer));
656661router.post('/command-r/encode', jsonParser, createWebTokenizerEncodingHandler(commandTokenizer));
657662router.post('/nemo/encode', jsonParser, createWebTokenizerEncodingHandler(nemoTokenizer));
663+router.post('/deepseek/encode', jsonParser, createWebTokenizerEncodingHandler(deepseekTokenizer));
658664router.post('/llama/decode', jsonParser, createSentencepieceDecodingHandler(spp_llama));
659665router.post('/nerdstash/decode', jsonParser, createSentencepieceDecodingHandler(spp_nerd));
660666router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandler(spp_nerd_v2));
@@ -668,6 +674,7 @@ router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llam
668674router.post('/qwen2/decode', jsonParser, createWebTokenizerDecodingHandler(qwen2Tokenizer));
669675router.post('/command-r/decode', jsonParser, createWebTokenizerDecodingHandler(commandTokenizer));
670676router.post('/nemo/decode', jsonParser, createWebTokenizerDecodingHandler(nemoTokenizer));
677+router.post('/deepseek/decode', jsonParser, createWebTokenizerDecodingHandler(deepseekTokenizer));
671678
672679router.post('/openai/encode', jsonParser, async function (req, res) {
673680 try {
@@ -723,6 +730,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
723730 return handler(req, res);
724731 }
725732
733+ if (queryModel.includes('deepseek')) {
734+ const handler = createWebTokenizerEncodingHandler(deepseekTokenizer);
735+ return handler(req, res);
736+ }
737+
726738 const model = getTokenizerModel(queryModel);
727739 const handler = createTiktokenEncodingHandler(model);
728740 return handler(req, res);
@@ -786,6 +798,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
786798 return handler(req, res);
787799 }
788800
801+ if (queryModel.includes('deepseek')) {
802+ const handler = createWebTokenizerDecodingHandler(deepseekTokenizer);
803+ return handler(req, res);
804+ }
805+
789806 const model = getTokenizerModel(queryModel);
790807 const handler = createTiktokenDecodingHandler(model);
791808 return handler(req, res);
@@ -863,6 +880,13 @@ router.post('/openai/count', jsonParser, async function (req, res) {
863880 return res.send({ 'token_count': num_tokens });
864881 }
865882
883+ if (model === 'deepseek') {
884+ const instance = await deepseekTokenizer.get();
885+ if (!instance) throw new Error('Failed to load the DeepSeek tokenizer');
886+ num_tokens = countWebTokenizerTokens(instance, req.body);
887+ return res.send({ 'token_count': num_tokens });
888+ }
889+
866890 const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;
867891 const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;
868892 const tokensPadding = 3;
src/prompt-converters.js+7 -6
@@ -639,9 +639,10 @@ export function convertMistralMessages(messages, names) {
639639 * @param {any[]} messages Messages to merge
640640 * @param {PromptNames} names Prompt names
641641 * @param {boolean} strict Enable strict mode: only allow one system message at the start, force user first message
642+ * @param {boolean} placeholders Add user placeholders to the messages in strict mode
642643 * @returns {any[]} Merged messages
643644 */
644645export function mergeMessages(messages, names, strict, placeholders) {
645646 let mergedMessages = [];
646647
647648 /** @type {Map<string,object>} */
@@ -700,9 +701,9 @@ export function mergeMessages(messages, names, strict) {
700701 }
701702 });
702703
703704 // Prevent erroring out if the messagesmergedMessages array is empty.
704705 if (messagesmergedMessages.length === 0) {
705706 messagesmergedMessages.unshift({
706707 role: 'user',
707708 content: PROMPT_PLACEHOLDER,
708709 });
@@ -741,7 +742,7 @@ export function mergeMessages(messages, names, strict) {
741742 mergedMessages[i].role = 'user';
742743 }
743744 }
744745 if (mergedMessages.length && placeholders) {
745746 if (mergedMessages[0].role === 'system' && (mergedMessages.length === 1 || mergedMessages[1].role !== 'user')) {
746747 mergedMessages.splice(1, 0, { role: 'user', content: PROMPT_PLACEHOLDER });
747748 }
@@ -749,7 +750,7 @@ export function mergeMessages(messages, names, strict) {
749750 mergedMessages.unshift({ role: 'user', content: PROMPT_PLACEHOLDER });
750751 }
751752 }
752753 return mergeMessages(mergedMessages, names, false, placeholders);
753754 }
754755
755756 return mergedMessages;