Merge pull request #2868 from SillyTavern/sysprompt-divorce Decouple system prompts from instruct mode

733fff4e22878825ccd713ad65c7a2314057217b

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

Signed
64 files changed, +1051 -342Showing whitespace changes
default/content/index.json+48 -8
@@ -468,10 +468,6 @@
468 "type": "context"468 "type": "context"
469 },469 },
470 {470 {
471 "filename": "presets/context/Alpaca-Roleplay.json",
472 "type": "context"
473 },
474 {
475 "filename": "presets/context/Alpaca-Single-Turn.json",471 "filename": "presets/context/Alpaca-Single-Turn.json",
476 "type": "context"472 "type": "context"
477 },473 },
@@ -552,10 +548,6 @@
552 "type": "instruct"548 "type": "instruct"
553 },549 },
554 {550 {
555 "filename": "presets/instruct/Alpaca-Roleplay.json",
556 "type": "instruct"
557 },
558 {
559 "filename": "presets/instruct/Alpaca-Single-Turn.json",551 "filename": "presets/instruct/Alpaca-Single-Turn.json",
560 "type": "instruct"552 "type": "instruct"
561 },553 },
@@ -680,6 +672,54 @@
680 "type": "instruct"672 "type": "instruct"
681 },673 },
682 {674 {
675 "filename": "presets/sysprompt/Actor.json",
676 "type": "sysprompt"
677 },
678 {
679 "filename": "presets/sysprompt/Assistant - Expert.json",
680 "type": "sysprompt"
681 },
682 {
683 "filename": "presets/sysprompt/Assistant - Simple.json",
684 "type": "sysprompt"
685 },
686 {
687 "filename": "presets/sysprompt/Chain of Thought.json",
688 "type": "sysprompt"
689 },
690 {
691 "filename": "presets/sysprompt/Roleplay - Detailed.json",
692 "type": "sysprompt"
693 },
694 {
695 "filename": "presets/sysprompt/Roleplay - Immersive.json",
696 "type": "sysprompt"
697 },
698 {
699 "filename": "presets/sysprompt/Roleplay - Simple.json",
700 "type": "sysprompt"
701 },
702 {
703 "filename": "presets/sysprompt/Text Adventure.json",
704 "type": "sysprompt"
705 },
706 {
707 "filename": "presets/sysprompt/Writer - Creative.json",
708 "type": "sysprompt"
709 },
710 {
711 "filename": "presets/sysprompt/Writer - Realistic.json",
712 "type": "sysprompt"
713 },
714 {
715 "filename": "presets/sysprompt/Blank.json",
716 "type": "sysprompt"
717 },
718 {
719 "filename": "presets/sysprompt/Neutral - Chat.json",
720 "type": "sysprompt"
721 },
722 {
683 "filename": "presets/instruct/Mistral V1.json",723 "filename": "presets/instruct/Mistral V1.json",
684 "type": "instruct"724 "type": "instruct"
685 },725 },
default/content/presets/context/Alpaca-Roleplay.json+0 -11
@@ -1,11 +0,0 @@
1{
2 "story_string": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\n{{#if system}}{{system}}\n\n{{/if}}### Input:\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}\n\n",
3 "example_separator": "### New Roleplay:",
4 "chat_start": "### New Roleplay:",
5 "use_stop_strings": false,
6 "allow_jailbreak": false,
7 "always_force_name2": true,
8 "trim_sentences": false,
9 "single_line": false,
10 "name": "Alpaca-Roleplay"
11}
default/content/presets/context/Libra-32B.json+1 -1
@@ -1,5 +1,5 @@
1{1{
2 "story_string": "### Instruction:\nWrite {{char}}'s next reply in this roleplay with {{user}}. Use the provided character sheet and example dialogue for formatting direction and character speech patterns.\n\n{{#if system}}{{system}}\n\n{{/if}}### Character Sheet:\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",2 "story_string": "### Instruction:\n{{#if system}}{{system}}\n\n{{/if}}### Character Sheet:\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
3 "example_separator": "### Example:",3 "example_separator": "### Example:",
4 "chat_start": "### START ROLEPLAY:",4 "chat_start": "### START ROLEPLAY:",
5 "use_stop_strings": false,5 "use_stop_strings": false,
default/content/presets/instruct/Adventure.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]",
3 "input_sequence": "",2 "input_sequence": "",
4 "output_sequence": "",3 "output_sequence": "",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Alpaca-Roleplay.json+0 -23
@@ -1,23 +0,0 @@
1{
2 "system_prompt": "Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.",
3 "input_sequence": "### Instruction:",
4 "output_sequence": "### Response:",
5 "last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
6 "system_sequence": "### Input:",
7 "stop_sequence": "",
8 "wrap": true,
9 "macro": true,
10 "names_behavior": "always",
11 "activation_regex": "",
12 "system_sequence_prefix": "",
13 "system_sequence_suffix": "",
14 "first_output_sequence": "",
15 "skip_examples": false,
16 "output_suffix": "\n\n",
17 "input_suffix": "\n\n",
18 "system_suffix": "\n\n",
19 "user_alignment_message": "",
20 "system_same_as_user": false,
21 "last_system_sequence": "",
22 "name": "Alpaca-Roleplay"
23}
default/content/presets/instruct/Alpaca-Single-Turn.json+1 -2
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\nWrite 1 reply only, italicize actions, and avoid quotation marks. Use markdown. Be proactive, creative, and drive the plot and conversation forward. Include dialog as well as narration.",
3 "input_sequence": "",2 "input_sequence": "",
4 "output_sequence": "",3 "output_sequence": "",
5 "last_output_sequence": "\n### Response:",4 "last_output_sequence": "\n### Response:",
@@ -11,7 +10,7 @@
11 "activation_regex": "",10 "activation_regex": "",
12 "system_sequence_prefix": "",11 "system_sequence_prefix": "",
13 "system_sequence_suffix": "",12 "system_sequence_suffix": "",
14 "first_output_sequence": "<START OF ROLEPLAY>",13 "first_output_sequence": "<START OF CHAT>",
15 "skip_examples": false,14 "skip_examples": false,
16 "output_suffix": "",15 "output_suffix": "",
17 "input_suffix": "",16 "input_suffix": "",
default/content/presets/instruct/Alpaca.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
3 "input_sequence": "### Instruction:",2 "input_sequence": "### Instruction:",
4 "output_sequence": "### Response:",3 "output_sequence": "### Response:",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/ChatML-Names.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.",
3 "input_sequence": "<|im_start|>[{{name}}]",2 "input_sequence": "<|im_start|>[{{name}}]",
4 "output_sequence": "<|im_start|>[{{name}}]",3 "output_sequence": "<|im_start|>[{{name}}]",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/ChatML.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.",
3 "input_sequence": "<|im_start|>user",2 "input_sequence": "<|im_start|>user",
4 "output_sequence": "<|im_start|>assistant",3 "output_sequence": "<|im_start|>assistant",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Command R.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
3 "input_sequence": "<|START_OF_TURN_TOKEN|><|USER_TOKEN|>",2 "input_sequence": "<|START_OF_TURN_TOKEN|><|USER_TOKEN|>",
4 "output_sequence": "<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>",3 "output_sequence": "<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>",
5 "first_output_sequence": "",4 "first_output_sequence": "",
default/content/presets/instruct/DreamGen Role-Play V1 ChatML.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below.",
3 "input_sequence": "\n<|im_start|>text names= {{name}}\n",2 "input_sequence": "\n<|im_start|>text names= {{name}}\n",
4 "output_sequence": "\n<|im_start|>text names= {{name}}\n",3 "output_sequence": "\n<|im_start|>text names= {{name}}\n",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/DreamGen Role-Play V1 Llama3.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below.",
3 "input_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{user}}<|end_header_id|>\n\n",2 "input_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{user}}<|end_header_id|>\n\n",
4 "output_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{char}}<|end_header_id|>\n\n",3 "output_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{char}}<|end_header_id|>\n\n",
5 "first_output_sequence": "",4 "first_output_sequence": "",
default/content/presets/instruct/Gemma 2.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Continue writing this story and portray characters realistically.",
3 "input_sequence": "<start_of_turn>user",2 "input_sequence": "<start_of_turn>user",
4 "output_sequence": "<start_of_turn>model",3 "output_sequence": "<start_of_turn>model",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Koala.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
3 "input_sequence": "USER: ",2 "input_sequence": "USER: ",
4 "output_sequence": "GPT: ",3 "output_sequence": "GPT: ",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Libra-32B.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Avoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.",
3 "input_sequence": "",2 "input_sequence": "",
4 "output_sequence": "",3 "output_sequence": "",
5 "last_output_sequence": "\n### Response:",4 "last_output_sequence": "\n### Response:",
default/content/presets/instruct/Lightning 1.1.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\nTake the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace.\n\n",
3 "input_sequence": "### Instruction:",2 "input_sequence": "### Instruction:",
4 "output_sequence": "### Response: (length = unlimited)",3 "output_sequence": "### Response: (length = unlimited)",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Llama 2 Chat.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
3 "input_sequence": "[INST] ",2 "input_sequence": "[INST] ",
4 "output_sequence": "",3 "output_sequence": "",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Llama 3 Instruct.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}.",
3 "input_sequence": "<|start_header_id|>user<|end_header_id|>\n\n",2 "input_sequence": "<|start_header_id|>user<|end_header_id|>\n\n",
4 "output_sequence": "<|start_header_id|>assistant<|end_header_id|>\n\n",3 "output_sequence": "<|start_header_id|>assistant<|end_header_id|>\n\n",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Llama-3-Instruct-Names.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}.",
3 "input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",2 "input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
4 "output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",3 "output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Metharme.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Enter roleplay mode. You must act as {{char}}, whose persona follows:",
3 "input_sequence": "<|user|>",2 "input_sequence": "<|user|>",
4 "output_sequence": "<|model|>",3 "output_sequence": "<|model|>",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Mistral V1.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
3 "input_sequence": " [INST] ",2 "input_sequence": " [INST] ",
4 "output_sequence": " [/INST] ",3 "output_sequence": " [/INST] ",
5 "last_output_sequence": " [/INST]",4 "last_output_sequence": " [/INST]",
default/content/presets/instruct/Mistral V2 & V3.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
3 "input_sequence": "[INST] ",2 "input_sequence": "[INST] ",
4 "output_sequence": "[/INST] ",3 "output_sequence": "[/INST] ",
5 "last_output_sequence": "[/INST]",4 "last_output_sequence": "[/INST]",
default/content/presets/instruct/Mistral V3-Tekken.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
3 "input_sequence": "[INST]",2 "input_sequence": "[INST]",
4 "output_sequence": "[/INST]",3 "output_sequence": "[/INST]",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/OpenOrca-OpenChat.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "You are a helpful assistant. Please answer truthfully and write out your thinking step by step to be sure you get the right answer. If you make a mistake or encounter an error in your thinking, say so out loud and attempt to correct it. If you don't know or aren't sure about something, say so clearly. You will act as a professional logician, mathematician, and physicist. You will also act as the most appropriate type of expert to answer any particular question or solve the relevant problem; state which expert type your are, if so. Also think of any particular named expert that would be ideal to answer the relevant question or solve the relevant problem; name and act as them, if appropriate.\n",
3 "input_sequence": "\nUser: ",2 "input_sequence": "\nUser: ",
4 "output_sequence": "\nAssistant: ",3 "output_sequence": "\nAssistant: ",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Phi.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
3 "input_sequence": "<|user|>\n",2 "input_sequence": "<|user|>\n",
4 "output_sequence": "<|assistant|>\n",3 "output_sequence": "<|assistant|>\n",
5 "first_output_sequence": "",4 "first_output_sequence": "",
default/content/presets/instruct/Pygmalion.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Enter RP mode. You shall reply to {{user}} while staying in character. Your responses must be detailed, creative, immersive, and drive the scenario forward. You will follow {{char}}'s persona.",
3 "input_sequence": "<|user|>",2 "input_sequence": "<|user|>",
4 "output_sequence": "<|model|>",3 "output_sequence": "<|model|>",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Story.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "",
3 "input_sequence": "",2 "input_sequence": "",
4 "output_sequence": "",3 "output_sequence": "",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Synthia.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation.",
3 "input_sequence": "USER: ",2 "input_sequence": "USER: ",
4 "output_sequence": "ASSISTANT: ",3 "output_sequence": "ASSISTANT: ",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Vicuna 1.0.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
3 "input_sequence": "### Human:",2 "input_sequence": "### Human:",
4 "output_sequence": "### Assistant:",3 "output_sequence": "### Assistant:",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/Vicuna 1.1.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
3 "input_sequence": "\nUSER: ",2 "input_sequence": "\nUSER: ",
4 "output_sequence": "\nASSISTANT: ",3 "output_sequence": "\nASSISTANT: ",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/WizardLM-13B.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.\n\nWrite {{char}}'s next detailed reply in a fictional roleplay chat between {{user}} and {{char}}.",
3 "input_sequence": "USER: ",2 "input_sequence": "USER: ",
4 "output_sequence": "ASSISTANT: ",3 "output_sequence": "ASSISTANT: ",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/WizardLM.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
3 "input_sequence": "",2 "input_sequence": "",
4 "output_sequence": "### Response:",3 "output_sequence": "### Response:",
5 "last_output_sequence": "",4 "last_output_sequence": "",
default/content/presets/instruct/simple-proxy-for-tavern.json+0 -1
@@ -1,5 +1,4 @@
1{1{
2 "system_prompt": "[System note: Write one reply only. Do not decide what {{user}} says or does. Write at least one paragraph, up to four. Be descriptive and immersive, providing vivid details about {{char}}'s actions, emotions, and the environment. Write with a high degree of complexity and burstiness. Do not repeat this message.]",
3 "input_sequence": "### Instruction:\n#### {{name}}:",2 "input_sequence": "### Instruction:\n#### {{name}}:",
4 "output_sequence": "### Response:\n#### {{name}}:",3 "output_sequence": "### Response:\n#### {{name}}:",
5 "last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):\n#### {{name}}:",4 "last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):\n#### {{name}}:",
default/content/presets/sysprompt/Actor.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Actor",
3 "content": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}."
4}
default/content/presets/sysprompt/Assistant - Expert.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Assistant - Expert",
3 "content": "You are a helpful assistant. Please answer truthfully and write out your thinking step by step to be sure you get the right answer. If you make a mistake or encounter an error in your thinking, say so out loud and attempt to correct it. If you don't know or aren't sure about something, say so clearly. You will act as a professional logician, mathematician, and physicist. You will also act as the most appropriate type of expert to answer any particular question or solve the relevant problem; state which expert type your are, if so. Also think of any particular named expert that would be ideal to answer the relevant question or solve the relevant problem; name and act as them, if appropriate."
4}
default/content/presets/sysprompt/Assistant - Simple.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Assistant - Simple",
3 "content": "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions."
4}
default/content/presets/sysprompt/Blank.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Blank",
3 "content": ""
4}
default/content/presets/sysprompt/Chain of Thought.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Chain of Thought",
3 "content": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation."
4}
default/content/presets/sysprompt/Neutral - Chat.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Neutral - Chat",
3 "content": "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}."
4}
default/content/presets/sysprompt/Roleplay - Detailed.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Roleplay - Detailed",
3 "content": "Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions. Keep the story immersive and engaging."
4}
default/content/presets/sysprompt/Roleplay - Immersive.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Roleplay - Immersive",
3 "content": "[System note: Write one reply only. Do not decide what {{user}} says or does. Write at least one paragraph, up to four. Be descriptive and immersive, providing vivid details about {{char}}'s actions, emotions, and the environment. Write with a high degree of complexity and burstiness. Do not repeat this message.]"
4}
default/content/presets/sysprompt/Roleplay - Simple.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Roleplay - Simple",
3 "content": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}."
4}
default/content/presets/sysprompt/Text Adventure.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Text Adventure",
3 "content": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]"
4}
default/content/presets/sysprompt/Writer - Creative.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Writer - Creative",
3 "content": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below."
4}
default/content/presets/sysprompt/Writer - Realistic.json+4 -0
@@ -0,0 +1,4 @@
1{
2 "name": "Writer - Realistic",
3 "content": "Continue writing this story and portray characters realistically."
4}
default/content/settings.json+5 -3
@@ -149,11 +149,9 @@
149 "continue_on_send": false,149 "continue_on_send": false,
150 "trim_spaces": true,150 "trim_spaces": true,
151 "relaxed_api_urls": false,151 "relaxed_api_urls": false,
152 "default_instruct": "",
153 "instruct": {152 "instruct": {
154 "enabled": false,153 "enabled": false,
155 "preset": "Alpaca",154 "preset": "Alpaca",
156 "system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
157 "input_sequence": "### Instruction:",155 "input_sequence": "### Instruction:",
158 "output_sequence": "### Response:",156 "output_sequence": "### Response:",
159 "last_output_sequence": "",157 "last_output_sequence": "",
@@ -173,7 +171,11 @@
173 "user_alignment_message": "",171 "user_alignment_message": "",
174 "system_same_as_user": false172 "system_same_as_user": false
175 },173 },
176 "default_context": "Default",174 "sysprompt": {
175 "enabled": true,
176 "name": "Neutral - Chat",
177 "content": "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}."
178 },
177 "context": {179 "context": {
178 "preset": "Default",180 "preset": "Default",
179 "story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",181 "story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
public/index.html+207 -162
@@ -3162,9 +3162,22 @@
3162 <div class="drawer-icon fa-solid fa-font fa-fw closedIcon" title="AI Response Formatting" data-i18n="[title]AI Response Formatting"></div>3162 <div class="drawer-icon fa-solid fa-font fa-fw closedIcon" title="AI Response Formatting" data-i18n="[title]AI Response Formatting"></div>
3163 </div>3163 </div>
3164 <div id="AdvancedFormatting" class="drawer-content">3164 <div id="AdvancedFormatting" class="drawer-content">
3165 <h3 class="margin0" data-i18n="Advanced Formatting">3165 <div class="flex-container alignItemsBaseline">
3166 <h3 class="margin0 flex1" data-i18n="Advanced Formatting">
3166 Advanced Formatting3167 Advanced Formatting
3167 </h3>3168 </h3>
3169 <div class="flex-container">
3170 <input id="af_master_import_file" type="file" hidden accept=".json" class="displayNone">
3171 <div id="af_master_import" class="menu_button menu_button_icon" title="Import Advanced Formatting settings" data-i18n="[title]Import Advanced Formatting settings">
3172 <i class="fa-solid fa-file-import"></i>
3173 <span data-i18n="Master Import">Master Import</span>
3174 </div>
3175 <div id="af_master_export" class="menu_button menu_button_icon" title="Export Advanced Formatting settings" data-i18n="[title]Export Advanced Formatting settings">
3176 <i class="fa-solid fa-file-export"></i>
3177 <span data-i18n="Master Export">Master Export</span>
3178 </div>
3179 </div>
3180 </div>
3168 <div class="flex-container spaceEvenly">3181 <div class="flex-container spaceEvenly">
3169 <div id="ContextSettings" class="flex-container flexNoGap flexFlowColumn flex1">3182 <div id="ContextSettings" class="flex-container flexNoGap flexFlowColumn flex1">
3170 <div>3183 <div>
@@ -3176,18 +3189,17 @@
3176 </a>3189 </a>
3177 </div>3190 </div>
3178 </h4>3191 </h4>
3192 <div class="flex-container" title="Select your current Context Template" data-i18n="[title]Select your current Context Template">
3193 <select id="context_presets" data-preset-manager-for="context" class="flex1 text_pole"></select>
3179 <div class="flex-container justifyCenter">3194 <div class="flex-container justifyCenter">
3180 <input type="file" hidden data-preset-manager-file="context" accept=".json, .settings">3195 <input type="file" hidden data-preset-manager-file="context" accept=".json, .settings">
3181 <i id="context_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this template for Instruct Mode." data-i18n="[title]Auto-select this template for Instruct Mode"></i>
3182 <i data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>3196 <i data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>
3183 <i data-preset-manager-new="context" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i>3197 <i data-preset-manager-new="context" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i>
3184 <i data-preset-manager-import="context" class="menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>3198 <i data-preset-manager-import="context" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
3185 <i data-preset-manager-export="context" class="menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>3199 <i data-preset-manager-export="context" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
3186 <i data-preset-manager-restore="context" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i>3200 <i data-preset-manager-restore="context" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i>
3187 <i id="context_delete_preset" data-preset-manager-delete="context" class="menu_button fa-solid fa-trash-can" title="Delete the template" data-i18n="[title]Delete the template"></i>3201 <i id="context_delete_preset" data-preset-manager-delete="context" class="menu_button fa-solid fa-trash-can" title="Delete the template" data-i18n="[title]Delete the template"></i>
3188 </div>3202 </div>
3189 <div class="flex-container" title="Select your current Context Template" data-i18n="[title]Select your current Context Template">
3190 <select id="context_presets" data-preset-manager-for="context" class="flex1 text_pole"></select>
3191 </div>3203 </div>
3192 <div>3204 <div>
3193 <label for="context_story_string" class="flex-container">3205 <label for="context_story_string" class="flex-container">
@@ -3218,9 +3230,6 @@
3218 <span data-i18n="Context Formatting">3230 <span data-i18n="Context Formatting">
3219 Context Formatting3231 Context Formatting
3220 </span>3232 </span>
3221 <small data-i18n="(Saves to Template)">
3222 (Saves to Template)
3223 </small>
3224 </h4>3233 </h4>
32253234
3226 <label class="checkbox_label" for="always-force-name2-checkbox">3235 <label class="checkbox_label" for="always-force-name2-checkbox">
@@ -3266,31 +3275,6 @@
3266 <small data-i18n="Allow Post-History Instructions">Allow Post-History Instructions</small>3275 <small data-i18n="Allow Post-History Instructions">Allow Post-History Instructions</small>
3267 </label>3276 </label>
3268 </div>3277 </div>
3269 <div>
3270 <h4 class="range-block-title justifyLeft standoutHeader">
3271 <span data-i18n="Custom Stopping Strings">
3272 Custom Stopping Strings
3273 </span>
3274 <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#custom-stopping-strings" class="notes-link" target="_blank">
3275 <span class="fa-solid fa-circle-question note-link-span"></span>
3276 </a>
3277 </h4>
3278 <div>
3279 <small>
3280 <span data-i18n="JSON serialized array of strings">JSON serialized array of strings</span>
3281 <i class="fa-solid fa-question-circle opacity50p" title="e.g: [&quot;Ford&quot;, &quot;BMW&quot;, &quot;Fiat&quot;]"></i>
3282 </small>
3283 </div>
3284 <div>
3285 <textarea id="custom_stopping_strings" class="text_pole textarea_compact monospace autoSetHeight"></textarea>
3286 </div>
3287 <label class="checkbox_label" for="custom_stopping_strings_macro">
3288 <input id="custom_stopping_strings_macro" type="checkbox" checked>
3289 <small data-i18n="Replace Macro in Custom Stopping Strings">
3290 Replace Macro in Custom Stopping Strings
3291 </small>
3292 </label>
3293 </div>
3294 </div>3278 </div>
3295 </div>3279 </div>
3296 <div id="InstructSettingsColumn" class="flex-container flexNoGap flexFlowColumn flex1">3280 <div id="InstructSettingsColumn" class="flex-container flexNoGap flexFlowColumn flex1">
@@ -3303,29 +3287,28 @@
3303 </a>3287 </a>
3304 </div>3288 </div>
3305 <div class="flex-container">3289 <div class="flex-container">
3306 <label for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">
3307 <input id="instruct_enabled" type="checkbox" style="display:none;" />
3308 <small><i class="fa-solid fa-power-off menu_button margin0"></i></small>
3309 </label>
3310 <label for="instruct_bind_to_context" class="checkbox_label flex1" title="Bind to Context&#10If enabled, Context templates will be automatically selected based on selected Instruct template name or by preference." data-i18n="[title]instruct_bind_to_context">3290 <label for="instruct_bind_to_context" class="checkbox_label flex1" title="Bind to Context&#10If enabled, Context templates will be automatically selected based on selected Instruct template name or by preference." data-i18n="[title]instruct_bind_to_context">
3311 <input id="instruct_bind_to_context" type="checkbox" style="display:none;" />3291 <input id="instruct_bind_to_context" type="checkbox" style="display:none;" />
3312 <small><i class="fa-solid fa-link menu_button margin0"></i></small>3292 <small><i class="fa-solid fa-link menu_button margin0"></i></small>
3313 </label>3293 </label>
3294 <label for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">
3295 <input id="instruct_enabled" type="checkbox" style="display:none;" />
3296 <small><i class="fa-solid fa-power-off menu_button margin0"></i></small>
3297 </label>
3314 </div>3298 </div>
3315 </h4>3299 </h4>
3300
3301 <div class="flex-container" title="Select your current Instruct Template" data-i18n="[title]Select your current Instruct Template">
3302 <select id="instruct_presets" data-preset-manager-for="instruct" class="flex1 text_pole"></select>
3316 <div class="flex-container margin0 justifyCenter">3303 <div class="flex-container margin0 justifyCenter">
3317 <input type="file" hidden data-preset-manager-file="instruct" accept=".json, .settings">3304 <input type="file" hidden data-preset-manager-file="instruct" accept=".json, .settings">
3318 <i id="instruct_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this template on API connection." data-i18n="[title]Auto-select this template on API connection"></i>
3319 <i data-preset-manager-update="instruct" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>3305 <i data-preset-manager-update="instruct" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>
3320 <i data-preset-manager-new="instruct" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i>3306 <i data-preset-manager-new="instruct" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i>
3321 <i data-preset-manager-import="instruct" class=" menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>3307 <i data-preset-manager-import="instruct" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
3322 <i data-preset-manager-export="instruct" class=" menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>3308 <i data-preset-manager-export="instruct" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
3323 <i data-preset-manager-restore="instruct" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i>3309 <i data-preset-manager-restore="instruct" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i>
3324 <i data-preset-manager-delete="instruct" class="menu_button fa-solid fa-trash-can" title="Delete template" data-i18n="[title]Delete template"></i>3310 <i data-preset-manager-delete="instruct" class="menu_button fa-solid fa-trash-can" title="Delete template" data-i18n="[title]Delete template"></i>
3325 </div>3311 </div>
3326
3327 <div class="flex-container" title="Select your current Instruct Template" data-i18n="[title]Select your current Instruct Template">
3328 <select id="instruct_presets" data-preset-manager-for="instruct" class="flex1 text_pole"></select>
3329 </div>3312 </div>
3330 <label>3313 <label>
3331 <small>3314 <small>
@@ -3337,14 +3320,6 @@
3337 <input type="text" id="instruct_activation_regex" class="text_pole textarea_compact" placeholder="e.g. /llama(-)?[3|3.1]/i">3320 <input type="text" id="instruct_activation_regex" class="text_pole textarea_compact" placeholder="e.g. /llama(-)?[3|3.1]/i">
3338 </div>3321 </div>
3339 <div>3322 <div>
3340 <label for="instruct_system_prompt" class="flex-container">
3341 <small data-i18n="System Prompt">System Prompt</small>
3342 <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="instruct_system_prompt" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
3343 </label>
3344 <textarea id="instruct_system_prompt" class="text_pole textarea_compact autoSetHeight"></textarea>
3345 </div>
3346
3347 <div>
3348 <label for="instruct_wrap" class="checkbox_label">3323 <label for="instruct_wrap" class="checkbox_label">
3349 <input id="instruct_wrap" type="checkbox" />3324 <input id="instruct_wrap" type="checkbox" />
3350 <small data-i18n="Wrap Sequences with Newline">Wrap Sequences with Newline</small>3325 <small data-i18n="Wrap Sequences with Newline">Wrap Sequences with Newline</small>
@@ -3357,126 +3332,29 @@
3357 <input id="instruct_skip_examples" type="checkbox" />3332 <input id="instruct_skip_examples" type="checkbox" />
3358 <small data-i18n="Skip Example Dialogues Formatting">Skip Example Dialogues Formatting</small>3333 <small data-i18n="Skip Example Dialogues Formatting">Skip Example Dialogues Formatting</small>
3359 </label>3334 </label>
3360 <div id="instruct_names_behavior">3335 <div>
3361 <small data-i18n="Include Names">3336 <small data-i18n="Include Names">
3362 Include Names3337 Include Names
3363 </small>3338 </small>
3364 <label for="instruct_names_behavior_none" class="checkbox_label" title="Do not add name prefixes before the message contents.">3339 <select id="instruct_names_behavior">
3365 <input id="instruct_names_behavior_none" name="names_behavior" value="none" type="radio" />3340 <option value="none" data-i18n="Never">Never</option>
3366 <small data-i18n="Never">Never</small>3341 <option value="force" data-i18n="Groups and Past Personas">Groups and Past Personas</option>
3367 </label>3342 <option value="always" data-i18n="Always">Always</option>
3368 <label for="instruct_names_behavior_force" class="checkbox_label" title="Only add name prefixes to messages from group characters and past personas.">
3369 <input id="instruct_names_behavior_force" name="names_behavior" value="force" type="radio" />
3370 <small data-i18n="Groups and Past Personas">Groups and Past Personas</small>
3371 </label>
3372 <label for="instruct_names_behavior_always" class="checkbox_label" title="Always add name prefixes to messages.">
3373 <input id="instruct_names_behavior_always" name="names_behavior" value="always" type="radio" />
3374 <small data-i18n="Always">Always</small>
3375 </label>
3376 </div>
3377 </div>
3378 </div>
3379 <div name="tokenizerSettingsBlock">
3380 <div name="tokenizerSelectorBlock">
3381 <h4 class="standoutHeader"><span data-i18n="Tokenizer">Tokenizer</span>
3382 <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#tokenizer" class="notes-link" target="_blank">
3383 <span class="fa-solid fa-circle-question note-link-span"></span>
3384 </a>
3385 </h4>
3386 <select id="tokenizer">
3387 <option value="99">Best match (recommended)</option>
3388 <option value="0">None / Estimated</option>
3389 <option value="1">GPT-2</option>
3390 <!-- Option #2 was a legacy GPT-2/3 tokenizer -->
3391 <option value="3">Llama 1/2</option>
3392 <option value="12">Llama 3</option>
3393 <option value="13">Gemma / Gemini</option>
3394 <option value="14">Jamba</option>
3395 <option value="15">Qwen2</option>
3396 <option value="16">Command-R</option>
3397 <option value="4">NerdStash (NovelAI Clio)</option>
3398 <option value="5">NerdStash v2 (NovelAI Kayra)</option>
3399 <option value="7">Mistral V1</option>
3400 <option value="17">Mistral Nemo</option>
3401 <option value="8">Yi</option>
3402 <option value="11">Claude 1/2</option>
3403 <option value="6">API (WebUI / koboldcpp)</option>
3404 </select>3343 </select>
3405 </div>3344 </div>
3406 <div class="range-block flex-container flexnowrap" name="tokenPaddingBlock">
3407 <div class="range-block-title justifyLeft">
3408 <small data-i18n="Token Padding" class="flex-container">Token Padding
3409 <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#token-padding" class="notes-link" target="_blank">
3410 <span class="fa-solid fa-circle-question note-link-span"></span>
3411 </a>
3412 </small>
3413 </div>
3414 <input id="token_padding" class="text_pole textarea_compact" type="number" min="-2048" max="2048" />
3415 </div>
3416 </div>
3417 <div>
3418 <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4>
3419 <div>
3420 <small>
3421 <span data-i18n="Non-markdown strings">
3422 Non-markdown strings
3423 </span>
3424 </small>
3425 <div>
3426 <input id="markdown_escape_strings" class="text_pole textarea_compact" type="text" data-i18n="[placeholder]comma delimited,no spaces between" placeholder="comma delimited,no spaces between" />
3427 </div>
3428 </div>
3429
3430 <div name="startReplyWithBlock">
3431 <div>
3432 <small>
3433 <span data-i18n="Start Reply With">
3434 Start Reply With
3435 </span>
3436 </small>
3437 <div>
3438 <textarea id="start_reply_with" class="text_pole textarea_compact autoSetHeight"></textarea>
3439 </div>
3440 <label class="checkbox_label" for="chat-show-reply-prefix-checkbox">
3441 <input id="chat-show-reply-prefix-checkbox" type="checkbox" />
3442 <small data-i18n="Show reply prefix in chat">
3443 Show reply prefix in chat
3444 </small>
3445 </label>
3446 </div>
3447 </div>
3448 </div>3345 </div>
3449 </div>3346 </div>
3450 <div id="InstructSequencesColumn" class="flex-container flexNoGap flexFlowColumn flex1">3347 <div id="InstructSequencesColumn" class="wide100p flexFlowColumn">
3451 <div class="wide100p flexFlowColumn">
3452 <h4 class="standoutHeader title_restorable">3348 <h4 class="standoutHeader title_restorable">
3453 <b>3349 <b>
3454 <span data-i18n="Instruct Sequences">3350 <span data-i18n="Instruct Sequences">
3455 Instruct Sequences3351 Instruct Sequences
3456 </span>3352 </span>
3457 <small data-i18n="(Saves to Template)">
3458 (Saves to Template)
3459 </small>
3460 </b>3353 </b>
3461 </h4>3354 </h4>
3462 <div class="flex-container">3355 <!-- We keep one auto-open so the user would know what is going on in the picked template -->
3463 <div class="flexAuto" title="Inserted before a System prompt." data-i18n="[title]Inserted before a System prompt.">3356 <details open>
3464 <label for="instruct_system_sequence_prefix">3357 <summary>User Message Sequences</summary>
3465 <small data-i18n="System Prompt Prefix">System Prompt Prefix</small>
3466 </label>
3467 <div>
3468 <textarea id="instruct_system_sequence_prefix" class="text_pole textarea_compact autoSetHeight"></textarea>
3469 </div>
3470 </div>
3471 <div class="flexAuto" title="Inserted after a System prompt." data-i18n="[title]Inserted after a System prompt.">
3472 <label for="instruct_system_sequence_suffix">
3473 <small data-i18n="System Prompt Suffix">System Prompt Suffix</small>
3474 </label>
3475 <div>
3476 <textarea id="instruct_system_sequence_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3477 </div>
3478 </div>
3479 </div>
3480 <div class="flex-container">3358 <div class="flex-container">
3481 <div class="flexAuto" title="Inserted before a User message and as a last prompt line when impersonating." data-i18n="[title]Inserted before a User message and as a last prompt line when impersonating.">3359 <div class="flexAuto" title="Inserted before a User message and as a last prompt line when impersonating." data-i18n="[title]Inserted before a User message and as a last prompt line when impersonating.">
3482 <small data-i18n="User Prefix">User Message Prefix</small>3360 <small data-i18n="User Prefix">User Message Prefix</small>
@@ -3487,6 +3365,9 @@
3487 <textarea id="instruct_input_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>3365 <textarea id="instruct_input_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3488 </div>3366 </div>
3489 </div>3367 </div>
3368 </details>
3369 <details open>
3370 <summary>Assistant Message Sequences</summary>
3490 <div class="flex-container">3371 <div class="flex-container">
3491 <div class="flexAuto" title="Inserted before an Assistant message and as a last prompt line when generating an AI reply." data-i18n="[title]Inserted before an Assistant message and as a last prompt line when generating an AI reply.">3372 <div class="flexAuto" title="Inserted before an Assistant message and as a last prompt line when generating an AI reply." data-i18n="[title]Inserted before an Assistant message and as a last prompt line when generating an AI reply.">
3492 <small data-i18n="Assistant Prefix">Assistant Message Prefix</small>3373 <small data-i18n="Assistant Prefix">Assistant Message Prefix</small>
@@ -3497,6 +3378,9 @@
3497 <textarea id="instruct_output_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>3378 <textarea id="instruct_output_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3498 </div>3379 </div>
3499 </div>3380 </div>
3381 </details>
3382 <details>
3383 <summary>System Message Sequences</summary>
3500 <div class="flex-container">3384 <div class="flex-container">
3501 <div class="flexAuto" id="instruct_system_sequence_block" title="Inserted before a System (added by slash commands or extensions) message." data-i18n="[title]Inserted before a System (added by slash commands or extensions) message.">3385 <div class="flexAuto" id="instruct_system_sequence_block" title="Inserted before a System (added by slash commands or extensions) message." data-i18n="[title]Inserted before a System (added by slash commands or extensions) message.">
3502 <small data-i18n="System Prefix">System Message Prefix</small>3386 <small data-i18n="System Prefix">System Message Prefix</small>
@@ -3513,10 +3397,30 @@
3513 <small data-i18n="System same as User">System same as User</small>3397 <small data-i18n="System same as User">System same as User</small>
3514 </label>3398 </label>
3515 </div>3399 </div>
35163400 </details>
3517 <h5 class="textAlignCenter" data-i18n="Misc. Sequences">3401 <details>
3518 Misc. Sequences3402 <summary>System Prompt Sequences</summary>
3519 </h5>3403 <div class="flex-container">
3404 <div class="flexAuto" title="Inserted before a System prompt." data-i18n="[title]Inserted before a System prompt.">
3405 <label for="instruct_system_sequence_prefix">
3406 <small data-i18n="System Prompt Prefix">System Prompt Prefix</small>
3407 </label>
3408 <div>
3409 <textarea id="instruct_system_sequence_prefix" class="text_pole textarea_compact autoSetHeight"></textarea>
3410 </div>
3411 </div>
3412 <div class="flexAuto" title="Inserted after a System prompt." data-i18n="[title]Inserted after a System prompt.">
3413 <label for="instruct_system_sequence_suffix">
3414 <small data-i18n="System Prompt Suffix">System Prompt Suffix</small>
3415 </label>
3416 <div>
3417 <textarea id="instruct_system_sequence_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3418 </div>
3419 </div>
3420 </div>
3421 </details>
3422 <details>
3423 <summary data-i18n="Misc. Sequences">Misc. Sequences</summary>
3520 <div class="flex-container">3424 <div class="flex-container">
3521 <div class="flexAuto" title="Inserted before the first Assistant's message." data-i18n="[title]Inserted before the first Assistant's message.">3425 <div class="flexAuto" title="Inserted before the first Assistant's message." data-i18n="[title]Inserted before the first Assistant's message.">
3522 <small data-i18n="First Assistant Prefix">First Assistant Prefix</small>3426 <small data-i18n="First Assistant Prefix">First Assistant Prefix</small>
@@ -3553,6 +3457,147 @@
3553 <textarea id="instruct_user_alignment_message" class="text_pole textarea_compact autoSetHeight"></textarea>3457 <textarea id="instruct_user_alignment_message" class="text_pole textarea_compact autoSetHeight"></textarea>
3554 </div>3458 </div>
3555 </div>3459 </div>
3460 </details>
3461 </div>
3462 </div>
3463 <div id="SystemPromptColumn" class="flex-container flexNoGap flexFlowColumn flex1">
3464 <div id="SystemPromptBlock">
3465 <h4 class="standoutHeader title_restorable justifySpaceBetween">
3466 <div class="flex-container">
3467 <span data-i18n="System Prompt">System Prompt</span>
3468 <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#system-prompt" class="notes-link" target="_blank">
3469 <span class="fa-solid fa-circle-question note-link-span"></span>
3470 </a>
3471 </div>
3472 <div class="flex-container">
3473 <label for="sysprompt_enabled" class="checkbox_label flex1" title="Enable System Prompt" data-i18n="[title]sysprompt_enabled">
3474 <input id="sysprompt_enabled" type="checkbox" style="display:none;" />
3475 <small><i class="fa-solid fa-power-off menu_button margin0"></i></small>
3476 </label>
3477 </div>
3478 </h4>
3479
3480 <div class="flex-container" title="Select your current System Prompt" data-i18n="[title]Select your current System Prompt">
3481 <select id="sysprompt_select" data-preset-manager-for="sysprompt" class="flex1 text_pole"></select>
3482 <div class="flex-container margin0 justifyCenter">
3483 <input type="file" hidden data-preset-manager-file="sysprompt" accept=".json, .settings">
3484 <i data-preset-manager-update="sysprompt" class="menu_button fa-solid fa-save" title="Update current prompt" data-i18n="[title]Update current prompt"></i>
3485 <i data-preset-manager-new="sysprompt" class="menu_button fa-solid fa-file-circle-plus" title="Save prompt as" data-i18n="[title]Save prompt as"></i>
3486 <i data-preset-manager-import="sysprompt" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
3487 <i data-preset-manager-export="sysprompt" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
3488 <i data-preset-manager-restore="sysprompt" class="menu_button fa-solid fa-recycle" title="Restore current prompt" data-i18n="[title]Restore current prompt"></i>
3489 <i data-preset-manager-delete="sysprompt" class="menu_button fa-solid fa-trash-can" title="Delete prompt" data-i18n="[title]Delete prompt"></i>
3490 </div>
3491 </div>
3492
3493 <div>
3494 <label for="sysprompt_content" class="flex-container">
3495 <small data-i18n="Prompt Content">Prompt Content</small>
3496 <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="sysprompt_content" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
3497 </label>
3498 <textarea id="sysprompt_content" class="text_pole textarea_compact autoSetHeight"></textarea>
3499 </div>
3500 </div>
3501
3502 <div>
3503 &nbsp;
3504 </div>
3505
3506 <div>
3507 <h4 class="range-block-title justifyLeft standoutHeader">
3508 <span data-i18n="Custom Stopping Strings">
3509 Custom Stopping Strings
3510 </span>
3511 <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#custom-stopping-strings" class="notes-link" target="_blank">
3512 <span class="fa-solid fa-circle-question note-link-span"></span>
3513 </a>
3514 </h4>
3515 <div>
3516 <small>
3517 <span data-i18n="JSON serialized array of strings">JSON serialized array of strings</span>
3518 <i class="fa-solid fa-question-circle opacity50p" title="e.g: [&quot;Ford&quot;, &quot;BMW&quot;, &quot;Fiat&quot;]"></i>
3519 </small>
3520 </div>
3521 <div>
3522 <textarea id="custom_stopping_strings" class="text_pole textarea_compact monospace autoSetHeight"></textarea>
3523 </div>
3524 <label class="checkbox_label" for="custom_stopping_strings_macro">
3525 <input id="custom_stopping_strings_macro" type="checkbox" checked>
3526 <small data-i18n="Replace Macro in Custom Stopping Strings">
3527 Replace Macro in Custom Stopping Strings
3528 </small>
3529 </label>
3530 </div>
3531
3532 <div name="tokenizerSettingsBlock">
3533 <div name="tokenizerSelectorBlock">
3534 <h4 class="standoutHeader"><span data-i18n="Tokenizer">Tokenizer</span>
3535 <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#tokenizer" class="notes-link" target="_blank">
3536 <span class="fa-solid fa-circle-question note-link-span"></span>
3537 </a>
3538 </h4>
3539 <select id="tokenizer">
3540 <option value="99">Best match (recommended)</option>
3541 <option value="0">None / Estimated</option>
3542 <option value="1">GPT-2</option>
3543 <!-- Option #2 was a legacy GPT-2/3 tokenizer -->
3544 <option value="3">Llama 1/2</option>
3545 <option value="12">Llama 3</option>
3546 <option value="13">Gemma / Gemini</option>
3547 <option value="14">Jamba</option>
3548 <option value="15">Qwen2</option>
3549 <option value="16">Command-R</option>
3550 <option value="4">NerdStash (NovelAI Clio)</option>
3551 <option value="5">NerdStash v2 (NovelAI Kayra)</option>
3552 <option value="7">Mistral V1</option>
3553 <option value="17">Mistral Nemo</option>
3554 <option value="8">Yi</option>
3555 <option value="11">Claude 1/2</option>
3556 <option value="6">API (WebUI / koboldcpp)</option>
3557 </select>
3558 </div>
3559 <div class="range-block flex-container flexnowrap" name="tokenPaddingBlock">
3560 <div class="range-block-title justifyLeft">
3561 <small data-i18n="Token Padding" class="flex-container">Token Padding
3562 <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#token-padding" class="notes-link" target="_blank">
3563 <span class="fa-solid fa-circle-question note-link-span"></span>
3564 </a>
3565 </small>
3566 </div>
3567 <input id="token_padding" class="text_pole textarea_compact" type="number" min="-2048" max="2048" />
3568 </div>
3569 </div>
3570 <div>
3571 <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4>
3572 <div>
3573 <small>
3574 <span data-i18n="Non-markdown strings">
3575 Non-markdown strings
3576 </span>
3577 </small>
3578 <div>
3579 <input id="markdown_escape_strings" class="text_pole textarea_compact" type="text" data-i18n="[placeholder]comma delimited,no spaces between" placeholder="comma delimited,no spaces between" />
3580 </div>
3581 </div>
3582
3583 <div name="startReplyWithBlock">
3584 <div>
3585 <small>
3586 <span data-i18n="Start Reply With">
3587 Start Reply With
3588 </span>
3589 </small>
3590 <div>
3591 <textarea id="start_reply_with" class="text_pole textarea_compact autoSetHeight"></textarea>
3592 </div>
3593 <label class="checkbox_label" for="chat-show-reply-prefix-checkbox">
3594 <input id="chat-show-reply-prefix-checkbox" type="checkbox" />
3595 <small data-i18n="Show reply prefix in chat">
3596 Show reply prefix in chat
3597 </small>
3598 </label>
3599 </div>
3600 </div>
3556 </div>3601 </div>
3557 </div>3602 </div>
3558 </div>3603 </div>
public/script.js+11 -6
@@ -244,6 +244,7 @@ import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/Slash
244import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';244import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';
245import { initInputMarkdown } from './scripts/input-md-formatting.js';245import { initInputMarkdown } from './scripts/input-md-formatting.js';
246import { AbortReason } from './scripts/util/AbortReason.js';246import { AbortReason } from './scripts/util/AbortReason.js';
247import { initSystemPrompts } from './scripts/sysprompt.js';
247248
248//exporting functions and vars for mods249//exporting functions and vars for mods
249export {250export {
@@ -934,6 +935,8 @@ async function firstLoadInit() {
934 initDefaultSlashCommands();935 initDefaultSlashCommands();
935 initTextGenModels();936 initTextGenModels();
936 initOpenAI();937 initOpenAI();
938 initSystemPrompts();
939 await initPresetManager();
937 await getSystemMessages();940 await getSystemMessages();
938 sendSystemMessage(system_message_types.WELCOME);941 sendSystemMessage(system_message_types.WELCOME);
939 sendSystemMessage(system_message_types.WELCOME_PROMPT);942 sendSystemMessage(system_message_types.WELCOME_PROMPT);
@@ -946,7 +949,6 @@ async function firstLoadInit() {
946 await getCharacters();949 await getCharacters();
947 await getBackgrounds();950 await getBackgrounds();
948 await initTokenizers();951 await initTokenizers();
949 await initPresetManager();
950 initBackgrounds();952 initBackgrounds();
951 initAuthorsNote();953 initAuthorsNote();
952 initPersonas();954 initPersonas();
@@ -3525,9 +3527,12 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3525 jailbreak,3527 jailbreak,
3526 } = getCharacterCardFields();3528 } = getCharacterCardFields();
35273529
3528 if (isInstruct) {3530 if (power_user.sysprompt.enabled) {
3529 system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.instruct.system_prompt, name1, name2);3531 system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.sysprompt.content, name1, name2);
3530 system = formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.instruct.system_prompt));3532 system = isInstruct ? formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.sysprompt.content)) : system;
3533 } else {
3534 // Nullify if it's not enabled
3535 system = '';
3531 }3536 }
35323537
3533 // Depth prompt (character-specific A/N)3538 // Depth prompt (character-specific A/N)
@@ -3780,7 +3785,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3780 personality: personality,3785 personality: personality,
3781 persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',3786 persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',
3782 scenario: scenario,3787 scenario: scenario,
3783 system: isInstruct ? system : '',3788 system: system,
3784 char: name2,3789 char: name2,
3785 user: name1,3790 user: name1,
3786 wiBefore: worldInfoBefore,3791 wiBefore: worldInfoBefore,
@@ -4354,7 +4359,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4354 this_max_context: this_max_context,4359 this_max_context: this_max_context,
4355 padding: power_user.token_padding,4360 padding: power_user.token_padding,
4356 main_api: main_api,4361 main_api: main_api,
4357 instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',4362 instruction: power_user.sysprompt.enabled ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.sysprompt.content) : '',
4358 userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),4363 userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
4359 tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '',4364 tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '',
4360 };4365 };
public/scripts/extensions/connection-manager/index.js+9 -0
@@ -34,6 +34,8 @@ const TC_COMMANDS = [
34 'preset',34 'preset',
35 'api-url',35 'api-url',
36 'model',36 'model',
37 'sysprompt',
38 'sysprompt-state',
37 'instruct',39 'instruct',
38 'context',40 'context',
39 'instruct-state',41 'instruct-state',
@@ -46,6 +48,8 @@ const FANCY_NAMES = {
46 'preset': 'Settings Preset',48 'preset': 'Settings Preset',
47 'model': 'Model',49 'model': 'Model',
48 'proxy': 'Proxy Preset',50 'proxy': 'Proxy Preset',
51 'sysprompt-state': 'Use System Prompt',
52 'sysprompt': 'System Prompt Name',
49 'instruct-state': 'Instruct Mode',53 'instruct-state': 'Instruct Mode',
50 'instruct': 'Instruct Template',54 'instruct': 'Instruct Template',
51 'context': 'Context Template',55 'context': 'Context Template',
@@ -181,6 +185,11 @@ async function readProfileFromCommands(mode, profile, cleanUp = false) {
181 }185 }
182186
183 if (cleanUp) {187 if (cleanUp) {
188 for (const command of commands) {
189 if (command.endsWith('-state') && profile[command] === 'false') {
190 delete profile[command.replace('-state', '')];
191 }
192 }
184 for (const command of opposingCommands) {193 for (const command of opposingCommands) {
185 if (commands.includes(command)) {194 if (commands.includes(command)) {
186 continue;195 continue;
public/scripts/instruct-mode.js+27 -53
@@ -23,7 +23,6 @@ export const names_behavior_types = {
23const controls = [23const controls = [
24 { id: 'instruct_enabled', property: 'enabled', isCheckbox: true },24 { id: 'instruct_enabled', property: 'enabled', isCheckbox: true },
25 { id: 'instruct_wrap', property: 'wrap', isCheckbox: true },25 { id: 'instruct_wrap', property: 'wrap', isCheckbox: true },
26 { id: 'instruct_system_prompt', property: 'system_prompt', isCheckbox: false },
27 { id: 'instruct_system_sequence_prefix', property: 'system_sequence_prefix', isCheckbox: false },26 { id: 'instruct_system_sequence_prefix', property: 'system_sequence_prefix', isCheckbox: false },
28 { id: 'instruct_system_sequence_suffix', property: 'system_sequence_suffix', isCheckbox: false },27 { id: 'instruct_system_sequence_suffix', property: 'system_sequence_suffix', isCheckbox: false },
29 { id: 'instruct_input_sequence', property: 'input_sequence', isCheckbox: false },28 { id: 'instruct_input_sequence', property: 'input_sequence', isCheckbox: false },
@@ -35,7 +34,6 @@ const controls = [
35 { id: 'instruct_last_system_sequence', property: 'last_system_sequence', isCheckbox: false },34 { id: 'instruct_last_system_sequence', property: 'last_system_sequence', isCheckbox: false },
36 { id: 'instruct_user_alignment_message', property: 'user_alignment_message', isCheckbox: false },35 { id: 'instruct_user_alignment_message', property: 'user_alignment_message', isCheckbox: false },
37 { id: 'instruct_stop_sequence', property: 'stop_sequence', isCheckbox: false },36 { id: 'instruct_stop_sequence', property: 'stop_sequence', isCheckbox: false },
38 { id: 'instruct_names', property: 'names', isCheckbox: true },
39 { id: 'instruct_first_output_sequence', property: 'first_output_sequence', isCheckbox: false },37 { id: 'instruct_first_output_sequence', property: 'first_output_sequence', isCheckbox: false },
40 { id: 'instruct_last_output_sequence', property: 'last_output_sequence', isCheckbox: false },38 { id: 'instruct_last_output_sequence', property: 'last_output_sequence', isCheckbox: false },
41 { id: 'instruct_first_input_sequence', property: 'first_input_sequence', isCheckbox: false },39 { id: 'instruct_first_input_sequence', property: 'first_input_sequence', isCheckbox: false },
@@ -43,7 +41,7 @@ const controls = [
43 { id: 'instruct_activation_regex', property: 'activation_regex', isCheckbox: false },41 { id: 'instruct_activation_regex', property: 'activation_regex', isCheckbox: false },
44 { id: 'instruct_bind_to_context', property: 'bind_to_context', isCheckbox: true },42 { id: 'instruct_bind_to_context', property: 'bind_to_context', isCheckbox: true },
45 { id: 'instruct_skip_examples', property: 'skip_examples', isCheckbox: true },43 { id: 'instruct_skip_examples', property: 'skip_examples', isCheckbox: true },
46 { id: 'instruct_names_behavior input[name="names_behavior"]', property: 'names_behavior', isCheckbox: false },44 { id: 'instruct_names_behavior', property: 'names_behavior', isCheckbox: false },
47 { id: 'instruct_system_same_as_user', property: 'system_same_as_user', isCheckbox: true, trigger: true },45 { id: 'instruct_system_same_as_user', property: 'system_same_as_user', isCheckbox: true, trigger: true },
48];46];
4947
@@ -109,9 +107,10 @@ export async function loadInstructMode(data) {
109107
110 if (control.isCheckbox) {108 if (control.isCheckbox) {
111 $element.prop('checked', power_user.instruct[control.property]);109 $element.prop('checked', power_user.instruct[control.property]);
112 } else if (control.property === 'names_behavior') {110 } else if ($element.is('select')) {
113 const behavior = power_user.instruct[control.property];111 const value = power_user.instruct[control.property];
114 $element.filter(`[value="${behavior}"]`).prop('checked', true);112 $element.val(value);
113 $element.filter(`[value="${value}"]`).prop('checked', true);
115 } else {114 } else {
116 $element.val(power_user.instruct[control.property]);115 $element.val(power_user.instruct[control.property]);
117 }116 }
@@ -137,12 +136,6 @@ export async function loadInstructMode(data) {
137 option.selected = name === power_user.instruct.preset;136 option.selected = name === power_user.instruct.preset;
138 $('#instruct_presets').append(option);137 $('#instruct_presets').append(option);
139 });138 });
140
141 highlightDefaultPreset();
142}
143
144function highlightDefaultPreset() {
145 $('#instruct_set_default').toggleClass('default', power_user.default_instruct === power_user.instruct.preset);
146}139}
147140
148/**141/**
@@ -159,13 +152,6 @@ export function selectContextPreset(preset, { quiet = false, isAuto = false } =
159 !quiet && toastr.info(`Context Template: "${preset}" ${isAuto ? 'auto-' : ''}selected`);152 !quiet && toastr.info(`Context Template: "${preset}" ${isAuto ? 'auto-' : ''}selected`);
160 }153 }
161154
162 // If instruct mode is disabled, enable it, except for default context template
163 if (!power_user.instruct.enabled && preset !== power_user.default_context) {
164 power_user.instruct.enabled = true;
165 $('#instruct_enabled').prop('checked', true).trigger('change');
166 !quiet && toastr.info('Instruct Mode enabled');
167 }
168
169 saveSettingsDebounced();155 saveSettingsDebounced();
170}156}
171157
@@ -235,13 +221,6 @@ export function autoSelectInstructPreset(modelId) {
235 }221 }
236 }222 }
237 }223 }
238
239 if (power_user.instruct.bind_to_context && power_user.default_instruct && power_user.instruct.preset !== power_user.default_instruct) {
240 if (instruct_presets.some(p => p.name === power_user.default_instruct)) {
241 console.log(`Instruct mode: default preset "${power_user.default_instruct}" selected`);
242 $('#instruct_presets').val(power_user.default_instruct).trigger('change');
243 }
244 }
245 }224 }
246225
247 return false;226 return false;
@@ -580,10 +559,6 @@ function selectMatchingContextTemplate(name) {
580 break;559 break;
581 }560 }
582 }561 }
583 if (!foundMatch) {
584 // If no match was found, select default context preset
585 selectContextPreset(power_user.default_context, { isAuto: true });
586 }
587}562}
588563
589/**564/**
@@ -597,9 +572,13 @@ export function replaceInstructMacros(input, env) {
597 if (!input) {572 if (!input) {
598 return '';573 return '';
599 }574 }
575
576 const syspromptMacros = {
577 'systemPrompt': (power_user.prefer_character_prompt && env.charPrompt ? env.charPrompt : power_user.sysprompt.content),
578 'defaultSystemPrompt|instructSystem|instructSystemPrompt': power_user.sysprompt.content,
579 };
580
600 const instructMacros = {581 const instructMacros = {
601 'systemPrompt': (power_user.prefer_character_prompt && env.charPrompt ? env.charPrompt : power_user.instruct.system_prompt),
602 'instructSystem|instructSystemPrompt': power_user.instruct.system_prompt,
603 'instructSystemPromptPrefix': power_user.instruct.system_sequence_prefix,582 'instructSystemPromptPrefix': power_user.instruct.system_sequence_prefix,
604 'instructSystemPromptSuffix': power_user.instruct.system_sequence_suffix,583 'instructSystemPromptSuffix': power_user.instruct.system_sequence_suffix,
605 'instructInput|instructUserPrefix': power_user.instruct.input_sequence,584 'instructInput|instructUserPrefix': power_user.instruct.input_sequence,
@@ -622,6 +601,11 @@ export function replaceInstructMacros(input, env) {
622 input = input.replace(regex, power_user.instruct.enabled ? value : '');601 input = input.replace(regex, power_user.instruct.enabled ? value : '');
623 }602 }
624603
604 for (const [placeholder, value] of Object.entries(syspromptMacros)) {
605 const regex = new RegExp(`{{(${placeholder})}}`, 'gi');
606 input = input.replace(regex, power_user.sysprompt.enabled ? value : '');
607 }
608
625 input = input.replace(/{{exampleSeparator}}/gi, power_user.context.example_separator);609 input = input.replace(/{{exampleSeparator}}/gi, power_user.context.example_separator);
626 input = input.replace(/{{chatStart}}/gi, power_user.context.chat_start);610 input = input.replace(/{{chatStart}}/gi, power_user.context.chat_start);
627611
@@ -629,20 +613,6 @@ export function replaceInstructMacros(input, env) {
629}613}
630614
631jQuery(() => {615jQuery(() => {
632 $('#instruct_set_default').on('click', function () {
633 if (power_user.instruct.preset === power_user.default_instruct) {
634 power_user.default_instruct = null;
635 $(this).removeClass('default');
636 toastr.info('Default instruct template cleared');
637 } else {
638 power_user.default_instruct = power_user.instruct.preset;
639 $(this).addClass('default');
640 toastr.info(`Default instruct template set to ${power_user.default_instruct}`);
641 }
642
643 saveSettingsDebounced();
644 });
645
646 $('#instruct_system_same_as_user').on('input', function () {616 $('#instruct_system_same_as_user').on('input', function () {
647 const state = !!$(this).prop('checked');617 const state = !!$(this).prop('checked');
648 if (state) {618 if (state) {
@@ -671,9 +641,6 @@ jQuery(() => {
671 // When instruct mode gets enabled, select context template matching selected instruct preset641 // When instruct mode gets enabled, select context template matching selected instruct preset
672 if (power_user.instruct.enabled) {642 if (power_user.instruct.enabled) {
673 selectMatchingContextTemplate(power_user.instruct.preset);643 selectMatchingContextTemplate(power_user.instruct.preset);
674 } else {
675 // When instruct mode gets disabled, select default context preset
676 selectContextPreset(power_user.default_context);
677 }644 }
678 });645 });
679646
@@ -699,9 +666,10 @@ jQuery(() => {
699666
700 if (control.isCheckbox) {667 if (control.isCheckbox) {
701 $element.prop('checked', power_user.instruct[control.property]).trigger('input');668 $element.prop('checked', power_user.instruct[control.property]).trigger('input');
702 } else if (control.property === 'names_behavior') {669 } else if ($element.is('select')) {
703 const behavior = power_user.instruct[control.property];670 const value = power_user.instruct[control.property];
704 $element.filter(`[value="${behavior}"]`).prop('checked', true).trigger('input');671 $element.val(value);
672 $element.filter(`[value="${value}"]`).prop('checked', true).trigger('input');
705 } else {673 } else {
706 $element.val(power_user.instruct[control.property]);674 $element.val(power_user.instruct[control.property]);
707 $element.trigger('input');675 $element.trigger('input');
@@ -713,7 +681,13 @@ jQuery(() => {
713 // Select matching context template681 // Select matching context template
714 selectMatchingContextTemplate(name);682 selectMatchingContextTemplate(name);
715 }683 }
684 });
716685
717 highlightDefaultPreset();686 if (!CSS.supports('field-sizing', 'content')) {
687 $('#InstructSequencesColumn details').on('toggle', function () {
688 if ($(this).prop('open')) {
689 resetScrollHeight($(this).find('textarea'));
690 }
718 });691 });
692 }
719});693});
public/scripts/power-user.js+8 -24
@@ -50,6 +50,7 @@ import { AUTOCOMPLETE_SELECT_KEY, AUTOCOMPLETE_WIDTH } from './autocomplete/Auto
50import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';50import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
51import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';51import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
52import { POPUP_TYPE, callGenericPopup } from './popup.js';52import { POPUP_TYPE, callGenericPopup } from './popup.js';
53import { loadSystemPrompts } from './sysprompt.js';
5354
54export {55export {
55 loadPowerUserSettings,56 loadPowerUserSettings,
@@ -205,11 +206,9 @@ let power_user = {
205 disable_group_trimming: false,206 disable_group_trimming: false,
206 single_line: false,207 single_line: false,
207208
208 default_instruct: '',
209 instruct: {209 instruct: {
210 enabled: false,210 enabled: false,
211 preset: 'Alpaca',211 preset: 'Alpaca',
212 system_prompt: 'Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}\'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n',
213 input_sequence: '### Instruction:',212 input_sequence: '### Instruction:',
214 input_suffix: '',213 input_suffix: '',
215 output_sequence: '### Response:',214 output_sequence: '### Response:',
@@ -233,7 +232,6 @@ let power_user = {
233 separator_sequence: '',232 separator_sequence: '',
234 },233 },
235234
236 default_context: 'Default',
237 context: {235 context: {
238 preset: 'Default',236 preset: 'Default',
239 story_string: defaultStoryString,237 story_string: defaultStoryString,
@@ -244,6 +242,12 @@ let power_user = {
244 names_as_stop_strings: true,242 names_as_stop_strings: true,
245 },243 },
246244
245 sysprompt: {
246 enabled: true,
247 name: 'Neutral - Chat',
248 content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.',
249 },
250
247 personas: {},251 personas: {},
248 default_persona: null,252 default_persona: null,
249 persona_descriptions: {},253 persona_descriptions: {},
@@ -1595,6 +1599,7 @@ async function loadPowerUserSettings(settings, data) {
1595 reloadMarkdownProcessor(power_user.render_formulas);1599 reloadMarkdownProcessor(power_user.render_formulas);
1596 await loadInstructMode(data);1600 await loadInstructMode(data);
1597 await loadContextSettings();1601 await loadContextSettings();
1602 await loadSystemPrompts(data);
1598 loadMaxContextUnlocked();1603 loadMaxContextUnlocked();
1599 switchWaifuMode();1604 switchWaifuMode();
1600 switchSpoilerMode();1605 switchSpoilerMode();
@@ -1814,29 +1819,8 @@ async function loadContextSettings() {
1814 }1819 }
1815 }1820 }
18161821
1817 highlightDefaultContext();
1818 saveSettingsDebounced();1822 saveSettingsDebounced();
1819 });1823 });
1820
1821 $('#context_set_default').on('click', function () {
1822 if (power_user.context.preset !== power_user.default_context) {
1823 power_user.default_context = power_user.context.preset;
1824 $(this).addClass('default');
1825 toastr.info(`Default context template set to ${power_user.default_context}`);
1826
1827 highlightDefaultContext();
1828
1829 saveSettingsDebounced();
1830 }
1831 });
1832
1833 highlightDefaultContext();
1834}
1835
1836function highlightDefaultContext() {
1837 $('#context_set_default').toggleClass('default', power_user.default_context === power_user.context.preset);
1838 $('#context_set_default').toggleClass('disabled', power_user.default_context === power_user.context.preset);
1839 $('#context_delete_preset').toggleClass('disabled', power_user.default_context === power_user.context.preset);
1840}1824}
18411825
1842/**1826/**
public/scripts/preset-manager.js+259 -9
@@ -18,13 +18,15 @@ import {
18import { groups, selected_group } from './group-chats.js';18import { groups, selected_group } from './group-chats.js';
19import { instruct_presets } from './instruct-mode.js';19import { instruct_presets } from './instruct-mode.js';
20import { kai_settings } from './kai-settings.js';20import { kai_settings } from './kai-settings.js';
21import { Popup } from './popup.js';21import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
22import { context_presets, getContextSettings, power_user } from './power-user.js';22import { context_presets, getContextSettings, power_user } from './power-user.js';
23import { SlashCommand } from './slash-commands/SlashCommand.js';23import { SlashCommand } from './slash-commands/SlashCommand.js';
24import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';24import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
25import { enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';25import { enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
26import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';26import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
27import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';27import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
28import { checkForSystemPromptInInstructTemplate, system_prompts } from './sysprompt.js';
29import { renderTemplateAsync } from './templates.js';
28import {30import {
29 textgenerationwebui_preset_names,31 textgenerationwebui_preset_names,
30 textgenerationwebui_presets,32 textgenerationwebui_presets,
@@ -71,7 +73,7 @@ function autoSelectPreset() {
71 * @param {string} apiId API id73 * @param {string} apiId API id
72 * @returns {PresetManager} Preset manager74 * @returns {PresetManager} Preset manager
73 */75 */
74function getPresetManager(apiId = '') {76export function getPresetManager(apiId = '') {
75 if (!apiId) {77 if (!apiId) {
76 apiId = main_api == 'koboldhorde' ? 'kobold' : main_api;78 apiId = main_api == 'koboldhorde' ? 'kobold' : main_api;
77 }79 }
@@ -102,6 +104,215 @@ class PresetManager {
102 this.apiId = apiId;104 this.apiId = apiId;
103 }105 }
104106
107 static masterSections = {
108 'instruct': {
109 name: 'Instruct Template',
110 getData: () => {
111 const manager = getPresetManager('instruct');
112 const name = manager.getSelectedPresetName();
113 return manager.getPresetSettings(name);
114 },
115 setData: (data) => {
116 const manager = getPresetManager('instruct');
117 const name = data.name;
118 return manager.savePreset(name, data);
119 },
120 isValid: (data) => PresetManager.isPossiblyInstructData(data),
121 },
122 'context': {
123 name: 'Context Template',
124 getData: () => {
125 const manager = getPresetManager('context');
126 const name = manager.getSelectedPresetName();
127 return manager.getPresetSettings(name);
128 },
129 setData: (data) => {
130 const manager = getPresetManager('context');
131 const name = data.name;
132 return manager.savePreset(name, data);
133 },
134 isValid: (data) => PresetManager.isPossiblyContextData(data),
135 },
136 'sysprompt': {
137 name: 'System Prompt',
138 getData: () => {
139 const manager = getPresetManager('sysprompt');
140 const name = manager.getSelectedPresetName();
141 return manager.getPresetSettings(name);
142 },
143 setData: (data) => {
144 const manager = getPresetManager('sysprompt');
145 const name = data.name;
146 return manager.savePreset(name, data);
147 },
148 isValid: (data) => PresetManager.isPossiblySystemPromptData(data),
149 },
150 'preset': {
151 name: 'Text Completion Preset',
152 getData: () => {
153 const manager = getPresetManager('textgenerationwebui');
154 const name = manager.getSelectedPresetName();
155 const data = manager.getPresetSettings(name);
156 data['name'] = name;
157 return data;
158 },
159 setData: (data) => {
160 const manager = getPresetManager('textgenerationwebui');
161 const name = data.name;
162 return manager.savePreset(name, data);
163 },
164 isValid: (data) => PresetManager.isPossiblyTextCompletionData(data),
165 },
166 };
167
168 static isPossiblyInstructData(data) {
169 const instructProps = ['name', 'input_sequence', 'output_sequence'];
170 return data && instructProps.every(prop => Object.keys(data).includes(prop));
171 }
172
173 static isPossiblyContextData(data) {
174 const contextProps = ['name', 'story_string'];
175 return data && contextProps.every(prop => Object.keys(data).includes(prop));
176 }
177
178 static isPossiblySystemPromptData(data) {
179 const sysPromptProps = ['name', 'content'];
180 return data && sysPromptProps.every(prop => Object.keys(data).includes(prop));
181 }
182
183 static isPossiblyTextCompletionData(data) {
184 const textCompletionProps = ['temp', 'top_k', 'top_p', 'rep_pen'];
185 return data && textCompletionProps.every(prop => Object.keys(data).includes(prop));
186 }
187
188 /**
189 * Imports master settings from JSON data.
190 * @param {object} data Data to import
191 * @param {string} fileName File name
192 * @returns {Promise<void>}
193 */
194 static async performMasterImport(data, fileName) {
195 if (!data || typeof data !== 'object') {
196 toastr.error('Invalid data provided for master import');
197 return;
198 }
199
200 // Check for legacy file imports
201 // 1. Instruct Template
202 if (this.isPossiblyInstructData(data)) {
203 toastr.info('Importing instruct template...', 'Instruct template detected');
204 return await getPresetManager('instruct').savePreset(data.name, data);
205 }
206
207 // 2. Context Template
208 if (this.isPossiblyContextData(data)) {
209 toastr.info('Importing as context template...', 'Context template detected');
210 return await getPresetManager('context').savePreset(data.name, data);
211 }
212
213 // 3. System Prompt
214 if (this.isPossiblySystemPromptData(data)) {
215 toastr.info('Importing as system prompt...', 'System prompt detected');
216 return await getPresetManager('sysprompt').savePreset(data.name, data);
217 }
218
219 // 4. Text Completion settings
220 if (this.isPossiblyTextCompletionData(data)) {
221 toastr.info('Importing as settings preset...', 'Text Completion settings detected');
222 return await getPresetManager('textgenerationwebui').savePreset(fileName, data);
223 }
224
225 const validSections = [];
226 for (const [key, section] of Object.entries(this.masterSections)) {
227 if (key in data && section.isValid(data[key])) {
228 validSections.push(key);
229 }
230 }
231
232 if (validSections.length === 0) {
233 toastr.error('No valid sections found in imported data');
234 return;
235 }
236
237 const sectionNames = validSections.reduce((acc, key) => {
238 acc[key] = { key: key, name: this.masterSections[key].name, preset: data[key]?.name || '' };
239 return acc;
240 }, {});
241
242 const html = $(await renderTemplateAsync('masterImport', { sections: sectionNames }));
243 const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', {
244 okButton: 'Import',
245 cancelButton: 'Cancel',
246 });
247
248 const result = await popup.show();
249
250 // Import cancelled
251 if (result !== POPUP_RESULT.AFFIRMATIVE) {
252 return;
253 }
254
255 const importedSections = [];
256 const confirmedSections = html.find('input:checked').map((_, el) => el instanceof HTMLInputElement && el.value).get();
257
258 if (confirmedSections.length === 0) {
259 toastr.info('No sections selected for import');
260 return;
261 }
262
263 for (const section of confirmedSections) {
264 const sectionData = data[section];
265 const masterSection = this.masterSections[section];
266 if (sectionData && masterSection) {
267 await masterSection.setData(sectionData);
268 importedSections.push(masterSection.name);
269 }
270 }
271
272 toastr.success(`Imported ${importedSections.length} settings: ${importedSections.join(', ')}`);
273 }
274
275 /**
276 * Exports master settings to JSON data.
277 * @returns {Promise<string>} JSON data
278 */
279 static async performMasterExport() {
280 const sectionNames = Object.entries(this.masterSections).reduce((acc, [key, section]) => {
281 acc[key] = { key: key, name: section.name, checked: key !== 'preset' };
282 return acc;
283 }, {});
284 const html = $(await renderTemplateAsync('masterExport', { sections: sectionNames }));
285
286 const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', {
287 okButton: 'Export',
288 cancelButton: 'Cancel',
289 });
290
291 const result = await popup.show();
292
293 // Export cancelled
294 if (result !== POPUP_RESULT.AFFIRMATIVE) {
295 return;
296 }
297
298 const confirmedSections = html.find('input:checked').map((_, el) => el instanceof HTMLInputElement && el.value).get();
299 const data = {};
300
301 if (confirmedSections.length === 0) {
302 toastr.info('No sections selected for export');
303 return;
304 }
305
306 for (const section of confirmedSections) {
307 const masterSection = this.masterSections[section];
308 if (masterSection) {
309 data[section] = masterSection.getData();
310 }
311 }
312
313 return JSON.stringify(data, null, 4);
314 }
315
105 /**316 /**
106 * Gets all preset names.317 * Gets all preset names.
107 * @returns {string[]} List of preset names318 * @returns {string[]} List of preset names
@@ -182,6 +393,10 @@ class PresetManager {
182 }393 }
183394
184 async savePreset(name, settings) {395 async savePreset(name, settings) {
396 if (this.apiId === 'instruct' && settings) {
397 await checkForSystemPromptInInstructTemplate(name, settings);
398 }
399
185 const preset = settings ?? this.getPresetSettings(name);400 const preset = settings ?? this.getPresetSettings(name);
186401
187 const response = await fetch('/api/presets/save', {402 const response = await fetch('/api/presets/save', {
@@ -228,6 +443,10 @@ class PresetManager {
228 presets = instruct_presets;443 presets = instruct_presets;
229 preset_names = instruct_presets.map(x => x.name);444 preset_names = instruct_presets.map(x => x.name);
230 break;445 break;
446 case 'sysprompt':
447 presets = system_prompts;
448 preset_names = system_prompts.map(x => x.name);
449 break;
231 default:450 default:
232 console.warn(`Unknown API ID ${this.apiId}`);451 console.warn(`Unknown API ID ${this.apiId}`);
233 }452 }
@@ -236,11 +455,11 @@ class PresetManager {
236 }455 }
237456
238 isKeyedApi() {457 isKeyedApi() {
239 return this.apiId == 'textgenerationwebui' || this.apiId == 'context' || this.apiId == 'instruct';458 return this.apiId == 'textgenerationwebui' || this.isAdvancedFormatting();
240 }459 }
241460
242 isAdvancedFormatting() {461 isAdvancedFormatting() {
243 return this.apiId == 'context' || this.apiId == 'instruct';462 return this.apiId == 'context' || this.apiId == 'instruct' || this.apiId == 'sysprompt';
244 }463 }
245464
246 updateList(name, preset) {465 updateList(name, preset) {
@@ -298,6 +517,11 @@ class PresetManager {
298 instruct_preset['name'] = name || power_user.instruct.preset;517 instruct_preset['name'] = name || power_user.instruct.preset;
299 return instruct_preset;518 return instruct_preset;
300 }519 }
520 case 'sysprompt': {
521 const sysprompt_preset = structuredClone(power_user.sysprompt);
522 sysprompt_preset['name'] = name || power_user.sysprompt.preset;
523 return sysprompt_preset;
524 }
301 default:525 default:
302 console.warn(`Unknown API ID ${apiId}`);526 console.warn(`Unknown API ID ${apiId}`);
303 return {};527 return {};
@@ -601,11 +825,6 @@ export async function initPresetManager() {
601 return;825 return;
602 }826 }
603827
604 // default context preset cannot be deleted
605 if (apiId == 'context' && power_user.default_context === power_user.context.preset) {
606 return;
607 }
608
609 const headerText = !presetManager.isAdvancedFormatting() ? 'Delete this preset?' : 'Delete this template?';828 const headerText = !presetManager.isAdvancedFormatting() ? 'Delete this preset?' : 'Delete this template?';
610 const confirm = await Popup.show.confirm(headerText, 'This action is irreversible and your current settings will be overwritten.');829 const confirm = await Popup.show.confirm(headerText, 'This action is irreversible and your current settings will be overwritten.');
611 if (!confirm) {830 if (!confirm) {
@@ -682,4 +901,35 @@ export async function initPresetManager() {
682 toastr.success(successToast);901 toastr.success(successToast);
683 }902 }
684 });903 });
904
905 $('#af_master_import').on('click', () => {
906 $('#af_master_import_file').trigger('click');
907 });
908
909 $('#af_master_import_file').on('change', async function (e) {
910 if (!(e.target instanceof HTMLInputElement)) {
911 return;
912 }
913 const file = e.target.files[0];
914
915 if (!file) {
916 return;
917 }
918
919 const data = await parseJsonFile(file);
920 const fileName = file.name.replace('.json', '');
921 await PresetManager.performMasterImport(data, fileName);
922 e.target.value = null;
923 });
924
925 $('#af_master_export').on('click', async () => {
926 const data = await PresetManager.performMasterExport();
927
928 if (!data) {
929 return;
930 }
931
932 const shortDate = new Date().toISOString().split('T')[0];
933 download(data, `ST-formatting-${shortDate}.json`, 'application/json');
934 });
685}935}
public/scripts/sysprompt.js+250 -0
@@ -0,0 +1,250 @@
1import { saveSettingsDebounced } from '../script.js';
2import { callGenericPopup, POPUP_TYPE } from './popup.js';
3import { power_user } from './power-user.js';
4import { getPresetManager } from './preset-manager.js';
5import { SlashCommand } from './slash-commands/SlashCommand.js';
6import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
7import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
8import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
9import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
10import { renderTemplateAsync } from './templates.js';
11import { isTrueBoolean, resetScrollHeight } from './utils.js';
12
13export let system_prompts = [];
14
15const $enabled = $('#sysprompt_enabled');
16const $select = $('#sysprompt_select');
17const $content = $('#sysprompt_content');
18const $contentBlock = $('#SystemPromptBlock');
19
20async function migrateSystemPromptFromInstructMode() {
21 if ('system_prompt' in power_user.instruct) {
22 const prompt = String(power_user.instruct.system_prompt);
23 delete power_user.instruct.system_prompt;
24 power_user.sysprompt.enabled = power_user.instruct.enabled;
25 power_user.sysprompt.content = prompt;
26
27 const existingPromptName = system_prompts.find(x => x.content === prompt)?.name;
28
29 if (existingPromptName) {
30 power_user.sysprompt.name = existingPromptName;
31 } else {
32 const data = { name: `[Migrated] ${power_user.instruct.preset}`, content: prompt };
33 await getPresetManager('sysprompt')?.savePreset(data.name, data);
34 power_user.sysprompt.name = data.name;
35 }
36
37 saveSettingsDebounced();
38 toastr.info('System prompt settings have been moved from the Instruct Mode.', 'Migration notice', { timeOut: 5000 });
39 }
40}
41
42/**
43 * Loads sysprompt settings from the given data object.
44 * @param {object} data Settings data object.
45 */
46export async function loadSystemPrompts(data) {
47 if (data.sysprompt !== undefined) {
48 system_prompts = data.sysprompt;
49 }
50
51 await migrateSystemPromptFromInstructMode();
52 toggleSystemPromptDisabledControls();
53
54 for (const prompt of system_prompts) {
55 $('<option>').val(prompt.name).text(prompt.name).appendTo($select);
56 }
57
58 $enabled.prop('checked', power_user.sysprompt.enabled);
59 $select.val(power_user.sysprompt.name);
60 $content.val(power_user.sysprompt.content);
61 if (!CSS.supports('field-sizing', 'content')) {
62 await resetScrollHeight($content);
63 }
64}
65
66/**
67 * Checks if the instruct template has a system prompt and prompts the user to save it as a system prompt.
68 * @param {string} name Name of the instruct template
69 * @param {object} template Instruct template object
70 */
71export async function checkForSystemPromptInInstructTemplate(name, template) {
72 if (!template || !name || typeof name !== 'string' || typeof template !== 'object') {
73 return;
74 }
75 if ('system_prompt' in template && template.system_prompt) {
76 const existingName = system_prompts.find(x => x.content === template.system_prompt)?.name;
77 const html = await renderTemplateAsync('migrateInstructPrompt', { prompt: template.system_prompt, existing: existingName });
78 const confirm = await callGenericPopup(html, POPUP_TYPE.CONFIRM);
79 if (confirm) {
80 const migratedName = `[Migrated] ${name}`;
81 const prompt = { name: migratedName, content: template.system_prompt };
82 const presetManager = getPresetManager('sysprompt');
83 await presetManager.savePreset(migratedName, prompt);
84 toastr.success(`System prompt "${migratedName}" has been saved.`);
85 } else {
86 toastr.info('System prompt has been discarded.');
87 }
88
89 delete template.system_prompt;
90 }
91}
92
93function toggleSystemPromptDisabledControls() {
94 $enabled.parent().find('i').toggleClass('toggleEnabled', !!power_user.sysprompt.enabled);
95 $contentBlock.toggleClass('disabled', !power_user.sysprompt.enabled);
96}
97
98/**
99 * Sets the system prompt state.
100 * @param {boolean} state System prompt state
101 * @returns {string} Empty string
102 */
103function setSystemPromptStateCallback(state) {
104 power_user.sysprompt.enabled = state;
105 $enabled.prop('checked', state);
106 toggleSystemPromptDisabledControls();
107 saveSettingsDebounced();
108 return '';
109}
110
111function toggleSystemPromptCallback(_args, state) {
112 if (!state || typeof state !== 'string') {
113 return String(power_user.sysprompt.enabled);
114 }
115
116 const newState = isTrueBoolean(state);
117 setSystemPromptStateCallback(newState);
118 return String(power_user.sysprompt.enabled);
119}
120
121function selectSystemPromptCallback(args, name) {
122 if (!power_user.sysprompt.enabled && !isTrueBoolean(args.forceGet)) {
123 return '';
124 }
125
126 if (!name) {
127 return power_user.sysprompt.name ?? '';
128 }
129
130 const quiet = isTrueBoolean(args?.quiet);
131 const systemPromptNames = system_prompts.map(preset => preset.name);
132 let foundName = systemPromptNames.find(x => x.toLowerCase() === name.toLowerCase());
133
134 if (!foundName) {
135 const fuse = new Fuse(systemPromptNames);
136 const result = fuse.search(name);
137
138 if (result.length === 0) {
139 !quiet && toastr.warning(`System prompt "${name}" not found`);
140 return '';
141 }
142
143 foundName = result[0].item;
144 }
145
146 $select.val(foundName).trigger('input');
147 !quiet && toastr.success(`System prompt "${foundName}" selected`);
148 return foundName;
149}
150
151export function initSystemPrompts() {
152 $enabled.on('input', function () {
153 power_user.sysprompt.enabled = !!$(this).prop('checked');
154 toggleSystemPromptDisabledControls();
155 saveSettingsDebounced();
156 });
157
158 $select.on('change', async function () {
159 if (!power_user.sysprompt.enabled) {
160 $enabled.prop('checked', true).trigger('input');
161 }
162
163 const name = String($(this).val());
164 const prompt = system_prompts.find(p => p.name === name);
165 if (prompt) {
166 $content.val(prompt.content);
167 if (!CSS.supports('field-sizing', 'content')) {
168 await resetScrollHeight($content);
169 }
170
171 power_user.sysprompt.name = name;
172 power_user.sysprompt.content = prompt.content;
173 }
174 saveSettingsDebounced();
175 });
176
177 $content.on('input', function () {
178 power_user.sysprompt.content = String($(this).val());
179 saveSettingsDebounced();
180 });
181
182 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
183 name: 'sysprompt',
184 aliases: ['system-prompt'],
185 callback: selectSystemPromptCallback,
186 returns: 'current prompt name',
187 namedArgumentList: [
188 SlashCommandNamedArgument.fromProps({
189 name: 'quiet',
190 description: 'Suppress the toast message on prompt change',
191 typeList: [ARGUMENT_TYPE.BOOLEAN],
192 defaultValue: 'false',
193 enumList: commonEnumProviders.boolean('trueFalse')(),
194 }),
195 SlashCommandNamedArgument.fromProps({
196 name: 'forceGet',
197 description: 'Force getting a name even if system prompt is disabled',
198 typeList: [ARGUMENT_TYPE.BOOLEAN],
199 defaultValue: 'false',
200 enumList: commonEnumProviders.boolean('trueFalse')(),
201 }),
202 ],
203 unnamedArgumentList: [
204 SlashCommandArgument.fromProps({
205 description: 'system prompt name',
206 typeList: [ARGUMENT_TYPE.STRING],
207 enumProvider: () => system_prompts.map(x => new SlashCommandEnumValue(x.name, null, enumTypes.enum, enumIcons.preset)),
208 }),
209 ],
210 helpString: `
211 <div>
212 Selects a system prompt by name, using fuzzy search to find the closest match.
213 Gets the current system prompt if no name is provided and sysprompt is enabled or <code>forceGet=true</code> is passed.
214 </div>
215 <div>
216 <strong>Example:</strong>
217 <ul>
218 <li>
219 <pre><code class="language-stscript">/sysprompt </code></pre>
220 </li>
221 </ul>
222 </div>
223 `,
224 }));
225 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
226 name: 'sysprompt-on',
227 aliases: ['sysprompt-enable'],
228 callback: () => setSystemPromptStateCallback(true),
229 helpString: 'Enables system prompt.',
230 }));
231 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
232 name: 'sysprompt-off',
233 aliases: ['sysprompt-disable'],
234 callback: () => setSystemPromptStateCallback(false),
235 helpString: 'Disables system prompt',
236 }));
237 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
238 name: 'sysprompt-state',
239 aliases: ['sysprompt-toggle'],
240 helpString: 'Gets the current system prompt state. If an argument is provided, it will set the system prompt state.',
241 unnamedArgumentList: [
242 SlashCommandArgument.fromProps({
243 description: 'system prompt state',
244 typeList: [ARGUMENT_TYPE.BOOLEAN],
245 enumList: commonEnumProviders.boolean('trueFalse')(),
246 }),
247 ],
248 callback: toggleSystemPromptCallback,
249 }));
250}
public/scripts/templates/macros.html+2 -2
@@ -57,8 +57,8 @@
57 <li><tt>&lcub;&lcub;maxPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_41">max allowed prompt length in tokens = (context size - response length)</span></li>57 <li><tt>&lcub;&lcub;maxPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_41">max allowed prompt length in tokens = (context size - response length)</span></li>
58 <li><tt>&lcub;&lcub;exampleSeparator&rcub;&rcub;</tt> – <span data-i18n="help_macros_42">context template example dialogues separator</span></li>58 <li><tt>&lcub;&lcub;exampleSeparator&rcub;&rcub;</tt> – <span data-i18n="help_macros_42">context template example dialogues separator</span></li>
59 <li><tt>&lcub;&lcub;chatStart&rcub;&rcub;</tt> – <span data-i18n="help_macros_43">context template chat start line</span></li>59 <li><tt>&lcub;&lcub;chatStart&rcub;&rcub;</tt> – <span data-i18n="help_macros_43">context template chat start line</span></li>
60 <li><tt>&lcub;&lcub;systemPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_44">main system prompt (either character prompt override if chosen, or instructSystemPrompt)</span></li>60 <li><tt>&lcub;&lcub;systemPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_44">system prompt content if enabled (either character prompt override if allowed, or defaultSystemPrompt)</span></li>
61 <li><tt>&lcub;&lcub;instructSystemPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_45">instruct system prompt</span></li>61 <li><tt>&lcub;&lcub;defaultSystemPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_45">system prompt content</span></li>
62 <li><tt>&lcub;&lcub;instructSystemPromptPrefix&rcub;&rcub;</tt> – <span data-i18n="help_macros_46">instruct system prompt prefix sequence</span></li>62 <li><tt>&lcub;&lcub;instructSystemPromptPrefix&rcub;&rcub;</tt> – <span data-i18n="help_macros_46">instruct system prompt prefix sequence</span></li>
63 <li><tt>&lcub;&lcub;instructSystemPromptSuffix&rcub;&rcub;</tt> – <span data-i18n="help_macros_47">instruct system prompt suffix sequence</span></li>63 <li><tt>&lcub;&lcub;instructSystemPromptSuffix&rcub;&rcub;</tt> – <span data-i18n="help_macros_47">instruct system prompt suffix sequence</span></li>
64 <li><tt>&lcub;&lcub;instructUserPrefix&rcub;&rcub;</tt> – <span data-i18n="help_macros_48">instruct user prefix sequence</span></li>64 <li><tt>&lcub;&lcub;instructUserPrefix&rcub;&rcub;</tt> – <span data-i18n="help_macros_48">instruct user prefix sequence</span></li>
public/scripts/templates/masterExport.html+17 -0
@@ -0,0 +1,17 @@
1<h3>
2 Choose what to export
3</h3>
4<div class="flex-container flexFlowColumn justifyLeft">
5 {{#each sections}}
6 {{#with this}}
7 <label class="checkbox_label">
8 {{#if checked}}
9 <input type="checkbox" value="{{key}}" checked />
10 {{else}}
11 <input type="checkbox" value="{{key}}" />
12 {{/if}}
13 <span data-i18n="{{name}}">{{name}}</span>
14 </label>
15 {{/with}}
16 {{/each}}
17</div>
public/scripts/templates/masterImport.html+17 -0
@@ -0,0 +1,17 @@
1<h3>
2 Choose what to import
3</h3>
4<div class="flex-container flexFlowColumn justifyLeft">
5 {{#each sections}}
6 {{#with this}}
7 <label class="checkbox_label">
8 <input type="checkbox" value="{{key}}" checked>
9 <span data-i18n="{{name}}">{{name}}</span>
10 {{#if preset}}
11 <span>&ndash;</span>
12 <small>{{preset}}</small>
13 {{/if}}
14 </label>
15 {{/with}}
16 {{/each}}
17</div>
public/scripts/templates/migrateInstructPrompt.html+27 -0
@@ -0,0 +1,27 @@
1<h3>
2 This instruct template also contains a system prompt.
3</h3>
4<div>
5 Would you like to migrate the system prompt from the template?
6</div>
7{{#if existing}}
8<div class="marginTopBot5">
9 <b>Note:</b>
10 <span>you already have this prompt saved as:</span>
11 <span>{{existing}}</span>
12</div>
13{{/if}}
14<div class="justifyLeft marginTop5">
15 <div>
16 <small>
17 <b>"Yes"</b> &ndash; The prompt will be imported and selected as a current system prompt.
18 </small>
19
20 </div>
21 <div>
22 <small>
23 <b>"No"</b> &ndash; The prompt will be ignored, no changes to the current system prompt.
24 </small>
25 </div>
26</div>
27<textarea class="wide100p textarea_compact" rows="10">{{prompt}}</textarea>
public/style.css+10 -9
@@ -2573,7 +2573,7 @@ select {
2573 -moz-appearance: none;2573 -moz-appearance: none;
2574 -webkit-appearance: none;2574 -webkit-appearance: none;
2575 appearance: none;2575 appearance: none;
2576 padding: 3px 2px;2576 padding: 3px 5px;
2577 background-color: var(--black30a);2577 background-color: var(--black30a);
2578 border: 1px solid var(--SmartThemeBorderColor);2578 border: 1px solid var(--SmartThemeBorderColor);
2579 border-radius: 5px;2579 border-radius: 5px;
@@ -2630,14 +2630,6 @@ select option:not(:checked) {
2630 color: var(--active) !important;2630 color: var(--active) !important;
2631}2631}
26322632
2633#context_set_default.default {
2634 color: var(--preferred) !important;
2635}
2636
2637#instruct_set_default.default {
2638 color: var(--preferred) !important;
2639}
2640
2641.displayBlock {2633.displayBlock {
2642 display: block !important;2634 display: block !important;
2643}2635}
@@ -5477,3 +5469,12 @@ body:not(.movingUI) .drawer-content.maximized {
5477#AdvancedFormatting .autoSetHeight {5469#AdvancedFormatting .autoSetHeight {
5478 overflow-wrap: anywhere;5470 overflow-wrap: anywhere;
5479}5471}
5472
5473#InstructSequencesColumn summary {
5474 font-size: 0.95em;
5475 cursor: pointer;
5476}
5477
5478#InstructSequencesColumn details:not(:last-of-type) {
5479 margin-bottom: 5px;
5480}
server.js+1 -0
@@ -932,6 +932,7 @@ async function verifySecuritySettings() {
932userModule.initUserStorage(dataRoot)932userModule.initUserStorage(dataRoot)
933 .then(userModule.ensurePublicDirectoriesExist)933 .then(userModule.ensurePublicDirectoriesExist)
934 .then(userModule.migrateUserData)934 .then(userModule.migrateUserData)
935 .then(userModule.migrateSystemPrompts)
935 .then(verifySecuritySettings)936 .then(verifySecuritySettings)
936 .then(preSetupTasks)937 .then(preSetupTasks)
937 .finally(startServer);938 .finally(startServer);
src/constants.js+1 -0
@@ -41,6 +41,7 @@ const USER_DIRECTORY_TEMPLATE = Object.freeze({
41 files: 'user/files',41 files: 'user/files',
42 vectors: 'vectors',42 vectors: 'vectors',
43 backups: 'backups',43 backups: 'backups',
44 sysprompt: 'sysprompt',
44});45});
4546
46/**47/**
src/endpoints/content-manager.js+40 -1
@@ -43,6 +43,7 @@ const CONTENT_TYPES = {
43 CONTEXT: 'context',43 CONTEXT: 'context',
44 MOVING_UI: 'moving_ui',44 MOVING_UI: 'moving_ui',
45 QUICK_REPLIES: 'quick_replies',45 QUICK_REPLIES: 'quick_replies',
46 SYSPROMPT: 'sysprompt',
46};47};
4748
48/**49/**
@@ -56,7 +57,7 @@ function getDefaultPresets(directories) {
56 const presets = [];57 const presets = [];
5758
58 for (const contentItem of contentIndex) {59 for (const contentItem of contentIndex) {
59 if (contentItem.type.endsWith('_preset') || contentItem.type === 'instruct' || contentItem.type === 'context') {60 if (contentItem.type.endsWith('_preset') || contentItem.type === 'instruct' || contentItem.type === 'context' || contentItem.type === 'sysprompt') {
60 contentItem.name = path.parse(contentItem.filename).name;61 contentItem.name = path.parse(contentItem.filename).name;
61 contentItem.folder = getTargetByType(contentItem.type, directories);62 contentItem.folder = getTargetByType(contentItem.type, directories);
62 presets.push(contentItem);63 presets.push(contentItem);
@@ -218,6 +219,41 @@ function getContentIndex() {
218}219}
219220
220/**221/**
222 * Gets content by type and format.
223 * @param {string} type Type of content
224 * @param {'json'|'string'|'raw'} format Format of content
225 * @returns {string[]|Buffer[]} Array of content
226 */
227function getContentOfType(type, format) {
228 const contentIndex = getContentIndex();
229 const indexItems = contentIndex.filter((item) => item.type === type && item.folder);
230 const files = [];
231 for (const item of indexItems) {
232 if (!item.folder) {
233 continue;
234 }
235 try {
236 const filePath = path.join(item.folder, item.filename);
237 const fileContent = fs.readFileSync(filePath);
238 switch (format) {
239 case 'json':
240 files.push(JSON.parse(fileContent.toString()));
241 break;
242 case 'string':
243 files.push(fileContent.toString());
244 break;
245 case 'raw':
246 files.push(fileContent);
247 break;
248 }
249 } catch {
250 // Ignore errors
251 }
252 }
253 return files;
254}
255
256/**
221 * Gets the target directory for the specified asset type.257 * Gets the target directory for the specified asset type.
222 * @param {ContentType} type Asset type258 * @param {ContentType} type Asset type
223 * @param {import('../users').UserDirectoryList} directories User directories259 * @param {import('../users').UserDirectoryList} directories User directories
@@ -257,6 +293,8 @@ function getTargetByType(type, directories) {
257 return directories.movingUI;293 return directories.movingUI;
258 case CONTENT_TYPES.QUICK_REPLIES:294 case CONTENT_TYPES.QUICK_REPLIES:
259 return directories.quickreplies;295 return directories.quickreplies;
296 case CONTENT_TYPES.SYSPROMPT:
297 return directories.sysprompt;
260 default:298 default:
261 return null;299 return null;
262 }300 }
@@ -721,5 +759,6 @@ module.exports = {
721 checkForNewContent,759 checkForNewContent,
722 getDefaultPresets,760 getDefaultPresets,
723 getDefaultPresetFile,761 getDefaultPresetFile,
762 getContentOfType,
724 router,763 router,
725};764};
src/endpoints/presets.js+2 -0
@@ -27,6 +27,8 @@ function getPresetSettingsByAPI(apiId, directories) {
27 return { folder: directories.instruct, extension: '.json' };27 return { folder: directories.instruct, extension: '.json' };
28 case 'context':28 case 'context':
29 return { folder: directories.context, extension: '.json' };29 return { folder: directories.context, extension: '.json' };
30 case 'sysprompt':
31 return { folder: directories.sysprompt, extension: '.json' };
30 default:32 default:
31 return { folder: null, extension: null };33 return { folder: null, extension: null };
32 }34 }
src/endpoints/settings.js+2 -0
@@ -251,6 +251,7 @@ router.post('/get', jsonParser, (request, response) => {
251251
252 const instruct = readAndParseFromDirectory(request.user.directories.instruct);252 const instruct = readAndParseFromDirectory(request.user.directories.instruct);
253 const context = readAndParseFromDirectory(request.user.directories.context);253 const context = readAndParseFromDirectory(request.user.directories.context);
254 const sysprompt = readAndParseFromDirectory(request.user.directories.sysprompt);
254255
255 response.send({256 response.send({
256 settings,257 settings,
@@ -268,6 +269,7 @@ router.post('/get', jsonParser, (request, response) => {
268 quickReplyPresets,269 quickReplyPresets,
269 instruct,270 instruct,
270 context,271 context,
272 sysprompt,
271 enable_extensions: ENABLE_EXTENSIONS,273 enable_extensions: ENABLE_EXTENSIONS,
272 enable_extensions_auto_update: ENABLE_EXTENSIONS_AUTO_UPDATE,274 enable_extensions_auto_update: ENABLE_EXTENSIONS_AUTO_UPDATE,
273 enable_accounts: ENABLE_ACCOUNTS,275 enable_accounts: ENABLE_ACCOUNTS,
src/users.js+58 -0
@@ -9,6 +9,8 @@ const storage = require('node-persist');
9const express = require('express');9const express = require('express');
10const mime = require('mime-types');10const mime = require('mime-types');
11const archiver = require('archiver');11const archiver = require('archiver');
12const writeFileAtomicSync = require('write-file-atomic').sync;
13const _ = require('lodash');
1214
13const { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE } = require('./constants');15const { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE } = require('./constants');
14const { getConfigValue, color, delay, setConfigValue, generateTimestamp } = require('./util');16const { getConfigValue, color, delay, setConfigValue, generateTimestamp } = require('./util');
@@ -83,6 +85,7 @@ const STORAGE_KEYS = {
83 * @property {string} files - The directory where the uploaded files are stored85 * @property {string} files - The directory where the uploaded files are stored
84 * @property {string} vectors - The directory where the vectors are stored86 * @property {string} vectors - The directory where the vectors are stored
85 * @property {string} backups - The directory where the backups are stored87 * @property {string} backups - The directory where the backups are stored
88 * @property {string} sysprompt - The directory where the system prompt data is stored
86 */89 */
8790
88/**91/**
@@ -323,6 +326,60 @@ async function migrateUserData() {
323 console.log(color.green('Migration completed!'));326 console.log(color.green('Migration completed!'));
324}327}
325328
329async function migrateSystemPrompts() {
330 /**
331 * Gets the default system prompts.
332 * @returns {Promise<any[]>} - The list of default system prompts
333 */
334 async function getDefaultSystemPrompts() {
335 try {
336 const { getContentOfType } = await import('./endpoints/content-manager.js');
337 return getContentOfType('sysprompt', 'json');
338 } catch {
339 return [];
340 }
341 }
342
343 const directories = await getUserDirectoriesList();
344 for (const directory of directories) {
345 try {
346 const migrateMarker = path.join(directory.sysprompt, '.migrated');
347 if (fs.existsSync(migrateMarker)) {
348 continue;
349 }
350 const defaultPrompts = await getDefaultSystemPrompts();
351 const instucts = fs.readdirSync(directory.instruct);
352 let migratedPrompts = [];
353 for (const instruct of instucts) {
354 const instructPath = path.join(directory.instruct, instruct);
355 const sysPromptPath = path.join(directory.sysprompt, instruct);
356 if (path.extname(instruct) === '.json' && !fs.existsSync(sysPromptPath)) {
357 const instructData = JSON.parse(fs.readFileSync(instructPath, 'utf8'));
358 if ('system_prompt' in instructData && 'name' in instructData) {
359 const syspromptData = { name: instructData.name, content: instructData.system_prompt };
360 migratedPrompts.push(syspromptData);
361 delete instructData.system_prompt;
362 writeFileAtomicSync(instructPath, JSON.stringify(instructData, null, 4));
363 }
364 }
365 }
366 // Only leave unique contents
367 migratedPrompts = _.uniqBy(migratedPrompts, 'content');
368 // Only leave contents that are not in the default prompts
369 migratedPrompts = migratedPrompts.filter(x => !defaultPrompts.some(y => y.content === x.content));
370 for (const sysPromptData of migratedPrompts) {
371 sysPromptData.name = `[Migrated] ${sysPromptData.name}`;
372 const syspromptPath = path.join(directory.sysprompt, `${sysPromptData.name}.json`);
373 writeFileAtomicSync(syspromptPath, JSON.stringify(sysPromptData, null, 4));
374 console.log(`Migrated system prompt ${sysPromptData.name} for ${directory.root.split(path.sep).pop()}`);
375 }
376 writeFileAtomicSync(migrateMarker, '');
377 } catch {
378 // Ignore errors
379 }
380 }
381}
382
326/**383/**
327 * Converts a user handle to a storage key.384 * Converts a user handle to a storage key.
328 * @param {string} handle User handle385 * @param {string} handle User handle
@@ -724,6 +781,7 @@ module.exports = {
724 requireLoginMiddleware,781 requireLoginMiddleware,
725 requireAdminMiddleware,782 requireAdminMiddleware,
726 migrateUserData,783 migrateUserData,
784 migrateSystemPrompts,
727 getPasswordSalt,785 getPasswordSalt,
728 getPasswordHash,786 getPasswordHash,
729 getCsrfSecret,787 getCsrfSecret,